Skip to content

Commit 2a56cb3

Browse files
committed
Implement std::ostream &operator<<(std::ostream &, bigint<bits, is_signed> const &) so that it uses no heap allocation
1 parent b7f104b commit 2a56cb3

File tree

2 files changed

+120
-70
lines changed

2 files changed

+120
-70
lines changed

include/bigint23/bigint.hpp

Lines changed: 119 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef bigint_DISABLE_IO
1515
#include <iostream>
1616
#endif
17+
#include <memory>
1718
#include <ranges>
1819
#include <regex>
1920
#include <stdexcept>
@@ -825,6 +826,15 @@ namespace bigint {
825826
friend class bigint;
826827

827828
#ifndef bigint_DISABLE_IO
829+
template<std::size_t other_bits, bool other_is_signed>
830+
friend constexpr std::ostream &print_hex(std::ostream &, bigint<other_bits, other_is_signed> const &, bool);
831+
832+
template<std::size_t other_bits, bool other_is_signed>
833+
friend constexpr std::ostream &print_oct(std::ostream &, bigint<other_bits, other_is_signed> const &);
834+
835+
template<std::size_t other_bits, bool other_is_signed>
836+
friend constexpr std::ostream &print_dec(std::ostream &, bigint<other_bits, other_is_signed> const &);
837+
828838
template<std::size_t other_bits, bool other_is_signed>
829839
friend constexpr std::ostream &operator<<(std::ostream &, bigint<other_bits, other_is_signed> const &);
830840

@@ -986,82 +996,122 @@ namespace bigint {
986996
};
987997

988998
#ifndef bigint_DISABLE_IO
999+
template<std::size_t bits, bool is_signed>
1000+
constexpr std::ostream &print_hex(std::ostream &os, bigint<bits, is_signed> const &data, bool const use_uppercase) {
1001+
auto const &buf = data.data_;
1002+
auto start = buf.size();
1003+
while (start > 1 and buf[start - 1] == 0) {
1004+
--start;
1005+
}
1006+
1007+
if constexpr (std::endian::native == std::endian::little) {
1008+
for (auto const i: std::views::reverse(std::views::iota(0uz, start))) {
1009+
auto local = std::array<char, 3>{};
1010+
std::snprintf(
1011+
local.data(),
1012+
local.size(),
1013+
use_uppercase ? "%02X" : "%02x",
1014+
buf[i]
1015+
);
1016+
os.write(local.data(), local.size() - 1);
1017+
}
1018+
} else {
1019+
for (auto const i: std::views::iota(0uz, start)) {
1020+
auto local = std::array<char, 3>{};
1021+
std::snprintf(
1022+
local.data(),
1023+
local.size(),
1024+
use_uppercase ? "%02X" : "%02x",
1025+
buf[i]
1026+
);
1027+
os.write(local.data(), local.size() - 1);
1028+
}
1029+
}
1030+
1031+
return os;
1032+
}
1033+
1034+
template<std::size_t bits, bool is_signed>
1035+
constexpr std::ostream &print_oct(std::ostream &os, bigint<bits, is_signed> const &data) {
1036+
auto temp = data;
1037+
1038+
if (temp == std::int8_t{0}) {
1039+
os.put('0');
1040+
return os;
1041+
}
1042+
1043+
constexpr auto max_oct_digits = std::size_t{(bits / 3) + 2};
1044+
auto buffer = std::array<char, max_oct_digits>{};
1045+
auto pos = buffer.end();
1046+
1047+
while (temp != std::int8_t{0}) {
1048+
auto r = temp % std::int8_t{8};
1049+
auto digit = r.data_[0];
1050+
*--pos = static_cast<char>('0' + digit);
1051+
temp /= std::int8_t{8};
1052+
}
1053+
1054+
auto const length = static_cast<std::size_t>(buffer.end() - pos);
1055+
os.write(std::to_address(pos), length);
1056+
return os;
1057+
}
1058+
1059+
template<std::size_t bits, bool is_signed>
1060+
constexpr std::ostream &print_dec(std::ostream &os, bigint<bits, is_signed> const &data) {
1061+
auto temp = data;
1062+
1063+
if (temp == std::int8_t{0}) {
1064+
os.put('0');
1065+
return os;
1066+
}
1067+
1068+
auto negative = false;
1069+
if constexpr (is_signed) {
1070+
if (temp < std::int8_t{0}) {
1071+
negative = true;
1072+
temp = -temp;
1073+
}
1074+
}
1075+
1076+
constexpr auto max_dec_digits = std::size_t{static_cast<std::size_t>(bits * 0.3010299957) + 3}; //std::log10(2)
1077+
auto buffer = std::array<char, max_dec_digits>{};
1078+
auto pos = buffer.end();
1079+
1080+
while (temp != std::int8_t{0}) {
1081+
auto r = temp % std::int8_t{10};
1082+
auto digit = r.data_[0];
1083+
*--pos = static_cast<char>('0' + digit);
1084+
temp /= std::int8_t{10};
1085+
}
1086+
1087+
if (negative) {
1088+
os.put('-');
1089+
}
1090+
1091+
auto const length = static_cast<std::size_t>(buffer.end() - pos);
1092+
os.write(std::to_address(pos), length);
1093+
return os;
1094+
}
1095+
9891096
template<std::size_t bits, bool is_signed>
9901097
constexpr std::ostream &operator<<(std::ostream &os, bigint<bits, is_signed> const &data) {
9911098
auto const flags = os.flags();
992-
auto result = std::string{};
993-
9941099
auto const base_flag = flags & std::ios_base::basefield;
9951100
auto const use_uppercase = (flags & std::ios_base::uppercase) != 0;
9961101

997-
if (base_flag == std::ios_base::hex) {
998-
if constexpr (std::endian::native == std::endian::little) {
999-
for (auto const i: std::views::reverse(std::views::iota(1uz, data.data_.size()))) {
1000-
if (result.empty() and data.data_[i - 1] == 0 and i != 1) {
1001-
continue;
1002-
}
1003-
auto buffer = std::array<char, 3>{};
1004-
std::snprintf(buffer.data(), buffer.size(), use_uppercase ? "%02X" : "%02x", data.data_[i - 1]);
1005-
result.append(buffer.data());
1006-
}
1007-
} else {
1008-
for (auto const i: std::views::iota(0uz, data.data_.size())) {
1009-
if (result.empty() and data.data_[i] == 0 and i != 1) {
1010-
continue;
1011-
}
1012-
auto buffer = std::array<char, 3>{};
1013-
std::snprintf(buffer.data(), buffer.size(), use_uppercase ? "%02X" : "%02x", data.data_[i]);
1014-
result.append(buffer.data());
1015-
}
1016-
}
1017-
} else if (base_flag == std::ios_base::oct) {
1018-
auto temp = bigint<bits, is_signed>{data};
1019-
if (temp == std::int8_t{0}) {
1020-
result = "0";
1021-
} else {
1022-
while (temp != std::int8_t{0}) {
1023-
auto r = temp % static_cast<std::int8_t>(8);
1024-
auto digit = 0;
1025-
if constexpr (std::endian::native == std::endian::little) {
1026-
digit = r.data_[0];
1027-
} else {
1028-
digit = r.data_[r.data_.size() - 1];
1029-
}
1030-
result.push_back('0' + digit);
1031-
temp /= std::int8_t{8};
1032-
}
1033-
std::ranges::reverse(result);
1034-
}
1035-
} else if (base_flag == std::ios_base::dec) {
1036-
auto temp = bigint<bits, is_signed>{data};
1037-
auto negative = false;
1038-
if constexpr (is_signed) {
1039-
if (temp < std::int8_t{0}) {
1040-
negative = true;
1041-
temp = -temp;
1042-
}
1043-
}
1044-
if (temp == static_cast<std::int8_t>(0)) {
1045-
result = "0";
1046-
} else {
1047-
while (temp != static_cast<std::int8_t>(0)) {
1048-
auto r = temp % static_cast<std::int8_t>(10);
1049-
auto digit = 0;
1050-
if constexpr (std::endian::native == std::endian::little) {
1051-
digit = r.data_[0];
1052-
} else {
1053-
digit = r.data_[r.data_.size() - 1];
1054-
}
1055-
result.push_back('0' + digit);
1056-
temp /= static_cast<std::int8_t>(10);
1057-
}
1058-
std::ranges::reverse(result);
1059-
if (negative) {
1060-
result.insert(result.begin(), '-');
1061-
}
1062-
}
1102+
switch (base_flag) {
1103+
case std::ios_base::hex:
1104+
print_hex(os, data, use_uppercase);
1105+
break;
1106+
case std::ios_base::oct:
1107+
print_oct(os, data);
1108+
break;
1109+
case std::ios_base::dec:
1110+
default:
1111+
print_dec(os, data);
1112+
break;
10631113
}
1064-
os << result;
1114+
10651115
return os;
10661116
}
10671117

tests/io_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace {
3232
bigint::bigint<128, true> const a(-123456789);
3333
std::ostringstream oss;
3434
oss << std::hex << std::nouppercase << a;
35-
ASSERT_EQ(oss.str(), "fffffffffffffffffffffff8a432eb");
35+
ASSERT_EQ(oss.str(), "fffffffffffffffffffffffff8a432eb");
3636
}
3737

3838
TEST(bigint23, octal_unsigned_os_test) {

0 commit comments

Comments
 (0)