Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ add_library(dbps_common_lib STATIC
src/common/json_request.cpp
src/common/enum_utils.cpp
src/common/value_encryption_utils.cpp
src/common/typed_list_values.cpp
)
target_include_directories(dbps_common_lib PUBLIC
src/common
Expand All @@ -145,6 +146,7 @@ add_library(dbps_server_lib STATIC
target_link_libraries(dbps_server_lib PUBLIC dbps_common_lib snappy)
target_include_directories(dbps_server_lib PUBLIC
src/server
src/common
${CMAKE_BINARY_DIR}/_deps/cppcodec-src
${CMAKE_BINARY_DIR}/_deps/jwt-cpp-src/include
${CMAKE_BINARY_DIR}/_deps/nlohmann_json-src/include
Expand Down Expand Up @@ -261,6 +263,13 @@ if(BUILD_TESTS)
gtest_main
)

# Typed list values tests
add_executable(typed_list_values_test src/common/typed_list_values_test.cpp)
target_link_libraries(typed_list_values_test
dbps_common_lib
gtest_main
)

# Encryption sequencer tests
add_executable(encryption_sequencer_test src/server/encryption_sequencer_test.cpp)
target_link_libraries(encryption_sequencer_test
Expand All @@ -279,13 +288,13 @@ if(BUILD_TESTS)
target_include_directories(decoding_utils_test PRIVATE src/server)

# Bytes utils tests
add_executable(bytes_utils_test src/server/bytes_utils_test.cpp)
add_executable(bytes_utils_test src/common/bytes_utils_test.cpp)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you :)

target_link_libraries(bytes_utils_test
dbps_server_lib
dbps_common_lib
gtest_main
)
target_include_directories(bytes_utils_test PRIVATE src/server)
target_include_directories(bytes_utils_test PRIVATE src/server src/common)

# Compression utils tests
add_executable(compression_utils_test src/server/compression_utils_test.cpp)
Expand Down Expand Up @@ -473,6 +482,7 @@ if(BUILD_TESTS)
json_request_test
enum_utils_test
value_encryption_utils_test
typed_list_values_test
encryption_sequencer_test
decoding_utils_test
bytes_utils_test
Expand All @@ -493,6 +503,7 @@ if(BUILD_TESTS)
gtest_discover_tests(json_request_test)
gtest_discover_tests(enum_utils_test)
gtest_discover_tests(value_encryption_utils_test)
gtest_discover_tests(typed_list_values_test)
gtest_discover_tests(encryption_sequencer_test)
gtest_discover_tests(decoding_utils_test)
gtest_discover_tests(bytes_utils_test)
Expand Down
70 changes: 55 additions & 15 deletions src/server/bytes_utils.h → src/common/bytes_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,55 @@
#include <vector>
#include <cstdint>
#include <limits>
#include <cstring>
#include "exceptions.h"

// Little-endian helpers reused across modules
inline void append_u32_le(std::vector<uint8_t>& out, uint32_t v) {
out.push_back(static_cast<uint8_t>(v & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 8) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 16) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 24) & 0xFF));
}

inline void append_i32_le(std::vector<uint8_t>& out, int32_t v) {
append_u32_le(out, static_cast<uint32_t>(v));
}

inline void append_u64_le(std::vector<uint8_t>& out, uint64_t v) {
out.push_back(static_cast<uint8_t>(v & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 8) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 16) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 24) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 32) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 40) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 48) & 0xFF));
out.push_back(static_cast<uint8_t>((v >> 56) & 0xFF));
}

inline void append_i64_le(std::vector<uint8_t>& out, int64_t v) {
append_u64_le(out, static_cast<uint64_t>(v));
}

inline void append_f32_le(std::vector<uint8_t>& out, float v) {
uint32_t bits = 0;
std::memcpy(&bits, &v, sizeof(bits));
append_u32_le(out, bits);
}

