Quick answer: buffer_copy is memcpy-style: undefined on overlap. Either copy via a temp buffer, or implement memmove-safe direction (forward when dst < src, backward when dst > src).
A networking module rotates a ring buffer by copying tail bytes to front of same buffer. Output is garbage after the first byte. Source and destination overlap; buffer_copy doesn’t handle it.
Temp Buffer Pattern
function safe_shift(buf, src_offset, size, dst_offset) {
var tmp = buffer_create(size, buffer_fixed, 1);
buffer_copy(buf, src_offset, size, tmp, 0);
buffer_copy(tmp, 0, size, buf, dst_offset);
buffer_delete(tmp);
}
Two copies, but never overlapping; safe.
Memmove-Style Direction
If dst < src (shift earlier), iterate forward; if dst > src (shift later), iterate backward:
function buffer_move(buf, src, size, dst) {
if (dst < src) {
// forward
for (var i = 0; i < size; i++) {
buffer_poke(buf, dst + i, buffer_u8, buffer_peek(buf, src + i, buffer_u8));
}
} else if (dst > src) {
// backward
for (var i = size - 1; i >= 0; i--) {
buffer_poke(buf, dst + i, buffer_u8, buffer_peek(buf, src + i, buffer_u8));
}
}
}
Mirrors C’s memmove. Slower than buffer_copy (byte-by-byte) but safe.
For Large Shifts
Per-byte poke is slow. For large overlap shifts (KB+), the temp buffer approach is faster despite the double-copy.
Ring Buffer Without Shift
Ring buffer typically doesn’t shift — it uses a head index. Read from (head + i) % capacity. No copy needed; just advance the head.
If you find yourself copying within a ring buffer, you may have a design issue. Reconsider the index-based approach.
Verifying
Shift bytes within a buffer; print before/after. Bytes match expected positions. No garbage in overlap region.
“Memory overlap is silent corruption. Either use a temp buffer or write a memmove helper.”
For network buffers, index-based ring buffers eliminate the need for shifts entirely — consider refactoring rather than band-aiding.