From a10fbf111aabc2cc13da1b313de6ca98db82f727 Mon Sep 17 00:00:00 2001 From: Alejandro Valerio Date: Sat, 24 Jan 2026 18:13:17 -0600 Subject: [PATCH] - Remove UNDEFINED enum value from Type enum - Updated unittests to use new BuildByteArrayValueBytes misc function when creating BYTE_ARRAY test data - Removed support for Type::UNDEFINED along the codebase. --- src/common/dbpa_local_test.cpp | 15 +++-- src/common/enum_utils.cpp | 3 +- src/common/enum_utils_test.cpp | 21 ++---- src/common/enums.h | 3 +- src/processing/encryption_sequencer_test.cpp | 38 ++--------- src/processing/parquet_utils.cpp | 19 +++++- src/processing/parquet_utils.h | 10 +++ src/processing/parquet_utils_test.cpp | 29 +++++++++ src/processing/typed_list_values.cpp | 34 ---------- src/processing/typed_list_values.h | 3 +- src/processing/typed_list_values_test.cpp | 19 ------ .../value_encryption_utils_test.cpp | 14 ---- src/scripts/dbpa_remote_testapp.cpp | 65 +++++-------------- 13 files changed, 93 insertions(+), 180 deletions(-) diff --git a/src/common/dbpa_local_test.cpp b/src/common/dbpa_local_test.cpp index 04345e7..6912c76 100644 --- a/src/common/dbpa_local_test.cpp +++ b/src/common/dbpa_local_test.cpp @@ -16,6 +16,7 @@ // under the License. #include "dbpa_local.h" +#include "../processing/parquet_utils.h" #include #include #include @@ -45,9 +46,9 @@ TEST_F(LocalDataBatchProtectionAgentTest, SuccessfulEncryption) { std::string app_context = R"({"user_id": "test_user"})"; EXPECT_NO_THROW(agent.init("test_column", configuration_map, app_context, "test_key", - Type::UNDEFINED, std::nullopt, CompressionCodec::UNCOMPRESSED, std::nullopt)); + Type::BYTE_ARRAY, std::nullopt, CompressionCodec::UNCOMPRESSED, std::nullopt)); - std::vector test_data = {1, 2, 3, 4}; + std::vector test_data = BuildByteArrayValueBytes("test_ABC"); std::map encoding_attributes = {{"page_encoding", "PLAIN"}, {"page_type", "DICTIONARY_PAGE"}}; auto result = agent.Encrypt(test_data, encoding_attributes); @@ -88,9 +89,9 @@ TEST_F(LocalDataBatchProtectionAgentTest, SuccessfulDecryption) { std::string app_context = R"({"user_id": "test_user"})"; EXPECT_NO_THROW(agent.init("test_column", configuration_map, app_context, "test_key", - Type::UNDEFINED, std::nullopt, CompressionCodec::UNCOMPRESSED, DBPS_ENCRYPTION_METADATA)); + Type::BYTE_ARRAY, std::nullopt, CompressionCodec::UNCOMPRESSED, DBPS_ENCRYPTION_METADATA)); - std::vector test_data = {1, 2, 3, 4}; + std::vector test_data = BuildByteArrayValueBytes("test_EFG"); std::map encoding_attributes = {{"page_encoding", "PLAIN"}, {"page_type", "DICTIONARY_PAGE"}}; auto result = agent.Decrypt(test_data, encoding_attributes); @@ -107,10 +108,10 @@ TEST_F(LocalDataBatchProtectionAgentTest, RoundTripEncryptDecrypt) { std::string app_context = R"({"user_id": "test_user"})"; EXPECT_NO_THROW(encrypt_agent.init("test_column", configuration_map, app_context, "test_key", - Type::UNDEFINED, std::nullopt, CompressionCodec::UNCOMPRESSED, std::nullopt)); + Type::BYTE_ARRAY, std::nullopt, CompressionCodec::UNCOMPRESSED, std::nullopt)); // Original data to encrypt - std::vector original_data = {1, 2, 3, 4, 5}; + std::vector original_data = BuildByteArrayValueBytes("roundtrip_XYZ"); std::map encoding_attributes = {{"page_encoding", "PLAIN"}, {"page_type", "DICTIONARY_PAGE"}}; // Encrypt the data @@ -133,7 +134,7 @@ TEST_F(LocalDataBatchProtectionAgentTest, RoundTripEncryptDecrypt) { // Create a new agent for decryption with the encryption_metadata from the encryption result LocalDataBatchProtectionAgent decrypt_agent; EXPECT_NO_THROW(decrypt_agent.init("test_column", configuration_map, app_context, "test_key", - Type::UNDEFINED, std::nullopt, CompressionCodec::UNCOMPRESSED, encryption_metadata)); + Type::BYTE_ARRAY, std::nullopt, CompressionCodec::UNCOMPRESSED, encryption_metadata)); // Decrypt the ciphertext auto decrypt_result = decrypt_agent.Decrypt(ciphertext, encoding_attributes); diff --git a/src/common/enum_utils.cpp b/src/common/enum_utils.cpp index 049abf2..bf908de 100644 --- a/src/common/enum_utils.cpp +++ b/src/common/enum_utils.cpp @@ -52,7 +52,7 @@ constexpr std::optional from_string_impl( // For dbps::external::Type namespace { using T = ::dbps::external::Type::type; -inline constexpr std::array, 9> kTypePairs{{ +inline constexpr std::array, 8> kTypePairs{{ {T::BOOLEAN, "BOOLEAN"}, {T::INT32, "INT32"}, {T::INT64, "INT64"}, @@ -61,7 +61,6 @@ inline constexpr std::array, 9> kTypePairs{{ {T::DOUBLE, "DOUBLE"}, {T::BYTE_ARRAY, "BYTE_ARRAY"}, {T::FIXED_LEN_BYTE_ARRAY, "FIXED_LEN_BYTE_ARRAY"}, - {T::UNDEFINED, "UNDEFINED"}, }}; } // anon diff --git a/src/common/enum_utils_test.cpp b/src/common/enum_utils_test.cpp index bfdade7..953b375 100644 --- a/src/common/enum_utils_test.cpp +++ b/src/common/enum_utils_test.cpp @@ -34,7 +34,6 @@ TEST(EnumUtils, TypeToStringConversion) { ASSERT_EQ("DOUBLE", std::string(to_string(Type::DOUBLE))); ASSERT_EQ("BYTE_ARRAY", std::string(to_string(Type::BYTE_ARRAY))); ASSERT_EQ("FIXED_LEN_BYTE_ARRAY", std::string(to_string(Type::FIXED_LEN_BYTE_ARRAY))); - ASSERT_EQ("UNDEFINED", std::string(to_string(Type::UNDEFINED))); } TEST(EnumUtils, TypeFromStringConversion) { @@ -69,10 +68,6 @@ TEST(EnumUtils, TypeFromStringConversion) { result = to_datatype_enum("FIXED_LEN_BYTE_ARRAY"); ASSERT_TRUE(result.has_value()); ASSERT_EQ(Type::FIXED_LEN_BYTE_ARRAY, result.value()); - - result = to_datatype_enum("UNDEFINED"); - ASSERT_TRUE(result.has_value()); - ASSERT_EQ(Type::UNDEFINED, result.value()); } TEST(EnumUtils, TypeInvalidFromString) { @@ -246,7 +241,7 @@ TEST(EnumUtils, RoundTripTypeConversion) { // Test all Type enum values Type::type types[] = { Type::BOOLEAN, Type::INT32, Type::INT64, Type::INT96, - Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY, Type::UNDEFINED + Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY }; for (auto type : types) { @@ -347,7 +342,7 @@ TEST(EnumUtils, TypeEnumCompleteness) { // Define all known Type enum values Type::type all_types[] = { Type::BOOLEAN, Type::INT32, Type::INT64, Type::INT96, - Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY, Type::UNDEFINED + Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY }; // Test that every enum value can be converted to string and back @@ -406,12 +401,12 @@ TEST(EnumUtils, StringUniqueness) { // Collect all Type strings Type::type all_types[] = { Type::BOOLEAN, Type::INT32, Type::INT64, Type::INT96, - Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY, Type::UNDEFINED + Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY }; for (auto type : all_types) { type_strings.insert(std::string(to_string(type))); } - ASSERT_EQ(9, type_strings.size()); // All strings should be unique + ASSERT_EQ(8, type_strings.size()); // All strings should be unique // Collect all CompressionCodec strings CompressionCodec::type all_codecs[] = { @@ -444,7 +439,7 @@ TEST(EnumUtils, CrossEnumStringCollision) { // Collect all strings from all enums Type::type all_types[] = { Type::BOOLEAN, Type::INT32, Type::INT64, Type::INT96, - Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY, Type::UNDEFINED + Type::FLOAT, Type::DOUBLE, Type::BYTE_ARRAY, Type::FIXED_LEN_BYTE_ARRAY }; for (auto type : all_types) { all_strings.insert(std::string(to_string(type))); @@ -467,12 +462,8 @@ TEST(EnumUtils, CrossEnumStringCollision) { all_strings.insert(std::string(to_string(encoding))); } - // Verify two equally named enums are handled correctly. - ASSERT_EQ("UNDEFINED", std::string(to_string(Type::UNDEFINED))); ASSERT_EQ("UNDEFINED", std::string(to_string(Encoding::UNDEFINED))); - // Total should be 9 + 10 + 11 = 30 unique strings, but we have 1 collision - // (Type::UNDEFINED and Encoding::UNDEFINED both map to "UNDEFINED") - // So we expect 29 unique strings + // Total should be 8 + 10 + 11 = 29 unique strings ASSERT_EQ(29, all_strings.size()); } diff --git a/src/common/enums.h b/src/common/enums.h index 3aef5b4..c120d3c 100644 --- a/src/common/enums.h +++ b/src/common/enums.h @@ -31,8 +31,7 @@ struct Type { FLOAT = 4, DOUBLE = 5, BYTE_ARRAY = 6, - FIXED_LEN_BYTE_ARRAY = 7, - UNDEFINED = 8 + FIXED_LEN_BYTE_ARRAY = 7 }; }; diff --git a/src/processing/encryption_sequencer_test.cpp b/src/processing/encryption_sequencer_test.cpp index 01eace5..df59677 100644 --- a/src/processing/encryption_sequencer_test.cpp +++ b/src/processing/encryption_sequencer_test.cpp @@ -17,6 +17,7 @@ #include "encryption_sequencer.h" #include "compression_utils.h" +#include "parquet_utils.h" #include "../common/enums.h" #include "../common/bytes_utils.h" #include @@ -30,40 +31,11 @@ using namespace dbps::compression; using namespace dbps::external; -// TODO: Move this to a common test utility file. -// Methods that will pad byte arrays of strings (or pure bytes) with preceding -// bytes that specify the array length. Needed because this is how Parquet -// encodings represent their data. -std::vector EncodeStringByteArray(const std::vector& strings) { - std::vector result; - for (const auto& str : strings) { - uint32_t len = str.size(); - // Add 4-byte length prefix (little-endian) - result.push_back(len & 0xFF); - result.push_back((len >> 8) & 0xFF); - result.push_back((len >> 16) & 0xFF); - result.push_back((len >> 24) & 0xFF); - // Add string data - result.insert(result.end(), str.begin(), str.end()); - } - return result; -} - -std::vector EncodePlainByteArray(const std::vector& payload) { - std::vector out; - uint32_t len = static_cast(payload.size()); - out.push_back(static_cast( len & 0xFF)); - out.push_back(static_cast((len >> 8) & 0xFF)); - out.push_back(static_cast((len >> 16) & 0xFF)); - out.push_back(static_cast((len >> 24) & 0xFF)); - out.insert(out.end(), payload.begin(), payload.end()); - return out; -} - // Test data constants - pure binary data -const std::vector HELLO_WORLD_DATA = EncodeStringByteArray({"Hello, World!"}); -const std::vector BINARY_DATA = EncodePlainByteArray({0x00, 0x01, 0x02, 0x03, 0x04, 0x05}); -const std::vector SINGLE_CHAR_DATA = EncodeStringByteArray({"A"}); +const std::vector HELLO_WORLD_DATA = BuildByteArrayValueBytes("Hello, World!"); +const std::vector BINARY_DATA = BuildByteArrayValueBytes( + std::string("\x00\x01\x02\x03\x04\x05", 6)); +const std::vector SINGLE_CHAR_DATA = BuildByteArrayValueBytes("A"); const std::vector EMPTY_DATA = {}; const std::vector FIXED_LEN_BYTE_ARRAY_DATA = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', diff --git a/src/processing/parquet_utils.cpp b/src/processing/parquet_utils.cpp index ba7c076..f7e793f 100644 --- a/src/processing/parquet_utils.cpp +++ b/src/processing/parquet_utils.cpp @@ -127,8 +127,6 @@ inline static size_t GetFixedElemSizeOrThrow(Type::type datatype, const std::opt } return static_cast(datatype_length.value()); } - case Type::UNDEFINED: - return 1; case Type::BOOLEAN: throw InvalidInputException("BOOLEAN is bit-sized; not fixed byte-sized"); case Type::BYTE_ARRAY: @@ -254,6 +252,23 @@ std::vector CombineRawBytesIntoValueBytes( return out; } +std::vector BuildByteArrayValueBytes(const std::string& payload) { + std::vector elements; + elements.emplace_back(payload.begin(), payload.end()); + return CombineRawBytesIntoValueBytes( + elements, Type::BYTE_ARRAY, std::nullopt, Encoding::PLAIN); +} + +std::vector ParseByteArrayListValueBytes(const std::vector& bytes) { + TypedListValues list = ParseValueBytesIntoTypedList( + bytes, Type::BYTE_ARRAY, std::nullopt, Encoding::PLAIN); + const auto* values = std::get_if>(&list); + if (!values) { + throw InvalidInputException("Expected BYTE_ARRAY values"); + } + return *values; +} + LevelAndValueBytes DecompressAndSplit( const std::vector& plaintext, CompressionCodec::type compression, diff --git a/src/processing/parquet_utils.h b/src/processing/parquet_utils.h index 0cb067e..516dfa0 100644 --- a/src/processing/parquet_utils.h +++ b/src/processing/parquet_utils.h @@ -66,6 +66,16 @@ std::vector CombineRawBytesIntoValueBytes( const std::optional& datatype_length, Encoding::type encoding); +/** + * Build BYTE_ARRAY value bytes for a single string payload. + */ +std::vector BuildByteArrayValueBytes(const std::string& payload); + +/** + * Parse BYTE_ARRAY value bytes into a list of string payloads. + */ +std::vector ParseByteArrayListValueBytes(const std::vector& bytes); + /** * Decompresses and splits a Parquet page into level and value bytes. * Handles DATA_PAGE_V1, DATA_PAGE_V2 (including optional compression on value bytes), diff --git a/src/processing/parquet_utils_test.cpp b/src/processing/parquet_utils_test.cpp index e256a8b..7391c6c 100644 --- a/src/processing/parquet_utils_test.cpp +++ b/src/processing/parquet_utils_test.cpp @@ -347,6 +347,35 @@ TEST(ParquetUtils, CombineRawBytesIntoValueBytes_BYTE_ARRAY) { EXPECT_EQ(out, expected); } +TEST(ParquetUtils, BuildByteArrayValueBytes_SinglePayload) { + std::vector out = BuildByteArrayValueBytes("abc"); + ASSERT_EQ(out.size(), 7u); + EXPECT_EQ(read_u32_le(out, 0), 3u); + EXPECT_EQ(out[4], static_cast('a')); + EXPECT_EQ(out[5], static_cast('b')); + EXPECT_EQ(out[6], static_cast('c')); +} + +TEST(ParquetUtils, ParseByteArrayListValueBytes_SinglePayload) { + std::vector bytes = BuildByteArrayValueBytes("hello"); + std::vector out = ParseByteArrayListValueBytes(bytes); + ASSERT_EQ(out.size(), 1u); + EXPECT_EQ(out[0], "hello"); +} + +TEST(ParquetUtils, ParseByteArrayListValueBytes_MultiplePayloads) { + std::vector elems = { + {'a','b'}, + {'x','y','z'} + }; + std::vector bytes = CombineRawBytesIntoValueBytes( + elems, Type::BYTE_ARRAY, std::nullopt, Encoding::PLAIN); + std::vector out = ParseByteArrayListValueBytes(bytes); + ASSERT_EQ(out.size(), 2u); + EXPECT_EQ(out[0], "ab"); + EXPECT_EQ(out[1], "xyz"); +} + TEST(ParquetUtils, CombineRawBytesIntoValueBytes_FIXED_LEN_BYTE_ARRAY_SizeMismatch) { // Expect length 3, but provide a 2-byte element -> should throw std::vector elems = { diff --git a/src/processing/typed_list_values.cpp b/src/processing/typed_list_values.cpp index 8c4acb6..a4d2129 100644 --- a/src/processing/typed_list_values.cpp +++ b/src/processing/typed_list_values.cpp @@ -135,17 +135,6 @@ TypedListValues BuildTypedListFromRawBytes( } return out; } - case Type::UNDEFINED: { - std::vector out; - for (size_t i = 0; i < elements_bytes.size(); ++i) { - const std::vector& bytes = elements_bytes[i]; - if (bytes.size() != 1) { - throw std::runtime_error("DecryptTypedListValues: invalid UNDEFINED element size"); - } - out.push_back(bytes[0]); - } - return out; - } default: throw std::runtime_error("DecryptTypedListValues: unsupported datatype"); } @@ -203,7 +192,6 @@ const char* GetTypeName() { else if constexpr (std::is_same_v>>) return "INT96"; else if constexpr (std::is_same_v>) return "string (BYTE_ARRAY/FIXED_LEN_BYTE_ARRAY)"; - else if constexpr (std::is_same_v>) return "UNDEFINED (raw bytes)"; else if constexpr (std::is_same_v) return "empty/error"; else return "unknown"; } @@ -227,28 +215,6 @@ std::string TypedListToString(const TypedListValues& list) { << values[i][1] << ", " << values[i][2] << "]\n"; } } - else if constexpr (std::is_same_v>) { - // Special case for UNDEFINED - raw bytes as hex - out << "Decoded UNDEFINED type (raw bytes):\n"; - out << " Hex: "; - for (size_t i = 0; i < values.size(); ++i) { - out << std::hex << std::setw(2) << std::setfill('0') - << static_cast(values[i]); - if (i < values.size() - 1) out << " "; - } - out << std::dec << "\n"; // Reset to decimal - - // Also show as string if printable - out << " String: \""; - for (uint8_t byte : values) { - if (byte >= 32 && byte < 127) { - out << static_cast(byte); - } else { - out << "."; - } - } - out << "\"\n"; - } else if constexpr (std::is_same_v>) { // String values with quotes and the length of the string. out << "Decoded " << GetTypeName() << " values:\n"; diff --git a/src/processing/typed_list_values.h b/src/processing/typed_list_values.h index 108007b..383ef96 100644 --- a/src/processing/typed_list_values.h +++ b/src/processing/typed_list_values.h @@ -32,8 +32,7 @@ using TypedListValues = std::variant< std::vector, std::vector, std::vector>, // For INT96 - std::vector, // For BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY - std::vector // For UNDEFINED, a plain untyped byte sequence. + std::vector // For BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY >; /** diff --git a/src/processing/typed_list_values_test.cpp b/src/processing/typed_list_values_test.cpp index 8d9434b..17f3af4 100644 --- a/src/processing/typed_list_values_test.cpp +++ b/src/processing/typed_list_values_test.cpp @@ -124,19 +124,6 @@ TEST(TypedListValuesTest, BuildRawBytesFromTypedListValues_BuildTypedListFromRaw } } -TEST(TypedListValuesTest, BuildRawBytesFromTypedListValues_BuildTypedListFromRawBytes_RoundTrip_UNDEFINED) { - TypedListValues input = std::vector{0u, 255u, 42u}; - std::vector raw = BuildRawBytesFromTypedListValues(input); - ASSERT_EQ(raw.size(), 3u); - for (const auto& r : raw) { - EXPECT_EQ(r.size(), 1u); - } - auto out = BuildTypedListFromRawBytes(Type::UNDEFINED, raw); - const auto& out_vec = std::get>(out); - const auto& in_vec = std::get>(input); - ASSERT_EQ(out_vec, in_vec); -} - TEST(TypedListValuesTest, BuildTypedListFromRawBytes_InvalidElementSizes_Throws) { // INT32 wrong size { @@ -168,12 +155,6 @@ TEST(TypedListValuesTest, BuildTypedListFromRawBytes_InvalidElementSizes_Throws) std::vector v{r}; EXPECT_THROW((void)BuildTypedListFromRawBytes(Type::INT96, v), std::runtime_error); } - // UNDEFINED wrong size (expects exactly 1) - { - RawValueBytes r = {0xAA, 0xBB}; - std::vector v{r}; - EXPECT_THROW((void)BuildTypedListFromRawBytes(Type::UNDEFINED, v), std::runtime_error); - } } TEST(TypedListValuesTest, BuildTypedListFromRawBytes_UnsupportedType_Throws) { diff --git a/src/processing/value_encryption_utils_test.cpp b/src/processing/value_encryption_utils_test.cpp index 189749b..c6d92a3 100644 --- a/src/processing/value_encryption_utils_test.cpp +++ b/src/processing/value_encryption_utils_test.cpp @@ -210,20 +210,6 @@ TEST(ValueEncryptionUtilsTest, EncryptTypedListValues_DecryptTypedListValues_Rou ASSERT_EQ(out_vec, vals); } -TEST(ValueEncryptionUtilsTest, EncryptTypedListValues_DecryptTypedListValues_RoundTrip_UNDEFINED) { - auto enc = [](const std::vector& b) { return b; }; - auto dec = [](const std::vector& b) { return b; }; - TypedListValues input = std::vector{1u, 2u, 255u}; - auto encrypted = EncryptTypedListValues(input, enc); - ASSERT_EQ(encrypted.size(), 3u); - for (const auto& ev : encrypted) { - EXPECT_EQ(ev.size(), 1u); - } - auto decrypted = DecryptTypedListValues(encrypted, Type::UNDEFINED, dec); - const auto& out_vec = std::get>(decrypted); - ASSERT_EQ(out_vec, std::get>(input)); -} - TEST(ValueEncryptionUtilsTest, DecryptTypedListValues_InvalidDecryptedSize_Throws) { // Provide an encrypted value whose decrypted bytes are of incorrect size for FLOAT (expect 4, give 3) EncryptedValue ev = std::vector{0x00, 0x00, 0x80}; // 3 bytes diff --git a/src/scripts/dbpa_remote_testapp.cpp b/src/scripts/dbpa_remote_testapp.cpp index 80325c9..b87e623 100644 --- a/src/scripts/dbpa_remote_testapp.cpp +++ b/src/scripts/dbpa_remote_testapp.cpp @@ -30,6 +30,7 @@ #include "../client/httplib_client.h" #include "../common/enums.h" #include "../processing/compression_utils.h" +#include "../processing/parquet_utils.h" #include "../common/bytes_utils.h" #include "tcb/span.hpp" @@ -43,56 +44,16 @@ using span = tcb::span; namespace { const std::string SEQUENCER_ENCRYPTION_METADATA_VERSION = "v0.01"; - std::vector MakeByteArrayPayload(const std::string& s) { - std::vector out; - append_u32_le(out, static_cast(s.size())); - out.insert(out.end(), s.begin(), s.end()); - return out; - } - - std::string ParseByteArrayPayload(const std::vector& bytes) { - if (bytes.size() < 4) { - throw std::runtime_error("Invalid BYTE_ARRAY payload: too short for length prefix"); - } - uint32_t len = read_u32_le(bytes, 0); - if (bytes.size() != 4 + len) { - throw std::runtime_error("Invalid BYTE_ARRAY payload: length mismatch"); - } - return std::string(bytes.begin() + 4, bytes.end()); - } - std::vector MakeByteArrayListPayload(const std::vector& items) { - std::vector out; - size_t total = 0; + std::vector elements; + elements.reserve(items.size()); for (const auto& s : items) { - total += 4 + s.size(); + elements.emplace_back(s.begin(), s.end()); } - out.reserve(total); - for (const auto& s : items) { - append_u32_le(out, static_cast(s.size())); - out.insert(out.end(), s.begin(), s.end()); - } - return out; + return CombineRawBytesIntoValueBytes( + elements, Type::BYTE_ARRAY, std::nullopt, Encoding::PLAIN); } - std::vector ParseByteArrayListPayload(const std::vector& bytes) { - std::vector out; - size_t offset = 0; - while (offset < bytes.size()) { - if (offset + 4 > bytes.size()) { - throw std::runtime_error("Invalid BYTE_ARRAY list: incomplete length prefix"); - } - uint32_t len = read_u32_le(bytes, static_cast(offset)); - offset += 4; - if (offset + len > bytes.size()) { - throw std::runtime_error("Invalid BYTE_ARRAY list: length exceeds remaining data"); - } - out.emplace_back(bytes.begin() + static_cast(offset), - bytes.begin() + static_cast(offset + len)); - offset += len; - } - return out; - } } // Demo application class @@ -352,7 +313,7 @@ class DBPARemoteTestApp { auto decrypted_plain = Decompress( std::vector(decrypted_compressed.begin(), decrypted_compressed.end()), CompressionCodec::SNAPPY); - auto decrypted_list = ParseByteArrayListPayload(decrypted_plain); + auto decrypted_list = ParseByteArrayListValueBytes(decrypted_plain); // Verify data integrity if (decrypted_list == sample_data) { @@ -395,7 +356,7 @@ class DBPARemoteTestApp { } try { - auto plaintext = MakeByteArrayPayload(test_data); + auto plaintext = BuildByteArrayValueBytes(test_data); auto compressed_plaintext = Compress(plaintext, CompressionCodec::SNAPPY); // Encrypt @@ -432,7 +393,11 @@ class DBPARemoteTestApp { auto decrypted_plain = Decompress( std::vector(decrypted_compressed.begin(), decrypted_compressed.end()), CompressionCodec::SNAPPY); - std::string decrypted_string = ParseByteArrayPayload(decrypted_plain); + std::vector decrypted_values = ParseByteArrayListValueBytes(decrypted_plain); + if (decrypted_values.size() != 1u) { + throw std::runtime_error("Expected exactly one BYTE_ARRAY value"); + } + std::string decrypted_string = decrypted_values.front(); if (decrypted_string == test_data) { std::cout << " OK: Round-trip successful" << std::endl; @@ -793,7 +758,7 @@ class DBPARemoteTestApp { // Test with empty data std::cout << "\nTesting empty data handling:" << std::endl; try { - auto empty_data = MakeByteArrayPayload(""); + auto empty_data = BuildByteArrayValueBytes(""); auto compressed_empty = Compress(empty_data, CompressionCodec::SNAPPY); std::map encoding_attributes = {{"page_encoding", "PLAIN"}, {"page_type", "DICTIONARY_PAGE"}}; auto result = agent_->Encrypt(span(compressed_empty), encoding_attributes); @@ -813,7 +778,7 @@ class DBPARemoteTestApp { std::cout << "\nTesting large data handling:" << std::endl; try { std::string large_data(1000, 'X'); // 1KB of data - auto plaintext = MakeByteArrayPayload(large_data); + auto plaintext = BuildByteArrayValueBytes(large_data); auto compressed_plaintext = Compress(plaintext, CompressionCodec::SNAPPY); std::map encoding_attributes = {{"page_encoding", "PLAIN"}, {"page_type", "DICTIONARY_PAGE"}}; auto result = agent_->Encrypt(span(compressed_plaintext), encoding_attributes);