diff --git a/cpp/src/common/allocator/byte_stream.h b/cpp/src/common/allocator/byte_stream.h index 15f15b798d..a56c2b5916 100644 --- a/cpp/src/common/allocator/byte_stream.h +++ b/cpp/src/common/allocator/byte_stream.h @@ -1280,7 +1280,7 @@ class SerializationUtil { } else if (len != read_len) { ret = E_BUF_NOT_ENOUGH; } else { - str = std::string(tmp_buf); + str.assign(tmp_buf, len); } mem_free(tmp_buf); } diff --git a/cpp/test/common/allocator/byte_stream_test.cc b/cpp/test/common/allocator/byte_stream_test.cc index 3f57cbf84f..dde40fba42 100644 --- a/cpp/test/common/allocator/byte_stream_test.cc +++ b/cpp/test/common/allocator/byte_stream_test.cc @@ -22,6 +22,8 @@ #include #include +#include +#include namespace common { @@ -66,6 +68,21 @@ class ByteStreamTest : public ::testing::Test { ByteStream* byte_stream_; }; +TEST_F(ByteStreamTest, VarStringPreservesEmbeddedNulls) { + const std::vector values = { + "", std::string("\0", 1), std::string("a\0b", 3), + std::string("\0\1\xff\x7f", 4), std::string("tail\0", 5)}; + for (const auto& value : values) { + ASSERT_EQ(SerializationUtil::write_var_str(value, *byte_stream_), E_OK); + } + for (const auto& expected : values) { + std::string actual = "previous value"; + ASSERT_EQ(SerializationUtil::read_var_str(actual, *byte_stream_), E_OK); + ASSERT_EQ(actual, expected); + } + ASSERT_EQ(byte_stream_->remaining_size(), 0); +} + TEST_F(ByteStreamTest, WriteReadTest) { const uint8_t data[] = {0x01, 0x02, 0x03}; const uint32_t data_size = sizeof(data); diff --git a/cpp/test/encoding/dictionary_codec_test.cc b/cpp/test/encoding/dictionary_codec_test.cc index f9d814cba6..a05090cfa9 100644 --- a/cpp/test/encoding/dictionary_codec_test.cc +++ b/cpp/test/encoding/dictionary_codec_test.cc @@ -55,6 +55,50 @@ TEST_F(DictionaryTest, DictionaryEncoder) { } } +TEST_F(DictionaryTest, ReadJavaBinaryDictionary) { + // Java DictionaryEncoder: empty, NUL-prefixed binary, embedded/trailing + // NUL, and repeated dictionary entries. + const uint8_t java_bytes[] = {8, 0, 8, 0, 1, 255, 127, 6, + 97, 0, 98, 10, 116, 97, 105, 108, + 0, 5, 2, 3, 6, 27, 64}; + const std::vector expected = {"", + std::string("\0\1\xff\x7f", 4), + std::string("a\0b", 3), + std::string("tail\0", 5), + std::string("\0\1\xff\x7f", 4), + ""}; + common::ByteStream stream(16, common::MOD_DICENCODE_OBJ); + ASSERT_EQ(stream.write_buf(java_bytes, sizeof(java_bytes)), common::E_OK); + DictionaryDecoder decoder; + decoder.init(); + for (const auto& value : expected) { + ASSERT_TRUE(decoder.has_next(stream)); + ASSERT_EQ(decoder.read_string(stream), value); + } + ASSERT_FALSE(decoder.has_next(stream)); +} + +TEST_F(DictionaryTest, DictionaryPreservesBinaryStrings) { + const std::vector values = {"", + std::string("\0\1\xff\x7f", 4), + std::string("a\0b", 3), + std::string("tail\0", 5), + std::string("\0\1\xff\x7f", 4), + ""}; + DictionaryEncoder encoder; + common::ByteStream stream(16, common::MOD_DICENCODE_OBJ); + encoder.init(); + for (const auto& value : values) encoder.encode(value, stream); + ASSERT_EQ(encoder.flush(stream), common::E_OK); + DictionaryDecoder decoder; + decoder.init(); + for (const auto& expected : values) { + ASSERT_TRUE(decoder.has_next(stream)); + ASSERT_EQ(decoder.read_string(stream), expected); + } + ASSERT_FALSE(decoder.has_next(stream)); +} + TEST_F(DictionaryTest, DictionaryEncoderAndDecoder) { DictionaryEncoder encoder; common::ByteStream stream(1024, common::MOD_DICENCODE_OBJ);