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