1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <strops.h>
8#include <printf.h>
9#include <abort.h>
10
11/* Both memset and memcpy need a custom type that allows us to use a word
12 * that has the aliasing properties of a char.
13 */
14#ifdef __GNUC__
15#define HAS_MAY_ALIAS
16#elif defined(__clang__)
17#if __has_attribute(may_alias)
18#define HAS_MAY_ALIAS
19#endif
20#endif
21
22#ifdef HAS_MAY_ALIAS
23typedef word_t __attribute__((__may_alias__)) u_alias;
24#endif
25
26size_t strlen(const char *str)
27{
28    const char *s;
29    for (s = str; *s; ++s);
30    return (s - str);
31}
32
33int strcmp(const char *a, const char *b)
34{
35    while (1) {
36        if (*a != * b) {
37            return ((unsigned char) * a) - ((unsigned char) * b);
38        }
39        if (*a == 0) {
40            return 0;
41        }
42        a++;
43        b++;
44    }
45}
46
47int strncmp(const char *s1, const char *s2, size_t n)
48{
49    word_t i;
50    int diff;
51
52    for (i = 0; i < n; i++) {
53        diff = ((unsigned char *)s1)[i] - ((unsigned char *)s2)[i];
54        if (diff != 0 || s1[i] == '\0') {
55            return diff;
56        }
57    }
58
59    return 0;
60}
61
62void *memset(void *s, int c, size_t n)
63{
64    char *mem = (char *)s;
65
66#ifdef HAS_MAY_ALIAS
67    /* fill byte by byte until word aligned */
68    for (; (uintptr_t)mem % BYTE_PER_WORD != 0 && n > 0; mem++, n--) {
69        *mem = c;
70    }
71    /* construct word filler */
72    u_alias fill = ((u_alias) - 1 / 255) * (unsigned char)c;
73    /* do as many word writes as we can */
74    for (; n > BYTE_PER_WORD - 1; n -= BYTE_PER_WORD, mem += BYTE_PER_WORD) {
75        *(u_alias *)mem = fill;
76    }
77    /* fill byte by byte for any remainder */
78    for (; n > 0; n--, mem++) {
79        *mem = c;
80    }
81#else
82    /* Without the __may__alias__ attribute we cannot safely do word writes
83     * so fallback to bytes */
84    size_t i;
85    for (i = 0; i < n; i++) {
86        mem[i] = c;
87    }
88#endif
89
90    return s;
91}
92
93void *memmove(void *restrict dest, const void *restrict src, size_t n)
94{
95    unsigned char *d = (unsigned char *)dest;
96    const unsigned char *s = (const unsigned char *)src;
97
98    /* no copying to do */
99    if (d == s) {
100        return dest;
101    }
102    /* for non-overlapping regions, just use memcpy */
103    else if (s + n <= d || d + n <= s) {
104        return memcpy(dest, src, n);
105    }
106    /* if copying from the start of s to the start of d, just use memcpy */
107    else if (s > d) {
108        return memcpy(dest, src, n);
109    }
110
111    /* copy from end of 's' to end of 'd' */
112    size_t i;
113    for (i = 1; i <= n; i++) {
114        d[n - i] = s[n - i];
115    }
116
117    return dest;
118}
119
120void *memcpy(void *restrict dest, const void *restrict src, size_t n)
121{
122    unsigned char *d = (unsigned char *)dest;
123    const unsigned char *s = (const unsigned char *)src;
124
125    /* For ARM, we also need to consider if src is aligned.           *
126     * There are two cases: (1) If rs == 0 and rd == 0, dest          *
127     * and src are copy_unit-aligned. (2) If (rs == rd && rs != 0),   *
128     * src and dest can be made copy_unit-aligned by copying rs bytes *
129     * first. (1) is a special case of (2).                           */
130
131    size_t copy_unit = BYTE_PER_WORD;
132    while (1) {
133        int rs = (uintptr_t)s % copy_unit;
134        int rd = (uintptr_t)d % copy_unit;
135        if (rs == rd) {
136            break;
137        }
138        if (copy_unit == 1) {
139            break;
140        }
141        copy_unit >>= 1;
142    }
143
144#ifdef HAS_MAY_ALIAS
145    /* copy byte by byte until copy-unit aligned */
146    for (; (uintptr_t)d % copy_unit != 0 && n > 0; d++, s++, n--) {
147        *d = *s;
148    }
149    /* copy unit by unit as long as we can */
150    for (; n > copy_unit - 1; n -= copy_unit, s += copy_unit, d += copy_unit) {
151        switch (copy_unit) {
152        case 8:
153            *(uint64_t *)d = *(const uint64_t *)s;
154            break;
155        case 4:
156            *(uint32_t *)d = *(const uint32_t *)s;
157            break;
158        case 2:
159            *(uint16_t *)d = *(const uint16_t *)s;
160            break;
161        case 1:
162            *(uint8_t *)d = *(const uint8_t *)s;
163            break;
164        default:
165            printf("Invalid copy unit %ld\n", copy_unit);
166            abort();
167        }
168    }
169    /* copy any remainder byte by byte */
170    for (; n > 0; d++, s++, n--) {
171        *d = *s;
172    }
173#else
174    size_t i;
175    for (i = 0; i < n; i++) {
176        d[i] = s[i];
177    }
178#endif
179
180    return dest;
181}
182