// Copyright 2026 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// String and memory functions for wasm32.
//
// Clang lowers many visible memory operations directly to wasm bulk-memory
// instructions (memory.copy, memory.fill) when compiled with -mbulk-memory.
// Out-of-line libc symbols are still required for calls that survive to link
// time.

#include <errno.h>
#include <stdint.h>
#include <string.h>

//===----------------------------------------------------------------------===//
// Memory comparison and search
//===----------------------------------------------------------------------===//

void* memcpy(void* dest, const void* src, size_t count) {
  unsigned char* d = (unsigned char*)dest;
  const unsigned char* s = (const unsigned char*)src;
  for (size_t i = 0; i < count; ++i) {
    d[i] = s[i];
  }
  return dest;
}

void* memmove(void* dest, const void* src, size_t count) {
  unsigned char* d = (unsigned char*)dest;
  const unsigned char* s = (const unsigned char*)src;
  if (d == s || count == 0) return dest;
  if (d < s) {
    for (size_t i = 0; i < count; ++i) {
      d[i] = s[i];
    }
  } else {
    for (size_t i = count; i > 0; --i) {
      d[i - 1] = s[i - 1];
    }
  }
  return dest;
}

void* memset(void* dest, int value, size_t count) {
  unsigned char* d = (unsigned char*)dest;
  unsigned char v = (unsigned char)value;
  for (size_t i = 0; i < count; ++i) {
    d[i] = v;
  }
  return dest;
}

int memcmp(const void* left, const void* right, size_t count) {
  const unsigned char* a = (const unsigned char*)left;
  const unsigned char* b = (const unsigned char*)right;
  for (size_t i = 0; i < count; i++) {
    if (a[i] != b[i]) return a[i] < b[i] ? -1 : 1;
  }
  return 0;
}

void* memchr(const void* pointer, int value, size_t count) {
  const unsigned char* p = (const unsigned char*)pointer;
  unsigned char v = (unsigned char)value;
  for (size_t i = 0; i < count; i++) {
    if (p[i] == v) return (void*)(p + i);
  }
  return NULL;
}

//===----------------------------------------------------------------------===//
// String length
//===----------------------------------------------------------------------===//

size_t strlen(const char* string) {
  const char* p = string;
  while (*p) ++p;
  return (size_t)(p - string);
}

size_t strnlen(const char* string, size_t max_length) {
  const char* p = string;
  while (max_length-- && *p) ++p;
  return (size_t)(p - string);
}

//===----------------------------------------------------------------------===//
// String comparison
//===----------------------------------------------------------------------===//

int strcmp(const char* left, const char* right) {
  const unsigned char* a = (const unsigned char*)left;
  const unsigned char* b = (const unsigned char*)right;
  while (*a && *a == *b) {
    ++a;
    ++b;
  }
  return (int)*a - (int)*b;
}

int strncmp(const char* left, const char* right, size_t count) {
  const unsigned char* a = (const unsigned char*)left;
  const unsigned char* b = (const unsigned char*)right;
  while (count && *a && *a == *b) {
    ++a;
    ++b;
    --count;
  }
  if (count == 0) return 0;
  return (int)*a - (int)*b;
}

//===----------------------------------------------------------------------===//
// String search
//===----------------------------------------------------------------------===//

char* strchr(const char* string, int character) {
  char c = (char)character;
  while (*string) {
    if (*string == c) return (char*)string;
    ++string;
  }
  // strchr finds the null terminator when c == '\0'.
  return c == '\0' ? (char*)string : NULL;
}

char* strrchr(const char* string, int character) {
  char c = (char)character;
  const char* last = NULL;
  while (*string) {
    if (*string == c) last = string;
    ++string;
  }
  if (c == '\0') return (char*)string;
  return (char*)last;
}

char* strstr(const char* haystack, const char* needle) {
  if (!*needle) return (char*)haystack;

  for (; *haystack; ++haystack) {
    const char* h = haystack;
    const char* n = needle;
    while (*h && *n && *h == *n) {
      ++h;
      ++n;
    }
    if (!*n) return (char*)haystack;
  }
  return NULL;
}

//===----------------------------------------------------------------------===//
// String copying
//===----------------------------------------------------------------------===//

char* strcpy(char* dest, const char* src) {
  char* d = dest;
  while ((*d++ = *src++)) {
  }
  return dest;
}

char* strncpy(char* dest, const char* src, size_t count) {
  char* d = dest;
  while (count && (*d = *src)) {
    ++d;
    ++src;
    --count;
  }
  // Pad remaining bytes with null.
  while (count--) *d++ = '\0';
  return dest;
}

char* strcat(char* dest, const char* src) {
  char* d = dest;
  while (*d) ++d;
  while ((*d++ = *src++)) {
  }
  return dest;
}

char* strncat(char* dest, const char* src, size_t count) {
  char* d = dest;
  while (*d) ++d;
  while (count-- && (*d = *src)) {
    ++d;
    ++src;
  }
  *d = '\0';
  return dest;
}

//===----------------------------------------------------------------------===//
// Error string
//===----------------------------------------------------------------------===//

// Minimal strerror — returns the error name rather than a human-readable
// message. IREE uses iree_status_code_string() for its own error reporting;
// this is only needed for code that calls strerror directly.
char* strerror(int error_number) {
  switch (error_number) {
    case 0:
      return (char*)"Success";
    case EPERM:
      return (char*)"EPERM";
    case ENOENT:
      return (char*)"ENOENT";
    case EIO:
      return (char*)"EIO";
    case EBADF:
      return (char*)"EBADF";
    case ENOMEM:
      return (char*)"ENOMEM";
    case EACCES:
      return (char*)"EACCES";
    case EEXIST:
      return (char*)"EEXIST";
    case EINVAL:
      return (char*)"EINVAL";
    case ENOSYS:
      return (char*)"ENOSYS";
    case ERANGE:
      return (char*)"ERANGE";
    case EOVERFLOW:
      return (char*)"EOVERFLOW";
    case ENOTSUP:
      return (char*)"ENOTSUP";
    case ETIMEDOUT:
      return (char*)"ETIMEDOUT";
    case ECANCELED:
      return (char*)"ECANCELED";
    default:
      return (char*)"Unknown error";
  }
}
