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