#ifndef STRARR_H #define STRARR_H #include #include #include #include typedef struct strarr { char *data; /* the underlying data */ size_t len; /* the amount of string entries */ /* For instance, if the strings are { "Hello", "World" }, and 100 bytes are allocated, `used` is 13 bytes (w/ two str NULLs and one arr NULL), and `cap` is 100 bytes. The index of the arr NULL is [used - 1] */ size_t used; /* the total bytes used of the capacity for the strings, incl NULLs */ size_t cap; /* the total bytes allocated to the buffer, incl NULLs */ } strarr; /* The capacity should _include_ the trailing NULL of the array. */ void strarr_init_empty(strarr *sa, size_t cap) { sa->len = 0; sa->used = 1; /* as includes the NULL */ sa->cap = cap; sa->data = (char*) malloc(cap); sa->data[sa->cap] = '\0'; } /* Get a pointer to the NULL that terminates the array. Useful for bounds checking, * or over-writing it in the case of, e.g. strarr_app. */ char * strarr_internal_ptr_to_arr_null(strarr *sa) { size_t i = sa->used == 0 ? 0 : sa->used - 1; return &sa->data[i]; } int strarr_internal_is_corrupted(strarr *sa) { if ( sa->used > sa->cap || *strarr_internal_ptr_to_arr_null(sa) != '\0' ) { return 1; } else { return 0; } } void strarr_init_from_str_split(strarr *sa, const char *str, char del) { size_t len = strlen(str); char* data = (char*) malloc(len + 2); /* include the NULL terminator for str and arr*/ sa->data = data; sa->cap = len + 2; /* includes the NULL terminators */ memcpy(sa->data, str, len + 1); /* include the NULL of the str but no extra bytes. */ /* There are at minimum one entries. */ sa->len = 1; /* Swap all `del` for '\0'. */ for (size_t i = 0; i < sa->cap; ++i) { if (sa->data[i] == del) { sa->data[i] = '\0'; sa->len++; } } sa->used = len + 2; // + str NULL + arr NULL /* Add the NULL terminator for the array. */ sa->data[sa->used] = '\0'; } void strarr_debug(strarr *sa) { printf("sa->len :: %d\n", sa->len); printf("sa->cap :: %d\n", sa->cap); printf("sa->used :: %d\n", sa->used); printf("used ck :: %s\n", sa->used > sa->cap ? "exceeds cap" : "within cap"); printf("corrupted :: %s\n", strarr_internal_is_corrupted(sa) ? "yes" : "no" ); printf("sa->data :: "); for (int i = 0; i < sa->cap; ++i) { sa->data[i] == '\0' ? printf("<0>") : printf("%c", sa->data[i]); } printf("\n"); } void strarr_free(strarr *sa) { free(sa->data); } /* Copy string into strarr, appending it in the process. TODO: add check for when appending in excess of `cap` and returning an error. */ void strarr_app(strarr *sa, char *str) { size_t len = strlen(str); // Overwrite arr NULL: TODO: add bounds check with `cap`. strcpy(strarr_internal_ptr_to_arr_null(sa), str); sa->used += (len + 1); /* new str + new str NULL */ *strarr_internal_ptr_to_arr_null(sa) = '\0'; /* as was overwritten */ sa->len++; /* Add one more entry */ } char * strarr_index(strarr *sa, size_t idx) { size_t cnt = 0; if (idx == 0) { return sa->data; } for (char *i = sa->data; i < strarr_internal_ptr_to_arr_null(sa) - 1; ++i) { if (*i == '\0') { if (++cnt == idx) { // If we've encountered the NULL, the index of the next str // is one byte after it. return i + 1; } } } return NULL; } /* Initialize `sa` from `from`, whilst pointing to the same underlying data. The caller does not need to call `free`; since the array is shared, any actions taken may corrupt the original structure. */ void strarr_init_clone_shallow(strarr *sa, const strarr *from) { sa->cap = from->cap; sa->data = from->data; sa->len = from->len; sa->used = from->used; } /* Initialize `sa` from `from`, allocating a new array and copying the contents of from into it. The caller must call strarr_free. */ void strarr_init_clone_deep(strarr *sa, const strarr *from) { sa->cap = from->cap; sa->len = from->len; sa->used = from->used; sa->data = (char*) malloc(from->cap); strcpy(sa->data, from->data); } char * strarr_next(strarr *sa, char *ptr) { if (!ptr) { return NULL; } while (*ptr++) ; return *ptr ? ptr : NULL; } #endif