blob: 0d654f0395423ce1ee91bcf1061d19b6d9838e58 [file] [log] [blame]
/* Copyright 2021 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Standard library utility functions for Chrome EC */
#include "common.h"
#include "console.h"
#include "printf.h"
#include "util.h"
#include <stdio.h>
/*
* The following macros are defined in stdlib.h in the C standard library, which
* conflict with the definitions in this file.
*/
#undef isspace
#undef isdigit
#undef isalpha
#undef isupper
#undef isprint
#undef tolower
/* Context for snprintf() */
struct snprintf_context {
char *str;
int size;
};
/**
* Add a character to the string context.
*
* @param context Context receiving character
* @param c Character to add
* @return 0 if character added, 1 if character dropped because no space.
*/
static int snprintf_addchar(void *context, int c)
{
struct snprintf_context *ctx = (struct snprintf_context *)context;
if (!ctx->size)
return 1;
*(ctx->str++) = c;
ctx->size--;
return 0;
}
int crec_vsnprintf(char *str, size_t size, const char *format, va_list args)
{
struct snprintf_context ctx;
int rv;
if (!str || !format || size <= 0)
return -EC_ERROR_INVAL;
ctx.str = str;
ctx.size = size - 1; /* Reserve space for terminating '\0' */
rv = vfnprintf(snprintf_addchar, &ctx, format, args);
/* Terminate string */
*ctx.str = '\0';
return (rv == EC_SUCCESS) ? (ctx.str - str) : -rv;
}
#ifndef CONFIG_ZEPHYR
int vsnprintf(char *str, size_t size, const char *format, va_list args)
__attribute__((weak, alias("crec_vsnprintf")));
#endif /* CONFIG_ZEPHYR */
int crec_snprintf(char *str, size_t size, const char *format, ...)
{
va_list args;
int rv;
va_start(args, format);
rv = crec_vsnprintf(str, size, format, args);
va_end(args);
return rv;
}
#ifndef CONFIG_ZEPHYR
int snprintf(char *str, size_t size, const char *format, ...)
__attribute__((weak, alias("crec_snprintf")));
#endif /* CONFIG_ZEPHYR */
/*
* TODO(b/237712836): Zephyr's libc should provide strcasecmp. For now we'll
* use the EC implementation.
*/
__stdlib_compat int strcasecmp(const char *s1, const char *s2)
{
int diff;
do {
diff = tolower(*s1) - tolower(*s2);
if (diff)
return diff;
} while (*(s1++) && *(s2++));
return 0;
}
/*
* TODO(b/237712836): Remove this conditional once strcasecmp is added to
* Zephyr's libc.
*/
#ifndef CONFIG_ZEPHYR
__stdlib_compat size_t strlen(const char *s)
{
int len = 0;
while (*s++)
len++;
return len;
}
__stdlib_compat size_t strnlen(const char *s, size_t maxlen)
{
size_t len = 0;
while (len < maxlen && *s) {
s++;
len++;
}
return len;
}
__stdlib_compat size_t strcspn(const char *s, const char *reject)
{
size_t i;
size_t reject_len = strlen(reject);
for (i = 0; s[i] != 0; i++)
for (size_t j = 0; j < reject_len; j++)
if (s[i] == reject[j])
return i;
return i;
}
__stdlib_compat int isspace(int c)
{
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
__stdlib_compat int isdigit(int c)
{
return c >= '0' && c <= '9';
}
__stdlib_compat int isalpha(int c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
__stdlib_compat int isupper(int c)
{
return c >= 'A' && c <= 'Z';
}
__stdlib_compat int isprint(int c)
{
return c >= ' ' && c <= '~';
}
__stdlib_compat int tolower(int c)
{
return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c;
}
__stdlib_compat int strncasecmp(const char *s1, const char *s2, size_t size)
{
int diff;
if (!size)
return 0;
do {
diff = tolower(*s1) - tolower(*s2);
if (diff)
return diff;
} while (*(s1++) && *(s2++) && --size);
return 0;
}
__stdlib_compat char *strstr(const char *s1, const char *s2)
{
const char *p, *q, *r;
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
if (len1 == 0 || len2 == 0 || len1 < len2)
return NULL;
r = s1 + len1 - len2 + 1;
for (; s1 < r; s1++) {
if (*s1 == *s2) {
p = s1 + 1;
q = s2 + 1;
for (; q < s2 + len2;) {
if (*p++ != *q++)
break;
}
if (*q == '\0')
return (char *)s1;
}
}
return NULL;
}
__stdlib_compat unsigned long long int strtoull(const char *nptr, char **endptr,
int base)
{
uint64_t result = 0;
int c = '\0';
while ((c = *nptr++) && isspace(c))
;
if (c == '+') {
c = *nptr++;
} else if (c == '-') {
if (endptr)
*endptr = (char *)nptr - 1;
return result;
}
base = find_base(base, &c, &nptr);
while (c) {
if (c >= '0' && c < '0' + MIN(base, 10))
result = result * base + (c - '0');
else if (c >= 'A' && c < 'A' + base - 10)
result = result * base + (c - 'A' + 10);
else if (c >= 'a' && c < 'a' + base - 10)
result = result * base + (c - 'a' + 10);
else
break;
c = *nptr++;
}
if (endptr)
*endptr = (char *)nptr - 1;
return result;
}
BUILD_ASSERT(sizeof(unsigned long long int) == sizeof(uint64_t));
__stdlib_compat int atoi(const char *nptr)
{
int result = 0;
int neg = 0;
char c = '\0';
while ((c = *nptr++) && isspace(c))
;
if (c == '-') {
neg = 1;
c = *nptr++;
}
while (isdigit(c)) {
result = result * 10 + (c - '0');
c = *nptr++;
}
return neg ? -result : result;
}
__keep __stdlib_compat int memcmp(const void *s1, const void *s2, size_t len)
{
const char *sa = s1;
const char *sb = s2;
int diff = 0;
while (len-- > 0) {
diff = *(sa++) - *(sb++);
if (diff)
return diff;
}
return 0;
}
#if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer))
__keep __stdlib_compat void *memcpy(void *dest, const void *src, size_t len)
{
char *d = (char *)dest;
const char *s = (const char *)src;
uint32_t *dw;
const uint32_t *sw;
char *head;
char *const tail = (char *)dest + len;
/* Set 'body' to the last word boundary */
uint32_t *const body = (uint32_t *)((uintptr_t)tail & ~3);
if (((uintptr_t)dest & 3) != ((uintptr_t)src & 3)) {
/* Misaligned. no body, no tail. */
head = tail;
} else {
/* Aligned */
if ((uintptr_t)tail < (((uintptr_t)d + 3) & ~3))
/* len is shorter than the first word boundary */
head = tail;
else
/* Set 'head' to the first word boundary */
head = (char *)(((uintptr_t)d + 3) & ~3);
}
/* Copy head */
while (d < head)
*(d++) = *(s++);
/* Copy body */
dw = (uint32_t *)d;
sw = (uint32_t *)s;
while (dw < body)
*(dw++) = *(sw++);
/* Copy tail */
d = (char *)dw;
s = (const char *)sw;
while (d < tail)
*(d++) = *(s++);
return dest;
}
#endif /* address_sanitizer || memory_sanitizer */
#if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer))
__keep __stdlib_compat __visible void *memset(void *dest, int c, size_t len)
{
char *d = (char *)dest;
uint32_t cccc;
uint32_t *dw;
char *head;
char *const tail = (char *)dest + len;
/* Set 'body' to the last word boundary */
uint32_t *const body = (uint32_t *)((uintptr_t)tail & ~3);
c &= 0xff; /* Clear upper bits before ORing below */
cccc = c | (c << 8) | (c << 16) | (c << 24);
if ((uintptr_t)tail < (((uintptr_t)d + 3) & ~3))
/* len is shorter than the first word boundary */
head = tail;
else
/* Set 'head' to the first word boundary */
head = (char *)(((uintptr_t)d + 3) & ~3);
/* Copy head */
while (d < head)
*(d++) = c;
/* Copy body */
dw = (uint32_t *)d;
while (dw < body)
*(dw++) = cccc;
/* Copy tail */
d = (char *)dw;
while (d < tail)
*(d++) = c;
return dest;
}
#endif /* address_sanitizer || memory_sanitizer */
#if !(__has_feature(address_sanitizer) || __has_feature(memory_sanitizer))
__keep __stdlib_compat void *memmove(void *dest, const void *src, size_t len)
{
if ((uintptr_t)dest <= (uintptr_t)src ||
(uintptr_t)dest >= (uintptr_t)src + len) {
/* Start of destination doesn't overlap source, so just use
* memcpy().
*/
return memcpy(dest, src, len);
} else {
/* Need to copy from tail because there is overlap. */
char *d = (char *)dest + len;
const char *s = (const char *)src + len;
uint32_t *dw;
const uint32_t *sw;
char *head;
char *const tail = (char *)dest;
/* Set 'body' to the last word boundary */
uint32_t *const body = (uint32_t *)(((uintptr_t)tail + 3) & ~3);
if (((uintptr_t)dest & 3) != ((uintptr_t)src & 3)) {
/* Misaligned. no body, no tail. */
head = tail;
} else {
/* Aligned */
if ((uintptr_t)tail > ((uintptr_t)d & ~3))
/* Shorter than the first word boundary */
head = tail;
else
/* Set 'head' to the first word boundary */
head = (char *)((uintptr_t)d & ~3);
}
/* Copy head */
while (d > head)
*(--d) = *(--s);
/* Copy body */
dw = (uint32_t *)d;
sw = (uint32_t *)s;
while (dw > body)
*(--dw) = *(--sw);
/* Copy tail */
d = (char *)dw;
s = (const char *)sw;
while (d > tail)
*(--d) = *(--s);
return dest;
}
}
#endif /* address_sanitizer || memory_sanitizer */
__stdlib_compat void *memchr(const void *buffer, int c, size_t n)
{
char *current = (char *)buffer;
char *end = current + n;
while (current != end) {
if (*current == c)
return current;
current++;
}
return NULL;
}
__stdlib_compat char *strncpy(char *dest, const char *src, size_t n)
{
char *d = dest;
while (n && *src) {
*d++ = *src++;
n--;
}
if (n)
*d = '\0';
return dest;
}
__stdlib_compat int strncmp(const char *s1, const char *s2, size_t n)
{
while (n--) {
if (*s1 != *s2)
return *s1 - *s2;
if (!*s1)
break;
s1++;
s2++;
}
return 0;
}
#endif /* !CONFIG_ZEPHYR */