From 993424547df0d253d546dbe7adee9b2448294b08 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 15 Jun 2024 11:42:29 -0700 Subject: Add dynamically-sized strings. --- cstring/CMakeLists.txt | 7 ++-- cstring/include/cstring.h | 37 ++++++++++++++++- cstring/src/cstring.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 cstring/src/cstring.c (limited to 'cstring') diff --git a/cstring/CMakeLists.txt b/cstring/CMakeLists.txt index df2fa45..bc385c3 100644 --- a/cstring/CMakeLists.txt +++ b/cstring/CMakeLists.txt @@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.0) project(cstring) -add_library(cstring INTERFACE) +add_library(cstring + src/cstring.c) -target_include_directories(cstring INTERFACE +target_include_directories(cstring PUBLIC include) -target_link_libraries(cstring INTERFACE +target_link_libraries(cstring PUBLIC cassert -lbsd) diff --git a/cstring/include/cstring.h b/cstring/include/cstring.h index 134e68e..a3f6b3f 100644 --- a/cstring/include/cstring.h +++ b/cstring/include/cstring.h @@ -1,12 +1,16 @@ /// Fixed-size strings with value semantics. #pragma once -#include #include + +#include #include #include #include +// ----------------------------------------------------------------------------- +// Fix-sized strings. + /// A fixed-size string. /// The string is null-terminated so that it can be used with the usual C APIs. #define DEF_STRING(STRING, SIZE) \ @@ -118,3 +122,34 @@ DEF_STRING(sstring, 32) // Small. DEF_STRING(mstring, 256) // Medium. DEF_STRING(lstring, 1024) // Large. DEF_STRING(xlstring, 4096) // Extra large. + +// ----------------------------------------------------------------------------- +// Dynamically-sized strings. + +typedef struct string { + const char* data; + size_t length; +} string; + +/// Create a new string. +string string_new(const char*); + +/// Delete the string. +void string_del(string*); + +/// Get the string's length. +static inline size_t string_length(const string str) { return str.length; } + +/// Get the string's data. +static inline const char* string_data(const string str) { return str.data; } + +/// Concatenate two strings. +string string_concat(string left, string right); + +/// Convert a size to string. +string string_from_size(size_t); + +/// Convert and format a size to string. +/// The result uses B for bytes, K for kilobytes (1024), M for megabytes +/// (2**20), and G for gigabytes (2**30), with two decimal digits. +string string_format_size(size_t); diff --git a/cstring/src/cstring.c b/cstring/src/cstring.c new file mode 100644 index 0000000..832cb85 --- /dev/null +++ b/cstring/src/cstring.c @@ -0,0 +1,103 @@ +#include + +#include +#include +#include + +string string_new(const char* cstr) { + const size_t length = strlen(cstr); + const size_t size = length + 1; + + char* data = calloc(size, sizeof(char)); + ASSERT(data); + if (length > 0) { + memcpy(data, cstr, length); + } + + return (string){ + .data = data, + .length = length, + }; +} + +void string_del(string* str) { + if (str->data) { + free((void*)str->data); + str->data = 0; + str->length = 0; + } +} + +string string_concat(string left, string right) { + const size_t length = left.length + right.length; + const size_t size = length + 1; + + char* data = calloc(size, sizeof(char)); + ASSERT(data); + if (length > 0) { + memcpy(data, left.data, left.length); + memcpy(data + left.length, right.data, right.length); + } + + return (string){ + .data = data, + .length = length, + }; +} + +string string_from_size(size_t size) { + const size_t length = snprintf(NULL, 0, "%zu", size) + 1; + char* data = calloc(length, sizeof(char)); + ASSERT(data); + snprintf(data, length, "%zu", size); + return (string){ + .data = data, + .length = length, + }; +} + +string string_format_size(size_t size) { + const size_t multiples[] = {1073741824, 1048576, 1024, 1}; + const char* units[] = {"G", "M", "K", "B"}; + + size_t integer = 0; + size_t fractional = 0; + + int i; + for (i = 0; i < 4; ++i) { + const size_t m = multiples[i]; + if (size >= m) { + integer = size / m; + fractional = size % m; + break; + } + } + + double s; + const char* unit; + const char* format; + if (i == 4) { // 0 + s = (double)size; + unit = ""; + format = "%f%s"; + } else if (i == 3) { // Bytes + s = (double)integer + (double)fractional / (double)multiples[i]; + unit = units[i]; + format = "%.0f%s"; + } else { // KB, MB, GB + assert(i >= 0); + assert(i < 3); + s = (double)integer + (double)fractional / (double)multiples[i]; + unit = units[i]; + format = "%.2f%s"; + } + + const size_t length = snprintf(NULL, 0, format, s, unit) + 1; + char* data = calloc(length, sizeof(char)); + ASSERT(data); + snprintf(data, length, format, s, unit); + return (string){ + .data = data, + .length = length, + }; +} -- cgit v1.2.3