feat(string): implement core string/memory functions (level 1)

Word-at-a-time memcpy/memmove/memset/strlen/strcmp implementing the
level-1 string slice. memcpy carries an optimize(no-tree-loop-
distribute-patterns) attribute: GCC's loop-distribution pass rewrites
its word-copy loop into a self-recursive memcpy@plt call at -O2/-O3
otherwise.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <[email protected]>
This commit is contained in:
2026-08-31 21:14:49 -04:00
co-authored by Sisyphus
parent 7d28d7cc9d
commit d23deaa32a
5 changed files with 309 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
/*
* Copy n bytes from src to dst. The regions must not overlap (restrict).
* Word-at-a-time: copy whole unsigned long words once dst is aligned, with
* a byte head and tail. Word loads and stores go through __builtin_memcpy
* so unaligned src is handled without undefined behavior.
*
* The word-copy loop below is the canonical memcpy idiom; GCC's
* -ftree-loop-distribute-patterns (on by default at -O2 and -O3) rewrites
* it into a call to memcpy() — this very function — causing infinite
* self-recursion and stack overflow. Disable that one transformation for
* this function only.
*/
__attribute__((optimize("no-tree-loop-distribute-patterns")))
void *
memcpy(void *restrict dst, const void *restrict src, size_t n)
{
const unsigned long word = sizeof(unsigned long);
unsigned char *d = dst;
const unsigned char *s = src;
/* Copy the unaligned head byte-wise. */
for (; (unsigned long)d % word != 0 && n != 0; n--)
{
*d++ = *s++;
}
/* Copy whole words. */
while (n >= word)
{
unsigned long w;
__builtin_memcpy(&w, s, sizeof w);
__builtin_memcpy(d, &w, sizeof w);
d += word;
s += word;
n -= word;
}
/* Copy the remaining tail byte-wise. */
while (n != 0)
{
*d++ = *s++;
n--;
}
return dst;
}
+71
View File
@@ -0,0 +1,71 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
/*
* Copy n bytes from src to dst. The regions may overlap, so the copy
* direction is chosen by the relative positions: forward when dst starts
* at or before src, or at or after the end of src; backward when dst
* starts inside src. Word-at-a-time in both directions, with byte heads
* and tails; word loads and stores go through __builtin_memcpy.
*/
void *
memmove(void *dst, const void *src, size_t n)
{
const unsigned long word = sizeof(unsigned long);
unsigned char *d = dst;
const unsigned char *s = src;
if (d <= s || d >= s + n)
{
/* Forward copy. */
for (; (unsigned long)d % word != 0 && n != 0; n--)
{
*d++ = *s++;
}
while (n >= word)
{
unsigned long w;
__builtin_memcpy(&w, s, sizeof w);
__builtin_memcpy(d, &w, sizeof w);
d += word;
s += word;
n -= word;
}
while (n != 0)
{
*d++ = *s++;
n--;
}
}
else
{
/* Backward copy, starting from the last byte. */
d += n;
s += n;
for (; (unsigned long)d % word != 0 && n != 0; n--)
{
*--d = *--s;
}
while (n >= word)
{
unsigned long w;
d -= word;
s -= word;
__builtin_memcpy(&w, s, sizeof w);
__builtin_memcpy(d, &w, sizeof w);
n -= word;
}
while (n != 0)
{
*--d = *--s;
n--;
}
}
return dst;
}
+43
View File
@@ -0,0 +1,43 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
/*
* Fill n bytes at dst with c (converted to unsigned char). Word-at-a-time:
* broadcast the byte into every byte of an unsigned long, align dst, fill
* whole words, then finish with a byte tail. The broadcast multiply cannot
* carry between bytes because each byte is at most 0xff.
*/
void *
memset(void *dst, int c, size_t n)
{
const unsigned long word = sizeof(unsigned long);
const unsigned char byte = (unsigned char)c;
const unsigned long fill = (unsigned long)byte * ((unsigned long)-1 / 0xff);
unsigned char *d = dst;
/* Fill the unaligned head byte-wise. */
for (; (unsigned long)d % word != 0 && n != 0; n--)
{
*d++ = byte;
}
/* Fill whole words. */
while (n >= word)
{
__builtin_memcpy(d, &fill, sizeof fill);
d += word;
n -= word;
}
/* Fill the remaining tail byte-wise. */
while (n != 0)
{
*d++ = byte;
n--;
}
return dst;
}
+80
View File
@@ -0,0 +1,80 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
/*
* True if w contains a zero byte. Subtracting ONES from w propagates a
* borrow into the high bit of a byte exactly when that byte is zero;
* ANDing with ~w keeps only bytes that were zero and high-bit-clear in w.
* ONES and its shifted form are derived from the word width, so this works
* for both 32- and 64-bit unsigned long.
*/
static unsigned long
haszero(unsigned long w)
{
const unsigned long ones = (unsigned long)-1 / 0xff;
return (w - ones) & ~w & (ones << 7);
}
/*
* Compare the strings lhs and rhs; return negative, zero, or positive when
* lhs is less than, equal to, or greater than rhs. Bytes are compared as
* unsigned char, so values at or above 0x80 sort above 0x7f.
*/
int
strcmp(const char *lhs, const char *rhs)
{
const unsigned long word = sizeof(unsigned long);
const unsigned char *l = (const unsigned char *)lhs;
const unsigned char *r = (const unsigned char *)rhs;
/* Compare the unaligned head, aligning l to a word boundary. */
for (; (unsigned long)l % word != 0; l++, r++)
{
if (*l != *r)
{
return *l < *r ? -1 : 1;
}
if (*l == '\0')
{
return 0;
}
}
if ((unsigned long)r % word == 0)
{
/* Both pointers are word-aligned: scan whole words. */
for (;;)
{
unsigned long wl;
unsigned long wr;
__builtin_memcpy(&wl, l, sizeof wl);
__builtin_memcpy(&wr, r, sizeof wr);
if (wl != wr || haszero(wl))
{
break;
}
l += word;
r += word;
}
}
/* Resolve the differing or terminating word byte-wise. */
for (;;)
{
if (*l != *r)
{
return *l < *r ? -1 : 1;
}
if (*l == '\0')
{
return 0;
}
l++;
r++;
}
}
+62
View File
@@ -0,0 +1,62 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
/*
* True if w contains a zero byte. Subtracting ONES from w propagates a
* borrow into the high bit of a byte exactly when that byte is zero;
* ANDing with ~w keeps only bytes that were zero and high-bit-clear in w.
* ONES and its shifted form are derived from the word width, so this works
* for both 32- and 64-bit unsigned long.
*/
static unsigned long
haszero(unsigned long w)
{
const unsigned long ones = (unsigned long)-1 / 0xff;
return (w - ones) & ~w & (ones << 7);
}
/*
* Return the length of s, excluding the terminating NUL.
*/
size_t
strlen(const char *s)
{
const unsigned long word = sizeof(unsigned long);
const char *p = s;
/* Check the head byte-wise until p is word-aligned. */
for (; (unsigned long)p % word != 0; p++)
{
if (*p == '\0')
{
return (size_t)(p - s);
}
}
/* Scan whole words for a zero byte. */
for (;;)
{
unsigned long w;
__builtin_memcpy(&w, p, sizeof w);
if (haszero(w))
{
break;
}
p += word;
}
/* Resolve the terminating word byte-wise. */
for (;;)
{
if (*p == '\0')
{
return (size_t)(p - s);
}
p++;
}
}