/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ #include <gs/util/bytebuffer.h> #include <gs/util/check.h> #include <stdio.h> #define GS_BYTEBUFFER_F_FAILED 0x01 #define GS_BYTEBUFFER_F_OVERRUN 0x02 gs_error_t gs_bytebuffer_init(gs_bytebuffer_t * bb, void * buffer, size_t buffer_size) { GS_CHECK_HANDLE(bb != NULL); memset(bb, 0, sizeof(*bb)); if (buffer) { if (buffer_size < 2) { // must always have room for NUL termination. return GS_ERROR_ARG; } bb->buffer = buffer; bb->size = buffer_size; } else { // dry run - don't insert anything in buffer, but increment used } return GS_OK; } void gs_bytebuffer_vprintf(gs_bytebuffer_t * bb, const char * format, va_list ap) { int res; if (bb->buffer == NULL) { // dry run char buf[3]; res = vsnprintf(buf, 0, format, ap); if (res >= 0) { bb->used += res; } } else { const size_t free_bytes = gs_bytebuffer_get_free(bb); res = vsnprintf((char*)&bb->buffer[bb->used], free_bytes, format, ap); if (res > 0) { if ((size_t)res >= free_bytes) { // over run bb->flags |= GS_BYTEBUFFER_F_OVERRUN; bb->used = bb->size; bb->buffer[bb->size - 1] = 0; } else { bb->used += res; } } } if (res < 0) { bb->flags |= GS_BYTEBUFFER_F_FAILED; } } void gs_bytebuffer_printf(gs_bytebuffer_t * bb, const char * format, ...) { va_list ap; va_start(ap, format); gs_bytebuffer_vprintf(bb, format, ap); va_end(ap); } void gs_bytebuffer_append(gs_bytebuffer_t * bb, const void * data, size_t length) { if (bb->buffer == NULL) { // dry run bb->used += length; } else { const size_t free_bytes = gs_bytebuffer_get_free(bb); if (free_bytes >= length) { memcpy(&bb->buffer[bb->used], data, length); bb->used += length; } else { memcpy(&bb->buffer[bb->used], data, free_bytes); bb->flags |= GS_BYTEBUFFER_F_OVERRUN; bb->used += free_bytes; } } } void gs_bytebuffer_append_string(gs_bytebuffer_t * bb, const char * string) { if (gs_string_empty(string) == false) { gs_bytebuffer_append(bb, string, strlen(string)); } } void gs_bytebuffer_append_string_max(gs_bytebuffer_t * bb, const char * string, size_t max_length) { if (gs_string_empty(string) == false) { gs_bytebuffer_append(bb, string, strnlen(string, max_length)); } } char * gs_bytebuffer_get_as_string(gs_bytebuffer_t * bb, gs_error_t * error) { if (bb && bb->buffer) { // handle NUL termination if (bb->used < bb->size) { bb->buffer[bb->used] = 0; } else { // overrun - truncation buffer bb->flags |= GS_BYTEBUFFER_F_OVERRUN; bb->buffer[bb->used - 1] = 0; } if (error) { *error = gs_bytebuffer_get_state(bb); } return (char*) bb->buffer; } return NULL; } gs_error_t gs_bytebuffer_get_state(gs_bytebuffer_t * bb) { if (bb) { if (bb->flags & GS_BYTEBUFFER_F_FAILED) { return GS_ERROR_DATA; } if (bb->flags & GS_BYTEBUFFER_F_OVERRUN) { return GS_ERROR_OVERFLOW; } return GS_OK; } return GS_ERROR_HANDLE; }