Skip to content

Instantly share code, notes, and snippets.

@RealNeGate
Created December 27, 2021 22:25
Show Gist options
  • Select an option

  • Save RealNeGate/c5dbd1dd55baecb1e9e88eaa84abc74b to your computer and use it in GitHub Desktop.

Select an option

Save RealNeGate/c5dbd1dd55baecb1e9e88eaa84abc74b to your computer and use it in GitHub Desktop.
// 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