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
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
|
|
|
|
|