diff --git a/lib/mem.c b/lib/mem.c index f182efd656..5228082a5b 100644 --- a/lib/mem.c +++ b/lib/mem.c @@ -2,6 +2,7 @@ * This file is part of the LinuxBIOS project. * * Copyright (C) 2007 Ronald G. Minnich + * Copyright (C) 2007 Peter Stuge * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,27 +18,91 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA */ -/* Simple memory routines that have no optimizations for anything. */ +/* Memory routines with some optimizations. */ #include -/* this one is pretty stupid. Won't handle overlaps, it's not efficient, etc. */ +/** + * memcpy() and memmove() helper that uses unsigned long copying when dest and + * src are both unsigned long aligned, or offset by the same amount from being + * unsigned long aligned. + * + * Copies data one byte at a time until both dest and src are aligned to + * sizeof(unsigned long) or until no data is left. + * + * Copies data one unsigned long at a time until fewer than + * sizeof(unsigned long) bytes remain. + * + * Finally, or if dest and src have different alignment, copies any remaining + * data one byte at a time. + * + * @param dest Memory area to copy to. + * @param src Memory area to copy from. + * @param len Number of bytes to copy. + * @param backwards Start at the end, used by memmove() when dest > src. + */ +static void memcpy_helper(void *dest, const void *src, int len, int backwards) { + u8 *d = dest; + const u8 *s = src; + unsigned long *ld; + const unsigned long *ls; + u8 longmask = sizeof(unsigned long) - 1; + u8 longlen = sizeof(unsigned long); + + if (d == s || !len) + return; + + if (backwards) { + d += len; + s += len; + if (((unsigned long)d & longmask) == ((unsigned long)s & longmask)) { + while (((unsigned long)d & longmask || (unsigned long)s & longmask) && len) { + *--d = *--s; + len--; + } + ld = (unsigned long *)d; + ls = (const unsigned long *)s; + while (len >= longlen) { + *--ld = *--ls; + len -= longlen; + } + d = (u8 *)ld; + s = (const u8 *)ls; + } + while (len--) + *--d = *--s; + } else { + if (((unsigned long)d & longmask) == ((unsigned long)s & longmask)) { + while (((unsigned long)d & longmask || (unsigned long)s & longmask) && len) { + *d++ = *s++; + len--; + } + ld = (unsigned long *)d; + ls = (const unsigned long *)s; + while (len >= longlen) { + *ld++ = *ls++; + len -= longlen; + } + d = (u8 *)ld; + s = (const u8 *)ls; + } + while (len--) + *d++ = *s++; + } +} + +/* Won't handle overlaps. */ /* Please don't be silly and inline these. Inlines are not as wonderful as people think */ void memcpy(void *dest, const void *src, int len) { - unsigned char *d = dest; - const unsigned char *s = src; - while (len--) - *d++ = *s++; + memcpy_helper(dest, src, len, 0); } +/* Handles overlapping memory. */ /* seperate function in case we decide to use the built-in -- not sure yet. */ void memmove(void *dest, const void *src, int len) { - unsigned char *d = dest; - const unsigned char *s = src; - while (len--) - *d++ = *s++; + memcpy_helper(dest, src, len, dest > src && dest < (src + len)); } void memset(void *v, unsigned char a, int len)