diff options
Diffstat (limited to 'common/util.c')
-rw-r--r-- | common/util.c | 44 |
1 files changed, 36 insertions, 8 deletions
diff --git a/common/util.c b/common/util.c index 628aef9e82..5158d8aa06 100644 --- a/common/util.c +++ b/common/util.c @@ -238,17 +238,45 @@ void *memmove(void *dest, const void *src, int len) * memcpy(). */ return memcpy(dest, src, len); } else { - /* Copy from end, so we don't overwrite the source */ + /* Need to copy from tail because there is overlap. */ char *d = (char *)dest + len; const char *s = (const char *)src + len; - /* - * TODO(crosbug.com/p/23720): if src/dest are aligned, copy a - * word at a time instead. - */ - while (len > 0) { - *(--d) = *(--s); - len--; + uint32_t *dw; + const uint32_t *sw; + char *head; + char * const tail = (char *)dest; + /* Set 'body' to the last word boundary */ + uint32_t * const body = (uint32_t *)(((uintptr_t)tail+3) & ~3); + + if (((uintptr_t)dest & 3) != ((uintptr_t)src & 3)) { + /* Misaligned. no body, no tail. */ + head = tail; + } else { + /* Aligned */ + if ((uintptr_t)tail > ((uintptr_t)d & ~3)) + /* Shorter than the first word boundary */ + head = tail; + else + /* Set 'head' to the first word boundary */ + head = (char *)((uintptr_t)d & ~3); } + + /* Copy head */ + while (d > head) + *(--d) = *(--s); + + /* Copy body */ + dw = (uint32_t *)d; + sw = (uint32_t *)s; + while (dw > body) + *(--dw) = *(--sw); + + /* Copy tail */ + d = (char *)dw; + s = (const char *)sw; + while (d > tail) + *(--d) = *(--s); + return dest; } } |