├── LICENSE ├── README.md ├── str.c └── str.h /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C String Library 2 | A simple string that can be passed to most library functions, e.g. printf, and supports its own methods such as add, insert, replace, and remove. 3 | 4 | This string library is partially based on anitrez's [Simple Dynamic Strings](https://github.com/antirez/sds/), and like it, this library does not require a function call to access the string, e.g. STRING_GET_CONTENTS(s). Instead, it can be easily passed to many library functions such as printf: 5 | 6 | ```c 7 | printf("%s\n", str); 8 | ``` 9 | 10 | In most other libraries this, printing a string looks like this: 11 | 12 | ```c 13 | printf("%s\n", str->buf); 14 | ``` 15 | 16 | or 17 | 18 | ```c 19 | printf("%s\n", getStringPointer(str)); 20 | ``` 21 | 22 | Similarly to SDS strings, this is achieved by using an alternative design, in which a pointer to the actual string is provided rather than a structure that points to a buffer stored elsewhere. In both libraries, header information containing the string's length and amount of allocated memory is stored directly _before_ the buffer like so: 23 | 24 | 25 | +--------+-------------------------------+-----------+ 26 | | Header | Binary safe c-alike string... | Null term | 27 | +--------+-------------------------------+-----------+ 28 | | 29 | `-> Pointer returned to the user. 30 | 31 | 32 | # Usage 33 | 34 | This library's strings are the same type as a regular c string, `char*`, but this library defines `string` as an alias, which should be used for every string associated with this library to avoid confusion. 35 | 36 | You can create a string by calling `string_create` like so: 37 | 38 | ```c 39 | string s = string_create("Hello world!"); 40 | ``` 41 | or 42 | 43 | ```c 44 | string s = string_create(NULL); // will create an empty string 45 | ``` 46 | 47 | This library includes a variety functions such as `string_add`, `string_insert`, and `string_replace`, most of which take a `string*` (which is the same as a `char**`) as an argument: 48 | 49 | ```c 50 | string_insert(&s, 2, "hello"); 51 | ``` 52 | 53 | Some functions never have to move the string to a new location, so they just take a regular `string` (or a `char*`) as an argument: 54 | 55 | ```c 56 | string_remove(s, 2, 3); 57 | ``` 58 | 59 | or 60 | 61 | ```c 62 | int size = string_size(s); 63 | ``` 64 | 65 | # Differences 66 | 67 | This library has a few notable differences from the _Simple Dynamic Strings_ library, the most significant of which being the function calls. Because these strings have to be moved to a new location sometimes, most functions calls in SDS look like this: 68 | 69 | ```c 70 | s = sdscat(s,"Some more data"); 71 | ``` 72 | 73 | In order to make these function calls look better, this library's function calls work like this: 74 | 75 | ```c 76 | string_add(&s, "Some more data"); 77 | ``` 78 | 79 | The second main difference is memory usage and speed. SDS uses less memory in it's headers, but each time it's functions are called, flags have to be read from part of the header to determine the rest of it's size, which is slightly less efficient. 80 | 81 | If memory usage is your main concern and you want to store a variety of very small and extremely large strings, SDS is probably the best choice. This library is definitley a good choice if you are planning on storing very large objects or files. If you want this library's slight advantage in efficiency and also want to store a large amount of small strings, consider replacing the header's members with a smaller data type such as `unsigned char` or even `int`. 82 | 83 | This can be done by changing the line at the top of `str.h` from `typedef size_t str_size;` to `typedef unsigned char str_size;`, for example. 84 | -------------------------------------------------------------------------------- /str.c: -------------------------------------------------------------------------------- 1 | // 2 | // str.c 3 | // 4 | // Created by Mashpoe on 2/28/19. 5 | // 6 | 7 | #include "str.h" 8 | #include 9 | #include 10 | 11 | typedef struct string_data string_data; 12 | 13 | struct string_data { 14 | str_size alloc; // stores the number of bytes allocated 15 | str_size length; 16 | char buff[]; 17 | }; 18 | 19 | string_data* string_alloc(str_size length) { 20 | string_data* s_data = malloc(sizeof(string_data) + length + 1); // plus 1 for null terminator 21 | s_data->alloc = length+1; 22 | s_data->length = length; 23 | return s_data; 24 | } 25 | 26 | string_data* string_get_data(string s) { 27 | return (string_data*)&s[-sizeof(string_data)]; 28 | } 29 | 30 | string string_create(const char* str) { 31 | string_data* s_data = NULL; 32 | 33 | if (str != NULL) { 34 | str_size str_length = strlen(str); 35 | s_data = string_alloc(str_length); 36 | memcpy(&s_data->buff, str, str_length); 37 | s_data->buff[str_length] = '\0'; 38 | } else { 39 | s_data = string_alloc(0); // will only allocate enough for a null terminator 40 | s_data->buff[0] = '\0'; 41 | } 42 | 43 | return s_data->buff; 44 | } 45 | 46 | bool string_has_space(string_data* s_data) { 47 | // allocate based on s_data->length + 1 to account for the null terminator 48 | return s_data->alloc - (s_data->length + 1) > 0; 49 | } 50 | 51 | void string_add_char(string* s, char c) { 52 | string_data* s_data = string_get_data(*s); 53 | 54 | if (!string_has_space(s_data)) { 55 | str_size new_alloc = s_data->alloc * 2; 56 | s_data = realloc(s_data, sizeof(string_data) + new_alloc); 57 | s_data->alloc = new_alloc; 58 | } 59 | s_data->buff[s_data->length++] = c; 60 | s_data->buff[s_data->length] = '\0'; // add the new null terminator 61 | 62 | *s = s_data->buff; 63 | } 64 | 65 | void string_add(string* s, const char* str) { 66 | string_data* s_data = string_get_data(*s); 67 | 68 | str_size str_length = strlen(str); 69 | 70 | str_size new_length = s_data->length + str_length; 71 | 72 | // make sure there is enough room for new characters and null terminator 73 | if (s_data->alloc <= new_length + 1) { 74 | s_data = realloc(s_data, sizeof(string_data) + new_length); 75 | s_data->alloc = new_length + 1; 76 | } 77 | 78 | // copy str chars 79 | memcpy(&s_data->buff[s_data->length], str, str_length); 80 | 81 | s_data->length = new_length; 82 | 83 | s_data->buff[new_length] = '\0'; // add new null terminator 84 | 85 | *s = s_data->buff; 86 | } 87 | 88 | void string_insert(string* s, str_size pos, const char* str) { 89 | string_data* s_data = string_get_data(*s); 90 | 91 | str_size str_length = strlen(str); 92 | 93 | str_size new_length = s_data->length + str_length; 94 | 95 | // make sure there is enough room for new characters and null terminator 96 | if (s_data->alloc <= new_length + 1) { 97 | string_data* new_s_data = string_alloc(new_length); 98 | 99 | memcpy(new_s_data->buff, s_data->buff, pos); // copy leading characters 100 | memcpy(&new_s_data->buff[pos+str_length], &s_data->buff[pos], s_data->length-pos); // copy trailing characters 101 | 102 | free(s_data); 103 | s_data = new_s_data; 104 | 105 | } else { 106 | memmove(&s_data->buff[pos], &s_data->buff[pos+str_length], s_data->length - pos); // move trailing characters 107 | } 108 | 109 | s_data->length = new_length; 110 | 111 | // copy str chars 112 | memcpy(&s_data->buff[pos], str, str_length); 113 | 114 | s_data->buff[new_length] = '\0'; // add new null terminator 115 | 116 | *s = s_data->buff; 117 | } 118 | 119 | void string_replace(string* s, str_size pos, str_size len, const char* str) { 120 | string_data* s_data = string_get_data(*s); 121 | 122 | str_size str_length = strlen(str); 123 | 124 | str_size new_length = s_data->length + str_length - len; 125 | 126 | // make sure there is enough room for new characters and null terminator 127 | if (s_data->alloc <= new_length + 1) { 128 | string_data* new_s_data = string_alloc(new_length); 129 | 130 | memcpy(new_s_data->buff, s_data->buff, pos); // copy leading characters 131 | memcpy(&new_s_data->buff[pos+str_length], &s_data->buff[pos+len], s_data->length-pos-len); // copy trailing characters 132 | 133 | free(s_data); 134 | 135 | s_data = new_s_data; 136 | 137 | } else { 138 | memmove(&s_data->buff[pos+str_length], &s_data->buff[pos+len], s_data->length-pos-len); // move trailing characters 139 | } 140 | 141 | s_data->length = new_length; 142 | 143 | // copy str chars 144 | memcpy(&s_data->buff[pos], str, str_length); 145 | 146 | s_data->buff[new_length] = '\0'; // add new null terminator 147 | 148 | *s = s_data->buff; 149 | } 150 | 151 | void string_remove(string s, str_size pos, str_size len) { 152 | string_data* s_data = string_get_data(s); 153 | // anyone who puts in a bad index can face the consequences on their own 154 | memmove(&s_data->buff[pos], &s_data->buff[pos+len], s_data->length - pos); 155 | s_data->length -= len; 156 | s_data->buff[s_data->length] = '\0'; 157 | } 158 | 159 | void string_free(string s) { 160 | free(string_get_data(s)); 161 | } 162 | 163 | str_size string_size(string s) { 164 | return ((str_size*)s)[-1]; 165 | } 166 | 167 | str_size string_get_alloc(string s) { 168 | return ((str_size*)s)[-2]; 169 | } 170 | -------------------------------------------------------------------------------- /str.h: -------------------------------------------------------------------------------- 1 | // 2 | // str.h 3 | // 4 | // Created by Mashpoe on 2/28/19. 5 | // 6 | 7 | #ifndef str_h 8 | #define str_h 9 | 10 | #include 11 | #include 12 | 13 | typedef char* string; 14 | typedef size_t str_size; 15 | 16 | string string_create(const char* str); 17 | 18 | void string_add_char(string* s, char c); 19 | 20 | void string_add(string* s, const char* str); 21 | 22 | void string_insert(string* s, str_size pos, const char* str); 23 | 24 | void string_replace(string* s, str_size pos, str_size len, const char* str); 25 | 26 | void string_remove(string s, str_size pos, str_size len); // removing elements does not require reallocation 27 | 28 | void string_free(string s); 29 | 30 | str_size string_size(string s); 31 | 32 | str_size string_get_alloc(string s); 33 | 34 | #endif /* str_h */ 35 | --------------------------------------------------------------------------------