A collection of basic/generally desirable code I use across multiple C++ projects.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

323 lines
13 KiB

#include "config.h"
#ifdef ULE_CONFIG_OPTION_SERIALIZATION
#include <fast_float/fast_float.h>
#include "types.h"
#include "serialize.h"
#include "string.h"
#include "print.h"
static inline const char* getFormatStringOut(u8 v) { ULE_TYPES_H_FTAG; return "%hu\n"; }
static inline const char* getFormatStringOut(u16 v) { ULE_TYPES_H_FTAG; return "%hu\n"; }
static inline const char* getFormatStringOut(u32 v) { ULE_TYPES_H_FTAG; return "%u\n"; }
static inline const char* getFormatStringOut(u64 v) { ULE_TYPES_H_FTAG; return "%llu\n"; }
static inline const char* getFormatStringOut(s8 v) { ULE_TYPES_H_FTAG; return "%hd\n"; }
static inline const char* getFormatStringOut(s16 v) { ULE_TYPES_H_FTAG; return "%hd\n"; }
static inline const char* getFormatStringOut(s32 v) { ULE_TYPES_H_FTAG; return "%d\n"; }
static inline const char* getFormatStringOut(s64 v) { ULE_TYPES_H_FTAG; return "%lld\n"; }
static inline const char* getFormatStringOut(float v) { ULE_TYPES_H_FTAG; return "%f\n"; }
static inline const char* getFormatStringOut(double v) { ULE_TYPES_H_FTAG; return "%f\n"; }
// important constraint - strings need to be wrapped in double-quotes.
// the sentinel value 'null' without quotations is used to denote null values, which means
// if strings were not wrapped in double quotes, you would not be able to distinguish null
// values from the literal string "null".
static inline const char* getFormatStringOut(char* v) { ULE_TYPES_H_FTAG; return "\"%s\"\n"; }
static inline const char* getFormatStringOut(const char* v) { ULE_TYPES_H_FTAG; return "\"%s\"\n"; }
#ifdef ULE_CONFIG_OPTION_USE_GLM
static inline const char* getFormatStringOut(glm::vec<2, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; return "%f %f\n"; }
static inline const char* getFormatStringOut(glm::vec<3, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; return "%f %f %f\n"; }
static inline const char* getFormatStringOut(glm::vec<4, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; return "%f %f %f %f\n"; }
static inline const char* getFormatStringOut(glm::mat<2, 2, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; return "%f %f %f %f\n"; }
static inline const char* getFormatStringOut(glm::mat<3, 3, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; return "%f %f %f %f %f %f %f %f %f\n"; }
static inline const char* getFormatStringOut(glm::mat<4, 4, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; return "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n"; }
#endif
#define SERIALIZE_H_FUNC_BODY str->appendf(getFormatStringOut(v), v);
void serialize(String* str, u8 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, u16 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, u32 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, u64 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, s8 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, s16 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, s32 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, s64 v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, float v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
void serialize(String* str, double v) { ULE_TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY }
template<typename T> // @TODO do not use a template for this.
static inline void deserializeInteger(char** buffer, T* v) {
ULE_TYPES_H_FTAG;
char* _buffer = *buffer;
T value = 0;
// skip leading whitespace - all our functions do this, so gotta
while (String::isAsciiWhitespace(*_buffer)) {
_buffer++;
}
// check the first character for a negative sign
bool negative = false;
if (_buffer[0] == '-') {
negative = true;
_buffer++;
}
// parse the digits of the number. doesn't account for overflow
while (String::isDigit(*_buffer)) {
value = 10 * value + (*_buffer++ - '0');
}
// make it negative if the first character was '-'
if (negative) value *= -1;
// store the value back into v
*v = value;
// if the original pointer is the same as the current, we didn't parse anything
massert(_buffer != *buffer, "tried to parse an integer, and found nothing");
// report back how far we advanced the buffer
*buffer = _buffer;
}
// just for integers, signed/unsigned including size_t
#define SERIALIZE_H_DESERIALIZE_FUNC_BODY deserializeInteger(buffer, v);
void deserialize(char** buffer, u8* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
void deserialize(char** buffer, u16* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
void deserialize(char** buffer, u32* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
void deserialize(char** buffer, u64* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
void deserialize(char** buffer, s8* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
void deserialize(char** buffer, s16* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
void deserialize(char** buffer, s32* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
void deserialize(char** buffer, s64* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
// base value = ceil(log10(2^64)) or ceil(log10(2^32))
// is this right? lemire's paper mentions you only need 17 digits for 64 bit floats: https://lemire.me/en/publication/arxiv2101/
// +1 for 'e'
// +1 for '.'
// +1 for '^'
// +1 for '-'/'+'
static const u32 BINARY32_MAX_CHARS = 14;
static const u32 BINARY64_MAX_CHARS = 24;
void deserialize(char** buffer, float* v) {
ULE_TYPES_H_FTAG;
char* _buffer = *buffer;
while (String::isAsciiWhitespace(*_buffer)) _buffer++;
fast_float::from_chars_result result = fast_float::from_chars(_buffer, _buffer + BINARY32_MAX_CHARS, *v);
massert(result.ec == std::errc(), "failed to parse a float");
*buffer = (char*) result.ptr;
}
void deserialize(char** buffer, double* v) {
ULE_TYPES_H_FTAG;
char* _buffer = *buffer;
while (String::isAsciiWhitespace(*_buffer)) _buffer++;
fast_float::from_chars_result result = fast_float::from_chars(_buffer, _buffer + BINARY64_MAX_CHARS, *v);
massert(result.ec == std::errc(), "failed to parse a double");
*buffer = (char*) result.ptr;
}
#ifndef _WIN32
// win32 doesn't treat size_t as different than a u64, which causes ambiguous function calls
static inline const char* getFormatStringOut(size_t v) { ULE_TYPES_H_FTAG; return "%lu\n"; }
void serialize(String* str, size_t v) { SERIALIZE_H_FUNC_BODY }
void deserialize(char** buffer, size_t* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY }
#endif
// STRING STUFF
void serialize(String* str, char* v) {
ULE_TYPES_H_FTAG;
SERIALIZE_HANDLE_NULL(str, v);
SERIALIZE_H_FUNC_BODY;
}
void serialize(String* str, const char* v) {
ULE_TYPES_H_FTAG;
SERIALIZE_HANDLE_NULL(str, v);
SERIALIZE_H_FUNC_BODY;
}
#define SERIALIZE_SCRATCH_BUFFER_SIZE 2048
// make this number be the largest number of bytes you can expect to deserialize when deserializing a single string, in the case when the memory allocation must be done by the deserialization code.
// meaning, you don't have a pre-allocated buffer of a fixed, known size, like a char name[32] or something.
//
// if you call `deserialize(char** buffer, char* v)`, it's expected that you know what you're doing,
// and the |buffer| won't contain more characters than you can fit in |v|.
//
// in the case where you call `deserialize(char** buffer, char** v)`, it's assumed the size of the string
// is mostly unknown, but some buffer still must be allocated in order to be filled with the deserialized data.
// we use the SERIALIZE_SCRATCH_BUFFER for this duty, and Strcpy out of it once done to avoid wasting space.
//
// the ideal size of this buffer is use-case dependent, and you will have to change that yourself.
//
// there are asserts in the codepaths that use it for overflowing it, if you are unsure.
//
static char SERIALIZE_SCRATCH_BUFFER[SERIALIZE_SCRATCH_BUFFER_SIZE];
static s32 deserializeString(char** buffer, char* v, s32 vSize) {
ULE_TYPES_H_FTAG;
char* _buffer = *buffer;
while (String::isAsciiWhitespace(*_buffer)) _buffer++;
massert(_buffer[0] == '"', "expecting to deserialize a string, but found something other than a double quote");
_buffer++; // skip over the first quote
s32 i = 0;
while (_buffer[i] != '"') {
// @TODO handle escaped quotes?
v[i] = _buffer[i];
i++;
massert(i <= (vSize - 1), "deserializing a string bigger than the reported size of the buffer we are trying to fill!");
}
v[i] = '\0';
*buffer = _buffer + i + 1; // +i for the number of chars in string, +1 for final quotemark
return i;
}
static s32 deserializeString(char** buffer, char* v) {
ULE_TYPES_H_FTAG;
char* _buffer = *buffer;
while (String::isAsciiWhitespace(*_buffer)) _buffer++;
massert(_buffer[0] == '"', "expecting to deserialize a string, but found something other than a double quote");
_buffer++; // skip over the first quote
s32 i = 0;
while (_buffer[i] != '"') {
// @TODO handle escaped quotes?
v[i] = _buffer[i];
i++;
}
v[i] = '\0';
*buffer = _buffer + i + 1; // +i for the number of chars in string, +1 for final quotemark
return i;
}
void deserialize(char** buffer, char* v) {
ULE_TYPES_H_FTAG;
deserializeString(buffer, v);
}
void deserialize(char** buffer, const char* v) {
ULE_TYPES_H_FTAG;
deserializeString(buffer, (char*) v);
}
void deserialize(char** buffer, char** v) {
ULE_TYPES_H_FTAG;
DESERIALIZE_HANDLE_NULL(buffer, v);
s32 i = deserializeString(buffer, SERIALIZE_SCRATCH_BUFFER, SERIALIZE_SCRATCH_BUFFER_SIZE);
massert(i >= 0, "didn't deserialize anything when expecting a string!");
*v = String::cpy(SERIALIZE_SCRATCH_BUFFER, (u32) i);
}
void deserialize(char** buffer, const char** v) {
ULE_TYPES_H_FTAG;
DESERIALIZE_HANDLE_NULL(buffer, (char*) v); // error: readonly variable is not assignable
s32 i = deserializeString(buffer, SERIALIZE_SCRATCH_BUFFER, SERIALIZE_SCRATCH_BUFFER_SIZE);
massert(i >= 0, "didn't deserialize anything when expecting a string!");
*v = String::cpy(SERIALIZE_SCRATCH_BUFFER, (u32) i);
}
#ifdef ULE_CONFIG_OPTION_USE_GLM
// I have no fucking idea why, but the declarations of glm types here resolve to a type,
// that has a template parameter == 0, but all other instances of those types in my program
// have that template parameter == 3, so everything below becomes unresolved symbols if
// I don't do the nasty template garbage here
void serialize(String* str, glm::vec<2, float, (glm::qualifier) (glm::qualifier) 3> v) {
ULE_TYPES_H_FTAG;
str->appendf(getFormatStringOut(v), v[0], v[1]);
}
void serialize(String* str, glm::vec<3, float, (glm::qualifier) (glm::qualifier) 3> v) {
ULE_TYPES_H_FTAG;
str->appendf(getFormatStringOut(v), v[0], v[1], v[2]);
}
void serialize(String* str, glm::vec<4, float, (glm::qualifier) 3> v) {
ULE_TYPES_H_FTAG;
str->appendf(getFormatStringOut(v), v[0], v[1], v[2], v[3]);
}
void serialize(String* str, glm::mat<2, 2, float, (glm::qualifier) 3> v) {
ULE_TYPES_H_FTAG;
str->appendf(getFormatStringOut(v)
, v[0][0], v[0][1]
, v[1][0], v[1][1]);
}
void serialize(String* str, glm::mat<3, 3, float, (glm::qualifier) 3> v) {
ULE_TYPES_H_FTAG;
str->appendf(getFormatStringOut(v)
, v[0][0], v[0][1], v[0][2]
, v[1][0], v[1][1], v[1][2]
, v[2][0], v[2][1], v[2][2]);
}
void serialize(String* str, glm::mat<4, 4, float, (glm::qualifier) 3> v) {
ULE_TYPES_H_FTAG;
str->appendf(getFormatStringOut(v)
, v[0][0], v[0][1], v[0][2], v[0][3]
, v[1][0], v[1][1], v[1][2], v[1][3]
, v[2][0], v[2][1], v[2][2], v[2][3]
, v[3][0], v[3][1], v[3][2], v[3][3]);
}
void deserialize(char** buffer, glm::vec<2, float, (glm::qualifier) 3>* v) {
ULE_TYPES_H_FTAG;
float* _v = (float*) v;
for (u32 i = 0; i < 2; i++) {
deserialize(buffer, _v + i);
}
}
void deserialize(char** buffer, glm::vec<3, float, (glm::qualifier) 3>* v) {
ULE_TYPES_H_FTAG;
float* _v = (float*) v;
for (u32 i = 0; i < 3; i++) {
deserialize(buffer, _v + i);
}
}
void deserialize(char** buffer, glm::vec<4, float, (glm::qualifier) 3>* v) {
ULE_TYPES_H_FTAG;
float* _v = (float*) v;
for (u32 i = 0; i < 4; i++) {
deserialize(buffer, _v + i);
}
}
void deserialize(char** buffer, glm::mat<2, 2, float, (glm::qualifier) 3>* v) {
ULE_TYPES_H_FTAG;
float* m = (float*) v;
for (u32 i = 0; i < 4; i++) {
deserialize(buffer, m + i);
}
}
void deserialize(char** buffer, glm::mat<3, 3, float, (glm::qualifier) 3>* v) {
ULE_TYPES_H_FTAG;
float* m = (float*) v;
for (u32 i = 0; i < 9; i++) {
deserialize(buffer, m + i);
}
}
void deserialize(char** buffer, glm::mat<4, 4, float, (glm::qualifier) 3>* v) {
ULE_TYPES_H_FTAG;
float* m = (float*) v;
for (u32 i = 0; i < 16; i++) {
deserialize(buffer, m + i);
}
}
#endif // ULE_CONFIG_OPTION_USE_GLM
#undef SERIALIZE_H_FUNC_BODY
#undef SERIALIZE_H_DESERIALIZE_FUNC_BODY
#endif