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 * 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 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 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