eive-obsw/gomspace/libutil/src/bytebuffer.c
2020-11-19 18:24:03 +01:00

129 lines
3.4 KiB
C

/* 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;
}