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