diff --git a/include/data_buffer.h b/include/data_buffer.h index 95d8821..45eba7c 100644 --- a/include/data_buffer.h +++ b/include/data_buffer.h @@ -66,16 +66,7 @@ namespace gs */ class DataBufferException : public std::runtime_error { - public: - explicit DataBufferException(const std::string &what_arg) : - std::runtime_error(what_arg) - { - } - - explicit DataBufferException(const char *what_arg) : - std::runtime_error(what_arg) - { - } + using std::runtime_error::runtime_error; }; // DataBuffer object declaration diff --git a/include/gs_decoder.h b/include/gs_decoder.h index 2eb5a0d..f227846 100644 --- a/include/gs_decoder.h +++ b/include/gs_decoder.h @@ -60,16 +60,7 @@ namespace gs // DecoderException exception definition class DecoderException : public std::runtime_error { - public: - explicit DecoderException(const std::string &what_arg) : - std::runtime_error(what_arg) - { - } - - explicit DecoderException(const char *what_arg) : - std::runtime_error(what_arg) - { - } + using std::runtime_error::runtime_error; }; // Game State Decoder object diff --git a/include/gs_deserializer.h b/include/gs_deserializer.h index f9a013c..c20a60d 100644 --- a/include/gs_deserializer.h +++ b/include/gs_deserializer.h @@ -67,16 +67,7 @@ namespace gs // DeserializerException exception definition class DeserializerException : public std::runtime_error { - public: - explicit DeserializerException(const std::string &what_arg) : - std::runtime_error(what_arg) - { - } - - explicit DeserializerException(const char *what_arg) : - std::runtime_error(what_arg) - { - } + using std::runtime_error::runtime_error; }; // Game State Deserializer object diff --git a/include/gs_encoder.h b/include/gs_encoder.h index 21ece79..22470b4 100644 --- a/include/gs_encoder.h +++ b/include/gs_encoder.h @@ -66,16 +66,7 @@ namespace gs // EncoderException exception definition class EncoderException : public std::runtime_error { - public: - explicit EncoderException(const std::string &what_arg) : - std::runtime_error(what_arg) - { - } - - explicit EncoderException(const char *what_arg) : - std::runtime_error(what_arg) - { - } + using std::runtime_error::runtime_error; }; // Return result from Encode() calls, which is a count of objects and @@ -105,6 +96,13 @@ class Encoder EncodeResult Encode(DataBuffer &data_buffer, const UnknownObject &value); + // Determine the required buffer length to encode objects + template + EncodeResult GetEncodeLength(const T &value) + { + return Encode(null_buffer, value); + } + // Ensure no implicit conversions calling Encode template EncodeResult Encode(DataBuffer &data_buffer, const T &value) = delete; diff --git a/include/gs_serializer.h b/include/gs_serializer.h index 7da157b..e40592c 100644 --- a/include/gs_serializer.h +++ b/include/gs_serializer.h @@ -71,16 +71,7 @@ namespace gs // SerializerException exception definition class SerializerException : public std::runtime_error { - public: - explicit SerializerException(const std::string &what_arg) : - std::runtime_error(what_arg) - { - } - - explicit SerializerException(const char *what_arg) : - std::runtime_error(what_arg) - { - } + using std::runtime_error::runtime_error; }; // Game State Serializer object diff --git a/src/gs_api_internal.cpp b/src/gs_api_internal.cpp index 521d388..b428fa8 100644 --- a/src/gs_api_internal.cpp +++ b/src/gs_api_internal.cpp @@ -413,8 +413,8 @@ int GSSerializeObject(GS_Encoder_Context_Internal &context, if (object.parent_present) object1.parent = gs::ObjectID{object.parent}; // Serialize the object - auto [object_count, - octet_count] = context.encoder.Encode(context.data_buffer, object1); + auto [object_count, octet_count] = + context.encoder.Encode(context.data_buffer, object1); return static_cast(object_count); } @@ -453,8 +453,8 @@ int GSSerializeObject(GS_Encoder_Context_Internal &context, if (object.ipd_present) head1.ipd = gs::HeadIPD1{object.ipd.ipd}; // Serialize the object - auto [object_count, - octet_count] = context.encoder.Encode(context.data_buffer, head1); + auto [object_count, octet_count] = + context.encoder.Encode(context.data_buffer, head1); return static_cast(object_count); } @@ -493,8 +493,8 @@ int GSSerializeObject(GS_Encoder_Context_Internal &context, SerializeCopy(object.rotation, hand1.rotation); // Serialize the object - auto [object_count, - octet_count] = context.encoder.Encode(context.data_buffer, hand1); + auto [object_count, octet_count] = + context.encoder.Encode(context.data_buffer, hand1); return static_cast(object_count); } @@ -555,8 +555,8 @@ int GSSerializeObject(GS_Encoder_Context_Internal &context, } // Serialize the object - auto [object_count, - octet_count] = context.encoder.Encode(context.data_buffer, mesh1); + auto [object_count, octet_count] = + context.encoder.Encode(context.data_buffer, mesh1); return static_cast(object_count); } @@ -601,8 +601,8 @@ int GSSerializeObject(GS_Encoder_Context_Internal &context, SerializeCopy(object.pinky, hand2.pinky); // Serialize the object - auto [object_count, - octet_count] = context.encoder.Encode(context.data_buffer, hand2); + auto [object_count, octet_count] = + context.encoder.Encode(context.data_buffer, hand2); return static_cast(object_count); } @@ -640,8 +640,8 @@ int GSSerializeObject(GS_Encoder_Context_Internal &context, object.data + object.data_length); // Serialize the object - auto [object_count, - octet_count] = context.encoder.Encode(context.data_buffer, unknown); + auto [object_count, octet_count] = + context.encoder.Encode(context.data_buffer, unknown); return static_cast(object_count); } @@ -676,8 +676,8 @@ int GSSerializeObject(GS_Encoder_Context_Internal &context, head_ipd1.ipd.value = object.ipd; // Serialize the object - auto [object_count, - octet_count] = context.encoder.Encode(context.data_buffer, head_ipd1); + auto [object_count, octet_count] = + context.encoder.Encode(context.data_buffer, head_ipd1); return static_cast(object_count); } diff --git a/src/gs_encoder.cpp b/src/gs_encoder.cpp index f2160b3..a5eb2f7 100644 --- a/src/gs_encoder.cpp +++ b/src/gs_encoder.cpp @@ -63,7 +63,9 @@ namespace gs * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The objects to serialize to the end of the DataBuffer. @@ -72,7 +74,9 @@ namespace gs * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -103,7 +107,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const GSObjects &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -112,7 +118,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const GSObjects &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -135,7 +143,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const GSObject &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -144,7 +154,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const GSObject &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -167,12 +179,19 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Object1 &value) data_length.value += Serialize(null_buffer, value.parent.value()); } + // Compute the total space required + total_length = Serialize(null_buffer, Tag::Object1) + + Serialize(null_buffer, data_length) + data_length.value; + // Ensure the data buffer has sufficient space - if ((data_buffer.GetDataLength() + Serialize(null_buffer, Tag::Object1) + - Serialize(null_buffer, data_length) + data_length.value) > + if ((data_buffer.GetDataLength() + total_length) > data_buffer.GetBufferSize()) { - return { 0, 0 }; + // If the buffer is zero-length, just return sizing data + if (data_buffer.GetBufferSize() == 0) return {1, total_length}; + + // Indicate an encoding error + return {0, 0}; } // Serialize the object (evaluation order matters) @@ -190,7 +209,7 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Object1 &value) total_length += Serialize(data_buffer, value.parent.value()); } - return { 1, total_length }; + return {1, total_length}; } /* @@ -202,7 +221,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Object1 &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -211,7 +232,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Object1 &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -232,11 +255,18 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Head1 &value) data_length.value += Serialize(null_buffer, value.ipd.value()); } + // Compute the total space required + total_length = Serialize(null_buffer, Tag::Head1) + + Serialize(null_buffer, data_length) + data_length.value; + // Ensure the data buffer has sufficient space - if ((data_buffer.GetDataLength() + Serialize(null_buffer, Tag::Head1) + - Serialize(null_buffer, data_length) + data_length.value) > + if ((data_buffer.GetDataLength() + total_length) > data_buffer.GetBufferSize()) { + // If the buffer is zero-length, just return sizing data + if (data_buffer.GetBufferSize() == 0) return {1, total_length}; + + // Indicate an encoding error return {0, 0}; } @@ -265,7 +295,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Head1 &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -274,7 +306,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Head1 &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -291,11 +325,18 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Hand1 &value) Serialize(null_buffer, value.location) + Serialize(null_buffer, value.rotation); + // Compute the total space required + total_length = Serialize(null_buffer, Tag::Hand1) + + Serialize(null_buffer, data_length) + data_length.value; + // Ensure the data buffer has sufficient space - if ((data_buffer.GetDataLength() + Serialize(null_buffer, Tag::Hand1) + - Serialize(null_buffer, data_length) + data_length.value) > + if ((data_buffer.GetDataLength() + total_length) > data_buffer.GetBufferSize()) { + // If the buffer is zero-length, just return sizing data + if (data_buffer.GetBufferSize() == 0) return {1, total_length}; + + // Indicate an encoding error return {0, 0}; } @@ -320,7 +361,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Hand1 &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -329,7 +372,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Hand1 &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -346,11 +391,18 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Mesh1 &value) Serialize(null_buffer, value.textures) + Serialize(null_buffer, value.triangles); + // Compute the total space required + total_length = Serialize(null_buffer, Tag::Mesh1) + + Serialize(null_buffer, data_length) + data_length.value; + // Ensure the data buffer has sufficient space - if ((data_buffer.GetDataLength() + Serialize(null_buffer, Tag::Mesh1) + - Serialize(null_buffer, data_length) + data_length.value) > + if ((data_buffer.GetDataLength() + total_length) > data_buffer.GetBufferSize()) { + // If the buffer is zero-length, just return sizing data + if (data_buffer.GetBufferSize() == 0) return {1, total_length}; + + // Indicate an encoding error return {0, 0}; } @@ -375,7 +427,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Mesh1 &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -384,7 +438,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Mesh1 &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -407,11 +463,18 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Hand2 &value) Serialize(null_buffer, value.ring) + Serialize(null_buffer, value.pinky); + // Compute the total space required + total_length = Serialize(null_buffer, Tag::Hand2) + + Serialize(null_buffer, data_length) + data_length.value; + // Ensure the data buffer has sufficient space - if ((data_buffer.GetDataLength() + Serialize(null_buffer, Tag::Hand2) + - Serialize(null_buffer, data_length) + data_length.value) > + if ((data_buffer.GetDataLength() + total_length) > data_buffer.GetBufferSize()) { + // If the buffer is zero-length, just return sizing data + if (data_buffer.GetBufferSize() == 0) return {1, total_length}; + + // Indicate an encoding error return {0, 0}; } @@ -442,7 +505,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Hand2 &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -451,7 +516,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const Hand2 &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * HeadIPD1 is an object that only exists within a Head1 object. As such, @@ -462,10 +529,17 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const HeadIPD1 &value) { std::size_t total_length{}; + // Compute the total space required + total_length = Serialize(null_buffer, value); + // Ensure the data buffer has sufficient space - if ((data_buffer.GetDataLength() + Serialize(null_buffer, value)) > + if ((data_buffer.GetDataLength() + total_length) > data_buffer.GetBufferSize()) { + // If the buffer is zero-length, just return sizing data + if (data_buffer.GetBufferSize() == 0) return {1, total_length}; + + // Indicate an encoding error return {0, 0}; } @@ -484,7 +558,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const HeadIPD1 &value) * * Parameters: * data_buffer [in] - * The data buffer into which the value shall be written. + * The data buffer into which the value shall be written. If given + * a buffer of zero-length, this call will just return the octets + * required to perform the encoding and not actually encode. * * value [in] * The object to serialize to the end of the DataBuffer. @@ -493,7 +569,9 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, const HeadIPD1 &value) * A pair representing the number of objects and number of octets * serialized onto the data buffer. A value less than expected number of * objects would indicate there was no more room for additional objects - * in the data buffer. + * in the data buffer. If the given data buffer is of zero-length, + * this function will just return a count of objects and octets without + * actually encoding to allow one to predetermine the space requirements. * * Comments: * None. @@ -503,10 +581,18 @@ EncodeResult Encoder::Encode(DataBuffer &data_buffer, { std::size_t total_length{}; + // Compute the total space required + total_length = Serialize(null_buffer, value.tag) + + Serialize(null_buffer, value.data); + // Ensure the data buffer has sufficient space - if ((data_buffer.GetDataLength() + Serialize(null_buffer, value.tag) + - Serialize(null_buffer, value.data)) > data_buffer.GetBufferSize()) + if ((data_buffer.GetDataLength() + total_length) > + data_buffer.GetBufferSize()) { + // If the buffer is zero-length, just return sizing data + if (data_buffer.GetBufferSize() == 0) return {1, total_length}; + + // Indicate an encoding error return {0, 0}; } diff --git a/src/gs_serializer.cpp b/src/gs_serializer.cpp index d7e6831..5b773ba 100644 --- a/src/gs_serializer.cpp +++ b/src/gs_serializer.cpp @@ -287,7 +287,7 @@ std::size_t Serializer::Write(DataBuffer &data_buffer, Int64 value) const } /* - * Serializer::WriteVarUint + * Serializer::Write * * Description: * This function will write the unsigned integer as a variable-length @@ -374,7 +374,7 @@ std::size_t Serializer::Write(DataBuffer &data_buffer, } /* - * Serializer::WriteVarInt + * Serializer::Write * * Description: * This function will write the signed integer as a variable-length diff --git a/test/test_gs_encoder/test_gs_encoder.cpp b/test/test_gs_encoder/test_gs_encoder.cpp index 369a3e7..92c9511 100644 --- a/test/test_gs_encoder/test_gs_encoder.cpp +++ b/test/test_gs_encoder/test_gs_encoder.cpp @@ -98,6 +98,9 @@ namespace { head.rotation.ej.value = 0.0f; head.rotation.ek.value = 0.0f; + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(head).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, head), std::make_pair(std::size_t(1), expected.size())); @@ -176,6 +179,9 @@ namespace { unknown.data.push_back(expected[i]); } + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(unknown).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, unknown), std::make_pair(std::size_t(1), expected.size())); @@ -219,6 +225,9 @@ namespace { // Store the object as a variant gs::GSObject object = head; + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(object).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, object), std::make_pair(std::size_t(1), expected.size())); @@ -299,6 +308,9 @@ namespace { mesh.textures.push_back({{1}, {129}}); + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(mesh).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, mesh), std::make_pair(std::size_t(1), expected.size())); @@ -376,6 +388,10 @@ namespace { objects.push_back(mesh); objects.push_back(head); + // Check that the encoding length matches the expected length + ASSERT_EQ(objects.size(), encoder.GetEncodeLength(objects).first); + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(objects).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, objects), std::make_pair(std::size_t(3), expected.size())); @@ -453,6 +469,10 @@ namespace { objects.push_back(mesh); objects.push_back(head); + // Check that the encoding length matches the expected length + ASSERT_EQ(objects.size(), encoder.GetEncodeLength(objects).first); + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(objects).second); + // Define a short data buffer gs::DataBuffer db(100); @@ -498,6 +518,9 @@ namespace { hand1.rotation.ej.value = 0.0f; hand1.rotation.ek.value = 3.140625f; + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(hand1).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, hand1), std::make_pair(std::size_t(1), expected.size())); @@ -606,6 +629,9 @@ namespace { hand2.pinky.tip.tx.value = 3.140625f; + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(hand2).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, hand2), std::make_pair(std::size_t(1), expected.size())); @@ -664,6 +690,9 @@ namespace { object1.scale.z = 9.0f; object1.active = true; + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(object1).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, object1), std::make_pair(std::size_t(1), expected.size())); @@ -693,6 +722,9 @@ namespace { ipd = gs::HeadIPD1{3.140625}; + // Check that the encoding length matches the expected length + ASSERT_EQ(expected.size(), encoder.GetEncodeLength(ipd).second); + // Check the expected encoded length ASSERT_EQ(encoder.Encode(data_buffer, ipd), std::make_pair(std::size_t(1), expected.size()));