inline void append_f64_le(std::vector<uint8_t>& out, double v) {
uint64_t bits = 0;
std::memcpy(&bits, &v, sizeof(bits));
append_u64_le(out, bits);
}

inline uint32_t read_u32_le(const std::vector<uint8_t>& in, size_t offset) {
return static_cast<uint32_t>(in[offset]) |
(static_cast<uint32_t>(in[offset + 1]) << 8) |
(static_cast<uint32_t>(in[offset + 2]) << 16) |
(static_cast<uint32_t>(in[offset + 3]) << 24);
}

struct SplitBytesPair {
std::vector<uint8_t> leading;
std::vector<uint8_t> trailing;
Expand All @@ -35,7 +82,7 @@ struct SplitBytesPair {
* @param trailing The second part of the bytes
* @return Combined bytes vector with leading followed by trailing
*/
inline std::vector<uint8_t> Join(const std::vector<uint8_t>& leading, const std::vector<uint8_t>& trailing) {
inline std::vector<uint8_t> Join(const std::vector<uint8_t>& leading, const std::vector<uint8_t>& trailing) {
std::vector<uint8_t> result;
result.reserve(leading.size() + trailing.size());
result.insert(result.end(), leading.begin(), leading.end());
Expand Down Expand Up @@ -78,20 +125,16 @@ inline std::vector<uint8_t> JoinWithLengthPrefix(const std::vector<uint8_t>& lea
throw InvalidInputException("Leading bytes size exceeds maximum representable value");
}

// Calculate the length of the leading bytes
uint32_t leading_length = static_cast<uint32_t>(leading.size());
std::vector<uint8_t> result;
result.reserve(4 + leading.size() + trailing.size());

// Prepend 4-byte length (little-endian)
result.push_back(leading_length & 0xFF);
result.push_back((leading_length >> 8) & 0xFF);
result.push_back((leading_length >> 16) & 0xFF);
result.push_back((leading_length >> 24) & 0xFF);
// Prepend 4-byte length
append_u32_le(result, leading_length);

// Append leading bytes
// Append leading and trailing bytes
result.insert(result.end(), leading.begin(), leading.end());

// Append trailing bytes
result.insert(result.end(), trailing.begin(), trailing.end());

return result;
Expand All @@ -110,11 +153,8 @@ inline SplitBytesPair SplitWithLengthPrefix(const std::vector<uint8_t>& bytes) {
throw InvalidInputException("Invalid length-prefixed data: insufficient bytes for length prefix");
}

// Read 4-byte length (little-endian)
uint32_t leading_length = static_cast<uint32_t>(bytes[0]) |
(static_cast<uint32_t>(bytes[1]) << 8) |
(static_cast<uint32_t>(bytes[2]) << 16) |
(static_cast<uint32_t>(bytes[3]) << 24);
// Read 4-byte length
uint32_t leading_length = read_u32_le(bytes, 0);

if (bytes.size() < 4 + leading_length) {
throw InvalidInputException("Invalid length-prefixed data: insufficient bytes for leading data (expected " +
Expand All @@ -125,7 +165,7 @@ inline SplitBytesPair SplitWithLengthPrefix(const std::vector<uint8_t>& bytes) {

// Extract leading bytes (skip the 4-byte length prefix)
result.leading = std::vector<uint8_t>(bytes.begin() + 4, bytes.begin() + 4 + leading_length);

// Extract trailing bytes (everything after leading)
result.trailing = std::vector<uint8_t>(bytes.begin() + 4 + leading_length, bytes.end());

Expand Down
46 changes: 32 additions & 14 deletions src/server/bytes_utils_test.cpp → src/common/bytes_utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "bytes_utils.h"
#include "exceptions.h"

#include <vector>
#include <gtest/gtest.h>

Expand Down Expand Up @@ -125,8 +126,8 @@ TEST(BytesUtils, JoinWithLengthPrefix_EmptyLeading) {
std::vector<uint8_t> trailing = {0x04, 0x05, 0x06};
std::vector<uint8_t> result = JoinWithLengthPrefix(leading, trailing);

EXPECT_EQ(7, result.size()); // 4 bytes length + 0 bytes leading + 3 bytes trailing
EXPECT_EQ(0x00, result[0]); // length = 0
EXPECT_EQ(7, result.size()); // 4 bytes length + 0 leading + 3 trailing
EXPECT_EQ(0x00, result[0]);
EXPECT_EQ(0x00, result[1]);
EXPECT_EQ(0x00, result[2]);
EXPECT_EQ(0x00, result[3]);
Expand All @@ -140,29 +141,47 @@ TEST(BytesUtils, JoinWithLengthPrefix_EmptyTrailing) {
std::vector<uint8_t> trailing;
std::vector<uint8_t> result = JoinWithLengthPrefix(leading, trailing);

EXPECT_EQ(7, result.size()); // 4 bytes length + 3 bytes leading + 0 bytes trailing
EXPECT_EQ(0x03, result[0]); // length = 3
EXPECT_EQ(7, result.size()); // 4 bytes length + 3 leading + 0 trailing
EXPECT_EQ(0x03, result[0]);
EXPECT_EQ(0x00, result[1]);
EXPECT_EQ(0x00, result[2]);
EXPECT_EQ(0x00, result[3]);
EXPECT_EQ(0x01, result[4]);
EXPECT_EQ(0x02, result[5]);
EXPECT_EQ(0x03, result[6]);
}

TEST(BytesUtils, JoinWithLengthPrefix_BothEmpty) {
std::vector<uint8_t> leading;
std::vector<uint8_t> trailing;
std::vector<uint8_t> result = JoinWithLengthPrefix(leading, trailing);

EXPECT_EQ(4, result.size()); // only the 4-byte length prefix
EXPECT_EQ(0x00, result[0]);
EXPECT_EQ(0x00, result[1]);
EXPECT_EQ(0x00, result[2]);
EXPECT_EQ(0x00, result[3]);
}

TEST(BytesUtils, SplitWithLengthPrefix_Normal) {
// Create data with JoinWithLengthPrefix
std::vector<uint8_t> leading = {0x01, 0x02, 0x03};
std::vector<uint8_t> trailing = {0x04, 0x05, 0x06};
std::vector<uint8_t> joined = JoinWithLengthPrefix(leading, trailing);

// Parse it back
SplitBytesPair result = SplitWithLengthPrefix(joined);
std::vector<uint8_t> combined = JoinWithLengthPrefix(leading, trailing);
SplitBytesPair result = SplitWithLengthPrefix(combined);

EXPECT_EQ(leading, result.leading);
EXPECT_EQ(trailing, result.trailing);
}

TEST(BytesUtils, SplitWithLengthPrefix_InvalidTooShort) {
std::vector<uint8_t> invalid = {0x01, 0x02}; // Less than 4 bytes
EXPECT_THROW(SplitWithLengthPrefix(invalid), InvalidInputException);
TEST(BytesUtils, SplitWithLengthPrefix_InvalidData_Short) {
std::vector<uint8_t> bytes = {0x01, 0x02, 0x03}; // too short for length prefix
EXPECT_THROW(SplitWithLengthPrefix(bytes), InvalidInputException);
}

TEST(BytesUtils, SplitWithLengthPrefix_InvalidData_TruncatedLeading) {
// length=5 but only 4 bytes provided after prefix
std::vector<uint8_t> bytes = {0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04};
EXPECT_THROW(SplitWithLengthPrefix(bytes), InvalidInputException);
}

TEST(BytesUtils, SplitWithLengthPrefix_InvalidInsufficientData) {
Expand All @@ -180,5 +199,4 @@ TEST(BytesUtils, JoinWithLengthPrefixAndSplit_RoundTrip) {

EXPECT_EQ(leading, parsed.leading);
EXPECT_EQ(trailing, parsed.trailing);
}

}
File renamed without changes.
Loading