I recently wrote up a quick header file that provides some useful operations pertaining to C-strings using the CRT. It's just at an early starting point, and I'll probably only implement additional features as I need them in my project, but it's already quite useful. If you have any suggestions, see any problems with the code, or want me to implement something, let me know.
Here's the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #ifndef _DSTRING_H #define _DSTRING_H #include <stdint.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #define _DSTRING_STARTING_CAP 16 #define ds_len(a) ds_size(a) #define ds_length(a) ds_size(a) #define ds_free(a) _ds_free(&a) typedef char *dstr; typedef char *dstring; inline uint32_t *_ds_raw(char *dstr) { return dstr ? ((uint32_t *)dstr) - 2 : NULL; } inline uint32_t ds_size(char *dstr) { return dstr ? _ds_raw(dstr)[0] : 0; } inline uint32_t ds_cap(char *dstr) { return dstr ? _ds_raw(dstr)[1] : 0; } inline char *_ds_grow(char *dstr, uint32_t required_chars) { if(ds_cap(dstr) < required_chars) { uint32_t new_cap = ds_cap(dstr) > _DSTRING_STARTING_CAP ? ds_cap(dstr) : _DSTRING_STARTING_CAP; while(required_chars >= new_cap) { new_cap = 3 * (new_cap / 2); } dstr = (char *)((uint32_t *)realloc(_ds_raw(dstr), new_cap * sizeof(char) + 2 * sizeof(uint32_t)) + 2); *(_ds_raw(dstr) + 1) = new_cap; } return dstr; } inline char *ds_new(const char *str, ...) { char *dstr = NULL; va_list args; va_start(args, str); size_t required_len = vsnprintf(NULL, 0, str, args) + 1; va_end(args); dstr = _ds_grow(dstr, required_len); va_start(args, str); vsprintf(dstr, str, args); va_end(args); *(_ds_raw(dstr)) = required_len; return dstr; } inline void _ds_free(char **str) { if(*str) { free(_ds_raw(*str)); } *str = NULL; } inline char *ds_add_s(char *str, const char *add) { if(str) { uint32_t new_len = ds_size(str) + strlen(add) + 1; str = _ds_grow(str, new_len); strcpy(str + ds_size(str), add); *(_ds_raw(str)) = new_len; } else { str = ds_new("%s", add); } return str; } inline char *ds_add_c(char *str, char c) { char c_str[2] = { c, 0 }; str = ds_add_s(str, c_str); return str; } inline char *ds_add_i(char *str, int i) { char *i_str = ds_new("%i", i); str = ds_add_s(str, i_str); ds_free(i_str); return str; } inline char *ds_add_f(char *str, double f) { char *i_str = ds_new("%lf", f); str = ds_add_s(str, i_str); ds_free(i_str); return str; } inline char *ds_erase(char *str, uint32_t i) { str = (char *)memmove(str + i, str + i + 1, ds_size(str) - i - 1); (_ds_raw(str))[0]--; if((_ds_raw(str))[0] < 1) { ds_free(str); } return str; } #endif |
Here's some sample usage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | dstring a = ds_new("This is a string."); dstring b = ds_new("This is a string with an int. %i", 12345); dstring c = ds_new("This is a string with a double. %f", 123.45); dstring d = NULL; d = ds_add_s(d, "I can add a string to a string."); dstring e = NULL; d = ds_add_i(d, 12345); //or an int (or a double or a char) dstring f = ds_new("I can erase from a string too"); f = ds_erase(f, 0); //you might want to free everything once you're done ds_free(a); ds_free(b); ds_free(c); ds_free(d); ds_free(e); ds_free(f); |
-Delix