diff --git a/include/science/libndtp/utils.h b/include/science/libndtp/utils.h index cb0369a..a4e8ec6 100644 --- a/include/science/libndtp/utils.h +++ b/include/science/libndtp/utils.h @@ -33,7 +33,7 @@ inline uint16_t crc16(const ByteArray& data, uint16_t poly = 0x8005, uint16_t in * Handles both signed and unsigned integers. */ template -std::pair to_bytes( +std::tuple to_bytes( const std::vector& values, uint8_t bit_width, const ByteArray& existing = {}, @@ -61,29 +61,18 @@ std::pair to_bytes( uint8_t current_byte = (continue_last && !result.empty()) ? result.back() : 0; int bits_in_current_byte = writing_bit_offset; + bool status_good = true; for (const auto& value : values) { - int val = value; + T val = value; if (is_signed) { - int min_value = -(1 << (bit_width - 1)); - int max_value = (1 << (bit_width - 1)) - 1; - if (val < min_value || val > max_value) { - throw std::invalid_argument( - "signed value " + std::to_string(val) + " doesn't fit in " + std::to_string(bit_width) + " bits" - ); - } - if (val < 0) { - val = (1 << bit_width) + val; + if ( val > (1 << (bit_width - 1)) - 1 || val < -(1 << (bit_width - 1))) { + status_good = false; } } else { - if (val < 0) { - throw std::invalid_argument("unsigned packing specified, but value is negative"); - } - if (val >= (1 << bit_width)) { - throw std::invalid_argument( - "unsigned value " + std::to_string(val) + " doesn't fit in " + std::to_string(bit_width) + " bits" - ); + if (val > (1 << bit_width) - 1) { + status_good = false; } - } + } int remaining_bits = bit_width; while (remaining_bits > 0) { @@ -119,7 +108,7 @@ std::pair to_bytes( result.push_back(current_byte); } - return { result, bits_in_current_byte }; + return { result, bits_in_current_byte, status_good }; } diff --git a/src/science/libndtp/ndtp.cpp b/src/science/libndtp/ndtp.cpp index 5840293..3246eab 100644 --- a/src/science/libndtp/ndtp.cpp +++ b/src/science/libndtp/ndtp.cpp @@ -177,7 +177,7 @@ ByteArray NDTPPayloadSpiketrain::pack() const { result.push_back(bin_size_ms); // pack clamped spike counts - auto [bytes, final_bit_offset] = to_bytes(clamped_counts, BIT_WIDTH_BINNED_SPIKES, {}, 0); + auto [bytes, final_bit_offset, _] = to_bytes(clamped_counts, BIT_WIDTH_BINNED_SPIKES, {}, 0); result.insert(result.end(), bytes.begin(), bytes.end()); return result; diff --git a/test/test_utils.cpp b/test/test_utils.cpp index f11568e..d081f85 100644 --- a/test/test_utils.cpp +++ b/test/test_utils.cpp @@ -6,54 +6,66 @@ namespace science::libndtp { TEST(UtilsTest, ToBytesBasicFunctionality) { - auto [result1, offset1] = to_bytes({1, 2, 3, 0}, 2); + auto [result1, offset1, success1] = to_bytes({1, 2, 3, 0}, 2); EXPECT_EQ(result1, (std::vector{0x6C})); EXPECT_EQ(offset1, 0); + EXPECT_TRUE(success1); - auto [result2, offset2] = to_bytes({1, 2, 3, 2, 1}, 2); + auto [result2, offset2, success2] = to_bytes({1, 2, 3, 2, 1}, 2); EXPECT_EQ(result2, (std::vector{0x6E, 0x40})); EXPECT_EQ(offset2, 2); + EXPECT_TRUE(success2); - auto [result3, offset3] = to_bytes({7, 5, 3, 1}, 12); + auto [result3, offset3, success3] = to_bytes({7, 5, 3, 1}, 12); EXPECT_EQ(result3, (std::vector{0x00, 0x70, 0x05, 0x00, 0x30, 0x01})); EXPECT_EQ(offset3, 0); + EXPECT_TRUE(success3); - auto [result4, offset4] = to_bytes({-7, -5, -3, -1}, 12, {}, 0, true); + auto [result4, offset4, success4] = to_bytes({-7, -5, -3, -1}, 12, {}, 0, true); EXPECT_EQ(result4, (std::vector{0xFF, 0x9F, 0xFB, 0xFF, 0xDF, 0xFF})); EXPECT_EQ(offset4, 0); + EXPECT_TRUE(success4); - auto [result5, offset5] = to_bytes({7, 5, 3}, 12, {0x01, 0x00}, 4); + auto [result5, offset5, success5] = to_bytes({7, 5, 3}, 12, {0x01, 0x00}, 4); EXPECT_EQ(result5, (std::vector{0x01, 0x00, 0x07, 0x00, 0x50, 0x03})); EXPECT_EQ(offset5, 0); + EXPECT_TRUE(success5); - auto [result6, offset6] = to_bytes({-7, -5, -3}, 12, {0x01, 0x00}, 4, true); + auto [result6, offset6, success6] = to_bytes({-7, -5, -3}, 12, {0x01, 0x00}, 4, true); EXPECT_EQ(result6, (std::vector{0x01, 0x0F, 0xF9, 0xFF, 0xBF, 0xFD})); EXPECT_EQ(offset6, 0); + EXPECT_TRUE(success6); - auto [result7, offset7] = to_bytes({7, 5, 3}, 12); + auto [result7, offset7, success7] = to_bytes({7, 5, 3}, 12); EXPECT_EQ(result7, (std::vector{0x00, 0x70, 0x05, 0x00, 0x30})); EXPECT_EQ(offset7, 4); + EXPECT_TRUE(success7); - auto [result8, offset8] = to_bytes({1, 2, 3, 4}, 8); + auto [result8, offset8, success8] = to_bytes({1, 2, 3, 4}, 8); EXPECT_EQ(result8, (std::vector{0x01, 0x02, 0x03, 0x04})); EXPECT_EQ(offset8, 0); + EXPECT_TRUE(success8); std::vector result9; int offset9; - std::tie(result9, offset9) = to_bytes({7, 5, 3}, 12); + bool success9; + std::tie(result9, offset9, success9) = to_bytes({7, 5, 3}, 12); EXPECT_EQ(result9, (std::vector{0x00, 0x70, 0x05, 0x00, 0x30})); EXPECT_EQ(result9.size(), 5); EXPECT_EQ(offset9, 4); + EXPECT_TRUE(success9); - auto [result10, offset10] = to_bytes({3, 5, 7}, 12, result9, offset9); + auto [result10, offset10, success10] = to_bytes({3, 5, 7}, 12, result9, offset9); EXPECT_EQ(result10, (std::vector{0x00, 0x70, 0x05, 0x00, 0x30, 0x03, 0x00, 0x50, 0x07})); EXPECT_EQ(result10.size(), 9); EXPECT_EQ(offset10, 0); + EXPECT_TRUE(success10); } TEST(UtilsTest, ToBytesErrorCases) { // Test case: 8 doesn't fit in 3 bits - EXPECT_THROW(to_bytes({8}, 3), std::invalid_argument); + auto [_1, _2, success] = to_bytes({8}, 3); + EXPECT_FALSE(success); // Test case: Invalid bit width EXPECT_THROW(to_bytes({1, 2, 3, 0}, 0), std::invalid_argument);