1 /* $NetBSD: memset2.c,v 1.11 2022/01/15 10:38:56 andvar Exp $ */
2
3 /*-
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas <matt@3am-software.com>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: memset2.c,v 1.11 2022/01/15 10:38:56 andvar Exp $");
35 #endif /* LIBC_SCCS and not lint */
36
37 #include <sys/types.h>
38
39 #if !defined(_KERNEL) && !defined(_STANDALONE)
40 #include <assert.h>
41 #include <limits.h>
42 #include <string.h>
43 #include <inttypes.h>
44 #else
45 #include <lib/libkern/libkern.h>
46 #include <machine/limits.h>
47 #endif
48
49 #include <sys/endian.h>
50 #include <machine/types.h>
51
52 #undef __OPTIMIZE_SIZE__
53 #define __OPTIMIZE_SIZE__ 1 /* other code path is very broken */
54
55 #ifdef TEST
56 #include <assert.h>
57 #define _DIAGASSERT(a) assert(a)
58 #endif
59
60 #ifdef _FORTIFY_SOURCE
61 #undef bzero
62 #endif
63 #undef memset
64
65 /*
66 * Assume __register_t is the widest non-synthetic unsigned type.
67 */
68 typedef __register_t memword_t;
69
70 #ifdef BZERO
71 static inline
72 #define memset memset0
73 #endif
74
75 #ifdef TEST
76 static
77 #define memset test_memset
78 #endif
79
80 void *
memset(void * addr,int c,size_t len)81 memset(void *addr, int c, size_t len)
82 {
83 memword_t *dstp = addr;
84 memword_t *edstp;
85 memword_t fill;
86 #ifndef __OPTIMIZE_SIZE__
87 memword_t keep_mask = 0;
88 #endif
89 size_t fill_count;
90
91 _DIAGASSERT(addr != 0);
92
93 if (__predict_false(len == 0))
94 return addr;
95
96 /*
97 * Pad out the fill byte (v) across a memword_t.
98 * The conditional at the end prevents GCC from complaining about
99 * shift count >= width of type
100 */
101 fill = (unsigned char)c;
102 fill |= fill << 8;
103 fill |= fill << 16;
104 fill |= fill << (sizeof(c) < sizeof(fill) ? 32 : 0);
105
106 /*
107 * Get the number of unaligned bytes to fill in the first word.
108 */
109 fill_count = -(uintptr_t)addr & (sizeof(memword_t) - 1);
110
111 if (__predict_false(fill_count != 0)) {
112 #ifndef __OPTIMIZE_SIZE__
113 /*
114 * We want to clear <fill_count> trailing bytes in the word.
115 * On big/little endian, these are the least/most significant,
116 * bits respectively. So as we shift, the keep_mask will only
117 * have bits set for the bytes we won't be filling.
118 */
119 #if BYTE_ORDER == BIG_ENDIAN
120 keep_mask = ~(memword_t)0U << (fill_count * 8);
121 #endif
122 #if BYTE_ORDER == LITTLE_ENDIAN
123 keep_mask = ~(memword_t)0U >> (fill_count * 8);
124 #endif
125 /*
126 * Make sure dstp is aligned to a memword_t boundary.
127 */
128 dstp = (memword_t *)((uintptr_t)addr & -sizeof(memword_t));
129 if (len >= fill_count) {
130 /*
131 * If we can fill the rest of this word, then we mask
132 * off the bytes we are filling and then fill in those
133 * bytes with the new fill value.
134 */
135 *dstp = (*dstp & keep_mask) | (fill & ~keep_mask);
136 len -= fill_count;
137 if (__predict_false(len == 0))
138 return addr;
139 /*
140 * Since we were able to fill the rest of this word,
141 * we will advance to the next word and thus have no
142 * bytes to preserve.
143 *
144 * If we don't have enough to fill the rest of this
145 * word, we will fall through the following loop
146 * (since there are no full words to fill). Then we
147 * use the keep_mask above to preserve the leading
148 * bytes of word.
149 */
150 dstp++;
151 keep_mask = 0;
152 } else {
153 len += (uintptr_t)addr & (sizeof(memword_t) - 1);
154 }
155 #else /* __OPTIMIZE_SIZE__ */
156 uint8_t *dp, *ep;
157 if (len < fill_count)
158 fill_count = len;
159 for (dp = (uint8_t *)dstp, ep = dp + fill_count;
160 dp != ep; dp++)
161 *dp = fill;
162 if ((len -= fill_count) == 0)
163 return addr;
164 dstp = (memword_t *)ep;
165 #endif /* __OPTIMIZE_SIZE__ */
166 }
167
168 /*
169 * Simply fill memory one word at time (for as many full words we have
170 * to write).
171 */
172 for (edstp = dstp + len / sizeof(memword_t); dstp != edstp; dstp++)
173 *dstp = fill;
174
175 /*
176 * We didn't subtract out the full words we just filled since we know
177 * by the time we get here we will have less than a words worth to
178 * write. So we can concern ourselves with only the subword len bits.
179 */
180 len &= sizeof(memword_t)-1;
181 if (len > 0) {
182 #ifndef __OPTIMIZE_SIZE__
183 /*
184 * We want to clear <len> leading bytes in the word.
185 * On big/little endian, these are the most/least significant
186 * bits, respectively. But as we want the mask of the bytes to
187 * keep, we have to complement the mask. So after we shift,
188 * the keep_mask will only have bits set for the bytes we won't
189 * be filling.
190 *
191 * But the keep_mask could already have bytes to preserve
192 * if the amount to fill was less than the amount of trailing
193 * space in the first word.
194 */
195 #if BYTE_ORDER == BIG_ENDIAN
196 keep_mask |= ~(memword_t)0U >> (len * 8);
197 #endif
198 #if BYTE_ORDER == LITTLE_ENDIAN
199 keep_mask |= ~(memword_t)0U << (len * 8);
200 #endif
201 /*
202 * Now we mask off the bytes we are filling and then fill in
203 * those bytes with the new fill value.
204 */
205 *dstp = (*dstp & keep_mask) | (fill & ~keep_mask);
206 #else /* __OPTIMIZE_SIZE__ */
207 uint8_t *dp, *ep;
208 for (dp = (uint8_t *)dstp, ep = dp + len;
209 dp != ep; dp++)
210 *dp = fill;
211 #endif /* __OPTIMIZE_SIZE__ */
212 }
213
214 /*
215 * Return the initial addr
216 */
217 return addr;
218 }
219
220 #ifdef BZERO
221 /*
222 * For bzero, simply inline memset and let the compiler optimize things away.
223 */
224 void
bzero(void * addr,size_t len)225 bzero(void *addr, size_t len)
226 {
227 memset(addr, 0, len);
228 }
229 #endif
230
231 #ifdef TEST
232 #include <stdbool.h>
233 #include <stdio.h>
234
235 #undef memset
236
237 static union {
238 uint8_t bytes[sizeof(memword_t) * 4];
239 memword_t words[4];
240 } testmem;
241
242 int
main(int argc,char ** argv)243 main(int argc, char **argv)
244 {
245 size_t start;
246 size_t len;
247 bool failed = false;
248
249 for (start = 1; start < sizeof(testmem) - 1; start++) {
250 for (len = 1; start + len < sizeof(testmem) - 1; len++) {
251 bool ok = true;
252 size_t i;
253 uint8_t check_value;
254 memset(testmem.bytes, 0xff, sizeof(testmem));
255 test_memset(testmem.bytes + start, 0x00, len);
256 for (i = 0; i < sizeof(testmem); i++) {
257 if (i == 0 || i == start + len)
258 check_value = 0xff;
259 else if (i == start)
260 check_value = 0x00;
261 if (testmem.bytes[i] != check_value) {
262 if (ok)
263 printf("pass @ %zu .. %zu failed",
264 start, start + len - 1);
265 ok = false;
266 printf(" [%zu]=0x%02x(!0x%02x)",
267 i, testmem.bytes[i], check_value);
268 }
269 }
270 if (!ok) {
271 printf("\n");
272 failed = 1;
273 }
274 }
275 }
276
277 return failed ? 1 : 0;
278 }
279 #endif /* TEST */
280