As the algorithm being requested is somewhat special, I'm trying to explain what I want first, then I'll ask concrete questions that I'm quite unsure about:
Introduction
The algorithm is not C-specific, but I'll start with a C-example, namely memmove()
:
Memmove can copy a region of memory to another, handling overlaps correctly.
Basically, it means that if the source-destination address is lower than the source address, the first item will be copied and the index increases; otherwise the last item is copied, and the index decreases.
So the ordering of the memory regions is important.
Problem
I want to move regions in an array-based ring buffer, that is a region may wrap at the end of the array, making it a lot harder to decide which region has a lower address. To make things even more complicated the actual modulo value being used for source and destination may be different. Let's go concrete, and assume this C prototype (just for clarity):
mod_move(item_t items[], unsigned s_i, unsigned d_i, unsigned s_mod, unsigned d_mod, unsigned len);
items[]
holds the data items.
s_i
is the source index.
d_i
is the destination index.
s_mod
is the modulo value for s_i
.
d_mod
is the modulo value for d_i
.
len
is the length of the region to copy.
Illustration
Illustrating the blocks (free
, f1
, f2
, f3
, and f4
are free blocks; source
, s1
, and s2
are source blocks; dest
, d1, and
d2are destinations blocks;
..` is indicating some random width):
A block in a ring buffer can be either (A) one contiguous range,
or (B) two contiguous ranges wrapped at the end:
start start+len (start+len)%size start
v v v v
+----+---------+----+ +----+-----------+----+
| f1 | used | f2 | | u2 | free | u1 |
+----+---------+----+ +----+-----------+----+
So for source and destination, there are basically four cases:
AA: Use memmove() algorithm
+-..-+--------+-..-+
| f1 | source | f2 |
+-..-+--------+-..-+
+-..-+--------+-..-+
| f3 | dest | f4 |
+-..-+--------+-..-+
AB: ?
+-..-+--------+-..-+
| f1 | source | f2 |
+-..-+--------+-..-+
+-..-+--....--+-..-+
| d2 | free | d1 |
+-..-+--....--+-..-+
BA: ?
+-..-+--....--+-..-+
| s2 | free | s1 |
+-..-+--....--+-..-+
+-..-+--------+-..-+
| f1 | dest | f2 |
+-..-+--------+-..-+
BB: ?
+-..-+--....--+-..-+
| s2 | free1 | s1 |
+-..-+--....--+-..-+
+-..-+--....--+-..-+
| d2 | free2 | d1 |
+-..-+--....--+-..-+
Questions
How could a function
int overlaps(unsigned s_i, unsigned d_i, unsigned s_mod, unsigned d_mod, unsigned len)
be written that returns!= 0
if the source region (described bys_i
,s_mod
, andlen
) overlaps the destination region (described byd_i
,d_mod
, andlen
)? The idea is to optimizemod_move()
for non-overlapping regions, possibly usingmemcpy()
(or equivalent). My first guess is likes_i + len > d_i || d_i + len > s_i
, but I feel it's incomplete.When deciding whether to use an incrementing or a decrementing index, I need to find out whether the source region or the destination region has the lower address; does that make any sense in the case of modulo?
If there's more than one item to copy it is intended to use either
memmove()
ormemcpy()
, and I wonder how many non-contiguous blocks could exist: Four at most?Assuming that
len
is at most the minimum ofs_mod
andd_mod
, is it guaranteed that an algorithm exists that does not lose any items?The primitive algorithm I would like to replace would be like this:
if (index_increases) {
while (len-- > 0) {
items[d_i = (d_i + 1) % d_mod] = items[s_i = (s_i + 1) % s_mod];
}
} else {
s_i = (s_i + len - 1) % s_mod;
d_i = (d_i + len - 1) % d_mod;
while (len-- > 0) {
items[d_i = (d_i + d_mod - 1) % d_mod] = items[s_i = (s_i + s_mod - 1) % s_mod];
}
}
- Is there any special case to consider that I forgot to mention?
Illustration of the Problem (Failed Example)
Consider this array with numbers ([23]133
means "the item at index 23 is 133"):
[0]110|[1]111|[2]112|[3]113|[4]114|[5]115|[6]116|[7]117|[8]118|[9]119|[10]120|[11]121|[12]122|[13]123|[14]124|[15]125|[16]126|[17]127|[18]128|[19]129|[20]130|[21]131|[22]132|[23]133|[24]134|[25]135|[26]136|[27]137|[28]1|[29]1|[30]1|[31]1|[32]1|[33]1|[34]1|[35]1|[36]1|[37]1|[38]101|[39]102|[40]103|[41]104|[42]105|[43]106|[44]107|[45]108|[46]109
And the operation desired is mod_move(s_i=44, s_mod=47, d_i=0, d_mod=44, len=31)
, meaning "move 31 items starting from position 44 (modulo 47) to position 0 (modulo 44)".
Here is an attempt that failed (DDD: [d] = [s] (n)
meaning "debug message: array element at s
containing n
was copied to element d
"):
DDD: [0] = [44] (107)
DDD: [1] = [45] (108)
DDD: [2] = [46] (109)
DDD: [3] = [0] (107)
DDD: [4] = [1] (108)
DDD: [5] = [2] (109)
DDD: [6] = [3] (107)
DDD: [7] = [4] (108)
DDD: [8] = [5] (109)
DDD: [9] = [6] (107)
DDD: [10] = [7] (108)
DDD: [11] = [8] (109)
DDD: [12] = [9] (107)
DDD: [13] = [10] (108)
DDD: [14] = [11] (109)
DDD: [15] = [12] (107)
DDD: [16] = [13] (108)
DDD: [17] = [14] (109)
DDD: [18] = [15] (107)
DDD: [19] = [16] (108)
DDD: [20] = [17] (109)
DDD: [21] = [18] (107)
DDD: [22] = [19] (108)
DDD: [23] = [20] (109)
DDD: [24] = [21] (107)
DDD: [25] = [22] (108)
DDD: [26] = [23] (109)
DDD: [27] = [24] (107)
DDD: [28] = [25] (108)
DDD: [29] = [26] (109)
DDD: [30] = [27] (107)
Another Example
Source size is 35, source position is 27. Destination size is 27, destination position is 0. Length is 27.
Original values were:
[0]109|[1]110|[2]111|[3]112|[4]113|[5]114|[6]115|[7]116|[8]117|[9]118|[10]119|[11]120|[12]121|[13]122|[14]123|[15]124|[16]125|[17]126|[18]127|[19]128|[20]129|[21]130|[22]131|[23]132|[24]133|[25]134|[26]135|[27]101|[28]102|[29]103|[30]104|[31]105|[32]106|[33]107|[34]108
Incrementing the indices would lead to:
DDD: [0] = [27] (101)
DDD: [1] = [28] (102)
DDD: [2] = [29] (103)
DDD: [3] = [30] (104)
DDD: [4] = [31] (105)
DDD: [5] = [32] (106)
DDD: [6] = [33] (107)
DDD: [7] = [34] (108)
DDD: [8] = [0] (101)
DDD: [9] = [1] (102)
...
[8]
should have gotten value 109
, originally stored in [0]
.
So you get the idea.