Skip to content

Commit

Permalink
Adding byte buffer reading and writing to big_unsigned.
Browse files Browse the repository at this point in the history
  • Loading branch information
gpdaniels committed Oct 4, 2023
1 parent 1a8fdc5 commit 74ec473
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 22 deletions.
72 changes: 56 additions & 16 deletions source/math/big_unsigned
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand All @@ -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<void>(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<char>(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<char>(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<unsigned char>(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<unsigned char>(temp.chunks[0] & 0xFF);
temp >>= 8;
}
return this->get_length_bytes();
}
};
}
Expand Down
8 changes: 4 additions & 4 deletions tests/math/big_integer.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1183,23 +1183,23 @@ 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;

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) {
Expand Down
66 changes: 64 additions & 2 deletions tests/math/big_unsigned.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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));
}

0 comments on commit 74ec473

Please sign in to comment.