You can only ask user to explicitly mark their input as literal or allocated string.
However, as @Mints97 mentions in his answer, basically this approach is architecturally incorrect: you force user of your library for some explicit actions, and if he forgets to, it leads most likely to a memory leak (or even to an application crash). So use it only if:
- You want to drastically reduce amount of allocations. In my case it was JSON node names, that never change during lifetime of a program.
- You have good control of code of consumers of your library. In my case, libraries are shipped with binaries and tightly bound to them.
Implementation example
#define AAS_DYNAMIC 'D'
#define AAS_STATIC 'S'
#define AAS_STATIC_PREFIX "S"
#define AAS_CONST_STR(str) ((AAS_STATIC_PREFIX str) + 1)
char* aas_allocate(size_t count) {
char* buffer = malloc(count + 2);
if(buffer == NULL)
return NULL;
*buffer = AAS_DYNAMIC;
return buffer + 1;
}
void aas_free(char* aas) {
if(aas != NULL) {
if(*(aas - 1) == AAS_DYNAMIC) {
free(aas - 1);
}
}
}
...
char* s1 = AAS_CONST_STR("test1");
char* s2 = aas_allocate(10);
strcpy(s2, "test2");
aas_free(s1);
aas_free(s2);
Testing performance (note #1)
I benchmarked my libtsjson library with following code (800k iterations):
node = json_new_node(NULL);
json_add_integer(node, NODE_NAME("i"), 10);
json_add_string(node, NODE_NAME("s1"), json_str_create("test1"));
json_add_string(node, NODE_NAME("s2"), json_str_create("test2"));
json_node_destroy(node);
My CPU is Intel Core i7 860.
If NODE_NAME
is just a macro, time per iteration was 479ns
If NODE_NAME
is a memory allocation, time per iteration was 609ns
Hinting user or compiler (note #2)
Add a hint to all such pointers, i.e. Linux static source analyser Sparse may catch such issues
char __autostring* s1 = aas_copy("test"); /* OK */
char __autostring* s2 = strdup("test"); /* Should be fail? */
char* s3 = s1; /* Confuses sparse */
char* s4 = (char*) s1; /* Explicit conversion - OK */
(not completely sure about outputs of Sparse)
This approach is one more step to a world of a dirty C hacks, i.e. sizeof(*aas_t)
is now > 1.
Full source with changes may be found here. If compiled with -DAAS_STRICT
it will raise tons of errors: https://ideone.com/xxmSat Even for correct code it can complain about strcpy()
(not reproduced on ideone).