#include "config.h" #ifdef ULE_CONFIG_OPTION_SERIALIZATION #include #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::vec2 v) { ULE_TYPES_H_FTAG; return "%f %f\n"; } static inline const char* getFormatStringOut(glm::vec3 v) { ULE_TYPES_H_FTAG; return "%f %f %f\n"; } static inline const char* getFormatStringOut(glm::vec4 v) { ULE_TYPES_H_FTAG; return "%f %f %f %f\n"; } static inline const char* getFormatStringOut(glm::mat2 v) { ULE_TYPES_H_FTAG; return "%f %f %f %f\n"; } static inline const char* getFormatStringOut(glm::mat3 v) { ULE_TYPES_H_FTAG; return "%f %f %f %f %f %f %f %f %f\n"; } static inline const char* getFormatStringOut(glm::mat4 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 } #define SERIALIZE_H_FUNC_BODY_INT(T) \ static inline void deserializeInteger(char** buffer, T* v) { \ ULE_TYPES_H_FTAG; \ char* _buffer = *buffer; \ T value = 0; \ while (String::isAsciiWhitespace(*_buffer)) { \ _buffer++; \ } \ bool negative = false; \ if (_buffer[0] == '-') { \ negative = true; \ _buffer++; \ } \ while (String::isDigit(*_buffer)) { \ value = 10 * value + (*_buffer++ - '0'); \ } \ if (negative) value *= -1; \ *v = value; \ massert(_buffer != *buffer, "tried to parse an integer, and found nothing"); \ *buffer = _buffer; \ } SERIALIZE_H_FUNC_BODY_INT(u8) SERIALIZE_H_FUNC_BODY_INT(u16) SERIALIZE_H_FUNC_BODY_INT(u32) SERIALIZE_H_FUNC_BODY_INT(u64) SERIALIZE_H_FUNC_BODY_INT(s8) SERIALIZE_H_FUNC_BODY_INT(s16) SERIALIZE_H_FUNC_BODY_INT(s32) SERIALIZE_H_FUNC_BODY_INT(s64) #undef SERIALIZE_H_FUNC_BODY_INT // 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 void serialize(String* str, glm::vec2 v) { ULE_TYPES_H_FTAG; str->appendf(getFormatStringOut(v), v[0], v[1]); } void serialize(String* str, glm::vec3 v) { ULE_TYPES_H_FTAG; str->appendf(getFormatStringOut(v), v[0], v[1], v[2]); } void serialize(String* str, glm::vec4 v) { ULE_TYPES_H_FTAG; str->appendf(getFormatStringOut(v), v[0], v[1], v[2], v[3]); } void serialize(String* str, glm::mat2 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::mat3 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::mat4 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::vec2* 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::vec3* 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::vec4* 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::mat2* 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::mat3* 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::mat4* 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