In this article I will present to you a dynamic string data structure helper implementation. In C, a string is just an array of characters terminated with the null character (NUL in ASCII). Also, some times we handle a string by using a pointer to a buffer containing sequential characters terminated also with the null character. The C standard liibrary provides various functions for string manipulation but it seems that whenever we want to extend a string by using pointers we have to perform all the time memory reallocation which leads to a bad evolution of replicated code. In a recent project, I needed to create a string and append to it any number of new strings in order to extend it without using strcat, strcpy, strlen and realloc functions all the time. So, I created a simple dynamic string data structure which solved my specific problem and I would like to share it with you people.
Below, is the header containing the data type definition of the dynamic string:
/* * Copyright (C) 2014 Efstathios Chatzikyriakidis (stathis.chatzikyriakidis@gmail.com). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _DYNAMIC_STRING_HELPER_TYPES_H_ #define _DYNAMIC_STRING_HELPER_TYPES_H_ #include <stddef.h> #include "shared-types.h" /* * type definitions. */ // dynamic string type definition. typedef struct dynamic_string_t { string_t string; size_t string_length; size_t buffer_length; } dynamic_string_t; #endif // _DYNAMIC_STRING_HELPER_TYPES_H_
Next, follows the header of the source code unit:
/* * Copyright (C) 2014 Efstathios Chatzikyriakidis (stathis.chatzikyriakidis@gmail.com). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _DYNAMIC_STRING_HELPER_UTILITY_H_ #define _DYNAMIC_STRING_HELPER_UTILITY_H_ #include "dynamic-string-helper-types.h" dynamic_string_t * const dynamic_string_create (size_t buffer_length); void dynamic_string_clear (dynamic_string_t * dynamic_string); bool_t dynamic_string_append (dynamic_string_t * const dynamic_string, const string_t const string); size_t dynamic_string_length (const dynamic_string_t * const dynamic_string); #endif // _DYNAMIC_STRING_HELPER_UTILITY_H_
Here is the implementation of the source code unit:
/* * Copyright (C) 2014 Efstathios Chatzikyriakidis (stathis.chatzikyriakidis@gmail.com). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <string.h> #include <stdlib.h> #include "memory-management-helper-macros.h" #include "dynamic-string-helper-utility.h" /* * functions. */ static unsigned int get_next_power_of_buffer_length (unsigned int current_length) { --current_length; current_length |= current_length >> 0x01; current_length |= current_length >> 0x02; current_length |= current_length >> 0x04; current_length |= current_length >> 0x08; current_length |= current_length >> 0x10; return ++current_length; } static bool_t dynamic_string_extend (dynamic_string_t * const dynamic_string, const size_t extra_length) { const size_t total_buffer_length = dynamic_string->string_length + extra_length; if (total_buffer_length <= dynamic_string->buffer_length) { return true; } const unsigned int next_power_of_buffer_length = get_next_power_of_buffer_length (total_buffer_length); const string_t const string = (string_t) realloc (dynamic_string->string, next_power_of_buffer_length); if (!string) { return false; } dynamic_string->buffer_length = next_power_of_buffer_length; dynamic_string->string = string; return true; } dynamic_string_t * const dynamic_string_create (size_t buffer_length) { static const size_t initial_buffer_length = 32; if (buffer_length < 0) { buffer_length = initial_buffer_length; } dynamic_string_t * dynamic_string = (dynamic_string_t *) calloc (1, sizeof (dynamic_string_t)); if (!dynamic_string) { return NULL; } dynamic_string->string = NULL; dynamic_string->string_length = 0; dynamic_string->buffer_length = buffer_length; if (dynamic_string->buffer_length > 0) { dynamic_string->string = (string_t) malloc (dynamic_string->buffer_length); if (!dynamic_string->string) { safe_free (dynamic_string); return NULL; } } return dynamic_string; } void dynamic_string_clear (dynamic_string_t * dynamic_string) { if (dynamic_string) { safe_free(dynamic_string->string); safe_free(dynamic_string); } } size_t dynamic_string_length (const dynamic_string_t * const dynamic_string) { return dynamic_string->string_length; } bool_t dynamic_string_append (dynamic_string_t * const dynamic_string, const string_t const string) { const size_t string_length = strlen (string); if (!dynamic_string_extend (dynamic_string, string_length + 1)) { return false; } memcpy (&dynamic_string->string[dynamic_string->string_length], string, string_length); dynamic_string->string_length += string_length; dynamic_string->string[dynamic_string->string_length] = '\0'; return true; }
Lastly, here is an example of using the dynamic string data structure helper:
bool_t write_http_response (const int file_descriptor, const http_response_t * const http_response) { dynamic_string_t * const response = dynamic_string_create (http_response_initial_buffer_length); if (!response) { return false; } if (!dynamic_string_append (response, http_response->protocol_version) || !dynamic_string_append (response, " ") || !dynamic_string_append (response, http_response->status_code) || !dynamic_string_append (response, " ") || !dynamic_string_append (response, http_response->reason_phrase) || !dynamic_string_append (response, http_line_end_delimiter)) { dynamic_string_clear (response); return false; } const http_header_field_t * headers = http_response->headers; while (headers) { if (!dynamic_string_append (response, headers->name) || !dynamic_string_append (response, http_header_field_delimiter) || !dynamic_string_append (response, headers->value) || !dynamic_string_append (response, http_line_end_delimiter)) { dynamic_string_clear (response); return false; } headers = headers->next; } if (!dynamic_string_append (response, http_line_end_delimiter) || !dynamic_string_append (response, http_response->body->string)) { dynamic_string_clear (response); return false; } const bool_t written = write_to_file_descriptor (file_descriptor, response->string, dynamic_string_length (response)); dynamic_string_clear (response); return written != -1; }
Happy Hacking!