1 // SPDX-License-Identifier: GPL-2.0-only
3 * String functions optimized for hardware which doesn't
4 * handle unaligned memory accesses efficiently.
6 * Copyright (C) 2021 Matteo Croce
10 #include <linux/types.h>
11 #include <linux/module.h>
13 /* Minimum size for a word copy to be convenient */
14 #define BYTES_LONG sizeof(long)
15 #define WORD_MASK (BYTES_LONG - 1)
16 #define MIN_THRESHOLD (BYTES_LONG * 2)
18 /* convenience union to avoid cast between different pointer types */
21 unsigned long *as_ulong;
27 unsigned long *as_ulong;
31 void *__memcpy(void *dest, const void *src, size_t count)
33 union const_types s = { .as_u8 = src };
34 union types d = { .as_u8 = dest };
37 if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) {
38 if (count < MIN_THRESHOLD)
41 /* Copy a byte at time until destination is aligned. */
42 for (; d.as_uptr & WORD_MASK; count--)
43 *d.as_u8++ = *s.as_u8++;
45 distance = s.as_uptr & WORD_MASK;
49 unsigned long last, next;
52 * s is distance bytes ahead of d, and d just reached
53 * the alignment boundary. Move s backward to word align it
54 * and shift data to compensate for distance, in order to do
60 for (; count >= BYTES_LONG; count -= BYTES_LONG) {
64 d.as_ulong[0] = last >> (distance * 8) |
65 next << ((BYTES_LONG - distance) * 8);
71 /* Restore s with the original offset. */
75 * If the source and dest lower bits are the same, do a simple
76 * 32/64 bit wide copy.
78 for (; count >= BYTES_LONG; count -= BYTES_LONG)
79 *d.as_ulong++ = *s.as_ulong++;
84 *d.as_u8++ = *s.as_u8++;
88 EXPORT_SYMBOL(__memcpy);
90 void *memcpy(void *dest, const void *src, size_t count) __weak __alias(__memcpy);
91 EXPORT_SYMBOL(memcpy);
94 * Simply check if the buffer overlaps an call memcpy() in case,
95 * otherwise do a simple one byte at time backward copy.
97 void *__memmove(void *dest, const void *src, size_t count)
99 if (dest < src || src + count <= dest)
100 return __memcpy(dest, src, count);
103 const char *s = src + count;
104 char *tmp = dest + count;
111 EXPORT_SYMBOL(__memmove);
113 void *memmove(void *dest, const void *src, size_t count) __weak __alias(__memmove);
114 EXPORT_SYMBOL(memmove);