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