Index: apps/buffering.c =================================================================== --- apps/buffering.c (revision 23741) +++ apps/buffering.c (working copy) @@ -389,6 +389,143 @@ return m; } +static size_t gcd(size_t a, size_t b) +{ + while (b) + { + size_t c = a%b; + a = b; + b = c; + } + return a; +} + +static void memmove_wrap(char *dst, const char *src, size_t n, const char *start, const char *end) +{ + const 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 + while (n--) + { + *dst++ = *src++; + if (dst == end) + dst = (char *)start; + if (src == end) + src = start; + } +#else + if (dst+n > end && src+n > end) + { + size_t dchunk = end-dst; + size_t schunk = end-src; + if (dchunk < schunk) + { + memmove(dst, src, dchunk); + memmove(start, src+dchunk, schunk-dchunk); + memmove(start+schunk-dchunk, start, n-schunk); + } + else + { + memmove(dst, src, schunk); + memmove(dst+schunk, start, dchunk-schunk); + memmove(start, start+dchunk-schunk, n-dchunk); + } + } + else if (dst+n > end) + { + size_t chunk = end-dst; + memmove(dst, src, chunk); + memmove(start, src+chunk, n-chunk); + } + else if (src+n > end) + { + size_t chunk = end-src; + memmove(dst, src, chunk); + memmove(dst+chunk, start, n-chunk); + } + else + { + memmove(dst, src, n); + } +#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 */ + if (2*n <= end-start) + { + ssize_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); + } + else + { + const char *dstend = dst+n; + if (dstend >= end) dstend -= end-start; + ssize_t step = src-dst; + if (step < 0) + step += end-start; + size_t g = gcd(end-start, step); + size_t i; + for (i = 0; i < g; i++) + { + const char *s = src+i; + if (s >= end) + s -= end-start; + char *d = dst+i; + if (d >= end) + d -= end-start; + char *dorig = d; + char old = *d; + do + { + if ((dst < dstend && d >= dst && d < dstend) || + (dst > dstend && (d >= dst || d < dstend))) + *d = *s; + d += step; + if (d >= end) + d -= end-start; + s += step; + if (s >= end) + s -= end-start; + } while (s != dorig); + *d = old; + } + } + } +} /* Move a memory handle and data_size of its data delta bytes along the buffer. delta maximum bytes available to move the handle. If the move is performed it is set to the actual distance moved. @@ -506,6 +643,7 @@ n = (size_to_move & ~3)/4; if ( overlap_old > 0 || overlap > 0 ) { +#if 0 /* Old or moved handle wraps */ while (n--) { if (here < begin) @@ -514,6 +652,9 @@ there = end; *there-- = *here--; } +#else + memmove_wrap(dest, src, n, begin, end+1); +#endif } else { /* both handles do not wrap */ memmove(dest,src,size_to_move);