mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-14 22:21:10 +00:00
347 lines
9.5 KiB
C
347 lines
9.5 KiB
C
/* ----------------------------------------------------------------------------
|
|
Copyright (c) 2021, Daan Leijen
|
|
This is free software; you can redistribute it and/or modify it
|
|
under the terms of the MIT License. A copy of the license can be
|
|
found in the "LICENSE" file at the root of this distribution.
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include "common.h"
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// String wrappers for ssize_t
|
|
//-------------------------------------------------------------
|
|
|
|
ic_private ssize_t ic_strlen( const char* s ) {
|
|
if (s==NULL) return 0;
|
|
return to_ssize_t(strlen(s));
|
|
}
|
|
|
|
ic_private void ic_memmove( void* dest, const void* src, ssize_t n ) {
|
|
assert(dest!=NULL && src != NULL);
|
|
if (n <= 0) return;
|
|
memmove(dest,src,to_size_t(n));
|
|
}
|
|
|
|
|
|
ic_private void ic_memcpy( void* dest, const void* src, ssize_t n ) {
|
|
assert(dest!=NULL && src != NULL);
|
|
if (dest == NULL || src == NULL || n <= 0) return;
|
|
memcpy(dest,src,to_size_t(n));
|
|
}
|
|
|
|
ic_private void ic_memset(void* dest, uint8_t value, ssize_t n) {
|
|
assert(dest!=NULL);
|
|
if (dest == NULL || n <= 0) return;
|
|
memset(dest,(int8_t)value,to_size_t(n));
|
|
}
|
|
|
|
ic_private bool ic_memnmove( void* dest, ssize_t dest_size, const void* src, ssize_t n ) {
|
|
assert(dest!=NULL && src != NULL);
|
|
if (n <= 0) return true;
|
|
if (dest_size < n) { assert(false); return false; }
|
|
memmove(dest,src,to_size_t(n));
|
|
return true;
|
|
}
|
|
|
|
ic_private bool ic_strcpy( char* dest, ssize_t dest_size /* including 0 */, const char* src) {
|
|
assert(dest!=NULL && src != NULL);
|
|
if (dest == NULL || dest_size <= 0) return false;
|
|
ssize_t slen = ic_strlen(src);
|
|
if (slen >= dest_size) return false;
|
|
strcpy(dest,src);
|
|
assert(dest[slen] == 0);
|
|
return true;
|
|
}
|
|
|
|
|
|
ic_private bool ic_strncpy( char* dest, ssize_t dest_size /* including 0 */, const char* src, ssize_t n) {
|
|
assert(dest!=NULL && n < dest_size);
|
|
if (dest == NULL || dest_size <= 0) return false;
|
|
if (n >= dest_size) return false;
|
|
if (src==NULL || n <= 0) {
|
|
dest[0] = 0;
|
|
}
|
|
else {
|
|
strncpy(dest,src,to_size_t(n));
|
|
dest[n] = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------
|
|
// String matching
|
|
//-------------------------------------------------------------
|
|
|
|
ic_public bool ic_starts_with( const char* s, const char* prefix ) {
|
|
if (s==prefix) return true;
|
|
if (prefix==NULL) return true;
|
|
if (s==NULL) return false;
|
|
|
|
ssize_t i;
|
|
for( i = 0; s[i] != 0 && prefix[i] != 0; i++) {
|
|
if (s[i] != prefix[i]) return false;
|
|
}
|
|
return (prefix[i] == 0);
|
|
}
|
|
|
|
ic_private char ic_tolower( char c ) {
|
|
return (c >= 'A' && c <= 'Z' ? c - 'A' + 'a' : c);
|
|
}
|
|
|
|
ic_private void ic_str_tolower(char* s) {
|
|
while(*s != 0) {
|
|
*s = ic_tolower(*s);
|
|
s++;
|
|
}
|
|
}
|
|
|
|
ic_public bool ic_istarts_with( const char* s, const char* prefix ) {
|
|
if (s==prefix) return true;
|
|
if (prefix==NULL) return true;
|
|
if (s==NULL) return false;
|
|
|
|
ssize_t i;
|
|
for( i = 0; s[i] != 0 && prefix[i] != 0; i++) {
|
|
if (ic_tolower(s[i]) != ic_tolower(prefix[i])) return false;
|
|
}
|
|
return (prefix[i] == 0);
|
|
}
|
|
|
|
|
|
ic_private int ic_strnicmp(const char* s1, const char* s2, ssize_t n) {
|
|
if (s1 == NULL && s2 == NULL) return 0;
|
|
if (s1 == NULL) return -1;
|
|
if (s2 == NULL) return 1;
|
|
ssize_t i;
|
|
for (i = 0; s1[i] != 0 && i < n; i++) { // note: if s2[i] == 0 the loop will stop as c1 != c2
|
|
char c1 = ic_tolower(s1[i]);
|
|
char c2 = ic_tolower(s2[i]);
|
|
if (c1 < c2) return -1;
|
|
if (c1 > c2) return 1;
|
|
}
|
|
return ((i >= n || s2[i] == 0) ? 0 : -1);
|
|
}
|
|
|
|
ic_private int ic_stricmp(const char* s1, const char* s2) {
|
|
ssize_t len1 = ic_strlen(s1);
|
|
ssize_t len2 = ic_strlen(s2);
|
|
if (len1 < len2) return -1;
|
|
if (len1 > len2) return 1;
|
|
return (ic_strnicmp(s1, s2, (len1 >= len2 ? len1 : len2)));
|
|
}
|
|
|
|
|
|
static const char* ic_stristr(const char* s, const char* pat) {
|
|
if (s==NULL) return NULL;
|
|
if (pat==NULL || pat[0] == 0) return s;
|
|
ssize_t patlen = ic_strlen(pat);
|
|
for (ssize_t i = 0; s[i] != 0; i++) {
|
|
if (ic_strnicmp(s + i, pat, patlen) == 0) return (s+i);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ic_private bool ic_contains(const char* big, const char* s) {
|
|
if (big == NULL) return false;
|
|
if (s == NULL) return true;
|
|
return (strstr(big,s) != NULL);
|
|
}
|
|
|
|
ic_private bool ic_icontains(const char* big, const char* s) {
|
|
if (big == NULL) return false;
|
|
if (s == NULL) return true;
|
|
return (ic_stristr(big,s) != NULL);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// Unicode
|
|
// QUTF-8: See <https://github.com/koka-lang/koka/blob/master/kklib/include/kklib/string.h>
|
|
// Raw bytes are code points 0xEE000 - 0xEE0FF
|
|
//-------------------------------------------------------------
|
|
#define IC_UNICODE_RAW ((unicode_t)(0xEE000U))
|
|
|
|
ic_private unicode_t unicode_from_raw(uint8_t c) {
|
|
return (IC_UNICODE_RAW + c);
|
|
}
|
|
|
|
ic_private bool unicode_is_raw(unicode_t u, uint8_t* c) {
|
|
if (u >= IC_UNICODE_RAW && u <= IC_UNICODE_RAW + 0xFF) {
|
|
*c = (uint8_t)(u - IC_UNICODE_RAW);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ic_private void unicode_to_qutf8(unicode_t u, uint8_t buf[5]) {
|
|
memset(buf, 0, 5);
|
|
if (u <= 0x7F) {
|
|
buf[0] = (uint8_t)u;
|
|
}
|
|
else if (u <= 0x07FF) {
|
|
buf[0] = (0xC0 | ((uint8_t)(u >> 6)));
|
|
buf[1] = (0x80 | (((uint8_t)u) & 0x3F));
|
|
}
|
|
else if (u <= 0xFFFF) {
|
|
buf[0] = (0xE0 | ((uint8_t)(u >> 12)));
|
|
buf[1] = (0x80 | (((uint8_t)(u >> 6)) & 0x3F));
|
|
buf[2] = (0x80 | (((uint8_t)u) & 0x3F));
|
|
}
|
|
else if (u <= 0x10FFFF) {
|
|
if (unicode_is_raw(u, &buf[0])) {
|
|
buf[1] = 0;
|
|
}
|
|
else {
|
|
buf[0] = (0xF0 | ((uint8_t)(u >> 18)));
|
|
buf[1] = (0x80 | (((uint8_t)(u >> 12)) & 0x3F));
|
|
buf[2] = (0x80 | (((uint8_t)(u >> 6)) & 0x3F));
|
|
buf[3] = (0x80 | (((uint8_t)u) & 0x3F));
|
|
}
|
|
}
|
|
}
|
|
|
|
// is this a utf8 continuation byte?
|
|
ic_private bool utf8_is_cont(uint8_t c) {
|
|
return ((c & 0xC0) == 0x80);
|
|
}
|
|
|
|
ic_private unicode_t unicode_from_qutf8(const uint8_t* s, ssize_t len, ssize_t* count) {
|
|
unicode_t c0 = 0;
|
|
if (len <= 0 || s == NULL) {
|
|
goto fail;
|
|
}
|
|
// 1 byte
|
|
c0 = s[0];
|
|
if (c0 <= 0x7F && len >= 1) {
|
|
if (count != NULL) *count = 1;
|
|
return c0;
|
|
}
|
|
else if (c0 <= 0xC1) { // invalid continuation byte or invalid 0xC0, 0xC1
|
|
goto fail;
|
|
}
|
|
// 2 bytes
|
|
else if (c0 <= 0xDF && len >= 2 && utf8_is_cont(s[1])) {
|
|
if (count != NULL) *count = 2;
|
|
return (((c0 & 0x1F) << 6) | (s[1] & 0x3F));
|
|
}
|
|
// 3 bytes: reject overlong and surrogate halves
|
|
else if (len >= 3 &&
|
|
((c0 == 0xE0 && s[1] >= 0xA0 && s[1] <= 0xBF && utf8_is_cont(s[2])) ||
|
|
(c0 >= 0xE1 && c0 <= 0xEC && utf8_is_cont(s[1]) && utf8_is_cont(s[2]))
|
|
))
|
|
{
|
|
if (count != NULL) *count = 3;
|
|
return (((c0 & 0x0F) << 12) | ((unicode_t)(s[1] & 0x3F) << 6) | (s[2] & 0x3F));
|
|
}
|
|
// 4 bytes: reject overlong
|
|
else if (len >= 4 &&
|
|
(((c0 == 0xF0 && s[1] >= 0x90 && s[1] <= 0xBF && utf8_is_cont(s[2]) && utf8_is_cont(s[3])) ||
|
|
(c0 >= 0xF1 && c0 <= 0xF3 && utf8_is_cont(s[1]) && utf8_is_cont(s[2]) && utf8_is_cont(s[3])) ||
|
|
(c0 == 0xF4 && s[1] >= 0x80 && s[1] <= 0x8F && utf8_is_cont(s[2]) && utf8_is_cont(s[3])))
|
|
))
|
|
{
|
|
if (count != NULL) *count = 4;
|
|
return (((c0 & 0x07) << 18) | ((unicode_t)(s[1] & 0x3F) << 12) | ((unicode_t)(s[2] & 0x3F) << 6) | (s[3] & 0x3F));
|
|
}
|
|
fail:
|
|
if (count != NULL) *count = 1;
|
|
return unicode_from_raw(s[0]);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// Debug
|
|
//-------------------------------------------------------------
|
|
|
|
#if defined(IC_NO_DEBUG_MSG)
|
|
// nothing
|
|
#elif !defined(IC_DEBUG_TO_FILE)
|
|
ic_private void debug_msg(const char* fmt, ...) {
|
|
if (getenv("ISOCLINE_DEBUG")) {
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vfprintf(stderr, fmt, args);
|
|
va_end(args);
|
|
}
|
|
}
|
|
#else
|
|
ic_private void debug_msg(const char* fmt, ...) {
|
|
static int debug_init;
|
|
static const char* debug_fname = "isocline.debug.txt";
|
|
// initialize?
|
|
if (debug_init==0) {
|
|
debug_init = -1;
|
|
const char* rdebug = getenv("ISOCLINE_DEBUG");
|
|
if (rdebug!=NULL && strcmp(rdebug,"1") == 0) {
|
|
FILE* fdbg = fopen(debug_fname, "w");
|
|
if (fdbg!=NULL) {
|
|
debug_init = 1;
|
|
fclose(fdbg);
|
|
}
|
|
}
|
|
}
|
|
if (debug_init <= 0) return;
|
|
|
|
// write debug messages
|
|
FILE* fdbg = fopen(debug_fname, "a");
|
|
if (fdbg==NULL) return;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vfprintf(fdbg, fmt, args);
|
|
fclose(fdbg);
|
|
va_end(args);
|
|
}
|
|
#endif
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// Allocation
|
|
//-------------------------------------------------------------
|
|
|
|
ic_private void* mem_malloc(alloc_t* mem, ssize_t sz) {
|
|
return mem->malloc(to_size_t(sz));
|
|
}
|
|
|
|
ic_private void* mem_zalloc(alloc_t* mem, ssize_t sz) {
|
|
void* p = mem_malloc(mem, sz);
|
|
if (p != NULL) memset(p, 0, to_size_t(sz));
|
|
return p;
|
|
}
|
|
|
|
ic_private void* mem_realloc(alloc_t* mem, void* p, ssize_t newsz) {
|
|
return mem->realloc(p, to_size_t(newsz));
|
|
}
|
|
|
|
ic_private void mem_free(alloc_t* mem, const void* p) {
|
|
mem->free((void*)p);
|
|
}
|
|
|
|
ic_private char* mem_strdup(alloc_t* mem, const char* s) {
|
|
if (s==NULL) return NULL;
|
|
ssize_t n = ic_strlen(s);
|
|
char* p = mem_malloc_tp_n(mem, char, n+1);
|
|
if (p == NULL) return NULL;
|
|
ic_memcpy(p, s, n+1);
|
|
return p;
|
|
}
|
|
|
|
ic_private char* mem_strndup(alloc_t* mem, const char* s, ssize_t n) {
|
|
if (s==NULL || n < 0) return NULL;
|
|
char* p = mem_malloc_tp_n(mem, char, n+1);
|
|
if (p == NULL) return NULL;
|
|
ssize_t i;
|
|
for (i = 0; i < n && s[i] != 0; i++) {
|
|
p[i] = s[i];
|
|
}
|
|
assert(i <= n);
|
|
p[i] = 0;
|
|
return p;
|
|
}
|
|
|