158234Skato/*
258234Skato * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
358234Skato *
458234Skato * SPDX-License-Identifier: GPL-2.0-only
558234Skato */
658234Skato
758234Skato#include <strops.h>
858234Skato#include <printf.h>
958234Skato#include <abort.h>
1058234Skato
1158234Skato/* Both memset and memcpy need a custom type that allows us to use a word
1258234Skato * that has the aliasing properties of a char.
1358234Skato */
1458234Skato#ifdef __GNUC__
1558234Skato#define HAS_MAY_ALIAS
1658234Skato#elif defined(__clang__)
1758234Skato#if __has_attribute(may_alias)
1858234Skato#define HAS_MAY_ALIAS
1958234Skato#endif
2058234Skato#endif
2158234Skato
2258234Skato#ifdef HAS_MAY_ALIAS
2358234Skatotypedef word_t __attribute__((__may_alias__)) u_alias;
2458234Skato#endif
2558234Skato
2658234Skatosize_t strlen(const char *str)
27145748Snyan{
28145748Snyan    const char *s;
2958234Skato    for (s = str; *s; ++s);
30106047Snyan    return (s - str);
3158234Skato}
32104459Snyan
33106047Snyanint strcmp(const char *a, const char *b)
3458234Skato{
35106047Snyan    while (1) {
3658234Skato        if (*a != * b) {
3758234Skato            return ((unsigned char) * a) - ((unsigned char) * b);
3858234Skato        }
3958234Skato        if (*a == 0) {
40148062Snyan            return 0;
4169793Sobrien        }
42106047Snyan        a++;
43106047Snyan        b++;
4458234Skato    }
4558234Skato}
4658234Skato
4758234Skatoint strncmp(const char *s1, const char *s2, size_t n)
4858234Skato{
4958234Skato    word_t i;
5058234Skato    int diff;
5158234Skato
5258234Skato    for (i = 0; i < n; i++) {
5358234Skato        diff = ((unsigned char *)s1)[i] - ((unsigned char *)s2)[i];
5458234Skato        if (diff != 0 || s1[i] == '\0') {
5558234Skato            return diff;
5658234Skato        }
5758234Skato    }
5858234Skato
5958234Skato    return 0;
6058234Skato}
6158234Skato
6258234Skatovoid *memset(void *s, int c, size_t n)
6358234Skato{
6458234Skato    char *mem = (char *)s;
6558234Skato
6658234Skato#ifdef HAS_MAY_ALIAS
6758234Skato    /* fill byte by byte until word aligned */
6858234Skato    for (; (uintptr_t)mem % BYTE_PER_WORD != 0 && n > 0; mem++, n--) {
6958234Skato        *mem = c;
70106047Snyan    }
7158234Skato    /* construct word filler */
72106047Snyan    u_alias fill = ((u_alias) - 1 / 255) * (unsigned char)c;
7358234Skato    /* do as many word writes as we can */
74106047Snyan    for (; n > BYTE_PER_WORD - 1; n -= BYTE_PER_WORD, mem += BYTE_PER_WORD) {
7558234Skato        *(u_alias *)mem = fill;
76106047Snyan    }
7758234Skato    /* fill byte by byte for any remainder */
7858234Skato    for (; n > 0; n--, mem++) {
7958234Skato        *mem = c;
80108650Snyan    }
8158234Skato#else
8258234Skato    /* Without the __may__alias__ attribute we cannot safely do word writes
8358234Skato     * so fallback to bytes */
84106047Snyan    size_t i;
85148062Snyan    for (i = 0; i < n; i++) {
86106047Snyan        mem[i] = c;
87106047Snyan    }
88106047Snyan#endif
89106047Snyan
90106047Snyan    return s;
9158234Skato}
9258234Skato
9358234Skatovoid *memmove(void *restrict dest, const void *restrict src, size_t n)
9458234Skato{
9558234Skato    unsigned char *d = (unsigned char *)dest;
9658234Skato    const unsigned char *s = (const unsigned char *)src;
9758234Skato
9858234Skato    /* no copying to do */
9958234Skato    if (d == s) {
10058234Skato        return dest;
10158234Skato    }
10258234Skato    /* for non-overlapping regions, just use memcpy */
10358234Skato    else if (s + n <= d || d + n <= s) {
104156020Simp        return memcpy(dest, src, n);
10558234Skato    }
10658234Skato    /* if copying from the start of s to the start of d, just use memcpy */
10758234Skato    else if (s > d) {
10858234Skato        return memcpy(dest, src, n);
109106047Snyan    }
11058234Skato
11158234Skato    /* copy from end of 's' to end of 'd' */
11258234Skato    size_t i;
113106047Snyan    for (i = 1; i <= n; i++) {
11458234Skato        d[n - i] = s[n - i];
115106047Snyan    }
116106047Snyan
117106047Snyan    return dest;
11858234Skato}
11958234Skato
12058234Skatovoid *memcpy(void *restrict dest, const void *restrict src, size_t n)
12158234Skato{
12258234Skato    unsigned char *d = (unsigned char *)dest;
12358234Skato    const unsigned char *s = (const unsigned char *)src;
12458234Skato
125102231Strhodes    /* For ARM, we also need to consider if src is aligned.           *
126102231Strhodes     * There are two cases: (1) If rs == 0 and rd == 0, dest          *
12758234Skato     * and src are copy_unit-aligned. (2) If (rs == rd && rs != 0),   *
12858234Skato     * src and dest can be made copy_unit-aligned by copying rs bytes *
12958234Skato     * first. (1) is a special case of (2).                           */
13058234Skato
131102231Strhodes    size_t copy_unit = BYTE_PER_WORD;
13258234Skato    while (1) {
13358234Skato        int rs = (uintptr_t)s % copy_unit;
13458234Skato        int rd = (uintptr_t)d % copy_unit;
13558234Skato        if (rs == rd) {
13658234Skato            break;
13758234Skato        }
13858234Skato        if (copy_unit == 1) {
13958234Skato            break;
14058234Skato        }
14158234Skato        copy_unit >>= 1;
14258234Skato    }
14358234Skato
14458234Skato#ifdef HAS_MAY_ALIAS
14558234Skato    /* copy byte by byte until copy-unit aligned */
14658234Skato    for (; (uintptr_t)d % copy_unit != 0 && n > 0; d++, s++, n--) {
14758234Skato        *d = *s;
14858234Skato    }
14958234Skato    /* copy unit by unit as long as we can */
15058234Skato    for (; n > copy_unit - 1; n -= copy_unit, s += copy_unit, d += copy_unit) {
15158234Skato        switch (copy_unit) {
15258234Skato        case 8:
15358234Skato            *(uint64_t *)d = *(const uint64_t *)s;
154168934Simp            break;
155106047Snyan        case 4:
15658234Skato            *(uint32_t *)d = *(const uint32_t *)s;
157106047Snyan            break;
158106047Snyan        case 2:
159106047Snyan            *(uint16_t *)d = *(const uint16_t *)s;
160145765Snyan            break;
161106047Snyan        case 1:
16258234Skato            *(uint8_t *)d = *(const uint8_t *)s;
163148062Snyan            break;
164106047Snyan        default:
165106047Snyan            printf("Invalid copy unit %ld\n", copy_unit);
166106047Snyan            abort();
167106047Snyan        }
168106047Snyan    }
169106047Snyan    /* copy any remainder byte by byte */
17058234Skato    for (; n > 0; d++, s++, n--) {
171145765Snyan        *d = *s;
172156020Simp    }
17358234Skato#else
17458234Skato    size_t i;
17558234Skato    for (i = 0; i < n; i++) {
17658234Skato        d[i] = s[i];
177106047Snyan    }
17858234Skato#endif
179106047Snyan
180108650Snyan    return dest;
18158234Skato}
182156020Simp