Skip to content

Instantly share code, notes, and snippets.

@atoomic
Forked from bricef/smartystring.c
Created December 11, 2018 16:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atoomic/df5b9574778737c4e1ae9ee2146690ff to your computer and use it in GitHub Desktop.
Save atoomic/df5b9574778737c4e1ae9ee2146690ff to your computer and use it in GitHub Desktop.
Dynamically growing a string in C.
#include <stdio.h>
#include <stdlib.h>
#define SMARTY_SIZE_INIT 16
typedef struct {
char * str; // a null terminated C string
char * end; // a pointer to the null byte, to be able to repeatedly append
// without using strlen() every time.
size_t size; // currently allocated size for *str, so we know when we
// need to grow.
} smarty;
/*
* Creates a new string,
* if there is not enough memory, will return a null pointer instead.
*/
smarty * smarty_new(){
// allocate space for the smarty struct
smarty * ss = malloc(sizeof(smarty));
// quit if no memory
if(NULL == ss) goto smarty_new_fail;
// allocate space for the string itself
ss->str = malloc(SMARTY_SIZE_INIT);
//quit and cleanup if this failed
if(NULL == ss->str ) goto smarty_new_fail;
// set up the other bits of the smarty string
ss->end = ss->str;
*ss->end = '\0';
ss->size = SMARTY_SIZE_INIT;
return ss;
smarty_new_fail:
free(ss);
return NULL;
}
/*
* Append to a smarty string, growing the allocated memory if needed
* on error, will return the longest string possible, which may truncate the
* suffix.
*/
void smarty_append(smarty * ss, char * suffix){
// iterate through suffix by character. Could be more efficient by using
// memcopy or similar, but the idea is the same.
while(*suffix != '\0'){
// Check to see if the string needs to grow or not.
// also make sure we leave space for the null-termination byte at the end
if(! ((ss->end - ss->str) < (ss->size-1)) ){
// store the offset, since the end ptr will not be valid if realloc
// moves our memory block
size_t offset = ss->end - ss->str;
// allocate bigger space, grows by doubling (good amortization)
char* newstr = realloc(ss->str, (ss->size * 2) );
// if we're out of memory, we bail out.
if(NULL == newstr){ ss->end--; break; }
// reset the smarty string's internal to reflect the new size and
// possible memory block
ss->str = newstr;
ss->size = ss->size * 2;
ss->end = ss->str + offset;
}
//append chars to the string
*(ss->end) = *suffix;
suffix++;
ss->end++;
}
// make sure we're always null terminated correctly.
*(ss->end) = '\0';
}
/*
* clean things up when we're not needed
*/
void smarty_destroy(smarty * ss){
free(ss->str);
free(ss);
}
int main(void){
smarty * ss = smarty_new();
smarty_append(ss, "Hello 1 ");
smarty_append(ss, "Hello 2 ");
smarty_append(ss, "Hello 3 ");
smarty_append(ss, "Hello 4 ");
smarty_append(ss, "Hello 5 ");
smarty_append(ss, "Hello 6 ");
smarty_append(ss, "Hello 7 ");
smarty_append(ss, "Hello 8 ");
smarty_append(ss, "Hello 9 ");
printf("->%s\n", ss->str);
smarty_destroy(ss);
return 0;
}
➤ gcc -g smartystring.c -o smarty && valgrind --leak-check=full ./smarty
==16785== Memcheck, a memory error detector
==16785== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==16785== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==16785== Command: ./smarty
==16785==
==16785==
==16785== HEAP SUMMARY:
==16785== in use at exit: 0 bytes in 0 blocks
==16785== total heap usage: 5 allocs, 5 frees, 252 bytes allocated
==16785==
==16785== All heap blocks were freed -- no leaks are possible
==16785==
==16785== For counts of detected and suppressed errors, rerun with: -v
==16785== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment