1 1.11 andvar /* $NetBSD: memset2.c,v 1.11 2022/01/15 10:38:56 andvar 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.11 andvar __RCSID("$NetBSD: memset2.c,v 1.11 2022/01/15 10:38:56 andvar 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.11 andvar * The conditional at the end prevents GCC from complaining 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.11 andvar * 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.11 andvar * if the amount to fill was less than the amount of trailing 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