Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
189555f3
Commit
189555f3
authored
Sep 20, 2010
by
Sergey Petrunya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DS-MRR improvements: more code cleanup
- better comments - rename variables to better reflect their meaning
parent
18a34850
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
157 additions
and
115 deletions
+157
-115
sql/multi_range_read.cc
sql/multi_range_read.cc
+36
-39
sql/multi_range_read.h
sql/multi_range_read.h
+121
-76
No files found.
sql/multi_range_read.cc
View file @
189555f3
...
@@ -400,17 +400,6 @@ void SimpleBuffer::reset_for_writing()
...
@@ -400,17 +400,6 @@ void SimpleBuffer::reset_for_writing()
write_pos
=
read_pos
=
end
;
write_pos
=
read_pos
=
end
;
}
}
void
SimpleBuffer
::
reset_for_reading
()
{
/*
Do we need this at all?
if (direction == 1)
pos= start;
else
pos= end;
//end?
*/
}
uchar
*
SimpleBuffer
::
end_of_space
()
uchar
*
SimpleBuffer
::
end_of_space
()
{
{
...
@@ -478,15 +467,20 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
...
@@ -478,15 +467,20 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
use_key_pointers
=
test
(
mode
&
HA_MRR_MATERIALIZED_KEYS
);
use_key_pointers
=
test
(
mode
&
HA_MRR_MATERIALIZED_KEYS
);
}
}
do_r
owid_fetch
=
FALSE
;
do_r
ndpos_scan
=
FALSE
;
doing_cpk_scan
=
check_cpk_scan
(
thd
,
h
->
inited
==
handler
::
INDEX
?
bool
doing_cpk_scan
=
check_cpk_scan
(
thd
,
h
->
inited
==
handler
::
INDEX
?
h
->
active_index
:
h2
->
active_index
,
mode
);
h
->
active_index
:
h2
->
active_index
,
mode
);
if
(
!
doing_cpk_scan
/* && !index_only_read */
)
if
(
!
doing_cpk_scan
/* && !index_only_read */
)
{
{
/* Will use rowid buffer to store/sort rowids, etc */
/* Will use rowid buffer to store/sort rowids, etc */
do_r
owid_fetch
=
TRUE
;
do_r
ndpos_scan
=
TRUE
;
}
}
DBUG_ASSERT
(
do_sort_keys
||
do_rowid_fetch
);
/*
We should either sort keys, or do ordered rnd_pos scan, or both. If we
decide to do neither, we should have used default MRR implementation.
*/
DBUG_ASSERT
(
do_sort_keys
||
do_rndpos_scan
);
if
(
is_mrr_assoc
)
if
(
is_mrr_assoc
)
...
@@ -509,11 +503,11 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
...
@@ -509,11 +503,11 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
keyno
=
(
h
->
inited
==
handler
::
INDEX
)
?
h
->
active_index
:
h2
->
active_index
;
keyno
=
(
h
->
inited
==
handler
::
INDEX
)
?
h
->
active_index
:
h2
->
active_index
;
dsmrr_fill_key_buffer
();
dsmrr_fill_key_buffer
();
if
(
dsmrr_eof
&&
!
do_r
owid_fetch
)
if
(
dsmrr_eof
&&
!
do_r
ndpos_scan
)
buf
->
end_of_used_area
=
key_buffer
.
end_of_space
();
buf
->
end_of_used_area
=
key_buffer
.
end_of_space
();
}
}
if
(
!
do_r
owid_fetch
)
if
(
!
do_r
ndpos_scan
)
{
{
/*
/*
We have the keys and won't need to fetch rowids, as key lookup will be
We have the keys and won't need to fetch rowids, as key lookup will be
...
@@ -523,11 +517,6 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
...
@@ -523,11 +517,6 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
}
}
rowid_buff_elem_size
=
h
->
ref_length
+
(
is_mrr_assoc
?
sizeof
(
char
*
)
:
0
);
rowid_buff_elem_size
=
h
->
ref_length
+
(
is_mrr_assoc
?
sizeof
(
char
*
)
:
0
);
/*
psergey2: this is only needed when
- doing a rowid-to-row scan
- the buffer wasn't exhausted on the first pass.
*/
/*
/*
There can be two cases:
There can be two cases:
- This is the first call since index_init(), h2==NULL
- This is the first call since index_init(), h2==NULL
...
@@ -821,7 +810,7 @@ void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
...
@@ -821,7 +810,7 @@ void DsMrr_impl::setup_buffer_sizes(key_range *sample_key)
index_ranges_unique
=
test
(
key_info
->
flags
&
HA_NOSAME
&&
index_ranges_unique
=
test
(
key_info
->
flags
&
HA_NOSAME
&&
key_info
->
key_parts
==
key_info
->
key_parts
==
my_count_bits
(
sample_key
->
keypart_map
));
my_count_bits
(
sample_key
->
keypart_map
));
if
(
!
do_r
owid_fetch
)
if
(
!
do_r
ndpos_scan
)
{
{
/* Give all space to key buffer. */
/* Give all space to key buffer. */
key_buffer
.
set_buffer_space
(
full_buf
,
full_buf_end
,
SimpleBuffer
::
FORWARD
);
key_buffer
.
set_buffer_space
(
full_buf
,
full_buf_end
,
SimpleBuffer
::
FORWARD
);
...
@@ -908,7 +897,7 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
...
@@ -908,7 +897,7 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
uchar
*
key_ptr
;
uchar
*
key_ptr
;
if
(
know_key_tuple_params
)
if
(
know_key_tuple_params
)
{
{
if
(
do_r
owid_fetch
&&
rowid_buffer
.
is_empty
())
if
(
do_r
ndpos_scan
&&
rowid_buffer
.
is_empty
())
{
{
/*
/*
We're using two buffers and both of them are empty now. Restore the
We're using two buffers and both of them are empty now. Restore the
...
@@ -963,6 +952,18 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
...
@@ -963,6 +952,18 @@ void DsMrr_impl::dsmrr_fill_key_buffer()
}
}
/*
Take unused space from key buffer and give it to rowid buffer.
*/
void
DsMrr_impl
::
reallocate_buffer_space
()
{
uchar
*
unused_start
,
*
unused_end
;
key_buffer
.
remove_unused_space
(
&
unused_start
,
&
unused_end
);
rowid_buffer
.
grow
(
unused_start
,
unused_end
);
}
/*
/*
DS-MRR/CPK: multi_range_read_next() function
DS-MRR/CPK: multi_range_read_next() function
...
@@ -993,7 +994,7 @@ int DsMrr_impl::dsmrr_next_from_index(char **range_info_arg)
...
@@ -993,7 +994,7 @@ int DsMrr_impl::dsmrr_next_from_index(char **range_info_arg)
{
{
int
res
;
int
res
;
uchar
*
key_in_buf
;
uchar
*
key_in_buf
;
handler
*
file
=
do_r
owid_fetch
?
h2
:
h
;
handler
*
file
=
do_r
ndpos_scan
?
h2
:
h
;
bool
res2
;
bool
res2
;
while
(
in_identical_keys_range
)
while
(
in_identical_keys_range
)
...
@@ -1068,7 +1069,7 @@ check_record:
...
@@ -1068,7 +1069,7 @@ check_record:
When rowid fetching is used, it controls all buffer refills. When we're
When rowid fetching is used, it controls all buffer refills. When we're
on our own, try refilling our buffer.
on our own, try refilling our buffer.
*/
*/
if
(
!
do_r
owid_fetch
)
if
(
!
do_r
ndpos_scan
)
dsmrr_fill_key_buffer
();
dsmrr_fill_key_buffer
();
if
(
key_buffer
.
is_empty
())
if
(
key_buffer
.
is_empty
())
...
@@ -1078,17 +1079,13 @@ check_record:
...
@@ -1078,17 +1079,13 @@ check_record:
}
}
}
}
if
(
do_rowid_fetch
)
{
/*
/*
At this point we're not using anything what we've read from key
At this point we're not using anything what we've read from key
buffer. Cut off unused key buffer space and give it to the rowid
buffer. Cut off unused key buffer space and give it to the rowid
buffer.
buffer.
*/
*/
uchar
*
unused_start
,
*
unused_end
;
if
(
do_rndpos_scan
)
key_buffer
.
remove_unused_space
(
&
unused_start
,
&
unused_end
);
reallocate_buffer_space
();
rowid_buffer
.
grow
(
unused_start
,
unused_end
);
}
/* Get the next range to scan */
/* Get the next range to scan */
key_buffer
.
read
();
// reads to (cur_index_tuple, cur_range_info)
key_buffer
.
read
();
// reads to (cur_index_tuple, cur_range_info)
...
@@ -1147,7 +1144,7 @@ int DsMrr_impl::dsmrr_next(char **range_info)
...
@@ -1147,7 +1144,7 @@ int DsMrr_impl::dsmrr_next(char **range_info)
if
(
use_default_impl
)
if
(
use_default_impl
)
return
h
->
handler
::
multi_range_read_next
(
range_info
);
return
h
->
handler
::
multi_range_read_next
(
range_info
);
if
(
!
do_r
owid_fetch
)
if
(
!
do_r
ndpos_scan
)
return
dsmrr_next_from_index
(
range_info
);
return
dsmrr_next_from_index
(
range_info
);
while
(
last_identical_rowid
)
while
(
last_identical_rowid
)
...
@@ -1421,7 +1418,7 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
...
@@ -1421,7 +1418,7 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
bool
res
;
bool
res
;
THD
*
thd
=
current_thd
;
THD
*
thd
=
current_thd
;
doing_cpk_scan
=
check_cpk_scan
(
thd
,
keyno
,
*
flags
);
bool
doing_cpk_scan
=
check_cpk_scan
(
thd
,
keyno
,
*
flags
);
bool
using_cpk
=
test
(
keyno
==
table
->
s
->
primary_key
&&
bool
using_cpk
=
test
(
keyno
==
table
->
s
->
primary_key
&&
h
->
primary_key_is_clustered
());
h
->
primary_key_is_clustered
());
if
(
thd
->
variables
.
optimizer_use_mrr
==
2
||
*
flags
&
HA_MRR_INDEX_ONLY
||
if
(
thd
->
variables
.
optimizer_use_mrr
==
2
||
*
flags
&
HA_MRR_INDEX_ONLY
||
...
...
sql/multi_range_read.h
View file @
189555f3
...
@@ -74,6 +74,8 @@
...
@@ -74,6 +74,8 @@
| |
| |
usused space user data
usused space user data
For reverse buffer, start/end have the same meaning, but reading and
writing is done from end to start.
*/
*/
class
SimpleBuffer
class
SimpleBuffer
...
@@ -134,7 +136,6 @@ public:
...
@@ -134,7 +136,6 @@ public:
/* Read-mode functions */
/* Read-mode functions */
bool
is_empty
()
{
return
used_size
()
==
0
;
}
bool
is_empty
()
{
return
used_size
()
==
0
;
}
void
reset_for_reading
();
void
setup_reading
(
uchar
**
data1
,
size_t
len1
,
void
setup_reading
(
uchar
**
data1
,
size_t
len1
,
uchar
**
data2
,
size_t
len2
);
uchar
**
data2
,
size_t
len2
);
bool
read
();
bool
read
();
...
@@ -209,23 +210,31 @@ public:
...
@@ -209,23 +210,31 @@ public:
*/
*/
class
PeekIterator
class
PeekIterator
{
{
SimpleBuffer
*
buf
;
/* The buffer we're iterating over*/
/*
/*
if sb->direction==1 : pointer to what to return next
if buf->direction==FORWARD : pointer to what to return next
if sb->direction==-1: pointer to the end of what is to be returned next
if buf->direction==BACKWARD : pointer to the end of what is to be
returned next
*/
*/
uchar
*
pos
;
uchar
*
pos
;
SimpleBuffer
*
sb
;
public:
public:
void
init
(
SimpleBuffer
*
sb_arg
)
/*
Initialize the iterator. After intiialization, the first read_next() call
will read what buf_arg->read() would read.
*/
void
init
(
SimpleBuffer
*
buf_arg
)
{
{
sb
=
sb
_arg
;
buf
=
buf
_arg
;
pos
=
sb
->
read_pos
;
pos
=
buf
->
read_pos
;
}
}
/*
/*
If the buffer stores tuples, this call will return pointer to the first
Read the next value. The calling convention is the same as buf->read()
component.
has.
RETURN
FALSE - Ok
TRUE - EOF, reached the end of the buffer
*/
*/
bool
read_next
()
bool
read_next
()
{
{
...
@@ -234,11 +243,11 @@ public:
...
@@ -234,11 +243,11 @@ public:
have written the second component first).
have written the second component first).
*/
*/
uchar
*
res
;
uchar
*
res
;
if
((
res
=
get_next
(
sb
->
read_size1
)))
if
((
res
=
get_next
(
buf
->
read_size1
)))
{
{
*
(
sb
->
read_ptr1
)
=
res
;
*
(
buf
->
read_ptr1
)
=
res
;
if
(
sb
->
read_ptr2
)
if
(
buf
->
read_ptr2
)
*
sb
->
read_ptr2
=
get_next
(
sb
->
read_size2
);
*
buf
->
read_ptr2
=
get_next
(
buf
->
read_size2
);
return
FALSE
;
return
FALSE
;
}
}
return
TRUE
;
/* EOF */
return
TRUE
;
/* EOF */
...
@@ -247,9 +256,9 @@ public:
...
@@ -247,9 +256,9 @@ public:
/* Return pointer to next chunk of nbytes bytes and avance over it */
/* Return pointer to next chunk of nbytes bytes and avance over it */
uchar
*
get_next
(
size_t
nbytes
)
uchar
*
get_next
(
size_t
nbytes
)
{
{
if
(
sb
->
direction
==
1
)
if
(
buf
->
direction
==
1
)
{
{
if
(
pos
+
nbytes
>
sb
->
write_pos
)
if
(
pos
+
nbytes
>
buf
->
write_pos
)
return
NULL
;
return
NULL
;
uchar
*
res
=
pos
;
uchar
*
res
=
pos
;
pos
+=
nbytes
;
pos
+=
nbytes
;
...
@@ -257,7 +266,7 @@ public:
...
@@ -257,7 +266,7 @@ public:
}
}
else
else
{
{
if
(
pos
-
nbytes
<
sb
->
write_pos
)
if
(
pos
-
nbytes
<
buf
->
write_pos
)
return
NULL
;
return
NULL
;
pos
-=
nbytes
;
pos
-=
nbytes
;
return
pos
;
return
pos
;
...
@@ -288,6 +297,8 @@ private:
...
@@ -288,6 +297,8 @@ private:
S2. Sort Keys
S2. Sort Keys
S3. Sort Rowids
S3. Sort Rowids
psergey-TODO.
S1 is used for cases which DS-MRR is unable to handle for some reason.
S1 is used for cases which DS-MRR is unable to handle for some reason.
S2 is the actual DS-MRR. The basic algorithm is as follows:
S2 is the actual DS-MRR. The basic algorithm is as follows:
...
@@ -339,75 +350,78 @@ public:
...
@@ -339,75 +350,78 @@ public:
uint
*
flags
,
COST_VECT
*
cost
);
uint
*
flags
,
COST_VECT
*
cost
);
private:
private:
/*
/*
The "owner" handler object (the one that
calls dsmrr_XXX functions.
The "owner" handler object (the one that
is expected to "own" this object
It is used to retrieve full table rows by calling rnd_pos(
).
and call its functions
).
*/
*/
handler
*
h
;
handler
*
h
;
TABLE
*
table
;
/* Always equal to h->table */
TABLE
*
table
;
/* Always equal to h->table */
/*
/*
Secondary handler object
, if needed (we need it when we need to both scan
Secondary handler object
. (created when needed, we need it when we need
t
he index and return rows).
t
o run both index scan and rnd_pos() at the same time)
*/
*/
handler
*
h2
;
handler
*
h2
;
/* Full buffer that we're using (the buffer is obtained from SQL layer) */
/** Properties of current MRR scan **/
uint
keyno
;
/* index we're running the scan on */
bool
use_default_impl
;
/* TRUE <=> shortcut all calls to default MRR impl */
/* TRUE <=> need range association, buffers hold {rowid, range_id} pairs */
bool
is_mrr_assoc
;
/* TRUE <=> sort the keys before making index lookups */
bool
do_sort_keys
;
/* TRUE <=> sort rowids and use rnd_pos() to get and return full records */
bool
do_rndpos_scan
;
/*
(if do_sort_keys==TRUE) don't copy key values, use pointers to them
instead.
*/
bool
use_key_pointers
;
/* The whole buffer space that we're using */
uchar
*
full_buf
;
uchar
*
full_buf
;
uchar
*
full_buf_end
;
uchar
*
full_buf_end
;
/* Valid when using both rowid and key buffer: the original bound between them */
/*
When using both rowid and key buffers: the bound between key and rowid
parts of the buffer. This is the "original" value, actual memory ranges
used by key and rowid parts may be different because of dynamic space
reallocation between them.
*/
uchar
*
rowid_buffer_end
;
uchar
*
rowid_buffer_end
;
/* Buffer to store rowids, or (rowid, range_id) pairs */
SimpleBuffer
rowid_buffer
;
/* Reads from rowid buffer go to here: */
/** Index scaning and key buffer-related members **/
uchar
*
rowid
;
uchar
*
rowids_range_id
;
/*
/* TRUE <=> We can get at most one index tuple for a lookup key */
not-NULL: we're traversing a group of (rowid, range_id) pairs with
bool
index_ranges_unique
;
identical rowid values, and this is the pointer to the last one.
NULL: we're not in the group of indentical rowids.
*/
uchar
*
last_identical_rowid
;
/* Identical keys */
/* TRUE<=> we're in a middle of enumerating records for a key range */
bool
in_identical_keys_range
;
bool
in_index_range
;
uchar
*
last_identical_key_ptr
;
SimpleBuffer
::
PeekIterator
identical_key_it
;
/* Buffer to store (key, range_id) pairs */
SimpleBuffer
key_buffer
;
SimpleBuffer
key_buffer
;
uint
keyno
;
/* key_buffer.read() reads */
uchar
*
cur_index_tuple
;
/* Execution control */
bool
do_sort_keys
;
bool
use_key_pointers
;
bool
do_rowid_fetch
;
bool
dsmrr_eof
;
/* TRUE <=> We have reached EOF when reading index tuples */
/* if in_index_range==TRUE: range_id of the range we're enumerating */
char
*
cur_range_info
;
/*
/*
TRUE <=>
key buffer is exhausted (we need this because we may have a situation
TRUE <=>
we've got index tuples/rowids for all keys (need this flag because
w
here we've read everything from the key buffer but haven't finished with
w
e may have a situation where we've read everything from the key buffer but
scanning the last range
)
haven't finished with getting index tuples for the last key
)
*/
*/
bool
key_eof
;
bool
key_eof
;
/* TRUE <=> need range association, buffer holds {rowid, range_id} pairs */
bool
is_mrr_assoc
;
bool
use_default_impl
;
/* TRUE <=> shortcut all calls to default MRR impl */
bool
doing_cpk_scan
;
/* TRUE <=> DS-MRR/CPK variant is used */
/* Initially FALSE, becomes TRUE when we've set key_tuple_xxx members */
/* Initially FALSE, becomes TRUE when we've set key_tuple_xxx members */
bool
know_key_tuple_params
;
bool
know_key_tuple_params
;
/* Length of lookup tuple being used
, in bytes */
uint
key_tuple_length
;
/* Length of index lookup tuple
, in bytes */
uint
key_tuple_length
;
key_part_map
key_tuple_map
;
/* keyparts used in index lookup tuples */
key_part_map
key_tuple_map
;
/*
/*
This is
This is
= key_tuple_length if we copy keys to buffer
= key_tuple_length if we copy keys to buffer
...
@@ -418,22 +432,51 @@ private:
...
@@ -418,22 +432,51 @@ private:
/* = key_size_in_keybuf [ + sizeof(range_assoc_info) ] */
/* = key_size_in_keybuf [ + sizeof(range_assoc_info) ] */
uint
key_buff_elem_size
;
uint
key_buff_elem_size
;
/* = h->ref_length [ + sizeof(range_assoc_info) ] */
/*
uint
rowid_buff_elem_size
;
TRUE <=> we're doing key-ordered index scan and right now several
subsequent key values are the same as the one we've already retrieved and
returned index tuple for.
*/
bool
in_identical_keys_range
;
/* range_id of the first of the identical keys */
char
*
first_identical_range_info
;
/* Pointer to the last of the identical key values */
uchar
*
last_identical_key_ptr
;
/*
/*
TRUE <=> We're scanning on a full primary key (and not on prefix), and so
key_buffer iterator for walking the identical key range (we need to
can get max. one match for each key
enumerate the set of (identical_key, range_id) pairs multiple times,
and do that by walking from current buffer read position until we get
last_identical_key_ptr.
*/
*/
bool
index_ranges_unique
;
SimpleBuffer
::
PeekIterator
identical_key_it
;
/* TRUE<=> we're in a middle of enumerating records from a range */
bool
in_index_range
;
uchar
*
cur_index_tuple
;
/* if in_index_range==TRUE: range_id of the range we're enumerating */
char
*
cur_range_info
;
char
*
first_identical_range_info
;
/** rnd_pos() scan and rowid buffer-related members **/
/*
Buffer to store (rowid, range_id) pairs, or just rowids if
is_mrr_assoc==FALSE
*/
SimpleBuffer
rowid_buffer
;
/* rowid_buffer.read() will set the following: */
uchar
*
rowid
;
uchar
*
rowids_range_id
;
/*
not-NULL: we're traversing a group of (rowid, range_id) pairs with
identical rowid values, and this is the pointer to the last one.
NULL: we're not in the group of indentical rowids.
*/
uchar
*
last_identical_rowid
;
bool
dsmrr_eof
;
/* TRUE <=> We have reached EOF when reading index tuples */
/* = h->ref_length [ + sizeof(range_assoc_info) ] */
uint
rowid_buff_elem_size
;
bool
choose_mrr_impl
(
uint
keyno
,
ha_rows
rows
,
uint
*
flags
,
uint
*
bufsz
,
bool
choose_mrr_impl
(
uint
keyno
,
ha_rows
rows
,
uint
*
flags
,
uint
*
bufsz
,
COST_VECT
*
cost
);
COST_VECT
*
cost
);
...
@@ -446,8 +489,10 @@ private:
...
@@ -446,8 +489,10 @@ private:
int
dsmrr_next_from_index
(
char
**
range_info
);
int
dsmrr_next_from_index
(
char
**
range_info
);
void
setup_buffer_sizes
(
key_range
*
sample_key
);
void
setup_buffer_sizes
(
key_range
*
sample_key
);
void
reallocate_buffer_space
();
static
range_seq_t
key_buf_seq_init
(
void
*
init_param
,
uint
n_ranges
,
uint
flags
);
static
range_seq_t
key_buf_seq_init
(
void
*
init_param
,
uint
n_ranges
,
uint
flags
);
static
uint
key_buf_seq_next
(
range_seq_t
rseq
,
KEY_MULTI_RANGE
*
range
);
static
uint
key_buf_seq_next
(
range_seq_t
rseq
,
KEY_MULTI_RANGE
*
range
);
};
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment