///////////////////////////////////////////////////////////////// // // IMPORTANT: This is a header-only library that is included // by the graphics engine as well. It cannot contain references // to anything else in the engine. // #include #include #include #include /////////////////////////////////////////////////////////////// // // BaseWriter // // This base class provides the following methods: // // void write_uint8(uint64_t data) // void write_uint16(uint64_t data) // void write_uint32(uint64_t data) // void write_uint64(uint64_t data) // void write_int8(int64_t data) // void write_int16(int64_t data) // void write_int32(int64_t data) // void write_int64(int64_t data) // void write_char(char c) // void write_float(float data) // void write_double(double data) // void write_length(size_t data) // void write_string(std::string_view data) // // You should derive from BaseWriter using the CRTP pattern: // // class DerivedWriter : public BaseWriter // // You must provide two methods in the derived class: // // write_bytes(const char *n, size_t size) // raise_truncated() // /////////////////////////////////////////////////////////////// template class BaseWriter { protected: template void write_value_core(T arg) { static_cast(this)->write_bytes((const char *)&arg, sizeof(arg)); } template void write_int_core(XT arg) { T reduced = arg; if (XT(reduced) != arg) static_cast(this)->raise_truncated(); write_value_core(reduced); } public: void write_uint8(uint64_t data) { write_int_core(data); } void write_uint16(uint64_t data) { write_int_core(data); } void write_uint32(uint64_t data) { write_int_core(data); } void write_uint64(uint64_t data) { write_int_core(data); } void write_int8(int64_t data) { write_int_core(data); } void write_int16(int64_t data) { write_int_core(data); } void write_int32(int64_t data) { write_int_core(data); } void write_int64(int64_t data) { write_int_core(data); } void write_bool(bool b) { write_uint8(b ? 1:0); } void write_char(char c) { write_value_core(c); } void write_float(float arg) { write_value_core(arg); } void write_double(double arg) { write_value_core(arg); } void write_length(size_t len) { if (len >= 255) { write_uint8(0xFF); write_uint64(len); } else { write_uint8(len); } } void write_string(std::string_view s) { write_length(s.size()); static_cast(this)->write_bytes(s.data(), s.size()); } }; /////////////////////////////////////////////////////////////// // // BaseReader // // This base class provides the following methods: // // uint8_t read_uint8(); // uint16_t read_uint16(); // uint32_t read_uint32(); // uint64_t read_uint64(); // int8_t read_int8(); // int16_t read_int16(); // int32_t read_int32(); // int64_t read_int64(); // bool read_bool(); // char read_char(); // float read_float(); // double read_double(); // size_t read_length(); // String read_string_limit(uint64_t size); // String read_string(); // // You should derive from BaseReader using the CRTP pattern: // // class DerivedReader : public BaseReader // // The derived class must provide: // // using read_string_type = std::string; // or compatible // void read_bytes_into(char *n, size_t size) // void handle_string_too_long(); // // Error Handling: // // It is up to the derived class whether it wants // to report errors using exceptions or flags. // // If read_bytes_into discovers there's not enough bytes, // there are two valid options: throw an exception, OR, // set an error flag and fill the buffer with zeros. // // If read_string discovers that the string is longer than // the allowed limit, it will call handle_string_too_long. // This function may either throw an exception, or set an // error flag. // /////////////////////////////////////////////////////////////// template class BaseReader { protected: template T read_value_core() { T result; Derived *dthis = static_cast(this); dthis->read_bytes_into((char *)(&result), sizeof(result)); return result; } public: uint8_t read_uint8() { return read_value_core(); } uint16_t read_uint16() { return read_value_core(); } uint32_t read_uint32() { return read_value_core(); } uint64_t read_uint64() { return read_value_core(); } int8_t read_int8() { return read_value_core(); } int16_t read_int16() { return read_value_core(); } int32_t read_int32() { return read_value_core(); } int64_t read_int64() { return read_value_core(); } bool read_bool() { return (bool)read_uint8(); } char read_char() { return read_value_core(); } float read_float() { return read_value_core(); } double read_double() { return read_value_core(); } size_t read_length() { uint64_t len = read_uint8(); if (len == 255) { len = read_uint64(); } return len; } auto read_string_limit(uint64_t limit) { size_t len = read_length(); Derived *dthis = static_cast(this); if (len > limit) { dthis->raise_string_too_long(); len = 0; } typename Derived::read_string_type result(len, ' '); dthis->read_bytes_into(&(result[0]), len); return result; } auto read_string() { return read_string_limit(0x1000000); } // 16MB limit default };