/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Fast strcmp. This works one int at a time, using aligned pointers * if possible, misaligned pointers if necessary. To avoid taking * faults from going off the end of a page, the code is careful to go * a byte-at-a-time when a misaligned pointer is near a page boundary. * The code is almost portable, but see the assumptions below. */ /* * ASSUMPTIONS: * sizeof (int) is not greater than 8. * sizeof (int) is a power of 2. * An int pointer can always be dereferenced even if it is not properly * aligned (though aligned references are assumed to be faster). * It is OK to assign bogus values to a pointer (in particular, a * value that is before the beginning of the string) as long as that * pointer is only used with indices big enough to bring us back into * the string. * It is OK to reference bytes past the end of a string as long as we * don't cross a page boundary. */ #include "lint.h" #include <limits.h> #include <unistd.h> #include <sys/sysconfig.h> #include "libc.h" /* * This strange expression will test to see if *any* byte in the int is * a NUL. The constants are big enough to allow for ints up to 8 bytes. * The two arguments are actually two copies of the same value; this * allows the compiler freedom to play with both values for efficiency. */ #define ANYNUL(i1, i2) (((i1) - (int)0x0101010101010101LL) & ~(i2) & \ (int)0x8080808080808080ULL) int strcmp(const char *str1, const char *str2) { int *s1, *s2; int i1, i2; int count; int b1, b2; static int pagesize; if (str1 == str2) return (0); /* * Go 1 byte at a time until at least one pointer is word aligned. * Assumes that sizeof (int) is a power of 2. */ while ((((int) str1) & (sizeof (int) - 1)) && (((int) str2) & (sizeof (int) - 1))) { one_byte: if (*str1 != *str2) return ((unsigned char)*str1 - (unsigned char)*str2); if (*str1 == '\0') return (0); ++str1; ++str2; } /* * If one pointer is misaligned, we must be careful not to * dereference it when it points across a page boundary. * If we did, we might go past the end of the segment and * get a SIGSEGV. Set "count" to the number of ints we can * scan before running into such a boundary. */ count = INT_MAX; if (((int) str1) & (sizeof (int) - 1)) { if (pagesize == 0) pagesize = _sysconfig(_CONFIG_PAGESIZE); count = (pagesize - ((int)str1 & (pagesize - 1))) / sizeof (int); } else if (((int) str2) & (sizeof (int) - 1)) { if (pagesize == 0) pagesize = _sysconfig(_CONFIG_PAGESIZE); count = (pagesize - ((int)str2 & (pagesize - 1))) / sizeof (int); } s1 = (void *) str1; s2 = (void *) str2; /* * Go "sizeof (int)" bytes at a time until at least one pointer * is word aligned. * * Unwrap the loop for even a bit more speed. */ for (;;) { /* * Check whether we can test the next 4 ints without * hitting a page boundary. If we can only test 1, 2, * or 3, go and do that first. If we can't check any * more, go and test one byte, realign, and start again. */ count -= 4; switch (count) { case -1: --s1; --s2; goto do3; /* check only 3 ints */ case -2: s1 -= 2; s2 -= 2; goto do2; /* check only 2 ints */ case -3: s1 -= 3; s2 -= 3; goto do1; /* check only 1 int */ case -4: case -5: /* -5, -6, and -7 come up on the */ case -6: /* next time around after we do one */ case -7: /* of the 3 gotos above */ str1 = (void *) s1; str2 = (void *) s2; goto one_byte; /* * The goto above should be explained. By going * into the middle of the loop, it makes sure * that we advance at least one byte. We will * stay in that loop until the misaligned pointer * becomes aligned (at the page boundary). We * will then break out of that loop with the * formerly misaligned pointer now aligned, the * formerly aligned pointer now misaligned, and * we will come back into this loop until the * latter pointer reaches a page boundary. */ default: /* at least 4 ints to go */ break; } i1 = s1[0]; i2 = s2[0]; if (i1 != i2) break; else if (ANYNUL(i1, i2)) return (0); do3: i1 = s1[1]; i2 = s2[1]; if (i1 != i2) break; else if (ANYNUL(i1, i2)) return (0); do2: i1 = s1[2]; i2 = s2[2]; if (i1 != i2) break; else if (ANYNUL(i1, i2)) return (0); do1: i1 = s1[3]; i2 = s2[3]; if (i1 != i2) break; else if (ANYNUL(i1, i2)) return (0); s1 += 4; s2 += 4; } /* We found a difference. Go one byte at a time to find where. */ b1 = i1; /* save the ints in memory */ b2 = i2; str1 = (void *) &b1; /* point at them */ str2 = (void *) &b2; while (*str1 == *str2) { if (*str1 == '\0') return (0); ++str1; ++str2; } return ((unsigned char)*str1 - (unsigned char)*str2); }