From 65264ba6d1f7e7402009f33b60e3263bf1f02498 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Fri, 17 Apr 2020 18:04:04 -0700 Subject: init: prototype of code skeleton --- .gitignore | 5 + Makefile | 28 ++ compile_commands.json | 47 ++++ include/map.h | 754 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/str.h | 62 +++++ include/u.h | 117 ++++++++ rules.mk | 44 +++ src/.generated/utf8.c | 391 ++++++++++++++++++++++++++ src/error.c | 14 + src/mem.c | 49 ++++ src/rules.mk | 35 +++ src/str.c | 504 +++++++++++++++++++++++++++++++++ 12 files changed, 2050 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 compile_commands.json create mode 100644 include/map.h create mode 100644 include/str.h create mode 100644 include/u.h create mode 100644 rules.mk create mode 100644 src/.generated/utf8.c create mode 100644 src/error.c create mode 100644 src/mem.c create mode 100644 src/rules.mk create mode 100644 src/str.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5238af5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +lib/ +dep/ +build/ +vendor/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..65eb8b5 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +# Compiler, Linker, and Assembler +CC := clang +AR := ar +ASMR := nasm + +# All needed build directories +INC_DIR := include +SRC_DIR := src +BIN_DIR := bin +LIB_DIR := lib +OBJ_DIR := build + +# Flags, Libraries and Includes +INCS := -I$(INC_DIR) +CFLAGS := -g -fno-strict-aliasing -fwrapv -fms-extensions +AFLAGS := -f elf64 + +TGTFLAG := +TGTINCS := +TGTLIBS := + +# Named generic rules (must be evaluated lazily) +COMPILE = $(CC) -MMD $(CFLAGS) $(TGTFLAGS) $(INCS) $(TGTINCS) -o $@ -c $< +LINK = $(CC) -MMD $(CFLAGS) $(TGTFLAGS) -o $@ $^ $(LIBS) $(TGTLIBS) +COMPLINK = $(CC) -MMD $(CFLAGS) $(TGTFLAGS) $(INCS) $(TGTINCS) -o $@ $< $(LIBS) +ARCHIVE = $(AR) -crs $@ $^ + +include rules.mk diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..04f177a --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,47 @@ +[ + { + "arguments": [ + "clang", + "-c", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-Iinclude", + "-o", + "build/coro.o", + "src/coro.c" + ], + "directory": "/home/nolln/libn", + "file": "src/coro.c" + }, + { + "arguments": [ + "clang", + "-c", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-Iinclude", + "-o", + "build/mem.o", + "src/mem.c" + ], + "directory": "/home/nolln/libn", + "file": "src/mem.c" + }, + { + "arguments": [ + "clang", + "-c", + "-fno-strict-aliasing", + "-fwrapv", + "-fms-extensions", + "-Iinclude", + "-o", + "build/string.o", + "src/string.c" + ], + "directory": "/home/nolln/libn", + "file": "src/string.c" + } +] \ No newline at end of file diff --git a/include/map.h b/include/map.h new file mode 100644 index 0000000..f81dc0f --- /dev/null +++ b/include/map.h @@ -0,0 +1,754 @@ +// clang-format off +// +/* The MIT License + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: +#include "khash.h" +HASH_MAP_INIT_INT(32, char) +int main() { + int ret, is·missing; + mapiter_t k; + khash_t(32) *h = map·init(32); + k = map·put(32, h, 5, &ret); + map·value(h, k) = 10; + k = map·get(32, h, 10); + is·missing = (k == map·end(h)); + k = map·get(32, h, 5); + map·del(32, h, k); + for (k = map·begin(h); k != map·end(h); ++k) + if (map·exist(h, k)) map·value(h, k) = 1; + map·destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + * Use quadratic probing. When the capacity is power of 2, stepping + function i*(i+1)/2 guarantees to traverse each bucket. It is better than + double hashing on cache performance and is more robust than linear probing. In + theory, double hashing should be more robust than quadratic probing. However, + my implementation is probably not for large hash tables, because the second + hash function is closely tied to the first hash function, which reduce the + effectiveness of double hashing. Reference: + http://research.cs.vt.edu/AVresearch/hashing/quadratic.php 2011-12-29 (0.2.7): + * Minor code clean up; no actual effect. + 2011-09-16 (0.2.6): + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as + it is more robust to certain non-random input. + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + 2011-02-14 (0.2.5): + * Allow to declare global functions. + 2009-09-26 (0.2.4): + * Improve portability + 2008-09-19 (0.2.3): + * Corrected the example + * Improved interfaces + 2008-09-11 (0.2.2): + * Improved speed a little in map·put() + 2008-09-10 (0.2.1): + * Added map·clear() + * Fixed a compiling error + 2008-09-02 (0.2.0): + * Changed to token concatenation which increases flexibility. + 2008-08-31 (0.1.2): + * Fixed a bug in map·get(), which has not been tested previously. + 2008-08-31 (0.1.1): + * Added destructor +*/ + +// Modified by Nicholas Noll 2019. +// Modified API to be more inline with codebase. + +#pragma once + +/*! + @header + Generic hash table library. + */ + +#define AC_VERSION_HASH_H "0.2.8" + +/* compiler specific configuration */ + +// #if UINT_MAX == 0xffffffffu +// typedef unsigned int mapint32_t; +// #elif ULONG_MAX == 0xffffffffu +// typedef unsigned long mapint32_t; +// #endif +// +// #if ULONG_MAX == ULLONG_MAX +// typedef unsigned long mapint64_t; +// #else +// typedef unsigned long long mapint64_t; +// #endif + +#ifndef map·inline +#ifdef _MSC_VER +#define map·inline __inline +#else +#define map·inline inline +#endif +#endif /* map·inline */ + +#ifndef klib_unused +#if (defined __clang__ && __clang_major__ >= 3) || \ + (defined __GNUC__ && __GNUC__ >= 3) +#define klib_unused __attribute__((__unused__)) +#else +#define klib_unused +#endif +#endif /* klib_unused */ + +typedef int32 mapiter; + +#define __ac_isempty(flag, i) ((flag[i >> 4] >> ((i & 0xfU) << 1)) & 2) +#define __ac_isdel(flag, i) ((flag[i >> 4] >> ((i & 0xfU) << 1)) & 1) +#define __ac_iseither(flag, i) ((flag[i >> 4] >> ((i & 0xfU) << 1)) & 3) +#define __ac_set_isdel_false(flag, i) \ + (flag[i >> 4] &= ~(1ul << ((i & 0xfU) << 1))) +#define __ac_set_isempty_false(flag, i) \ + (flag[i >> 4] &= ~(2ul << ((i & 0xfU) << 1))) +#define __ac_set_isboth_false(flag, i) \ + (flag[i >> 4] &= ~(3ul << ((i & 0xfU) << 1))) +#define __ac_set_isdel_true(flag, i) (flag[i >> 4] |= 1ul << ((i & 0xfU) << 1)) + +#define __ac_fsize(m) ((m) < 16 ? 1 : (m) >> 4) + +#ifndef _roundup32 +#define _roundup32(x) \ + (--(x), \ + (x) |= (x) >> 1, \ + (x) |= (x) >> 2, \ + (x) |= (x) >> 4, \ + (x) |= (x) >> 8, \ + (x) |= (x) >> 16, \ + ++(x)) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __HASH_TYPE(name, map·key_t, map·val·t) \ + typedef struct map·##name##_s { \ + int32 n_buckets, size, n_occupied, upper_bound; \ + int32* flags; \ + map·key_t* keys; \ + map·val·t* vals; \ + } map·##name##_t; + +#define __HASH_PROTOTYPES(name, map·key_t, map·val·t) \ + extern map·##name##_t* map·init·##name(void); \ + extern void map·destroy_##name(map·##name##_t* h); \ + extern void map·clear_##name(map·##name##_t* h); \ + extern int32 map·get_##name(const map·##name##_t* h, mapkey_t key); \ + extern int map·resize_##name(map·##name##_t* h, int32 new_n_buckets); \ + extern int32 map·put_##name(map·##name##_t* h, mapkey_t key, int* ret); \ + extern void map·del_##name(map·##name##_t* h, int32 x); + +#define __HASH_IMPL( \ + name, SCOPE, mapkey_t, mapval·t, map·is·map, __hash_func, __hash_equal) \ + SCOPE map·##name##_t* map·init·##name(void) \ + { \ + return (map·##name##_t*)calloc(1, sizeof(map·##name##_t)); \ + } \ + SCOPE void map·destroy_##name(map·##name##_t* h) \ + { \ + if (h) \ + { \ + free((void*)h->keys); \ + free(h->flags); \ + free((void*)h->vals); \ + free(h); \ + } \ + } \ + SCOPE void map·clear_##name(map·##name##_t* h) \ + { \ + if (h && h->flags) \ + { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(int32)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE int32 map·get_##name(const map·##name##_t* h, mapkey_t key) \ + { \ + if (h->n_buckets) \ + { \ + int32 k = 0; \ + int32 i = 0; \ + int32 last = 0; \ + int32 mask = 0; \ + int32 step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); \ + i = k & mask; \ + last = i; \ + while ( \ + !__ac_isempty(h->flags, i) && \ + (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) \ + { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i) ? h->n_buckets : i; \ + } else \ + return 0; \ + } \ + SCOPE int map·resize_##name(map·##name##_t* h, int32 new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of \ + [sizeof(key_t+val·t)+.25]*n_buckets. */ \ + int32* new_flags = 0; \ + int32 j = 1; \ + { \ + _roundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (int32)(new_n_buckets * __ac_HASH_UPPER + 0.5)) \ + j = 0; /* requested size is too small */ \ + else \ + { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = \ + (int32*)malloc(__ac_fsize(new_n_buckets) * sizeof(int32)); \ + if (!new_flags) return -1; \ + memset( \ + new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(int32)); \ + if (h->n_buckets < new_n_buckets) \ + { /* expand */ \ + mapkey_t* new_keys = (mapkey_t*)realloc( \ + (void*)h->keys, new_n_buckets * sizeof(mapkey_t)); \ + if (!new_keys) \ + { \ + free(new_flags); \ + return -1; \ + } \ + h->keys = new_keys; \ + if (map·is·map) \ + { \ + mapval·t* new_vals = (mapval·t*)realloc( \ + (void*)h->vals, new_n_buckets * sizeof(mapval·t)); \ + if (!new_vals) \ + { \ + free(new_flags); \ + return -1; \ + } \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) \ + { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) \ + { \ + if (__ac_iseither(h->flags, j) == 0) \ + { \ + mapkey_t key = h->keys[j]; \ + mapval·t val; \ + int32 new_mask; \ + new_mask = new_n_buckets - 1; \ + if (map·is·map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) \ + { /* kick-out process; sort of like in Cuckoo hashing */ \ + int32 k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) \ + i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && \ + __ac_iseither(h->flags, i) == 0) \ + { /* kick out the existing element */ \ + { \ + mapkey_t tmp = h->keys[i]; \ + h->keys[i] = key; \ + key = tmp; \ + } \ + if (map·is·map) \ + { \ + mapval·t tmp = h->vals[i]; \ + h->vals[i] = val; \ + val = tmp; \ + } \ + __ac_set_isdel_true(h->flags, \ + i); /* mark it as deleted in \ + the old hash table */ \ + } else \ + { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (map·is·map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) \ + { /* shrink the hash table */ \ + h->keys = (mapkey_t*)realloc((void*)h->keys, \ + new_n_buckets * sizeof(mapkey_t)); \ + if (map·is·map) \ + h->vals = (mapval·t*)realloc( \ + (void*)h->vals, new_n_buckets * sizeof(mapval·t)); \ + } \ + free(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (int32)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE int32 map·put_##name(map·##name##_t* h, mapkey_t key, int* ret) \ + { \ + int32 x = 0; \ + if (h->n_occupied >= h->upper_bound) \ + { /* update the hash table */ \ + if (h->n_buckets > (h->size << 1)) \ + { \ + if (map·resize_##name(h, h->n_buckets - 1) < 0) \ + { /* clear "deleted" elements */ \ + *ret = -1; \ + return h->n_buckets; \ + } \ + } else if (map·resize_##name(h, h->n_buckets + 1) < 0) \ + { /* expand the hash table */ \ + *ret = -1; \ + return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already \ + support shrinking */ \ + { \ + int32 k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; \ + k = __hash_func(key); \ + i = k & mask; \ + if (__ac_isempty(h->flags, i)) \ + x = i; /* for speed up */ \ + else \ + { \ + last = i; \ + while ( \ + !__ac_isempty(h->flags, i) && \ + (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) \ + { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) \ + { \ + x = site; \ + break; \ + } \ + } \ + if (x == h->n_buckets) \ + { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) \ + x = site; \ + else \ + x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) \ + { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) \ + { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else \ + *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void map·del_##name(map·##name##_t* h, int32 x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) \ + { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define HASH_DECLARE(name, mapkey_t, mapval·t) \ + __HASH_TYPE(name, mapkey_t, mapval·t) \ + __HASH_PROTOTYPES(name, mapkey_t, mapval·t) + +#define MAP·MAKE2( \ + name, SCOPE, mapkey_t, mapval·t, map·is·map, __hash_func, __hash_equal) \ + __HASH_TYPE(name, mapkey_t, mapval·t) \ + __HASH_IMPL( \ + name, SCOPE, mapkey_t, mapval·t, map·is·map, __hash_func, __hash_equal) + +#define MAP·MAKE( \ + name, mapkey_t, mapval·t, map·is·map, __hash_func, __hash_equal) \ + MAP·MAKE2(name, \ + static map·inline klib_unused, \ + mapkey_t, \ + mapval·t, \ + map·is·map, \ + __hash_func, \ + __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [mapint32_t] + @return The hash value [mapint_t] + */ +#define map·int_hash_func(key) (int32)(key) +/*! @function + @abstract Integer comparison function + */ +#define map·int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [mapint64_t] + @return The hash value [mapint_t] + */ +#define map·int64_hash_func(key) (int32)((key) >> 33 ^ (key) ^ (key) << 11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define map·int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static map·inline int32 + __ac_X31_hash_string(const char* s) +{ + int32 h = (int32)(*s); + if (h != 0) { + for (const char* it = s; *it; ++it) + h = (h << 5) - h + (int32)*it; + } + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [mapint_t] + */ +#define map·str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define map·str_hash_equal(a, b) (strcmp(a, b) == 0) + +static inline int32 +__ac_Wang_hash(int32 key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define map·int_hash_func2(key) __ac_Wang_hash((int32)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define map·t(name) map·##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define map·init(name) map·init·##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define map·destroy(name, h) map·destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define map·clear(name, h) map·clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [mapint_t] + */ +#define map·resize(name, h, s) map·resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: -1 if the operation failed; + 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [mapint_t] + */ +#define map·put(name, h, k, r) map·put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or map·end(h) if the element is + absent [mapint_t] + */ +#define map·get(name, h, k) map·get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [mapint_t] + */ +#define map·del(name, h, k) map·del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [mapint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define map·exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [mapint_t] + @return Key [type of keys] + */ +#define map·key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [mapint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define map·val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of map·val() + */ +#define map·value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [mapint_t] + */ +#define map·begin(h) (int32)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [mapint_t] + */ +#define map·end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [mapint_t] + */ +#define map·len(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [mapint_t] + */ +#define map·n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define map·foreach(h, kvar, vvar, code) \ + { \ + int32 __i; \ + for (__i = map·begin(h); __i != map·end(h); ++__i) \ + { \ + if (!map·exist(h, __i)) continue; \ + (kvar) = map·key(h, __i); \ + (vvar) = map·val(h, __i); \ + code; \ + } \ + } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define map·foreach_val(h, vvar, code) \ + { \ + int32 __i; \ + for (__i = map·begin(h); __i != map·end(h); ++__i) \ + { \ + if (!map·exist(h, __i)) continue; \ + (vvar) = map·val(h, __i); \ + code; \ + } \ + } + +/*! @function + @abstract Iterate over the keys in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param code Block of code to execute + */ +#define map·foreach_key(h, kvar, code) \ + { \ + int32 __i; \ + for (__i = map·begin(h); __i != map·end(h); ++__i) \ + { \ + if (!map·exist(h, __i)) continue; \ + (kvar) = map·key(h, __i); \ + code; \ + } \ + } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param code Block of code to execute for free operation. + */ +#define map·free_all(h, code) \ + { \ + int32 __i; \ + for (__i = map·begin(h); __i != map·end(h); ++__i) \ + { \ + if (!map·exist(h, __i)) continue; \ + code(map·val(h, __i)); \ + } \ + } + +/* More convenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define define_int32_set(name) \ + MAP·MAKE(name, int32, char, 0, map·int_hash_func, map·int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param mapval·t Type of values [type] + */ +#define define_int32_map(name, mapval·t) \ + MAP·MAKE(name, int32, mapval·t, 1, map·int_hash_func, map·int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param mapval·t Type of values [type] + */ +#define define_uint32_set(name) \ + MAP·MAKE(name, uint32, char, 0, map·int_hash_func, map·int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param mapval·t Type of values [type] + */ +#define define_uint32_map(name, mapval·t) \ + MAP·MAKE(name, uint32, mapval·t, 1, map·int_hash_func, map·int_hash_equal) + +/*! @function + @abstract Instantiate a hash set containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define define_int64_set(name) \ + MAP·MAKE(name, int64, char, 0, map·int64_hash_func, map·int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param mapval·t Type of values [type] + */ +#define define_int64_map(name, mapval·t) \ + MAP·MAKE(name, int64, mapval·t, 1, map·int64_hash_func, map·int64_hash_equal) + +/*! @function + @abstract Instantiate a hash set containing 64-bit unsigned integer keys + @param name Name of the hash table [symbol] + */ +#define define_uint64_set(name) \ + MAP·MAKE(name, uint64, char, 0, map·int64_hash_func, map·int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit unsigned integer keys + @param name Name of the hash table [symbol] + @param mapval·t Type of values [type] + */ +#define define_uint64_map(name, mapval·t) \ + MAP·MAKE(name, uint64, mapval·t, 1, map·int64_hash_func, map·int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define define_str_hashset(name) \ + MAP·MAKE(name, const byte*, byte, 0, map·str_hash_func, map·str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param mapval·t Type of values [type] + */ +#define define_str·map(name, mapval·t) \ + MAP·MAKE(name, const byte*, mapval·t, 1, map·str_hash_func, map·str_hash_equal) diff --git a/include/str.h b/include/str.h new file mode 100644 index 0000000..c728bf0 --- /dev/null +++ b/include/str.h @@ -0,0 +1,62 @@ +#pragma once + +typedef byte* string; + +typedef struct str·Hdr +{ + vlong len; + vlong cap; + byte buf[]; +} str·Hdr; + +// ------------------------------------------------------------------------- +// UTF-8 functions. +// Perhaps break into own unit +// TODO: Add to(upper|lower|title) + +typedef uint32 Rune; + +enum +{ + UTFmax = 4, + RuneSync = 0x80, + RuneSelf = 0x80, + RuneErr = 0xFFFD, + RuneMax = 0x10FFFF, +}; + +int utf8·FullRune(byte* s, int n); +byte *utf8·FindRune(byte* s, long i); +int utf8·CharToRune(Rune *r, byte* s); +int utf8·RuneToChar(byte* s, Rune* r); +int utf8·Len(byte* s); +int utf8·RuneLen(Rune r); +int utf8·IsLetter(Rune r); +int utf8·IsDigit(Rune r); +int utf8·IsSpace(Rune r); +int utf8·IsTitle(Rune r); + +// ------------------------------------------------------------------------- +// Dynamic string functions + +string str·NewCap(const byte* s, vlong len, vlong cap); +string str·NewLen(const byte* s, vlong len); +string str·New(const byte* s); +string str·Newf(const byte* fmt, ...); +void str·Free(string s); +int str·Len(const string s); +int str·Cap(const string s); +string str·Clear(string s); +string str·Grow(string s, vlong delta); +string str·Fit(string s); +string str·AppendCount(string s, const byte* b, vlong len); +string str·Append(string s, const byte* b); +string str·Appendf(string s, const byte* fmt, ...); +string str·AppendByte(string s, const byte b); +bool str·Equals(const string s, const string t); +int str·Find(string s, const byte* substr); +void str·Lower(string s); +void str·Upper(string s); +void str·Replace(string s, const byte* from, const byte* to); +string* str·Split(string s, const byte* tok); +string str·Join(byte** fields, vlong numFields, const byte* sep); diff --git a/include/u.h b/include/u.h new file mode 100644 index 0000000..349a236 --- /dev/null +++ b/include/u.h @@ -0,0 +1,117 @@ +#pragma once + +// ------------------------------------------------------------------------ +// Standard library + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// ------------------------------------------------------------------------ +// Modern type aliases + +typedef char byte; +typedef unsigned char ubyte; +typedef signed char sbyte; + +typedef long long vlong; +typedef unsigned long long uvlong; + +typedef unsigned int uint; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef float float32; +typedef double float64; + +typedef uintptr_t uintptr; +typedef intptr_t intptr; + +typedef int error; + +#define nil NULL + +// ---------------------------------------------------------------------------- +// Dynamic array. + +typedef struct bufHdr +{ + vlong len; + vlong cap; + byte buf[]; +} bufHdr; + +#define _bufHdr(s) ((bufHdr*)((uint8*)(s)-offsetof(bufHdr, buf))) +#define buflen(s) ((s) ? (_bufHdr(s)->len) : 0) +#define bufcap(s) ((s) ? (_bufHdr(s)->cap) : 0) +#define bufend(s) ((s) + buflen(s)) +#define bufsize(s) ((s) ? (buflen(s) * sizeof((s)[0])) : 0) + +#define buffree(s) ((s) ? (free(_bufHdr(s)), (s) = nil) : 0) +#define buffit(s, n) ((n) <= bufcap(s) ? 0 : ((s) = bufgrow((s), (n), sizeof(*(s))))) + +#define bufresize(s, n) \ + do { \ + (buffit(s, n)); \ + ((_bufHdr(s)->len) = (n)); \ + } while (0) + +#define bufpush(s, ...) (buffit((s), 1 + buflen(s)), (s)[_bufHdr(s)->len++] = (__VA_ARGS__)) + +#define bufpop(s, i) (_bufpop((s), (i), sizeof(*(s))), (s)[_bufHdr(s)->len]) + +void* bufgrow(void*, vlong, vlong); +void _bufpop(void*, int, vlong); + +// ----------------------------------------------------------------------------- +// Strings + +#include "str.h" + +// ----------------------------------------------------------------------------- +// Maps or dictionaries + +#include "map.h" + +// ------------------------------------------------------------------ +// Global macros + +#ifndef RELEASE +#define Assert(x) assert(x) +#else +#define Assert(x) +#endif + +#define arrlen(Array) (sizeof(Array) / sizeof((Array)[0])) + +#define MAX(x, y) ((x) >= (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +// ----------------------------------------------------------------------------- +// Error handling functions. + +void errorf(const byte* fmt, ...); + +#define panicf(...) (errorf(__VA_ARGS__), assert(0)) diff --git a/rules.mk b/rules.mk new file mode 100644 index 0000000..cc495d6 --- /dev/null +++ b/rules.mk @@ -0,0 +1,44 @@ +# Standard housekeeping +.PHONY: all clean install +.SUFFIXES: +.SUFFIXES: .c .o + +all: targets + +# Targets & array of sources & intermediates +SRCS := +OBJS := +DEPS := + +LIBS := +BINS := + +# Iterate through directory tree +DIR := src +include $(DIR)/rules.mk +# ... (repeat) + +# Generic rules +%.a: %.o + $(ARCHIVE) + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c + $(COMPILE) + +%: %.o + $(LINK) + +$(OBJ_DIR)/%: $(SRC_DIR)/%.c + $(COMPLNK) + +.PHONY: targets +targets: $(LIBS) $(BINS) + +.PHONY: clean +clean: + rm $(OBJS) $(DEPS) + rm $(LIBS) $(BINS) + +.PHONY: install +install: targets + echo "TODO" diff --git a/src/.generated/utf8.c b/src/.generated/utf8.c new file mode 100644 index 0000000..862f100 --- /dev/null +++ b/src/.generated/utf8.c @@ -0,0 +1,391 @@ +Rune* +rbsearch(Rune c, Rune* t, int n, int nelem) +{ + Rune* p; + int m; + + while (n > 1) { + m = n >> 1; + p = t + m * nelem; + if (c >= p[0]) { + t = p; + n -= m; + } else + n = m; + } + + if (n && c >= t[0]) + return t; + else + return 0; +} +static Rune isspace_rtab[] = { + 0x0009, 0x000c, 0x2000, 0x200a, 0x2028, 0x2029, +}; + +static Rune isspace_stab[] = { + 0x0020, 0x0085, 0x00a0, 0x1680, 0x202f, 0x205f, 0x3000, +}; + +int +utf8·IsSpace(Rune c) +{ + Rune* p; + + p = rbsearch(c, isspace_rtab, arrlen(isspace_rtab) / 2, 2); + if (p && c >= p[0] && c <= p[1]) return 1; + p = rbsearch(c, isspace_stab, arrlen(isspace_stab), 1); + if (p && c == p[0]) return 1; + + return 0; +} + +static Rune islower_rtab[] = { + 0x0061, 0x007a, 0x00df, 0x00f6, 0x00f8, 0x00ff, 0x0137, 0x0138, 0x0148, 0x0149, + 0x017e, 0x0180, 0x018c, 0x018d, 0x0199, 0x019b, 0x01aa, 0x01ab, 0x01b9, 0x01ba, + 0x01bd, 0x01bf, 0x01dc, 0x01dd, 0x01ef, 0x01f0, 0x0233, 0x0239, 0x023f, 0x0240, + 0x024f, 0x0293, 0x0295, 0x02af, 0x037b, 0x037d, 0x03ac, 0x03ce, 0x03d0, 0x03d1, + 0x03d5, 0x03d7, 0x03ef, 0x03f3, 0x03fb, 0x03fc, 0x0430, 0x045f, 0x04ce, 0x04cf, + 0x0560, 0x0588, 0x10d0, 0x10fa, 0x10fd, 0x10ff, 0x13f8, 0x13fd, 0x1c80, 0x1c88, + 0x1d00, 0x1d2b, 0x1d6b, 0x1d77, 0x1d79, 0x1d9a, 0x1e95, 0x1e9d, 0x1eff, 0x1f07, + 0x1f10, 0x1f15, 0x1f20, 0x1f27, 0x1f30, 0x1f37, 0x1f40, 0x1f45, 0x1f50, 0x1f57, + 0x1f60, 0x1f67, 0x1f70, 0x1f7d, 0x1f80, 0x1f87, 0x1f90, 0x1f97, 0x1fa0, 0x1fa7, + 0x1fb0, 0x1fb4, 0x1fb6, 0x1fb7, 0x1fc2, 0x1fc4, 0x1fc6, 0x1fc7, 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fd7, 0x1fe0, 0x1fe7, 0x1ff2, 0x1ff4, 0x1ff6, 0x1ff7, 0x210e, 0x210f, + 0x213c, 0x213d, 0x2146, 0x2149, 0x2c30, 0x2c5e, 0x2c65, 0x2c66, 0x2c73, 0x2c74, + 0x2c76, 0x2c7b, 0x2ce3, 0x2ce4, 0x2d00, 0x2d25, 0xa72f, 0xa731, 0xa771, 0xa778, + 0xa793, 0xa795, 0xab30, 0xab5a, 0xab60, 0xab65, 0xab70, 0xabbf, 0xfb00, 0xfb06, + 0xfb13, 0xfb17, 0xff41, 0xff5a, 0x10428, 0x1044f, 0x104d8, 0x104fb, 0x10cc0, 0x10cf2, + 0x118c0, 0x118df, 0x16e60, 0x16e7f, 0x1d41a, 0x1d433, 0x1d44e, 0x1d454, 0x1d456, 0x1d467, + 0x1d482, 0x1d49b, 0x1d4b6, 0x1d4b9, 0x1d4bd, 0x1d4c3, 0x1d4c5, 0x1d4cf, 0x1d4ea, 0x1d503, + 0x1d51e, 0x1d537, 0x1d552, 0x1d56b, 0x1d586, 0x1d59f, 0x1d5ba, 0x1d5d3, 0x1d5ee, 0x1d607, + 0x1d622, 0x1d63b, 0x1d656, 0x1d66f, 0x1d68a, 0x1d6a5, 0x1d6c2, 0x1d6da, 0x1d6dc, 0x1d6e1, + 0x1d6fc, 0x1d714, 0x1d716, 0x1d71b, 0x1d736, 0x1d74e, 0x1d750, 0x1d755, 0x1d770, 0x1d788, + 0x1d78a, 0x1d78f, 0x1d7aa, 0x1d7c2, 0x1d7c4, 0x1d7c9, +}; + +static Rune islower_stab[] = { + 0x00b5, 0x0101, 0x0103, 0x0105, 0x0107, 0x0109, 0x010b, 0x010d, 0x010f, 0x0111, 0x0113, + 0x0115, 0x0117, 0x0119, 0x011b, 0x011d, 0x011f, 0x0121, 0x0123, 0x0125, 0x0127, 0x0129, + 0x012b, 0x012d, 0x012f, 0x0131, 0x0133, 0x0135, 0x013a, 0x013c, 0x013e, 0x0140, 0x0142, + 0x0144, 0x0146, 0x014b, 0x014d, 0x014f, 0x0151, 0x0153, 0x0155, 0x0157, 0x0159, 0x015b, + 0x015d, 0x015f, 0x0161, 0x0163, 0x0165, 0x0167, 0x0169, 0x016b, 0x016d, 0x016f, 0x0171, + 0x0173, 0x0175, 0x0177, 0x017a, 0x017c, 0x0183, 0x0185, 0x0188, 0x0192, 0x0195, 0x019e, + 0x01a1, 0x01a3, 0x01a5, 0x01a8, 0x01ad, 0x01b0, 0x01b4, 0x01b6, 0x01c6, 0x01c9, 0x01cc, + 0x01ce, 0x01d0, 0x01d2, 0x01d4, 0x01d6, 0x01d8, 0x01da, 0x01df, 0x01e1, 0x01e3, 0x01e5, + 0x01e7, 0x01e9, 0x01eb, 0x01ed, 0x01f3, 0x01f5, 0x01f9, 0x01fb, 0x01fd, 0x01ff, 0x0201, + 0x0203, 0x0205, 0x0207, 0x0209, 0x020b, 0x020d, 0x020f, 0x0211, 0x0213, 0x0215, 0x0217, + 0x0219, 0x021b, 0x021d, 0x021f, 0x0221, 0x0223, 0x0225, 0x0227, 0x0229, 0x022b, 0x022d, + 0x022f, 0x0231, 0x023c, 0x0242, 0x0247, 0x0249, 0x024b, 0x024d, 0x0371, 0x0373, 0x0377, + 0x0390, 0x03d9, 0x03db, 0x03dd, 0x03df, 0x03e1, 0x03e3, 0x03e5, 0x03e7, 0x03e9, 0x03eb, + 0x03ed, 0x03f5, 0x03f8, 0x0461, 0x0463, 0x0465, 0x0467, 0x0469, 0x046b, 0x046d, 0x046f, + 0x0471, 0x0473, 0x0475, 0x0477, 0x0479, 0x047b, 0x047d, 0x047f, 0x0481, 0x048b, 0x048d, + 0x048f, 0x0491, 0x0493, 0x0495, 0x0497, 0x0499, 0x049b, 0x049d, 0x049f, 0x04a1, 0x04a3, + 0x04a5, 0x04a7, 0x04a9, 0x04ab, 0x04ad, 0x04af, 0x04b1, 0x04b3, 0x04b5, 0x04b7, 0x04b9, + 0x04bb, 0x04bd, 0x04bf, 0x04c2, 0x04c4, 0x04c6, 0x04c8, 0x04ca, 0x04cc, 0x04d1, 0x04d3, + 0x04d5, 0x04d7, 0x04d9, 0x04db, 0x04dd, 0x04df, 0x04e1, 0x04e3, 0x04e5, 0x04e7, 0x04e9, + 0x04eb, 0x04ed, 0x04ef, 0x04f1, 0x04f3, 0x04f5, 0x04f7, 0x04f9, 0x04fb, 0x04fd, 0x04ff, + 0x0501, 0x0503, 0x0505, 0x0507, 0x0509, 0x050b, 0x050d, 0x050f, 0x0511, 0x0513, 0x0515, + 0x0517, 0x0519, 0x051b, 0x051d, 0x051f, 0x0521, 0x0523, 0x0525, 0x0527, 0x0529, 0x052b, + 0x052d, 0x052f, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, + 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, + 0x1e29, 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, 0x1e3b, 0x1e3d, + 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, + 0x1e55, 0x1e57, 0x1e59, 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, + 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, 0x1e7b, 0x1e7d, 0x1e7f, + 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e9f, + 0x1ea1, 0x1ea3, 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, + 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, + 0x1ecd, 0x1ecf, 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, + 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, 0x1ef3, 0x1ef5, 0x1ef7, + 0x1ef9, 0x1efb, 0x1efd, 0x1fbe, 0x210a, 0x2113, 0x212f, 0x2134, 0x2139, 0x214e, 0x2184, + 0x2c61, 0x2c68, 0x2c6a, 0x2c6c, 0x2c71, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, + 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, 0x2c9d, 0x2c9f, 0x2ca1, + 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, + 0x2cb9, 0x2cbb, 0x2cbd, 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, + 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2cdf, 0x2ce1, 0x2cec, + 0x2cee, 0x2cf3, 0x2d27, 0x2d2d, 0xa641, 0xa643, 0xa645, 0xa647, 0xa649, 0xa64b, 0xa64d, + 0xa64f, 0xa651, 0xa653, 0xa655, 0xa657, 0xa659, 0xa65b, 0xa65d, 0xa65f, 0xa661, 0xa663, + 0xa665, 0xa667, 0xa669, 0xa66b, 0xa66d, 0xa681, 0xa683, 0xa685, 0xa687, 0xa689, 0xa68b, + 0xa68d, 0xa68f, 0xa691, 0xa693, 0xa695, 0xa697, 0xa699, 0xa69b, 0xa723, 0xa725, 0xa727, + 0xa729, 0xa72b, 0xa72d, 0xa733, 0xa735, 0xa737, 0xa739, 0xa73b, 0xa73d, 0xa73f, 0xa741, + 0xa743, 0xa745, 0xa747, 0xa749, 0xa74b, 0xa74d, 0xa74f, 0xa751, 0xa753, 0xa755, 0xa757, + 0xa759, 0xa75b, 0xa75d, 0xa75f, 0xa761, 0xa763, 0xa765, 0xa767, 0xa769, 0xa76b, 0xa76d, + 0xa76f, 0xa77a, 0xa77c, 0xa77f, 0xa781, 0xa783, 0xa785, 0xa787, 0xa78c, 0xa78e, 0xa791, + 0xa797, 0xa799, 0xa79b, 0xa79d, 0xa79f, 0xa7a1, 0xa7a3, 0xa7a5, 0xa7a7, 0xa7a9, 0xa7af, + 0xa7b5, 0xa7b7, 0xa7b9, 0xa7fa, 0x1d4bb, 0x1d7cb, +}; + +int +utf8·IsLower(Rune c) +{ + Rune* p; + + p = rbsearch(c, islower_rtab, arrlen(islower_rtab) / 2, 2); + if (p && c >= p[0] && c <= p[1]) return 1; + p = rbsearch(c, islower_stab, arrlen(islower_stab), 1); + if (p && c == p[0]) return 1; + + return 0; +} + +static Rune isupper_rtab[] = { + 0x0041, 0x005a, 0x00c0, 0x00d6, 0x00d8, 0x00de, 0x0178, 0x0179, 0x0181, 0x0182, + 0x0186, 0x0187, 0x0189, 0x018b, 0x018e, 0x0191, 0x0193, 0x0194, 0x0196, 0x0198, + 0x019c, 0x019d, 0x019f, 0x01a0, 0x01a6, 0x01a7, 0x01ae, 0x01af, 0x01b1, 0x01b3, + 0x01b7, 0x01b8, 0x01f6, 0x01f8, 0x023a, 0x023b, 0x023d, 0x023e, 0x0243, 0x0246, + 0x0388, 0x038a, 0x038e, 0x038f, 0x0391, 0x03a1, 0x03a3, 0x03ab, 0x03d2, 0x03d4, + 0x03f9, 0x03fa, 0x03fd, 0x042f, 0x04c0, 0x04c1, 0x0531, 0x0556, 0x10a0, 0x10c5, + 0x13a0, 0x13f5, 0x1c90, 0x1cba, 0x1cbd, 0x1cbf, 0x1f08, 0x1f0f, 0x1f18, 0x1f1d, + 0x1f28, 0x1f2f, 0x1f38, 0x1f3f, 0x1f48, 0x1f4d, 0x1f68, 0x1f6f, 0x1fb8, 0x1fbb, + 0x1fc8, 0x1fcb, 0x1fd8, 0x1fdb, 0x1fe8, 0x1fec, 0x1ff8, 0x1ffb, 0x210b, 0x210d, + 0x2110, 0x2112, 0x2119, 0x211d, 0x212a, 0x212d, 0x2130, 0x2133, 0x213e, 0x213f, + 0x2c00, 0x2c2e, 0x2c62, 0x2c64, 0x2c6d, 0x2c70, 0x2c7e, 0x2c80, 0xa77d, 0xa77e, + 0xa7aa, 0xa7ae, 0xa7b0, 0xa7b4, 0xff21, 0xff3a, 0x10400, 0x10427, 0x104b0, 0x104d3, + 0x10c80, 0x10cb2, 0x118a0, 0x118bf, 0x16e40, 0x16e5f, 0x1d400, 0x1d419, 0x1d434, 0x1d44d, + 0x1d468, 0x1d481, 0x1d49e, 0x1d49f, 0x1d4a5, 0x1d4a6, 0x1d4a9, 0x1d4ac, 0x1d4ae, 0x1d4b5, + 0x1d4d0, 0x1d4e9, 0x1d504, 0x1d505, 0x1d507, 0x1d50a, 0x1d50d, 0x1d514, 0x1d516, 0x1d51c, + 0x1d538, 0x1d539, 0x1d53b, 0x1d53e, 0x1d540, 0x1d544, 0x1d54a, 0x1d550, 0x1d56c, 0x1d585, + 0x1d5a0, 0x1d5b9, 0x1d5d4, 0x1d5ed, 0x1d608, 0x1d621, 0x1d63c, 0x1d655, 0x1d670, 0x1d689, + 0x1d6a8, 0x1d6c0, 0x1d6e2, 0x1d6fa, 0x1d71c, 0x1d734, 0x1d756, 0x1d76e, 0x1d790, 0x1d7a8, +}; + +static Rune isupper_stab[] = { + 0x0100, 0x0102, 0x0104, 0x0106, 0x0108, 0x010a, 0x010c, 0x010e, 0x0110, 0x0112, 0x0114, + 0x0116, 0x0118, 0x011a, 0x011c, 0x011e, 0x0120, 0x0122, 0x0124, 0x0126, 0x0128, 0x012a, + 0x012c, 0x012e, 0x0130, 0x0132, 0x0134, 0x0136, 0x0139, 0x013b, 0x013d, 0x013f, 0x0141, + 0x0143, 0x0145, 0x0147, 0x014a, 0x014c, 0x014e, 0x0150, 0x0152, 0x0154, 0x0156, 0x0158, + 0x015a, 0x015c, 0x015e, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168, 0x016a, 0x016c, 0x016e, + 0x0170, 0x0172, 0x0174, 0x0176, 0x017b, 0x017d, 0x0184, 0x01a2, 0x01a4, 0x01a9, 0x01ac, + 0x01b5, 0x01bc, 0x01c4, 0x01c7, 0x01ca, 0x01cd, 0x01cf, 0x01d1, 0x01d3, 0x01d5, 0x01d7, + 0x01d9, 0x01db, 0x01de, 0x01e0, 0x01e2, 0x01e4, 0x01e6, 0x01e8, 0x01ea, 0x01ec, 0x01ee, + 0x01f1, 0x01f4, 0x01fa, 0x01fc, 0x01fe, 0x0200, 0x0202, 0x0204, 0x0206, 0x0208, 0x020a, + 0x020c, 0x020e, 0x0210, 0x0212, 0x0214, 0x0216, 0x0218, 0x021a, 0x021c, 0x021e, 0x0220, + 0x0222, 0x0224, 0x0226, 0x0228, 0x022a, 0x022c, 0x022e, 0x0230, 0x0232, 0x0241, 0x0248, + 0x024a, 0x024c, 0x024e, 0x0370, 0x0372, 0x0376, 0x037f, 0x0386, 0x038c, 0x03cf, 0x03d8, + 0x03da, 0x03dc, 0x03de, 0x03e0, 0x03e2, 0x03e4, 0x03e6, 0x03e8, 0x03ea, 0x03ec, 0x03ee, + 0x03f4, 0x03f7, 0x0460, 0x0462, 0x0464, 0x0466, 0x0468, 0x046a, 0x046c, 0x046e, 0x0470, + 0x0472, 0x0474, 0x0476, 0x0478, 0x047a, 0x047c, 0x047e, 0x0480, 0x048a, 0x048c, 0x048e, + 0x0490, 0x0492, 0x0494, 0x0496, 0x0498, 0x049a, 0x049c, 0x049e, 0x04a0, 0x04a2, 0x04a4, + 0x04a6, 0x04a8, 0x04aa, 0x04ac, 0x04ae, 0x04b0, 0x04b2, 0x04b4, 0x04b6, 0x04b8, 0x04ba, + 0x04bc, 0x04be, 0x04c3, 0x04c5, 0x04c7, 0x04c9, 0x04cb, 0x04cd, 0x04d0, 0x04d2, 0x04d4, + 0x04d6, 0x04d8, 0x04da, 0x04dc, 0x04de, 0x04e0, 0x04e2, 0x04e4, 0x04e6, 0x04e8, 0x04ea, + 0x04ec, 0x04ee, 0x04f0, 0x04f2, 0x04f4, 0x04f6, 0x04f8, 0x04fa, 0x04fc, 0x04fe, 0x0500, + 0x0502, 0x0504, 0x0506, 0x0508, 0x050a, 0x050c, 0x050e, 0x0510, 0x0512, 0x0514, 0x0516, + 0x0518, 0x051a, 0x051c, 0x051e, 0x0520, 0x0522, 0x0524, 0x0526, 0x0528, 0x052a, 0x052c, + 0x052e, 0x10c7, 0x10cd, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, + 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, + 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, + 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, + 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, + 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, + 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, + 0x1e94, 0x1e9e, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, + 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, + 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, + 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, + 0x1ef4, 0x1ef6, 0x1ef8, 0x1efa, 0x1efc, 0x1efe, 0x1f59, 0x1f5b, 0x1f5d, 0x1f5f, 0x2102, + 0x2107, 0x2115, 0x2124, 0x2126, 0x2128, 0x2145, 0x2183, 0x2c60, 0x2c67, 0x2c69, 0x2c6b, + 0x2c72, 0x2c75, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, + 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, + 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, + 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, + 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2, 0x2ceb, 0x2ced, 0x2cf2, 0xa640, + 0xa642, 0xa644, 0xa646, 0xa648, 0xa64a, 0xa64c, 0xa64e, 0xa650, 0xa652, 0xa654, 0xa656, + 0xa658, 0xa65a, 0xa65c, 0xa65e, 0xa660, 0xa662, 0xa664, 0xa666, 0xa668, 0xa66a, 0xa66c, + 0xa680, 0xa682, 0xa684, 0xa686, 0xa688, 0xa68a, 0xa68c, 0xa68e, 0xa690, 0xa692, 0xa694, + 0xa696, 0xa698, 0xa69a, 0xa722, 0xa724, 0xa726, 0xa728, 0xa72a, 0xa72c, 0xa72e, 0xa732, + 0xa734, 0xa736, 0xa738, 0xa73a, 0xa73c, 0xa73e, 0xa740, 0xa742, 0xa744, 0xa746, 0xa748, + 0xa74a, 0xa74c, 0xa74e, 0xa750, 0xa752, 0xa754, 0xa756, 0xa758, 0xa75a, 0xa75c, 0xa75e, + 0xa760, 0xa762, 0xa764, 0xa766, 0xa768, 0xa76a, 0xa76c, 0xa76e, 0xa779, 0xa77b, 0xa780, + 0xa782, 0xa784, 0xa786, 0xa78b, 0xa78d, 0xa790, 0xa792, 0xa796, 0xa798, 0xa79a, 0xa79c, + 0xa79e, 0xa7a0, 0xa7a2, 0xa7a4, 0xa7a6, 0xa7a8, 0xa7b6, 0xa7b8, 0x1d49c, 0x1d4a2, 0x1d546, + 0x1d7ca, +}; + +int +utf8·IsUpper(Rune c) +{ + Rune* p; + + p = rbsearch(c, isupper_rtab, arrlen(isupper_rtab) / 2, 2); + if (p && c >= p[0] && c <= p[1]) return 1; + p = rbsearch(c, isupper_stab, arrlen(isupper_stab), 1); + if (p && c == p[0]) return 1; + + return 0; +} + +static Rune istitle_rtab[] = { + 0x1f88, 0x1f8f, 0x1f98, 0x1f9f, 0x1fa8, 0x1faf, +}; + +static Rune istitle_stab[] = { + 0x01c5, 0x01c8, 0x01cb, 0x01f2, 0x1fbc, 0x1fcc, +}; + +int +utf8·IsTitle(Rune c) +{ + Rune* p; + + p = rbsearch(c, istitle_rtab, arrlen(istitle_rtab) / 2, 2); + if (p && c >= p[0] && c <= p[1]) return 1; + p = rbsearch(c, istitle_stab, arrlen(istitle_stab), 1); + if (p && c == p[0]) return 1; + + return 0; +} + +static Rune isletter_rtab[] = { + 0x0041, 0x005a, 0x0061, 0x007a, 0x00c0, 0x00d6, 0x00d8, 0x00f6, 0x00f8, 0x02c1, + 0x02c6, 0x02d1, 0x02e0, 0x02e4, 0x0370, 0x0374, 0x0376, 0x0377, 0x037a, 0x037d, + 0x0388, 0x038a, 0x038e, 0x03a1, 0x03a3, 0x03f5, 0x03f7, 0x0481, 0x048a, 0x052f, + 0x0531, 0x0556, 0x0560, 0x0588, 0x05d0, 0x05ea, 0x05ef, 0x05f2, 0x0620, 0x064a, + 0x066e, 0x066f, 0x0671, 0x06d3, 0x06e5, 0x06e6, 0x06ee, 0x06ef, 0x06fa, 0x06fc, + 0x0712, 0x072f, 0x074d, 0x07a5, 0x07ca, 0x07ea, 0x07f4, 0x07f5, 0x0800, 0x0815, + 0x0840, 0x0858, 0x0860, 0x086a, 0x08a0, 0x08b4, 0x08b6, 0x08bd, 0x0904, 0x0939, + 0x0958, 0x0961, 0x0971, 0x0980, 0x0985, 0x098c, 0x098f, 0x0990, 0x0993, 0x09a8, + 0x09aa, 0x09b0, 0x09b6, 0x09b9, 0x09dc, 0x09dd, 0x09df, 0x09e1, 0x09f0, 0x09f1, + 0x0a05, 0x0a0a, 0x0a0f, 0x0a10, 0x0a13, 0x0a28, 0x0a2a, 0x0a30, 0x0a32, 0x0a33, + 0x0a35, 0x0a36, 0x0a38, 0x0a39, 0x0a59, 0x0a5c, 0x0a72, 0x0a74, 0x0a85, 0x0a8d, + 0x0a8f, 0x0a91, 0x0a93, 0x0aa8, 0x0aaa, 0x0ab0, 0x0ab2, 0x0ab3, 0x0ab5, 0x0ab9, + 0x0ae0, 0x0ae1, 0x0b05, 0x0b0c, 0x0b0f, 0x0b10, 0x0b13, 0x0b28, 0x0b2a, 0x0b30, + 0x0b32, 0x0b33, 0x0b35, 0x0b39, 0x0b5c, 0x0b5d, 0x0b5f, 0x0b61, 0x0b85, 0x0b8a, + 0x0b8e, 0x0b90, 0x0b92, 0x0b95, 0x0b99, 0x0b9a, 0x0b9e, 0x0b9f, 0x0ba3, 0x0ba4, + 0x0ba8, 0x0baa, 0x0bae, 0x0bb9, 0x0c05, 0x0c0c, 0x0c0e, 0x0c10, 0x0c12, 0x0c28, + 0x0c2a, 0x0c39, 0x0c58, 0x0c5a, 0x0c60, 0x0c61, 0x0c85, 0x0c8c, 0x0c8e, 0x0c90, + 0x0c92, 0x0ca8, 0x0caa, 0x0cb3, 0x0cb5, 0x0cb9, 0x0ce0, 0x0ce1, 0x0cf1, 0x0cf2, + 0x0d05, 0x0d0c, 0x0d0e, 0x0d10, 0x0d12, 0x0d3a, 0x0d54, 0x0d56, 0x0d5f, 0x0d61, + 0x0d7a, 0x0d7f, 0x0d85, 0x0d96, 0x0d9a, 0x0db1, 0x0db3, 0x0dbb, 0x0dc0, 0x0dc6, + 0x0e01, 0x0e30, 0x0e32, 0x0e33, 0x0e40, 0x0e46, 0x0e81, 0x0e82, 0x0e87, 0x0e88, + 0x0e94, 0x0e97, 0x0e99, 0x0e9f, 0x0ea1, 0x0ea3, 0x0eaa, 0x0eab, 0x0ead, 0x0eb0, + 0x0eb2, 0x0eb3, 0x0ec0, 0x0ec4, 0x0edc, 0x0edf, 0x0f40, 0x0f47, 0x0f49, 0x0f6c, + 0x0f88, 0x0f8c, 0x1000, 0x102a, 0x1050, 0x1055, 0x105a, 0x105d, 0x1065, 0x1066, + 0x106e, 0x1070, 0x1075, 0x1081, 0x10a0, 0x10c5, 0x10d0, 0x10fa, 0x10fc, 0x1248, + 0x124a, 0x124d, 0x1250, 0x1256, 0x125a, 0x125d, 0x1260, 0x1288, 0x128a, 0x128d, + 0x1290, 0x12b0, 0x12b2, 0x12b5, 0x12b8, 0x12be, 0x12c2, 0x12c5, 0x12c8, 0x12d6, + 0x12d8, 0x1310, 0x1312, 0x1315, 0x1318, 0x135a, 0x1380, 0x138f, 0x13a0, 0x13f5, + 0x13f8, 0x13fd, 0x1401, 0x166c, 0x166f, 0x167f, 0x1681, 0x169a, 0x16a0, 0x16ea, + 0x16f1, 0x16f8, 0x1700, 0x170c, 0x170e, 0x1711, 0x1720, 0x1731, 0x1740, 0x1751, + 0x1760, 0x176c, 0x176e, 0x1770, 0x1780, 0x17b3, 0x1820, 0x1878, 0x1880, 0x1884, + 0x1887, 0x18a8, 0x18b0, 0x18f5, 0x1900, 0x191e, 0x1950, 0x196d, 0x1970, 0x1974, + 0x1980, 0x19ab, 0x19b0, 0x19c9, 0x1a00, 0x1a16, 0x1a20, 0x1a54, 0x1b05, 0x1b33, + 0x1b45, 0x1b4b, 0x1b83, 0x1ba0, 0x1bae, 0x1baf, 0x1bba, 0x1be5, 0x1c00, 0x1c23, + 0x1c4d, 0x1c4f, 0x1c5a, 0x1c7d, 0x1c80, 0x1c88, 0x1c90, 0x1cba, 0x1cbd, 0x1cbf, + 0x1ce9, 0x1cec, 0x1cee, 0x1cf1, 0x1cf5, 0x1cf6, 0x1d00, 0x1dbf, 0x1e00, 0x1f15, + 0x1f18, 0x1f1d, 0x1f20, 0x1f45, 0x1f48, 0x1f4d, 0x1f50, 0x1f57, 0x1f5f, 0x1f7d, + 0x1f80, 0x1fb4, 0x1fb6, 0x1fbc, 0x1fc2, 0x1fc4, 0x1fc6, 0x1fcc, 0x1fd0, 0x1fd3, + 0x1fd6, 0x1fdb, 0x1fe0, 0x1fec, 0x1ff2, 0x1ff4, 0x1ff6, 0x1ffc, 0x2090, 0x209c, + 0x210a, 0x2113, 0x2119, 0x211d, 0x212a, 0x212d, 0x212f, 0x2139, 0x213c, 0x213f, + 0x2145, 0x2149, 0x2183, 0x2184, 0x2c00, 0x2c2e, 0x2c30, 0x2c5e, 0x2c60, 0x2ce4, + 0x2ceb, 0x2cee, 0x2cf2, 0x2cf3, 0x2d00, 0x2d25, 0x2d30, 0x2d67, 0x2d80, 0x2d96, + 0x2da0, 0x2da6, 0x2da8, 0x2dae, 0x2db0, 0x2db6, 0x2db8, 0x2dbe, 0x2dc0, 0x2dc6, + 0x2dc8, 0x2dce, 0x2dd0, 0x2dd6, 0x2dd8, 0x2dde, 0x3005, 0x3006, 0x3031, 0x3035, + 0x303b, 0x303c, 0x3041, 0x3096, 0x309d, 0x309f, 0x30a1, 0x30fa, 0x30fc, 0x30ff, + 0x3105, 0x312f, 0x3131, 0x318e, 0x31a0, 0x31ba, 0x31f0, 0x31ff, 0xa000, 0xa48c, + 0xa4d0, 0xa4fd, 0xa500, 0xa60c, 0xa610, 0xa61f, 0xa62a, 0xa62b, 0xa640, 0xa66e, + 0xa67f, 0xa69d, 0xa6a0, 0xa6e5, 0xa717, 0xa71f, 0xa722, 0xa788, 0xa78b, 0xa7b9, + 0xa7f7, 0xa801, 0xa803, 0xa805, 0xa807, 0xa80a, 0xa80c, 0xa822, 0xa840, 0xa873, + 0xa882, 0xa8b3, 0xa8f2, 0xa8f7, 0xa8fd, 0xa8fe, 0xa90a, 0xa925, 0xa930, 0xa946, + 0xa960, 0xa97c, 0xa984, 0xa9b2, 0xa9e0, 0xa9e4, 0xa9e6, 0xa9ef, 0xa9fa, 0xa9fe, + 0xaa00, 0xaa28, 0xaa40, 0xaa42, 0xaa44, 0xaa4b, 0xaa60, 0xaa76, 0xaa7e, 0xaaaf, + 0xaab5, 0xaab6, 0xaab9, 0xaabd, 0xaadb, 0xaadd, 0xaae0, 0xaaea, 0xaaf2, 0xaaf4, + 0xab01, 0xab06, 0xab09, 0xab0e, 0xab11, 0xab16, 0xab20, 0xab26, 0xab28, 0xab2e, + 0xab30, 0xab5a, 0xab5c, 0xab65, 0xab70, 0xabe2, 0xd7b0, 0xd7c6, 0xd7cb, 0xd7fb, + 0xf900, 0xfa6d, 0xfa70, 0xfad9, 0xfb00, 0xfb06, 0xfb13, 0xfb17, 0xfb1f, 0xfb28, + 0xfb2a, 0xfb36, 0xfb38, 0xfb3c, 0xfb40, 0xfb41, 0xfb43, 0xfb44, 0xfb46, 0xfbb1, + 0xfbd3, 0xfd3d, 0xfd50, 0xfd8f, 0xfd92, 0xfdc7, 0xfdf0, 0xfdfb, 0xfe70, 0xfe74, + 0xfe76, 0xfefc, 0xff21, 0xff3a, 0xff41, 0xff5a, 0xff66, 0xffbe, 0xffc2, 0xffc7, + 0xffca, 0xffcf, 0xffd2, 0xffd7, 0xffda, 0xffdc, 0x10000, 0x1000b, 0x1000d, 0x10026, + 0x10028, 0x1003a, 0x1003c, 0x1003d, 0x1003f, 0x1004d, 0x10050, 0x1005d, 0x10080, 0x100fa, + 0x10280, 0x1029c, 0x102a0, 0x102d0, 0x10300, 0x1031f, 0x1032d, 0x10340, 0x10342, 0x10349, + 0x10350, 0x10375, 0x10380, 0x1039d, 0x103a0, 0x103c3, 0x103c8, 0x103cf, 0x10400, 0x1049d, + 0x104b0, 0x104d3, 0x104d8, 0x104fb, 0x10500, 0x10527, 0x10530, 0x10563, 0x10600, 0x10736, + 0x10740, 0x10755, 0x10760, 0x10767, 0x10800, 0x10805, 0x1080a, 0x10835, 0x10837, 0x10838, + 0x1083f, 0x10855, 0x10860, 0x10876, 0x10880, 0x1089e, 0x108e0, 0x108f2, 0x108f4, 0x108f5, + 0x10900, 0x10915, 0x10920, 0x10939, 0x10980, 0x109b7, 0x109be, 0x109bf, 0x10a10, 0x10a13, + 0x10a15, 0x10a17, 0x10a19, 0x10a35, 0x10a60, 0x10a7c, 0x10a80, 0x10a9c, 0x10ac0, 0x10ac7, + 0x10ac9, 0x10ae4, 0x10b00, 0x10b35, 0x10b40, 0x10b55, 0x10b60, 0x10b72, 0x10b80, 0x10b91, + 0x10c00, 0x10c48, 0x10c80, 0x10cb2, 0x10cc0, 0x10cf2, 0x10d00, 0x10d23, 0x10f00, 0x10f1c, + 0x10f30, 0x10f45, 0x11003, 0x11037, 0x11083, 0x110af, 0x110d0, 0x110e8, 0x11103, 0x11126, + 0x11150, 0x11172, 0x11183, 0x111b2, 0x111c1, 0x111c4, 0x11200, 0x11211, 0x11213, 0x1122b, + 0x11280, 0x11286, 0x1128a, 0x1128d, 0x1128f, 0x1129d, 0x1129f, 0x112a8, 0x112b0, 0x112de, + 0x11305, 0x1130c, 0x1130f, 0x11310, 0x11313, 0x11328, 0x1132a, 0x11330, 0x11332, 0x11333, + 0x11335, 0x11339, 0x1135d, 0x11361, 0x11400, 0x11434, 0x11447, 0x1144a, 0x11480, 0x114af, + 0x114c4, 0x114c5, 0x11580, 0x115ae, 0x115d8, 0x115db, 0x11600, 0x1162f, 0x11680, 0x116aa, + 0x11700, 0x1171a, 0x11800, 0x1182b, 0x118a0, 0x118df, 0x11a0b, 0x11a32, 0x11a5c, 0x11a83, + 0x11a86, 0x11a89, 0x11ac0, 0x11af8, 0x11c00, 0x11c08, 0x11c0a, 0x11c2e, 0x11c72, 0x11c8f, + 0x11d00, 0x11d06, 0x11d08, 0x11d09, 0x11d0b, 0x11d30, 0x11d60, 0x11d65, 0x11d67, 0x11d68, + 0x11d6a, 0x11d89, 0x11ee0, 0x11ef2, 0x12000, 0x12399, 0x12480, 0x12543, 0x13000, 0x1342e, + 0x14400, 0x14646, 0x16800, 0x16a38, 0x16a40, 0x16a5e, 0x16ad0, 0x16aed, 0x16b00, 0x16b2f, + 0x16b40, 0x16b43, 0x16b63, 0x16b77, 0x16b7d, 0x16b8f, 0x16e40, 0x16e7f, 0x16f00, 0x16f44, + 0x16f93, 0x16f9f, 0x16fe0, 0x16fe1, 0x18800, 0x18af2, 0x1b000, 0x1b11e, 0x1b170, 0x1b2fb, + 0x1bc00, 0x1bc6a, 0x1bc70, 0x1bc7c, 0x1bc80, 0x1bc88, 0x1bc90, 0x1bc99, 0x1d400, 0x1d454, + 0x1d456, 0x1d49c, 0x1d49e, 0x1d49f, 0x1d4a5, 0x1d4a6, 0x1d4a9, 0x1d4ac, 0x1d4ae, 0x1d4b9, + 0x1d4bd, 0x1d4c3, 0x1d4c5, 0x1d505, 0x1d507, 0x1d50a, 0x1d50d, 0x1d514, 0x1d516, 0x1d51c, + 0x1d51e, 0x1d539, 0x1d53b, 0x1d53e, 0x1d540, 0x1d544, 0x1d54a, 0x1d550, 0x1d552, 0x1d6a5, + 0x1d6a8, 0x1d6c0, 0x1d6c2, 0x1d6da, 0x1d6dc, 0x1d6fa, 0x1d6fc, 0x1d714, 0x1d716, 0x1d734, + 0x1d736, 0x1d74e, 0x1d750, 0x1d76e, 0x1d770, 0x1d788, 0x1d78a, 0x1d7a8, 0x1d7aa, 0x1d7c2, + 0x1d7c4, 0x1d7cb, 0x1e800, 0x1e8c4, 0x1e900, 0x1e943, 0x1ee00, 0x1ee03, 0x1ee05, 0x1ee1f, + 0x1ee21, 0x1ee22, 0x1ee29, 0x1ee32, 0x1ee34, 0x1ee37, 0x1ee4d, 0x1ee4f, 0x1ee51, 0x1ee52, + 0x1ee61, 0x1ee62, 0x1ee67, 0x1ee6a, 0x1ee6c, 0x1ee72, 0x1ee74, 0x1ee77, 0x1ee79, 0x1ee7c, + 0x1ee80, 0x1ee89, 0x1ee8b, 0x1ee9b, 0x1eea1, 0x1eea3, 0x1eea5, 0x1eea9, 0x1eeab, 0x1eebb, +}; + +static Rune isletter_stab[] = { + 0x00aa, 0x00b5, 0x00ba, 0x02ec, 0x02ee, 0x037f, 0x0386, 0x038c, 0x0559, 0x06d5, + 0x06ff, 0x0710, 0x07b1, 0x07fa, 0x081a, 0x0824, 0x0828, 0x093d, 0x0950, 0x09b2, + 0x09bd, 0x09ce, 0x09fc, 0x0a5e, 0x0abd, 0x0ad0, 0x0af9, 0x0b3d, 0x0b71, 0x0b83, + 0x0b9c, 0x0bd0, 0x0c3d, 0x0c80, 0x0cbd, 0x0cde, 0x0d3d, 0x0d4e, 0x0dbd, 0x0e84, + 0x0e8a, 0x0e8d, 0x0ea5, 0x0ea7, 0x0ebd, 0x0ec6, 0x0f00, 0x103f, 0x1061, 0x108e, + 0x10c7, 0x10cd, 0x1258, 0x12c0, 0x17d7, 0x17dc, 0x18aa, 0x1aa7, 0x1f59, 0x1f5b, + 0x1f5d, 0x1fbe, 0x2071, 0x207f, 0x2102, 0x2107, 0x2115, 0x2124, 0x2126, 0x2128, + 0x214e, 0x2d27, 0x2d2d, 0x2d6f, 0x2e2f, 0x3400, 0x4db5, 0x4e00, 0x9fef, 0xa8fb, + 0xa9cf, 0xaa7a, 0xaab1, 0xaac0, 0xaac2, 0xac00, 0xd7a3, 0xfb1d, 0xfb3e, 0x10808, + 0x1083c, 0x10a00, 0x10f27, 0x11144, 0x11176, 0x111da, 0x111dc, 0x11288, 0x1133d, 0x11350, + 0x114c7, 0x11644, 0x118ff, 0x11a00, 0x11a3a, 0x11a50, 0x11a9d, 0x11c40, 0x11d46, 0x11d98, + 0x16f50, 0x17000, 0x187f1, 0x1d4a2, 0x1d4bb, 0x1d546, 0x1ee24, 0x1ee27, 0x1ee39, 0x1ee3b, + 0x1ee42, 0x1ee47, 0x1ee49, 0x1ee4b, 0x1ee54, 0x1ee57, 0x1ee59, 0x1ee5b, 0x1ee5d, 0x1ee5f, + 0x1ee64, 0x1ee7e, 0x20000, 0x2a6d6, 0x2a700, 0x2b734, 0x2b740, 0x2b81d, 0x2b820, 0x2cea1, + 0x2ceb0, 0x2ebe0, +}; + +int +utf8·IsLetter(Rune c) +{ + Rune* p; + + p = rbsearch(c, isletter_rtab, arrlen(isletter_rtab) / 2, 2); + if (p && c >= p[0] && c <= p[1]) return 1; + p = rbsearch(c, isletter_stab, arrlen(isletter_stab), 1); + if (p && c == p[0]) return 1; + + return 0; +} + +static Rune isdigit_rtab[] = { + 0x0030, 0x0039, 0x0660, 0x0669, 0x06f0, 0x06f9, 0x07c0, 0x07c9, 0x0966, 0x096f, + 0x09e6, 0x09ef, 0x0a66, 0x0a6f, 0x0ae6, 0x0aef, 0x0b66, 0x0b6f, 0x0be6, 0x0bef, + 0x0c66, 0x0c6f, 0x0ce6, 0x0cef, 0x0d66, 0x0d6f, 0x0de6, 0x0def, 0x0e50, 0x0e59, + 0x0ed0, 0x0ed9, 0x0f20, 0x0f29, 0x1040, 0x1049, 0x1090, 0x1099, 0x17e0, 0x17e9, + 0x1810, 0x1819, 0x1946, 0x194f, 0x19d0, 0x19d9, 0x1a80, 0x1a89, 0x1a90, 0x1a99, + 0x1b50, 0x1b59, 0x1bb0, 0x1bb9, 0x1c40, 0x1c49, 0x1c50, 0x1c59, 0xa620, 0xa629, + 0xa8d0, 0xa8d9, 0xa900, 0xa909, 0xa9d0, 0xa9d9, 0xa9f0, 0xa9f9, 0xaa50, 0xaa59, + 0xabf0, 0xabf9, 0xff10, 0xff19, 0x104a0, 0x104a9, 0x10d30, 0x10d39, 0x11066, 0x1106f, + 0x110f0, 0x110f9, 0x11136, 0x1113f, 0x111d0, 0x111d9, 0x112f0, 0x112f9, 0x11450, 0x11459, + 0x114d0, 0x114d9, 0x11650, 0x11659, 0x116c0, 0x116c9, 0x11730, 0x11739, 0x118e0, 0x118e9, + 0x11c50, 0x11c59, 0x11d50, 0x11d59, 0x11da0, 0x11da9, 0x16a60, 0x16a69, 0x16b50, 0x16b59, + 0x1d7ce, 0x1d7ff, +}; + +int +utf8·IsDigit(Rune c) +{ + Rune* p; + + p = rbsearch(c, isdigit_rtab, arrlen(isdigit_rtab) / 2, 2); + if (p && c >= p[0] && c <= p[1]) return 1; + + return 0; +} diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..b3f2eb0 --- /dev/null +++ b/src/error.c @@ -0,0 +1,14 @@ +#include + +void +errorf(const byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + printf("error: "); + vprintf(fmt, args); + printf("\n"); + + va_end(args); +} diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000..3d35299 --- /dev/null +++ b/src/mem.c @@ -0,0 +1,49 @@ +#include + +// ------------------------------------------------------------------------- +// Dynamic buffer. + +/* Grow to particular size */ +void* +bufgrow(void* buf, vlong newLen, vlong eltsize) +{ + Assert(bufcap(buf) <= (SIZE_MAX - 1) / 2); + + vlong newCap = MAX(16, MAX(1 + 2 * bufcap(buf), newLen)); + + Assert(newLen <= newCap); + Assert(newCap <= (SIZE_MAX - offsetof(bufHdr, buf)) / eltsize); + + vlong newSize = offsetof(bufHdr, buf) + newCap * eltsize; + + bufHdr* newHdr; + if (buf) { + newHdr = _bufHdr(buf); + newHdr = (bufHdr*)realloc((void*)newHdr, newSize); + } else { + newHdr = (bufHdr*)malloc(newSize); + newHdr->len = 0; + } + + newHdr->cap = newCap; + return (void*)newHdr->buf; +} + +/* Pop out a value */ +void +_bufpop(void *buf, int i, vlong eltsize) +{ + int n; + byte *b; + byte stk[1024]; + Assert(eltsize < sizeof(stk)); + + b = (byte*) buf; + if (n = buflen(buf), i < n) { + memcpy(stk, b+eltsize*i, eltsize); + memcpy(b+eltsize*i, b+eltsize*(i+1), eltsize*(n-i-1)); + memcpy(b+eltsize*(n-1), stk, eltsize); + } + _bufHdr(buf)->len--; +} + diff --git a/src/rules.mk b/src/rules.mk new file mode 100644 index 0000000..2a13bff --- /dev/null +++ b/src/rules.mk @@ -0,0 +1,35 @@ +# ---- Push on stack ---- +SP := $(SP).x +DIRSTACK_$(SP) := $(d) +d := $(DIR) + +# Iterate through subdirectory tree +# ... + +# Local variables +SRCS_$(d) := $(wildcard $(d)/*.c) +OBJS_$(d) := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(SRCS_$(d):.c=.o)) +DEPS_$(d) := $(OBJS_$(d):.o=.d) + +OBJS := $(OBJS) $(OBJS_$(d)) +DEPS := $(DEPS) $(DEPS_$(d)) + +LIBS_$(d) := $(d)/libnbn.a +LIBS_$(d) := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(LIBS_$(d))) +BINS_$(d) := + +LIBS := $(LIBS) $(LIBS_$(d)) + +# Local rules +# $(LIBS_$(d)) := TGTFLAGS := +# $(LIBS_$(d)) := TGTINCS := +# $(LIBS_$(d)) := TGTLIBS := + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +# ---- Pop off stack ---- +-include $(DEPS_$(d)) + +d := $(DIRSTACK_$(SP)) +SP := $(basename $(SP)) diff --git a/src/str.c b/src/str.c new file mode 100644 index 0000000..9aa29b3 --- /dev/null +++ b/src/str.c @@ -0,0 +1,504 @@ +#include + +#define MAX_STRING_ALLOC 1024 * 1024 + +// ------------------------------------------------------------------------- +// UTF-8 functions + +#define Bit(i) (7-(i)) +/* N 0's preceded by i 1's e.g. T(Bit(2)) is 1100 0000 */ +#define Tbyte(i) (((1 << (Bit(i)+1))-1) ^ 0xFF) +/* 0000 0000 0000 0111 1111 1111 */ +#define RuneX(i) ((1 << (Bit(i) + ((i)-1)*Bitx))-1) + +enum +{ + Bitx = Bit(1), + Tx = Tbyte(1), + Rune1 = (1 << (Bit(0)+0*Bitx)) - 1, + + Maskx = (1 << Bitx) - 1, /* 0011 1111 */ + Testx = Maskx ^ 0xff, /* 1100 0000 */ + + SurrogateMin = 0xD800, + SurrogateMax = 0xDFFF, + Bad = RuneErr, +}; + +int +utf8·CharToRune(Rune* r, byte* s) +{ + int c[UTFmax], i; + Rune l; + + c[0] = *(ubyte*)(s); + if (c[0] < Tx) { + *r = c[0]; + return 1; + } + + l = c[0]; + for (i = 1; i < UTFmax; i++) { + c[i] = *(ubyte*)(s+i); + c[i] ^= Tx; + if (c[i] & Testx) goto bad; + + l = (l << Bitx) | c[i]; + if (c[0] < Tbyte(i + 2)) { + l &= RuneX(i + 1); + if (i == 1) { + if (c[0] < Tbyte(2) || l <= Rune1) + goto bad; + } else if (l <= RuneX(i) || l > RuneMax) + goto bad; + if (i == 2 && SurrogateMin <= l && l <= SurrogateMax) + goto bad; + + *r = l; + return i + 1; + } + } +bad: + *r = RuneErr; + return 1; +} + +int +utf8·RuneToChar(byte* s, Rune* r) +{ + int i, j; + Rune c; + + c = *r; + if (c <= Rune1) { + s[0] = c; + return 1; + } + + for (i = 2; i < UTFmax + 1; i++){ + if (i == 3){ + if (c > RuneMax) + c = RuneErr; + if (SurrogateMin <= c && c <= SurrogateMax) + c = RuneErr; + } + if (c <= RuneX(i) || i == UTFmax) { + s[0] = Tbyte(i) | (c >> (i - 1)*Bitx); + for(j = 1; j < i; j++) + s[j] = Tx | ((c >> (i - j - 1)*Bitx) & Maskx); + return i; + } + } + + return UTFmax; +} + +int +utf8·RuneLen(Rune r) +{ + byte s[10]; + return utf8·RuneToChar(s, &r); +} + +int +utf8·FullRune(byte* s, int n) +{ + int i; + Rune c; + + if (n <= 0) return 0; + c = *(ubyte*) s; + if (c < Tx) return 1; + + for (i = 3; i < UTFmax + 1; i++) { + if (c < Tbyte(i)) return n >= i - 1; + } + + return n >= UTFmax; +} + +byte* +utf8·FindRune(byte* s, long c) +{ + long c1; + Rune r; + int n; + + if (c < RuneSync) return strchr(s, c); + + for (;;) { + c1 = *(ubyte*)s; + if (c1 < RuneSelf) { + if (c1 == 0) return nil; + if (c1 == c) return s; + s++; + continue; + } + n = utf8·CharToRune(&r, s); + if (r == c) return s; + s += n; + } + + return nil; +} + +#undef Bit +#undef Tbyte +#undef RuneX + +#include "generated/utf8.c" + +// ------------------------------------------------------------------------- +// Dynamic string functions + +// New returns a new dynamic string object, initialized from the given C string. +// len defines the length of the C substring that we will copy into our buffer. +// The backing buffer will have capacity cap. +string +str·NewCap(const byte* s, vlong len, vlong cap) +{ + struct str·Hdr* h; + + h = malloc(sizeof(*h) + cap + 1); + if (s == nil) memset(h, 0, sizeof(*h)); + + if (h == nil) return nil; // Allocation failed. + + h->len = (s == nil) ? 0 : len; + h->cap = cap; + + if (cap < h->len) goto cleanup; + + if (s != nil && cap > 0) { + memcpy(h->buf, s, h->len); + memset(h->buf + h->len, '\0', h->cap - h->len + 1); + } + + return h->buf; + +cleanup: + free(h); + panicf("Attempted to create a string with less capacity than length"); +} + +// New returns a new dynamic string object, initialized from the given C string. +// The backing buffer capacity is equivalent to the string length. +string +str·NewLen(const byte* s, vlong len) +{ + vlong sl = (s == nil) ? 0 : strlen(s); + if (sl < len) panicf("attempted to take a bigger substring than string length"); + + vlong cap = (len == 0) ? 1 : len; + return str·NewCap(s, len, cap); +} + +// New returns a new dynamic string object, initialized from the given C string. +// The backing buffer capacity is equivalent to the string length. +string +str·New(const byte* s) +{ + vlong len = (s == nil) ? 0 : strlen(s); + return str·NewLen(s, len); +} + +// Newf returns a new dynamic string object +string +str·Newf(const byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + vlong n = vsnprintf(nil, 0, fmt, args); + va_end(args); + + string s = str·NewCap(nil, 0, n); + + va_start(args, fmt); + vsnprintf(s, n + 1, fmt, args); + va_end(args); + + str·Hdr* h = (str·Hdr*)(s - sizeof(str·Hdr)); + h->len = n; + + return s; +} + +// Free returns memory associated to the buffer. +void +str·Free(string s) +{ + free(s - sizeof(str·Hdr)); +} + +// Len returns the length of the string. +int +str·Len(const string s) +{ + str·Hdr* h = (str·Hdr*)(s - sizeof(str·Hdr)); + return h->len; +} + +// Cap returns the capacity of the string buffer. +int +str·Cap(const string s) +{ + str·Hdr* h = (str·Hdr*)(s - sizeof(str·Hdr)); + return h->cap; +} + +string +str·Clear(string s) +{ + str·Hdr* h = (str·Hdr*)(s - sizeof(str·Hdr)); + h->len = 0; + *s = 0; + + return s; +} + +// Grow ensures that the string can encompass AT LEAST delta bytes. +// If it already can, this is a NO OP. +// If it can't, the string will be reallocated. +string +str·Grow(string s, vlong delta) +{ + str·Hdr *h, *newh; + vlong cap = str·Cap(s); + vlong len = str·Len(s); + Assert(cap >= len); // To prevent unsigned behavior + + if (cap - len >= delta) return s; + + h = (str·Hdr*)(s - sizeof(str·Hdr)); + + vlong newCap = cap + delta; + Assert(newCap >= cap); // To prevent unsigned behavior + if (newCap < MAX_STRING_ALLOC) { + newCap *= 2; + } else + newCap += MAX_STRING_ALLOC; + + newh = (str·Hdr*)realloc(h, sizeof(*h) + newCap + 1); + if (newh == nil) return nil; + + memset(newh->buf + len, '\0', newCap - len); + newh->cap = newCap; + newh->len = len; + + return newh->buf; +} + +// Fit reallocates the string such that the buffer is exactly sized for the +// buffer. If the capacity equals the length, then the function is a NOOP. The +// byte array is unchanged. +string +str·Fit(string s) +{ + str·Hdr* h; + vlong cap = str·Cap(s); + vlong len = str·Len(s); + + if (cap == len) return s; + + h = (str·Hdr*)(s - sizeof(str·Hdr)); + h = realloc(h, sizeof(*h) + len + 1); + h->cap = len; + + return h->buf; +} + +// Append will append the given null terminated C string to the string data +// structure. This variant can append a substring of length len of the given +// string to our buffer. The result is reallocated if not enough room is present +// in the buffer. +string +str·AppendCount(string s, const byte* b, vlong n) +{ + vlong bl = strlen(b); + if (n > bl) panicf("attempted to make a substring longer than string"); + + s = str·Grow(s, n); + if (s == nil) return nil; + + str·Hdr* h = (str·Hdr*)(s - sizeof(str·Hdr)); + + memcpy(s + str·Len(s), b, n); + h->len += n; + s[h->len] = '\0'; + + return s; +} + +// Append will append the given null terminated C string to the string data +// structure. This variant will append the entire string. +string +str·Append(string s, const byte* b) +{ + return str·AppendCount(s, b, strlen(b)); +} + +// AppendByte will append the given byte to our string. +// NOTE: As the byte is on the stack, it is not null-terminated. +// Can not pass to the above functions. +string +str·AppendByte(string s, const byte b) +{ + s = str·Grow(s, 1); + if (s == nil) return nil; + + str·Hdr* h = (str·Hdr*)(s - sizeof(str·Hdr)); + + *(s + str·Len(s)) = b; + h->len++; + s[h->len] = '\0'; // NOTE: I don't think an explicit zero is required..? + + return s; +} + +// Equals returns true if string s and t are equivalent. +bool +str·Equals(const string s, const string t) +{ + vlong sL = str·Len(s); + vlong tL = str·Len(t); + if (sL != tL) return false; + + return memcmp(s, t, sL) == 0; +} + +// Utility Methods ------------------------------------ +// +// Appendf will append the given formatted string to our buffer. +// Returns the newly minted string. +string +str·Appendf(string s, const byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int remain = str·Cap(s) - str·Len(s); + int n = vsnprintf(s + str·Len(s), remain + 1, fmt, args); + va_end(args); + + if (n > remain) { + // If the first write was incomplete, we overwite the data again. + s = str·Grow(s, n); + va_list args; + va_start(args, fmt); + n = vsnprintf(s + str·Len(s), n + 1, fmt, args); + Assert(n - remain <= str·Cap(s)); + va_end(args); + } + + str·Hdr* h = (str·Hdr*)(s - sizeof(str·Hdr)); + h->len += n; + + return s; +} + +// Find will find the first occurence of +// substr in the string Returns -1 if nothing was found. +int +str·Find(string s, const byte* substr) +{ + byte* loc = strstr(s, substr); + if (loc == nil) return -1; + return (int)(loc - s); +} + +// +// Lower will force all runes in the string to be lowercase. +void +str·Lower(string s) +{ + byte *b, *e; + b = s; + e = b + str·Len(s); + while (b++ != e) + *b = tolower(*b); +} + +// Upper will force all runes in the string to be uppercase. +void +str·Upper(string s) +{ + byte *b, *e; + b = s; + e = b + str·Len(s); + while (b++ != e) + *b = toupper(*b); +} + +// Replace will replace all occurences of the given bytes 'from' to bytes 'to' +// Edits are done in place and modify the string. +// NOTE: As of now strings from and to must be the same size. +void +str·Replace(string s, const byte* from, const byte* to) +{ + vlong fromL = strlen(from); + vlong toL = strlen(to); + if (toL != fromL) { panicf("different sized replacement string not supported"); } + + vlong l = str·Len(s); + vlong i = l; + vlong j = l; + + for (i = 0; i < l; i++) { + for (j = 0; j < toL; j++) { + if (s[i] == from[j]) { + s[i] = to[j]; + break; + } + } + } +} + +// Split will split the string by the given token. +// Returns a stretchy buffer of strings that result from the partition. +// It is the caller's responsibility to clean the memory. +string* +str·Split(string s, const byte* tok) +{ + string* fields = nil; + vlong start = 0; + + vlong sL = str·Len(s); + vlong tokL = strlen(tok); + if (sL == 0 || tokL == 0) return nil; + + buffit(fields, 5); + + for (vlong i = 0; i < sL - tokL; i++) { + if ((tokL == 1 && s[i] == tokL) || !memcmp(s + i, tok, tokL)) { + bufpush(fields, str·NewLen(s + start, i - start)); + if (fields[buflen(fields) - 1] == nil) goto cleanup; + + start = i + tokL; + i += tokL - 1; + } + } + + bufpush(fields, str·NewLen(s + start, sL - start)); + + return fields; + +cleanup: + for (vlong i = 0; i < buflen(fields); i++) { + str·Free(fields[i]); + } + buffree(fields); + return nil; +} + +string +str·Join(byte** fields, vlong numFields, const byte* sep) +{ + string s = str·NewCap(nil, 0, 10); + int j = 0; + + for (j = 0; j < numFields; j++) { + s = str·Append(s, fields[j]); + if (j < numFields - 1) { s = str·AppendCount(s, sep, 1); } + } + + return s; +} -- cgit v1.2.1