511 lines
No EOL
13 KiB
C
511 lines
No EOL
13 KiB
C
#include "strstream.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <math.h> // HUGE_VALF
|
|
#include "tracelog.h"
|
|
|
|
/* == INPUT STREAM ============================================ */
|
|
|
|
str_istream_t istrInit(const char *str) {
|
|
return istrInitLen(str, strlen(str));
|
|
}
|
|
|
|
str_istream_t istrInitLen(const char *str, size_t len) {
|
|
str_istream_t res;
|
|
res.start = res.cur = str;
|
|
res.size = len;
|
|
return res;
|
|
}
|
|
|
|
char istrGet(str_istream_t *ctx) {
|
|
return *ctx->cur++;
|
|
}
|
|
|
|
void istrIgnore(str_istream_t *ctx, char delim) {
|
|
size_t position = ctx->cur - ctx->start;
|
|
size_t i;
|
|
for(i = position;
|
|
i < ctx->size && *ctx->cur != delim;
|
|
++i, ++ctx->cur);
|
|
}
|
|
|
|
char istrPeek(str_istream_t *ctx) {
|
|
return *ctx->cur;
|
|
}
|
|
|
|
void istrSkip(str_istream_t *ctx, size_t n) {
|
|
size_t remaining = ctx->size - (ctx->cur - ctx->start);
|
|
if(n > remaining) {
|
|
warn("skipping more then remaining: %zu -> %zu", n, remaining);
|
|
return;
|
|
}
|
|
ctx->cur += n;
|
|
}
|
|
|
|
void istrRead(str_istream_t *ctx, char *buf, size_t len) {
|
|
size_t remaining = ctx->size - (ctx->cur - ctx->start);
|
|
if(len > remaining) {
|
|
warn("istrRead: trying to read len %zu from remaining %zu", len, remaining);
|
|
return;
|
|
}
|
|
memcpy(buf, ctx->cur, len);
|
|
ctx->cur += len;
|
|
}
|
|
|
|
size_t istrReadMax(str_istream_t *ctx, char *buf, size_t len) {
|
|
size_t remaining = ctx->size - (ctx->cur - ctx->start);
|
|
len = remaining < len ? remaining : len;
|
|
memcpy(buf, ctx->cur, len);
|
|
ctx->cur += len;
|
|
return len;
|
|
}
|
|
|
|
void istrRewind(str_istream_t *ctx) {
|
|
ctx->cur = ctx->start;
|
|
}
|
|
|
|
size_t istrTell(str_istream_t *ctx) {
|
|
return ctx->cur - ctx->start;
|
|
}
|
|
|
|
bool istrIsFinished(str_istream_t *ctx) {
|
|
return ctx->cur == (ctx->start + ctx->size + 1);
|
|
}
|
|
|
|
bool istrGetbool(str_istream_t *ctx, bool *val) {
|
|
size_t remaining = ctx->size - (ctx->cur - ctx->start);
|
|
if(strncmp(ctx->cur, "true", remaining) == 0) {
|
|
*val = true;
|
|
return true;
|
|
}
|
|
if(strncmp(ctx->cur, "false", remaining) == 0) {
|
|
*val = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool istrGetu8(str_istream_t *ctx, uint8_t *val) {
|
|
char *end = NULL;
|
|
*val = (uint8_t) strtoul(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGetu8: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == UINT8_MAX) {
|
|
warn("istrGetu8: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGetu16(str_istream_t *ctx, uint16_t *val) {
|
|
char *end = NULL;
|
|
*val = (uint16_t) strtoul(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGetu16: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == UINT16_MAX) {
|
|
warn("istrGetu16: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGetu32(str_istream_t *ctx, uint32_t *val) {
|
|
char *end = NULL;
|
|
*val = (uint32_t) strtoul(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGetu32: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == UINT32_MAX) {
|
|
warn("istrGetu32: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGetu64(str_istream_t *ctx, uint64_t *val) {
|
|
char *end = NULL;
|
|
*val = strtoull(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGetu64: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == ULLONG_MAX) {
|
|
warn("istrGetu64: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGeti8(str_istream_t *ctx, int8_t *val) {
|
|
char *end = NULL;
|
|
*val = (int8_t) strtol(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGeti8: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == INT8_MAX || *val == INT8_MIN) {
|
|
warn("istrGeti8: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGeti16(str_istream_t *ctx, int16_t *val) {
|
|
char *end = NULL;
|
|
*val = (int16_t) strtol(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGeti16: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == INT16_MAX || *val == INT16_MIN) {
|
|
warn("istrGeti16: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGeti32(str_istream_t *ctx, int32_t *val) {
|
|
char *end = NULL;
|
|
*val = (int32_t) strtol(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGeti32: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == INT32_MAX || *val == INT32_MIN) {
|
|
warn("istrGeti32: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGeti64(str_istream_t *ctx, int64_t *val) {
|
|
char *end = NULL;
|
|
*val = strtoll(ctx->cur, &end, 0);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGeti64: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == INT64_MAX || *val == INT64_MIN) {
|
|
warn("istrGeti64: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGetfloat(str_istream_t *ctx, float *val) {
|
|
char *end = NULL;
|
|
*val = strtof(ctx->cur, &end);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGetfloat: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == HUGE_VALF || *val == -HUGE_VALF) {
|
|
warn("istrGetfloat: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
bool istrGetdouble(str_istream_t *ctx, double *val) {
|
|
char *end = NULL;
|
|
*val = strtod(ctx->cur, &end);
|
|
|
|
if(ctx->cur == end) {
|
|
warn("istrGetdouble: no valid conversion could be performed");
|
|
return false;
|
|
}
|
|
else if(*val == HUGE_VAL || *val == -HUGE_VAL) {
|
|
warn("istrGetdouble: value read is out of the range of representable values");
|
|
return false;
|
|
}
|
|
|
|
ctx->cur = end;
|
|
return true;
|
|
}
|
|
|
|
size_t istrGetstring(str_istream_t *ctx, char **val, char delim) {
|
|
const char *from = ctx->cur;
|
|
istrIgnore(ctx, delim);
|
|
// if it didn't actually find it, it just reached the end of the string
|
|
if(*ctx->cur != delim) {
|
|
*val = NULL;
|
|
return 0;
|
|
}
|
|
size_t len = ctx->cur - from;
|
|
*val = malloc(len + 1);
|
|
memcpy(*val, from, len);
|
|
(*val)[len] = '\0';
|
|
return len;
|
|
}
|
|
|
|
size_t istrGetstringBuf(str_istream_t *ctx, char *val, size_t len) {
|
|
size_t remaining = ctx->size - (ctx->cur - ctx->start);
|
|
len -= 1;
|
|
len = remaining < len ? remaining : len;
|
|
memcpy(val, ctx->cur, len);
|
|
val[len] = '\0';
|
|
ctx->cur += len;
|
|
return len;
|
|
}
|
|
|
|
strview_t istrGetview(str_istream_t *ctx, char delim) {
|
|
const char *from = ctx->cur;
|
|
istrIgnore(ctx, delim);
|
|
// if it didn't actually find it, it just reached the end of the string
|
|
if(*ctx->cur != delim) {
|
|
return strvInitLen(NULL, 0);
|
|
}
|
|
size_t len = ctx->cur - from;
|
|
return strvInitLen(from, len);
|
|
}
|
|
|
|
strview_t istrGetviewLen(str_istream_t *ctx, size_t off, size_t len) {
|
|
size_t remaining = ctx->size - (ctx->cur - ctx->start) - off;
|
|
if(len > remaining) len = remaining;
|
|
return strvInitLen(ctx->cur + off, len);
|
|
}
|
|
|
|
/* == OUTPUT STREAM =========================================== */
|
|
|
|
static void _ostrRealloc(str_ostream_t *ctx, size_t atlest) {
|
|
ctx->allocated = (ctx->allocated * 2) + atlest;
|
|
ctx->buf = realloc(ctx->buf, ctx->allocated);
|
|
}
|
|
|
|
str_ostream_t ostrInit() {
|
|
return ostrInitLen(1);
|
|
}
|
|
|
|
str_ostream_t ostrInitLen(size_t initial_alloc) {
|
|
str_ostream_t stream;
|
|
stream.buf = calloc(initial_alloc, 1);
|
|
stream.size = 0;
|
|
stream.allocated = initial_alloc;
|
|
return stream;
|
|
}
|
|
|
|
str_ostream_t ostrInitStr(const char *cstr, size_t len) {
|
|
str_ostream_t stream;
|
|
stream.buf = malloc(len + 1);
|
|
memcpy(stream.buf, cstr, len);
|
|
return stream;
|
|
}
|
|
|
|
char ostrBack(str_ostream_t *ctx) {
|
|
return ctx->buf[ctx->size - 1];
|
|
}
|
|
|
|
void ostrFree(str_ostream_t *ctx) {
|
|
free(ctx->buf);
|
|
ctx->size = 0;
|
|
ctx->allocated = 0;
|
|
}
|
|
|
|
size_t ostrMove(str_ostream_t *ctx, char **str) {
|
|
*str = ctx->buf;
|
|
ctx->buf = NULL;
|
|
size_t sz = ctx->size;
|
|
ostrFree(ctx);
|
|
return sz;
|
|
}
|
|
|
|
void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...) {
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
|
|
// vsnprintf returns the length of the formatted string, even if truncated
|
|
// we use this to get the actual length of the formatted string
|
|
char buf[1];
|
|
va_list vtemp;
|
|
va_copy(vtemp, va);
|
|
int len = vsnprintf(buf, sizeof(buf), fmt, vtemp);
|
|
va_end(vtemp);
|
|
if(len < 0) {
|
|
err("couldn't format string \"%s\"", fmt);
|
|
goto error;
|
|
}
|
|
|
|
size_t remaining = ctx->allocated - ctx->size;
|
|
if(remaining < (size_t)len) {
|
|
_ostrRealloc(ctx, len + 1);
|
|
remaining = ctx->allocated - ctx->size;
|
|
}
|
|
|
|
// actual formatting here
|
|
len = vsnprintf(ctx->buf + ctx->size, remaining, fmt, va);
|
|
if(len < 0) {
|
|
err("couldn't format stringh \"%s\"", fmt);
|
|
goto error;
|
|
}
|
|
ctx->size += len;
|
|
|
|
error:
|
|
va_end(va);
|
|
}
|
|
|
|
#define APPEND_BUF_LEN 20
|
|
|
|
void ostrPutc(str_ostream_t *ctx, char c) {
|
|
if(ctx->size >= ctx->allocated) {
|
|
_ostrRealloc(ctx, 1);
|
|
}
|
|
ctx->buf[ctx->size++] = c;
|
|
ctx->buf[ctx->size] = '\0';
|
|
}
|
|
|
|
void ostrAppendbool(str_ostream_t *ctx, bool val) {
|
|
ostrAppendview(ctx, strvInit(val ? "true" : "false"));
|
|
}
|
|
|
|
void ostrAppendu8(str_ostream_t *ctx, uint8_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
int len = snprintf(buf, sizeof(buf), "%hhu", val);
|
|
if(len <= 0) {
|
|
err("ostrAppendu8: couldn't write %hhu", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendu16(str_ostream_t *ctx, uint16_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
int len = snprintf(buf, sizeof(buf), "%hu", val);
|
|
if(len <= 0) {
|
|
err("ostrAppendu16: couldn't write %hu", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendu32(str_ostream_t *ctx, uint32_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
int len = snprintf(buf, sizeof(buf), "%u", val);
|
|
if(len <= 0) {
|
|
err("ostrAppendu32: couldn't write %u", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendu64(str_ostream_t *ctx, uint64_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
#if _WIN32
|
|
int len = snprintf(buf, sizeof(buf), "%llu", val);
|
|
#else
|
|
int len = snprintf(buf, sizeof(buf), "%lu", val);
|
|
#endif
|
|
if(len <= 0) {
|
|
err("ostrAppendu64: couldn't write %lu", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendi8(str_ostream_t *ctx, int8_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
int len = snprintf(buf, sizeof(buf), "%hhi", val);
|
|
if(len <= 0) {
|
|
err("ostrAppendi8: couldn't write %hhi", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendi16(str_ostream_t *ctx, int16_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
int len = snprintf(buf, sizeof(buf), "%hi", val);
|
|
if(len <= 0) {
|
|
err("ostrAppendi16: couldn't write %hi", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendi32(str_ostream_t *ctx, int32_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
int len = snprintf(buf, sizeof(buf), "%i", val);
|
|
if(len <= 0) {
|
|
err("ostrAppendi32: couldn't write %i", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendi64(str_ostream_t *ctx, int64_t val) {
|
|
char buf[APPEND_BUF_LEN];
|
|
#if _WIN32
|
|
int len = snprintf(buf, sizeof(buf), "%lli", val);
|
|
#else
|
|
int len = snprintf(buf, sizeof(buf), "%li", val);
|
|
#endif
|
|
if(len <= 0) {
|
|
err("ostrAppendi64: couldn't write %li", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendfloat(str_ostream_t *ctx, float val) {
|
|
char buf[APPEND_BUF_LEN * 3];
|
|
int len = snprintf(buf, sizeof(buf), "%f", (double)val);
|
|
if(len <= 0) {
|
|
err("ostrAppendfloat: couldn't write %f", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppenddouble(str_ostream_t *ctx, double val) {
|
|
char buf[APPEND_BUF_LEN * 3];
|
|
int len = snprintf(buf, sizeof(buf), "%f", val);
|
|
if(len <= 0) {
|
|
err("ostrAppenddouble: couldn't write %f", val);
|
|
return;
|
|
}
|
|
ostrAppendview(ctx, strvInitLen(buf, len));
|
|
}
|
|
|
|
void ostrAppendview(str_ostream_t *ctx, strview_t view) {
|
|
if((ctx->allocated - ctx->size) < view.size) {
|
|
_ostrRealloc(ctx, view.size + 1);
|
|
}
|
|
memcpy(ctx->buf + ctx->size, view.buf, view.size);
|
|
ctx->size += view.size;
|
|
ctx->buf[ctx->size] = '\0';
|
|
} |