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
9 #include <linux/types.h>
10 #include <linux/module.h>
12 /* Minimum size for a word copy to be convenient */
13 #define BYTES_LONG sizeof(long)
14 #define WORD_MASK (BYTES_LONG - 1)
15 #define MIN_THRESHOLD (BYTES_LONG * 2)
17 /* convenience union to avoid cast between different pointer types */
20 unsigned long *as_ulong
;
26 unsigned long *as_ulong
;
30 void *memcpy(void *dest
, const void *src
, size_t count
)
32 union const_types s
= { .as_u8
= src
};
33 union types d
= { .as_u8
= dest
};
36 if (count
< MIN_THRESHOLD
)
39 /* Copy a byte at time until destination is aligned. */
40 for (; d
.as_uptr
& WORD_MASK
; count
--)
41 *d
.as_u8
++ = *s
.as_u8
++;
43 distance
= s
.as_uptr
& WORD_MASK
;
46 unsigned long last
, next
;
49 * s is distance bytes ahead of d, and d just reached
50 * the alignment boundary. Move s backward to word align it
51 * and shift data to compensate for distance, in order to do
57 for (; count
>= BYTES_LONG
; count
-= BYTES_LONG
) {
61 d
.as_ulong
[0] = last
>> (distance
* 8) |
62 next
<< ((BYTES_LONG
- distance
) * 8);
68 /* Restore s with the original offset. */
72 * If the source and dest lower bits are the same, do a simple
73 * 32/64 bit wide copy.
75 for (; count
>= BYTES_LONG
; count
-= BYTES_LONG
)
76 *d
.as_ulong
++ = *s
.as_ulong
++;
81 *d
.as_u8
++ = *s
.as_u8
++;
85 EXPORT_SYMBOL(memcpy
);
88 * Simply check if the buffer overlaps an call memcpy() in case,
89 * otherwise do a simple one byte at time backward copy.
91 void *memmove(void *dest
, const void *src
, size_t count
)
93 if (dest
< src
|| src
+ count
<= dest
)
94 return memcpy(dest
, src
, count
);
97 const char *s
= src
+ count
;
98 char *tmp
= dest
+ count
;
105 EXPORT_SYMBOL(memmove
);
107 void *memset(void *s
, int c
, size_t count
)
109 union types dest
= { .as_u8
= s
};
111 if (count
>= MIN_THRESHOLD
) {
112 unsigned long cu
= (unsigned long)c
;
114 /* Compose an ulong with 'c' repeated 4/8 times */
117 /* Suppress warning on 32 bit machines */
118 cu
|= (cu
<< 16) << 16;
120 for (; count
&& dest
.as_uptr
& WORD_MASK
; count
--)
123 /* Copy using the largest size allowed */
124 for (; count
>= BYTES_LONG
; count
-= BYTES_LONG
)
125 *dest
.as_ulong
++ = cu
;
128 /* copy the remainder */
134 EXPORT_SYMBOL(memset
);