Last active
April 26, 2020 16:29
-
-
Save TotallyNotChase/d5789b5a12e794326e92f30cfe51b78e to your computer and use it in GitHub Desktop.
Some string functions to review
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<stdio.h> | |
#include<stdlib.h> | |
#include<string.h> | |
#include "strfuncs.h" | |
#include "strfuncsdef.h" | |
// Append given char to given string | |
// Returns NULL on failure | |
static char* str_append(char element, char* str, size_t end_index, size_t* size) | |
{ | |
char* temp; | |
if (end_index == *size) | |
{ | |
/* | |
This reallocates str to target_size | |
If realloc fails, it frees str and returns NULL | |
*/ | |
STR_INCREASE_ALLOCATION(str, temp, *size, str_append); | |
} | |
// Assign the char | |
str[end_index] = element; | |
// Assign the new size | |
return str; | |
} | |
// Inserts a string to given array at given index | |
// Returns NULL on failure | |
static char** strarr_append(char* elementstr, char** strarr, size_t end_index, size_t* size) | |
{ | |
char** temp; | |
if (end_index == *size) | |
{ | |
/* | |
This reallocates strarr to target_size | |
If realloc fails, it passes strarr to destroy_strarr | |
The current number of strings in strarr is *size (which is why we need to realloc) | |
So *size is passed as length param to destroy_strarr | |
*/ | |
STRARR_INCREASE_ALLOCATION(strarr, temp, *size, strarr_append); | |
} | |
// Assign the string | |
strarr[end_index] = elementstr; | |
// Assign the new size | |
return strarr; | |
} | |
// Minimize given string for exactly the amount of memory it needs | |
// Returns NULL on failure | |
static char* trunc_string(char* str, size_t curr_len) | |
{ | |
char* temp; | |
// 1 more slot for NULL terminator | |
/* | |
This reallocates str to len | |
If realloc fails, it frees str and returns NULL | |
*/ | |
STR_MINIMIZE_ALLOCATION(str, temp, curr_len, trunc_string); | |
// Null terminate the string | |
str[curr_len] = '\0'; | |
return str; | |
} | |
// Minimize given string array for exactly the amount of memory it needs | |
// Returns NULL on failure | |
static char** trunc_strarray(char** strarr, size_t curr_len) | |
{ | |
char** temp; | |
/* | |
This reallocates strarr to curr_len (which is equal to the number of strings in strarr) | |
If realloc fails, it passes strarr to destroy_strarr | |
The current number of strings in strarr is curr_len | |
So curr_len is passed as length param to destroy_strarr | |
*/ | |
STRARR_MINIMIZE_ALLOCATION(strarr, temp, curr_len, trunc_strarray); | |
return strarr; | |
} | |
// A function to get string user input | |
// Returns NULL on failure | |
char* get_string(const char* prompt) | |
{ | |
size_t index, capacity = 1; | |
int element; | |
char* string = malloc(capacity * sizeof(*string)); | |
if (string == NULL) | |
{ | |
return NULL; | |
} | |
// Print the given prompt | |
fputs(prompt, stdout); | |
for (index = 0; (element = fgetc(stdin)) != EOF && element != '\n'; index++) | |
{ | |
// Record every character input until user presses enter (and or we encounter EOF) | |
string = str_append((char) element, string, index, &capacity); | |
// Check if string is NULL, indicating error | |
STRINGFUNC_FAIL_CHECK(string, get_string); | |
} | |
if (ferror(stdin) != 0) | |
{ | |
// Free the buffer and return NULL | |
free(string); | |
// If an error occured while reading input let the user know | |
fprintf(stderr, "An error occured while reading input from stdin in get_string\n"); | |
// Clear the error as it has already been checked | |
clearerr(stdin); | |
return NULL; | |
} | |
if (index == 0 && element == EOF) | |
{ | |
// Input was just EOF, invalid | |
free(string); | |
fprintf(stderr, "Encountered EOF with no input\n"); | |
return NULL; | |
} | |
// Truncate and null terminate the string | |
string = trunc_string(string, index); | |
// Check if string is NULL, indicating error | |
STRINGFUNC_FAIL_CHECK(string, get_string); | |
return string; | |
} | |
// Splits a string by delimiter into an array of strings | |
// Returns NULL on failure | |
char** split_string(char delimiter, const char* string, size_t* length) | |
{ | |
// Variables to keep track of splitarr | |
size_t arrsize = 2, arrindex = 0; | |
// Variables to keep track of elementstr | |
size_t strsize = 2, strindex = 0; | |
// Set up splitarr and elementstr with an initial size; | |
char** splitarr = malloc(arrsize * sizeof(*splitarr)); | |
if (splitarr == NULL) | |
{ | |
fprintf(stderr, "malloc failed in split_string\n"); | |
return NULL; | |
} | |
char* elementstr = malloc(strsize * sizeof(*elementstr)); | |
if (elementstr == NULL) | |
{ | |
destroy_strarr(splitarr, arrindex); | |
fprintf(stderr, "malloc failed in split_string\n"); | |
return NULL; | |
} | |
char* temp; | |
// Defining a custom scope | |
{ | |
// Custom scope | |
int ignore_strindex_increment = 0; | |
for (int index = 0; string[index] != '\0'; strindex++, index++) | |
{ | |
if (ignore_strindex_increment) | |
{ | |
// Ignore latest increment | |
strindex--; | |
ignore_strindex_increment = 0; | |
} | |
if (string[index] == delimiter) | |
{ | |
// elementstr ends here | |
// Truncate and null terminate the string | |
elementstr = trunc_string(elementstr, strindex); | |
/* | |
Check if string is NULL, indicating error | |
If it is, make sure splitarr is freed before returning | |
*/ | |
STRINGFUNC_FAIL_CHECK_FREE_STRARR(elementstr, splitarr, arrindex, split_string); | |
// Add string to string array | |
splitarr = strarr_append(elementstr, splitarr, arrindex, &arrsize); | |
/* | |
Check if stringarr is NULL, indicating error | |
If it is, make sure elementstr is freed before returning | |
*/ | |
STRINGFUNC_FAIL_CHECK_FREE_STR(splitarr, elementstr, split_string); | |
arrindex++; | |
// Cleanup - make elementstr empty and have a size of 1 | |
// This is invoked right after encountering the delimiter | |
// This prepares elementstr for the next string | |
strsize = 1; | |
strindex = 0; | |
temp = realloc(NULL, strsize * sizeof(*temp)); | |
/* | |
Check if string is NULL, indicating error | |
If it is, make sure splitarr is freed before returning | |
*/ | |
STRINGFUNC_FAIL_CHECK_FREE_STRARR(temp, splitarr, arrindex, split_string); | |
elementstr = temp; | |
// The strindex will be incremented to 1 right afterwards, which we don't want | |
// elementstr should start from the beginning after cleanup | |
ignore_strindex_increment = 1; | |
} | |
else | |
{ | |
// non-delimiter character, append to elementstr | |
elementstr = str_append(string[index], elementstr, strindex, &strsize); | |
/* | |
Check if string is NULL, indicating error | |
If it is, make sure splitarr is freed before returning | |
*/ | |
STRINGFUNC_FAIL_CHECK_FREE_STRARR(elementstr, splitarr, arrindex, split_string); | |
} | |
} | |
if (ignore_strindex_increment) | |
{ | |
// Ignore latest increment | |
strindex--; | |
} | |
} | |
// Truncate and null terminate the final string | |
elementstr = trunc_string(elementstr, strindex); | |
/* | |
Check if string is NULL, indicating error | |
If it is, make sure splitarr is freed before returning | |
*/ | |
STRINGFUNC_FAIL_CHECK_FREE_STRARR(elementstr, splitarr, arrindex, split_string); | |
// Add final string to string array | |
splitarr = strarr_append(elementstr, splitarr, arrindex, &arrsize); | |
/* | |
Check if stringarr is NULL, indicating error | |
If it is, make sure elementstr is freed before returning | |
*/ | |
STRINGFUNC_FAIL_CHECK_FREE_STR(splitarr, elementstr, split_string); | |
// Truncate the string array | |
splitarr = trunc_strarray(splitarr, arrindex); | |
/* | |
Check if splitarr is NULL, indicating error | |
This is the only time when STRINGFUNC_FAIL_CHECK can be used | |
in a function with more than one pointer, at this point all the char pointers | |
are already in splitarr, so freeing just splitarr in case of failure is enough | |
*/ | |
STRINGFUNC_FAIL_CHECK(splitarr, split_string); | |
// Assign the length of the array | |
*length = arrindex + 1; | |
return splitarr; | |
} | |
// Free all strings inside an array of strings and the array itself | |
// Returns NULL on success | |
char** destroy_strarr(char** strarr, size_t length) | |
{ | |
size_t index = 0; | |
while (index < length) | |
{ | |
// Free the elements and assign the pointer to NULL | |
free(strarr[index]); | |
strarr[index++] = NULL; | |
} | |
// Free the array itself and assign to NULL | |
free(strarr); | |
strarr = NULL; | |
return strarr; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef STRFUNCS_H | |
#define STRFUNCS_H | |
/* | |
Take string input from user | |
Pass in a string prompt to display to the user prior to input | |
Returns a pointer to the input string | |
Returns NULL upon failure | |
Returned pointer should be freed by the user using free() | |
*/ | |
char* get_string(const char* prompt); | |
/* | |
Split given string by delimiter into an array of strings | |
Pass in the address of a variable to store the length of the array | |
Returns a pointer to the array of strings | |
Returns NULL upon failure | |
Returned pointer should be freed by the user using destroy_strarr() | |
*/ | |
char** split_string(char delimiter, const char* string, size_t* length); | |
/* | |
Free all the memory used by an array of strings | |
Assigns all the string elements as NULL | |
Returns NULL on success | |
*/ | |
char** destroy_strarr(char** strarr, size_t length); | |
#endif // !STRFUNCS_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef STRFUNCSDEF_H | |
#define STRFUNCSDEF_H | |
// DO NOT INCLUDE THIS HEADER IN YOUR CODE | |
// INTENDED FOR INTERNAL LIBRARY USE ONLY | |
// INTENDED FOR USE ONLY BY THIS HEADER | |
/* | |
Macro for sanity checking current length - should be used by char* str | |
Check if given length is suitable for increasing exponentially (multiply by 2) | |
If it is 0 (i.e exponential increase has no effect), change it to 1 | |
If it exceeds SIZE_MAX when increased, free the buffer and return NULL | |
*/ | |
#define STR_SIZE_CHECK(buffer, len, location) \ | |
do { \ | |
if (len == 0) \ | |
{ \ | |
len = 1; \ | |
} \ | |
else if (len > SIZE_MAX / 2) \ | |
{ \ | |
fprintf(stderr, "target allocation exceeds SIZE_MAX in " #location "\n"); \ | |
free(buffer); \ | |
buffer = NULL; \ | |
return NULL; \ | |
} \ | |
} while (0) | |
// INTENDED FOR USE ONLY BY THIS HEADER | |
/* | |
Macro for checking realloc return value - should be used by char* str | |
Check if the temporary pointer returned by realloc is NULL | |
If it is NULL, free the actual buffer and return NULL | |
If it is not NULL, assign the actual buffer with the temporary pointer | |
*/ | |
#define STR_ALLOC_FAIL_CHECK(buffer, temp, location) \ | |
if (temp == NULL) \ | |
{ \ | |
free(buffer); \ | |
buffer = NULL; \ | |
fprintf(stderr, "realloc failed in " #location "\n"); \ | |
return NULL; \ | |
} \ | |
buffer = temp | |
// INTENDED FOR USE ONLY BY THE LIB SOURCE FILE | |
/* | |
Macro for increasing memory for a string when needed - should be used by char* str | |
Includes guards for error checking | |
*/ | |
#define STR_INCREASE_ALLOCATION(buffer, temp, curr_len, location) \ | |
do { \ | |
STR_SIZE_CHECK(buffer, curr_len, location); \ | |
size_t target_capacity = curr_len * 2; \ | |
temp = realloc(buffer, target_capacity * sizeof(*buffer)); \ | |
STR_ALLOC_FAIL_CHECK(buffer, temp, location); \ | |
curr_len = target_capacity; \ | |
} while (0) | |
// INTENDED FOR USE ONLY BY THE LIB SOURCE FILE | |
/* | |
Macro for minimizing memory for a string - should be used by char* str | |
Includes guards for error checking | |
*/ | |
#define STR_MINIMIZE_ALLOCATION(buffer, temp, curr_len, location) \ | |
do { \ | |
temp = realloc(buffer, (curr_len + 1) * sizeof(*buffer)); \ | |
STR_ALLOC_FAIL_CHECK(buffer, temp, location); \ | |
} while (0) | |
// INTENDED FOR USE ONLY BY THIS HEADER | |
/* | |
Macro for sanity checking size - should be used by char** strarr | |
Check if given size is suitable for increasing exponentially (multiply by 2) | |
If it is 0 (i.e exponential increase has no effect), change it to 1 | |
If it exceeds SIZE_MAX when increased, free the buffer, all its contents and return NULL | |
*/ | |
#define STRARR_SIZE_CHECK(buffer, len, location) \ | |
do { \ | |
if (len == 0) \ | |
{ \ | |
len = 1; \ | |
} \ | |
else if (len > SIZE_MAX / 2) \ | |
{ \ | |
buffer = destroy_strarr(buffer, len); \ | |
fprintf(stderr, "target allocation exceeds SIZE_MAX in " #location "\n"); \ | |
return NULL; \ | |
} \ | |
} while (0) | |
// INTENDED FOR USE ONLY BY THIS HEADER | |
/* | |
Macro for checking realloc return value - should be used by char** strarr | |
Check if the temporary pointer returned by realloc is NULL | |
If it is NULL, free the actual buffer, all its contents and return NULL | |
If it is not NULL, assign the actual buffer with the temporary pointer | |
*/ | |
#define STRARR_ALLOC_FAIL_CHECK(buffer, temp, len, location) \ | |
if (temp == NULL) \ | |
{ \ | |
buffer = destroy_strarr(buffer, len); \ | |
fprintf(stderr, "realloc failed in " #location "\n"); \ | |
return NULL; \ | |
} \ | |
buffer = temp \ | |
// INTENDED FOR USE ONLY BY THE LIB SOURCE FILE | |
/* | |
Macro for increasing memory for an array of strings when needed - should be used by char** strarr | |
Includes guards for error checking | |
*/ | |
#define STRARR_INCREASE_ALLOCATION(buffer, temp, curr_len, location) \ | |
do { \ | |
STRARR_SIZE_CHECK(buffer, curr_len, location); \ | |
size_t target_capacity = curr_len * 2; \ | |
temp = realloc(buffer, target_capacity * sizeof(*buffer)); \ | |
STRARR_ALLOC_FAIL_CHECK(buffer, temp, curr_len, location); \ | |
curr_len = target_capacity; \ | |
} while (0) | |
// INTENDED FOR USE ONLY BY THE LIB SOURCE FILE | |
/* | |
Macro for minimizing memory for an array of strings string - should be used by char** strarr | |
Includes guards for error checking | |
*/ | |
#define STRARR_MINIMIZE_ALLOCATION(buffer, temp, curr_len, location) \ | |
do { \ | |
temp = realloc(buffer, (curr_len + 1) * sizeof(*buffer)); \ | |
STRARR_ALLOC_FAIL_CHECK(buffer, temp, curr_len, location); \ | |
} while (0) | |
// INTENDED FOR USE ONLY BY THE LIB SOURCE FILE | |
/* | |
Macro for checking internal function calls for errors | |
All internal (private) strfuncs library functions return NULL on failure | |
Check if the return value was NULL from any of these functions | |
If it was, return NULL from current function | |
*/ | |
#define STRINGFUNC_FAIL_CHECK(buffer, location) \ | |
do { \ | |
if (buffer == NULL) \ | |
{ \ | |
fprintf(stderr, "private strfunc function failed in " #location "\n"); \ | |
return NULL; \ | |
} \ | |
} while (0) | |
/* | |
NOTE: This only makes sure the given buffer is free, if there are more than one | |
buffer in current scope, make sure they are all freed before returning | |
*/ | |
// INTENDED FOR USE ONLY BY THE LIB SOURCE FILE | |
/* | |
Macro for memory cleanup before returning | |
Check if returned buffer from private stringfunc is NULL | |
If it is, free the other pointer in the current scope | |
which is the array of strings splitarr in case of split_strings | |
*/ | |
#define STRINGFUNC_FAIL_CHECK_FREE_STRARR(buffer, bufferarr, bufferarr_len, location) \ | |
do { \ | |
if (buffer == NULL) \ | |
{ \ | |
destroy_strarr(bufferarr, bufferarr_len); \ | |
fprintf(stderr, "private strfunc function failed in " #location "\n"); \ | |
return NULL; \ | |
} \ | |
} while (0) | |
/* | |
NOTE: This is to be used when there are more than one pointer in current scope | |
Private stringfunc function will free the pointer that failed and assign it to NULL | |
But if there is another variable (which is splitarr in case of split_strings), it needs | |
to be freed | |
*/ | |
// INTENDED FOR USE ONLY BY THE LIB SOURCE FILE | |
/* | |
Macro for memory cleanup before returning | |
Check if returned bufferarr from private stringfunc is NULL | |
If it is, free the other pointer in the current scope | |
which is the string elementstr in case of split_strings | |
*/ | |
#define STRINGFUNC_FAIL_CHECK_FREE_STR(bufferarr, buffer, location) \ | |
do { \ | |
if (bufferarr == NULL) \ | |
{ \ | |
free(buffer); \ | |
fprintf(stderr, "private strfunc function failed in " #location "\n"); \ | |
return NULL; \ | |
} \ | |
} while (0) | |
/* | |
NOTE: This should be used in the same situation as the previous macro | |
This one frees the char* str instead of the char** strarray | |
*/ | |
#endif // !STRFUNCSDEF_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment