Created
December 27, 2021 22:25
-
-
Save RealNeGate/c5dbd1dd55baecb1e9e88eaa84abc74b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // MIT License | |
| // | |
| // Copyright (c) 2021 Yasser Arguelles Snape | |
| // | |
| // 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. | |
| // | |
| // Example code: | |
| #ifndef NSTL_STRING | |
| #define NSTL_STRING | |
| #include <stdlib.h> | |
| #include <stdbool.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #ifndef nstl_assert | |
| #include <assert.h> | |
| #define nstl_assert(expr) assert(expr) | |
| #endif | |
| #ifndef NSTL_STRING_BUILDER_BLOCK_SIZE | |
| #define NSTL_STRING_BUILDER_BLOCK_SIZE (4096 - (2 * sizeof(size_t))) | |
| #endif | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| // ********************************** | |
| // TYPES | |
| // ********************************** | |
| typedef struct nstl_string { | |
| size_t length; | |
| const char* data; | |
| } nstl_string; | |
| typedef struct nstl_string_builder_page { | |
| struct nstl_string_builder_page* next; | |
| size_t used; | |
| char data[NSTL_STRING_BUILDER_BLOCK_SIZE]; | |
| } nstl_string_builder_page; | |
| typedef struct nstl_string_builder { | |
| nstl_string_builder_page* base; | |
| nstl_string_builder_page* top; | |
| } nstl_string_builder; | |
| // ********************************** | |
| // FUNCTIONS | |
| // ********************************** | |
| nstl_string nstl_string_init(size_t len, const char* dat); | |
| nstl_string nstl_string_from_cstr(const char* str); | |
| nstl_string nstl_string_clone(nstl_string* str); | |
| bool nstl_string_starts_with(const nstl_string str, const char prefix[]); | |
| char* nstl_string_cstr(nstl_string* str); | |
| nstl_string_builder nstl_string_builder_init(); | |
| void nstl_string_builder_append_number(nstl_string_builder* builder, int number); | |
| void nstl_string_builder_append_cstr(nstl_string_builder* builder, const char str[]); | |
| void nstl_string_builder_append_string(nstl_string_builder* builder, size_t length, const char* str); | |
| void nstl_string_builder_append_fmt(nstl_string_builder* builder, const char fmt[], ...); | |
| nstl_string nstl_string_builder_finish(nstl_string_builder* builder); | |
| void nstl_string_builder_finish_to_fstream(nstl_string_builder* builder, FILE* out); | |
| // ********************************** | |
| // SHORT NAMES | |
| // ********************************** | |
| #ifdef NSTL_STRING_SHORTNAMES | |
| typedef nstl_string string; | |
| typedef nstl_string_builder string_builder; | |
| #define string_init nstl_string_init | |
| #define string_from_cstr nstl_string_from_cstr | |
| #define string_clone nstl_string_clone | |
| #define string_starts_with nstl_string_starts_with | |
| #define string_cstr nstl_string_cstr | |
| #define string_builder_init nstl_string_builder_init | |
| #define string_builder_free nstl_string_builder_free | |
| #define string_builder_append_number nstl_string_builder_append_number | |
| #define string_builder_append_cstr nstl_string_builder_append_cstr | |
| #define string_builder_append_char nstl_string_builder_append_char | |
| #define string_builder_append_string nstl_string_builder_append_string | |
| #define string_builder_append_fmt nstl_string_builder_append_fmt | |
| #define string_builder_finish nstl_string_builder_finish | |
| #define string_builder_finish_to_fstream nstl_string_builder_finish_to_fstream | |
| #endif /* NSTL_STRING_SHORTNAMES */ | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| #endif /* NSTL_STRING */ | |
| #ifdef NSTL_STRING_IMPLEMENTATION | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| nstl_string nstl_string_init(size_t len, const char* dat) { | |
| return (nstl_string) { .length = len, .data = dat }; | |
| } | |
| nstl_string nstl_string_from_cstr(const char* str) { | |
| // strlen | |
| const char* end = str; | |
| while (*end != '\0') end++; | |
| return (nstl_string) { .length = (end - str), .data = str }; | |
| } | |
| nstl_string nstl_string_clone(nstl_string* str) { | |
| char* newstr = nstl_c_malloc(str->length + 1); | |
| nstl_c_memcpy(newstr, str->data, str->length); | |
| newstr[str->length] = '\0'; | |
| return (nstl_string) { .length = str->length, .data = newstr }; | |
| } | |
| bool nstl_string_starts_with(const nstl_string str, const char* prefix) { | |
| size_t prefix_len = strlen(prefix); | |
| if (str.length < prefix_len) return false; | |
| return memcmp(str.data, prefix, prefix_len) == 0; | |
| } | |
| char* nstl_string_cstr(nstl_string* str) { | |
| char* cstr = malloc(str->length + 1); | |
| nstl_c_memcpy(cstr, str->data, str->length); | |
| cstr[str->length] = '\0'; | |
| return cstr; | |
| } | |
| nstl_string_builder nstl_string_builder_init() { | |
| nstl_string_builder b; | |
| b.base = b.top = malloc(sizeof(nstl_string_builder_page)); | |
| b.base->next = NULL; | |
| b.base->used = 0; | |
| return b; | |
| } | |
| // The idea is from this thing: | |
| // Basically if you know the length of the number then you don't need, | |
| // to actually reverse it later you build it in reverse. | |
| // https://www.youtube.com/watch?v=fV6qYho-XVs | |
| void nstl_string_builder_append_number(nstl_string_builder* builder, int number) { | |
| nstl_assert(builder); | |
| // Count number of digits | |
| const static unsigned int PowersOf10[] = { | |
| 1, | |
| 10, | |
| 100, | |
| 1000, | |
| 10000, | |
| 100000, | |
| 1000000, | |
| 10000000, | |
| 100000000, | |
| 1000000000 | |
| }; | |
| unsigned int log2 = 31 - __builtin_clz(number); | |
| unsigned int log10Approx = (log2 + 1) * 1233 >> 12; | |
| unsigned int log10 = log10Approx - (number < PowersOf10[log10Approx]); | |
| unsigned int digits = log10 + 1; | |
| char out[12]; | |
| char* curr = &out[digits - 1]; | |
| size_t i = 0; | |
| while (number > 0) { | |
| *curr-- = '0' + (number % 10); | |
| number /= 10; | |
| i++; | |
| } | |
| nstl_string_builder_append_string(builder, digits, out); | |
| } | |
| void nstl_string_builder_append_char(nstl_string_builder* builder, const char ch) { | |
| nstl_string_builder_append_string(builder, 1, &ch); | |
| } | |
| void nstl_string_builder_append_cstr(nstl_string_builder* builder, const char* str) { | |
| nstl_assert(str); | |
| const char* end = str; | |
| while (*end != '\0') end++; | |
| nstl_string_builder_append_string(builder, (size_t)(end - str), str); | |
| } | |
| void nstl_string_builder_append_string(nstl_string_builder* builder, size_t length, const char* str) { | |
| nstl_assert(builder); | |
| nstl_assert(str); | |
| // early-out | |
| if (length == 0) return; | |
| // If this ever happens... tuff | |
| nstl_assert(length < sizeof(builder->base->data)); | |
| if (builder->top->used + length < sizeof(builder->base->data)) { | |
| void* ptr = &builder->top->data[builder->top->used]; | |
| builder->top->used += length; | |
| memcpy(ptr, str, length); | |
| } else { | |
| // Add new page | |
| nstl_string_builder_page* p = malloc(sizeof(nstl_string_builder_page)); | |
| nstl_assert(p); | |
| p->next = NULL; | |
| p->used = length; | |
| // Insert to top of nodes | |
| builder->top->next = p; | |
| builder->top = p; | |
| memcpy(p->data, str, length); | |
| } | |
| } | |
| void nstl_string_builder_append_fmt(nstl_string_builder* builder, const char* fmt, ...) { | |
| va_list args; | |
| va_start(args, fmt); | |
| char temp[1024]; | |
| int len = vsprintf_s(temp, 1024, fmt, args); | |
| nstl_string_builder_append_string(builder, len, temp); | |
| va_end(args); | |
| } | |
| nstl_string nstl_string_builder_finish(nstl_string_builder* builder) { | |
| // Calculate length | |
| size_t len = 0; | |
| nstl_string_builder_page* curr = builder->base; | |
| while (curr) { | |
| len += curr->used; | |
| curr = curr->next; | |
| } | |
| char* buffer = malloc(len + 1); | |
| char* stream = buffer; | |
| curr = builder->base; | |
| while (curr) { | |
| memcpy(stream, curr->data, curr->used); | |
| stream += curr->used; | |
| curr = curr->next; | |
| } | |
| return (nstl_string) { .length = len, .data = buffer }; | |
| } | |
| void nstl_string_builder_finish_to_fstream(nstl_string_builder* builder, FILE* out) { | |
| nstl_string_builder_page* curr = builder->base; | |
| while (curr) { | |
| fwrite(curr->data, 1, curr->used, out); | |
| curr = curr->next; | |
| } | |
| } | |
| void nstl_string_builder_free(nstl_string_builder* builder) { | |
| nstl_string_builder_page* curr = builder->base; | |
| while (curr) { | |
| nstl_string_builder_page* next = curr->next; | |
| free(curr); | |
| curr = next; | |
| } | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| #endif /* NSTL_STRING_IMPLEMENTATION */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment