diff --git a/source/math/big_unsigned b/source/math/big_unsigned index a387a53..a11e804 100644 --- a/source/math/big_unsigned +++ b/source/math/big_unsigned @@ -164,6 +164,28 @@ namespace gtl { *this = result; } + /// @brief Conversion constructor from an array of bytes. + /// @param value The value to store. + /// @param length The length of the data. + big_unsigned(const unsigned char* value, const unsigned long long int length) + : chunks(1, 0) { + // Sanity check length. + if (length == 0) { + return; + } + GTL_BIG_UNSIGNED_ASSERT(value != nullptr, "Value must not be null with a length greater than zero."); + // Loop over the data storing each byte. + big_unsigned result(value[0]); + for (unsigned long long int index = 1; index < length; ++index) { + result <<= 8; + result |= big_unsigned(value[index]); + } + // Remove leading zeros. + result.trim(); + // Apply the result to this. + *this = result; + } + /// @brief Copy constructor from another big number. /// @param other The other big number to copy. big_unsigned(const big_unsigned& other) @@ -881,16 +903,17 @@ namespace gtl { } public: - /// @brief Get the length of the big number in allocated bits, this will be a multiple of the bits in a chunk. + /// @brief Get the length of the big number in allocated bits, this will be a multiple of eight. /// @return The number of bits allocated to store the big number. unsigned long long int get_length_bits() const { return this->chunks.size() * big_unsigned::chunk_bits; } - /// @brief Get the length of the big number in allocated bytes, this will be a multiple of the size of a chunk. + /// @brief Get the length of the big number in allocated bytes. /// @return The number of bytes allocated to store the big number. unsigned long long int get_length_bytes() const { - return this->chunks.size() * sizeof(chunk_type); + const unsigned long long int remainder = (this->chunks.back() > 0xFFFFFF) + (this->chunks.back() > 0xFFFF) + (this->chunks.back() > 0xFF) + 1; + return remainder + (this->chunks.size() - 1) * sizeof(chunk_type); } /// @brief Get the length of the big number as a decimal number. @@ -911,25 +934,42 @@ namespace gtl { /// @param length The length of the output buffer. /// @return The decimal length of the big number, equal to the number of digits written. unsigned long long int to_string(char* buffer, const unsigned long long int length) const { - GTL_BIG_UNSIGNED_ASSERT(length > this->get_length_decimal(), "Buffer length must be greater than decimal length of number."); - static_cast(length); + const unsigned long long int length_digits = this->get_length_decimal(); + GTL_BIG_UNSIGNED_ASSERT(length > length_digits, "Buffer length must be greater than the decimal length of the number."); big_unsigned temp(*this); // Loop and decimate the big number storing the remainder as the current digit ascii char. - unsigned long long int digit_index = 0; - do { + for (unsigned long long int digit_index = 0; digit_index < length_digits; ++digit_index) { big_unsigned remainder; temp = big_unsigned::divide(temp, 10, remainder); - buffer[digit_index++] = '0' + static_cast(remainder.chunks[0]); - } while (temp); - // Reverse the string as digits were stored units first and we want units last. - for (unsigned long long int reverse_index = 0; reverse_index < digit_index / 2; ++reverse_index) { - char swap_character = buffer[reverse_index]; - buffer[reverse_index] = buffer[digit_index - reverse_index - 1]; - buffer[digit_index - reverse_index - 1] = swap_character; + buffer[length_digits - 1 - digit_index] = '0' + static_cast(remainder.chunks[0]); } // Null terminate the string. - buffer[digit_index] = 0; - return digit_index; + buffer[length_digits] = 0; + return length_digits; + } + + /// @brief Conver the big number to an array of bytes and output it in the provided buffer. + /// @param buffer The output buffer, must be larger than or equal to the byte length of the big number. + /// @param length The length of the output buffer. + /// @param pad_to_length Add leading zeros to pad the bytes to the length of the input buffer. + /// @return The byte length of the big number, equal to the number of bytes written. + unsigned long long int to_bytes(unsigned char* buffer, const unsigned long long int length, bool pad_to_length = false) const { + const unsigned long long int length_bytes = this->get_length_bytes(); + GTL_BIG_UNSIGNED_ASSERT(length >= length_bytes, "Buffer length must be greater than the bytes length of the number."); + big_unsigned temp(*this); + // Emit padding zeros. + unsigned long long int pad_index = 0; + if (pad_to_length) { + for (; pad_index < (length - length_bytes); ++pad_index) { + buffer[pad_index] = static_cast(0); + } + } + // Loop and output big number storing the least significant byte each iteration. + for (unsigned long long int byte_index = 0; byte_index < length_bytes; ++byte_index) { + buffer[pad_index + length_bytes - 1 - byte_index] = static_cast(temp.chunks[0] & 0xFF); + temp >>= 8; + } + return this->get_length_bytes(); } }; } diff --git a/tests/math/big_integer.test.cpp b/tests/math/big_integer.test.cpp index df1cfef..048ac51 100644 --- a/tests/math/big_integer.test.cpp +++ b/tests/math/big_integer.test.cpp @@ -1183,11 +1183,11 @@ TEST(big_integer, operator, get_length_bytes) { gtl::big_integer big_integer_lhs(lhs); gtl::big_integer big_integer_rhs(rhs); REQUIRE(big_integer_lhs.get_length_bytes() == 8); - REQUIRE(big_integer_rhs.get_length_bytes() == 4); + REQUIRE(big_integer_rhs.get_length_bytes() == 1); constexpr static const char* two_power_333 = "17498005798264095394980017816940970922825355447145699491406164851279623993595007385788105416184430592"; gtl::big_integer big_integer_333(two_power_333, testbench::string_length(two_power_333)); - REQUIRE(big_integer_333.get_length_bytes() == 44); + REQUIRE(big_integer_333.get_length_bytes() == 42); signed long long int lhs_negative = -(1ll << 62); signed long long int rhs_negative = -2; @@ -1195,11 +1195,11 @@ TEST(big_integer, operator, get_length_bytes) { gtl::big_integer big_integer_lhs_negative(lhs_negative); gtl::big_integer big_integer_rhs_negative(rhs_negative); REQUIRE(big_integer_lhs_negative.get_length_bytes() == 8); - REQUIRE(big_integer_rhs_negative.get_length_bytes() == 4); + REQUIRE(big_integer_rhs_negative.get_length_bytes() == 1); constexpr static const char* two_power_333_negative = "-17498005798264095394980017816940970922825355447145699491406164851279623993595007385788105416184430592"; gtl::big_integer big_integer_333_negative(two_power_333_negative, testbench::string_length(two_power_333_negative)); - REQUIRE(big_integer_333_negative.get_length_bytes() == 44); + REQUIRE(big_integer_333_negative.get_length_bytes() == 42); } TEST(big_integer, operator, get_length_decimal) { diff --git a/tests/math/big_unsigned.test.cpp b/tests/math/big_unsigned.test.cpp index d419384..8eac4b6 100644 --- a/tests/math/big_unsigned.test.cpp +++ b/tests/math/big_unsigned.test.cpp @@ -120,6 +120,27 @@ TEST(big_unsigned, constructor, string) { REQUIRE(check_power_of_two(big_unsigned_333, 0, 333, 1)); } +TEST(big_unsigned, constructor, bytes) { + constexpr static const unsigned char data[6] = { 1, 2, 3, 4, 5, 6 }; + gtl::big_unsigned big_unsigned(&data[0], 6); + REQUIRE(big_unsigned == 0x010203040506ull); + + constexpr static const unsigned char two_power_95[12] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + gtl::big_unsigned big_unsigned_95(&two_power_95[0], 12); + REQUIRE(check_power_of_two(big_unsigned_95, 0, 95, 1)); + + constexpr static const unsigned char two_power_333[42] = { + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + gtl::big_unsigned big_unsigned_333(&two_power_333[0], 42); + REQUIRE(check_power_of_two(big_unsigned_333, 0, 333, 1)); +} + TEST(big_unsigned, constructor, copy) { gtl::big_unsigned big_unsigned("123456", 6); gtl::big_unsigned big_unsigned_copy(big_unsigned); @@ -646,11 +667,11 @@ TEST(big_unsigned, operator, get_length_bytes) { gtl::big_unsigned big_unsigned_lhs(lhs); gtl::big_unsigned big_unsigned_rhs(rhs); REQUIRE(big_unsigned_lhs.get_length_bytes() == 8); - REQUIRE(big_unsigned_rhs.get_length_bytes() == 4); + REQUIRE(big_unsigned_rhs.get_length_bytes() == 1); constexpr static const char* two_power_333 = "17498005798264095394980017816940970922825355447145699491406164851279623993595007385788105416184430592"; gtl::big_unsigned big_unsigned_333(two_power_333, testbench::string_length(two_power_333)); - REQUIRE(big_unsigned_333.get_length_bytes() == 44); + REQUIRE(big_unsigned_333.get_length_bytes() == 42); } TEST(big_unsigned, operator, get_length_decimal) { @@ -686,3 +707,44 @@ TEST(big_unsigned, operator, to_string) { REQUIRE(big_unsigned_333.to_string(string_333, 102) == 101); REQUIRE(testbench::is_string_same(string_333, two_power_333)); } + +TEST(big_unsigned, operator, to_bytes) { + const unsigned long long int lhs = 1ull << 62; + const unsigned long long int rhs = 2; + + constexpr static const auto swap_endian = [](unsigned long long value)->unsigned long long{ + return (value >> 56) | + ((value << 40) & 0x00FF000000000000ull) | + ((value << 24) & 0x0000FF0000000000ull) | + ((value << 8) & 0x000000FF00000000ull) | + ((value >> 8) & 0x00000000FF000000ull) | + ((value >> 24) & 0x0000000000FF0000ull) | + ((value >> 40) & 0x000000000000FF00ull) | + (value << 56); + }; + + unsigned char bytes_lhs[8]; + unsigned char bytes_rhs[1]; + unsigned char bytes_rhs_padded[8]; + const unsigned long long int swapped_lhs = swap_endian(lhs); + const unsigned long long int swapped_rhs = swap_endian(rhs); + gtl::big_unsigned big_unsigned_lhs(lhs); + gtl::big_unsigned big_unsigned_rhs(rhs); + REQUIRE(big_unsigned_lhs.to_bytes(&bytes_lhs[0], 8) == 8); + REQUIRE(big_unsigned_rhs.to_bytes(&bytes_rhs[0], 1) == 1); + REQUIRE(big_unsigned_rhs.to_bytes(&bytes_rhs_padded[0], 8, true) == 1); + REQUIRE(testbench::is_memory_same(&bytes_lhs[0], &swapped_lhs, 8)); + REQUIRE(testbench::is_memory_same(&bytes_rhs[0], &rhs, 1)); + REQUIRE(testbench::is_memory_same(&bytes_rhs_padded[0], &swapped_rhs, 8)); + + constexpr static const unsigned char two_power_333[42] = { + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char bytes_333[42]; + gtl::big_unsigned big_unsigned_333(two_power_333, 42); + REQUIRE(big_unsigned_333.to_bytes(&bytes_333[0], 42) == 42); + REQUIRE(testbench::is_memory_same(&bytes_333[0], &two_power_333[0], 42)); +}