void memmove_wrap(char *dst, const char *src, size_t n, const char *start, const char *end) { char *srcend = src+n; if (srcend >= end) srcend -= end-start; if ((dst < src && (src < srcend || srcend <= dst)) || (src+n <= dst)) { /* dst isn't in [src; src+n[ * src srcend * start| | end * v v v v * |-----ssssssss---| * |---dddddddd-----| * ^ * dst * or * |ss-----sssssssss| * |d-----dddddddddd| * or * |--ssssssssss----| * |ddddddd------ddd| */ #if 0 /* slow */ while (n--) { *dst++ = *src++; if (dst == end) dst = start; if (src == end) src = start; } #else char *dstend = dst+n; if (dstend >= end) dstend -= end-start; bool src_wraps = src > srcend; bool dst_wraps = dst > dstend; if (dst < src) { if (!src_wraps) { /* src doesn't wrap, dst < src so dst doesn't wrap */ memmove(dst, src, n); } else /* srcend <= dst */ { /* src does wrap */ size_t first_move = end - src; memmove(dst, src, first_move); if (!dst_wraps) { /* dst doesn't wrap */ memmove(dst + first_move, start, n - first_move); } else { /* dst does wrap */ size_t second_move = end - dst + first_move; memmove(dst + first_move, start, second_move); size_t third_move = n - second_move - first_move; memmove(start, start + second_move, third_move); } } } else /* src+n <= dst */ { /* TODO */ } #endif } else if (src != dst) { /* dst is in [src; src+n[ * |-----ssssssss---| * |------dddddddd--| * or * |ss-----sssssssss| * |-ddddddddddd----| * or * |ss-----sssssssss| * |dddd-----ddddddd| */ /* copy non overlapping data first */ size_t offset = srcend-dst; if (offset < 0) offset += end-start; char *dstoffset = dst+offset; if (dstoffset >= end) dstoffset -= end-start; const char *srcoffset = src+offset; if (srcoffset >= end) srcoffset -= end-start; memmove_wrap(dstoffset, srcoffset, n-offset, start, end); memmove_wrap(dst, src, offset, start, end); } }