Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5ccca74

Browse files
authored
Merge pull request #1714 from evoskuil/master
Add read_line to stream readers.
2 parents 1a2adb4 + 2604f46 commit 5ccca74

File tree

7 files changed

+391
-5
lines changed

7 files changed

+391
-5
lines changed

include/bitcoin/system/impl/stream/streamers/byte_reader.ipp

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,21 @@ std::string byte_reader<IStream>::read_string_buffer(size_t size) NOEXCEPT
499499
return out;
500500
}
501501

502+
template <typename IStream>
503+
std::string byte_reader<IStream>::read_line(const std::string& end) NOEXCEPT
504+
{
505+
// This leaves current position after terminator, zero sized if invalid.
506+
const auto size = line_length(end);
507+
if (is_zero(size))
508+
return {};
509+
510+
std::string out(size, '\0');
511+
do_rewind_bytes(size + end.size());
512+
do_read_bytes(pointer_cast<uint8_t>(out.data()), size);
513+
do_skip_bytes(end.size());
514+
return out;
515+
}
516+
502517
// streams
503518
// ----------------------------------------------------------------------------
504519

@@ -635,7 +650,7 @@ byte_allocator& byte_reader<IStream>::get_allocator() const NOEXCEPT
635650

636651
// protected virtual
637652
// ----------------------------------------------------------------------------
638-
// These may only call non-virtual (private) methods (due to overriding).
653+
// These may only call non-virtual methods (due to protected overriding).
639654

640655
template <typename IStream>
641656
uint8_t byte_reader<IStream>::do_peek_byte() NOEXCEPT
@@ -730,7 +745,7 @@ bool byte_reader<IStream>::get_exhausted() const NOEXCEPT
730745

731746
// private
732747
// ----------------------------------------------------------------------------
733-
// These may only call other private methods (due to overriding).
748+
// These may only call other private methods (due to protected overriding).
734749

735750
template <typename IStream>
736751
bool byte_reader<IStream>::valid() const NOEXCEPT
@@ -803,7 +818,7 @@ bool byte_reader<IStream>::limiter(size_t size) NOEXCEPT
803818
if (size > remaining_)
804819
{
805820
// Does not reset the current position or the remaining limit.
806-
invalidate();
821+
invalid();
807822
return true;
808823
}
809824

@@ -835,6 +850,60 @@ void byte_reader<IStream>::seeker(typename IStream::pos_type offset) NOEXCEPT
835850
}
836851
}
837852

853+
// private
854+
// ----------------------------------------------------------------------------
855+
// This calls virtual methods but is isolated from protected overriding.
856+
857+
template <typename IStream>
858+
size_t byte_reader<IStream>::line_length(const std::string& end) NOEXCEPT
859+
{
860+
if (!end.empty())
861+
{
862+
size_t index{};
863+
size_t length{};
864+
865+
// Count bytes to end, avoids reallocations.
866+
while (!get_exhausted())
867+
{
868+
const auto byte = read_byte();
869+
if (byte == end.at(index))
870+
{
871+
// Full match, read line.
872+
if (++index == end.size())
873+
return length;
874+
}
875+
else
876+
{
877+
if (!is_zero(index))
878+
{
879+
// Partial match, include bytes up to mismatch.
880+
length += index;
881+
882+
// Re-check current byte for new terminator start.
883+
if (byte == end.front())
884+
{
885+
index = one;
886+
}
887+
else
888+
{
889+
index = zero;
890+
++length;
891+
}
892+
}
893+
else
894+
{
895+
// Non-match, just increment.
896+
++length;
897+
}
898+
}
899+
}
900+
}
901+
902+
// Line terminator not found.
903+
invalid();
904+
return {};
905+
}
906+
838907
BC_POP_WARNING()
839908

840909
} // namespace system

include/bitcoin/system/stream/streamers/byte_reader.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ class byte_reader
144144
/// This is only used for reading Bitcoin heading command text.
145145
std::string read_string_buffer(size_t size) NOEXCEPT override;
146146

147+
/// Read a line up to specified terminator (skip and exclude terminator).
148+
/// Returns empty and invalidates stream if terminator is not found.
149+
std::string read_line(const std::string& end="\r\n") NOEXCEPT override;
150+
147151
/// Streams.
148152
/// -----------------------------------------------------------------------
149153

@@ -215,6 +219,7 @@ class byte_reader
215219
void limit(size_t size) NOEXCEPT;
216220
bool limiter(size_t size) NOEXCEPT;
217221
void seeker(typename IStream::pos_type offset) NOEXCEPT;
222+
size_t line_length(const std::string& end) NOEXCEPT;
218223

219224
IStream& stream_;
220225
size_t remaining_;

include/bitcoin/system/stream/streamers/interfaces/bytereader.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ class bytereader
111111
/// This is only used for reading Bitcoin heading command text.
112112
virtual std::string read_string_buffer(size_t size) NOEXCEPT = 0;
113113

114+
/// Read a line up to specified terminator (skip and exclude terminator).
115+
/// Returns empty and invalidates stream if terminator is not found.
116+
virtual std::string read_line(const std::string& end="\r\n") NOEXCEPT = 0;
117+
114118
/// Streams.
115119
/// -----------------------------------------------------------------------
116120

test/stream/streamers/bit_flipper.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,83 @@ BOOST_AUTO_TEST_CASE(bit_flipper__read_string_buffer__partial_embedded_null__tru
10261026
BOOST_REQUIRE_EQUAL(stream.get(), '*');
10271027
}
10281028

1029+
// read_line
1030+
1031+
BOOST_AUTO_TEST_CASE(bit_flipper__read_line__no_terminator__invalidates)
1032+
{
1033+
const std::string value{ "hello" };
1034+
std::stringstream stream{ value };
1035+
flip::bits::iostream reader(stream);
1036+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1037+
BOOST_REQUIRE(!reader);
1038+
}
1039+
1040+
BOOST_AUTO_TEST_CASE(bit_flipper__read_line__custom_terminator__expected)
1041+
{
1042+
const std::string value{ "test|done" };
1043+
std::stringstream stream{ value };
1044+
flip::bits::iostream reader(stream);
1045+
BOOST_REQUIRE_EQUAL(reader.read_line("|"), "test");
1046+
BOOST_REQUIRE(reader);
1047+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 5u);
1048+
}
1049+
1050+
BOOST_AUTO_TEST_CASE(bit_flipper__read_line__terminator_only__expected_position_empty)
1051+
{
1052+
const std::string value{ "\r\n" };
1053+
std::stringstream stream{ value };
1054+
flip::bits::iostream reader(stream);
1055+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1056+
BOOST_REQUIRE(reader);
1057+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 2u);
1058+
}
1059+
1060+
BOOST_AUTO_TEST_CASE(bit_flipper__read_line__multiple_terminators__expected_positions_empty)
1061+
{
1062+
const std::string value{ "\r\n\r\n" };
1063+
std::stringstream stream{ value };
1064+
flip::bits::iostream reader(stream);
1065+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1066+
BOOST_REQUIRE(reader);
1067+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 2u);
1068+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1069+
BOOST_REQUIRE(reader);
1070+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 4u);
1071+
}
1072+
1073+
BOOST_AUTO_TEST_CASE(bit_flipper__read_line__text_before_terminator__returns_text)
1074+
{
1075+
const std::string value{ "hello\r\n" };
1076+
std::stringstream stream{ value };
1077+
flip::bits::iostream reader(stream);
1078+
BOOST_REQUIRE_EQUAL(reader.read_line(), "hello");
1079+
BOOST_REQUIRE(reader);
1080+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 7u);
1081+
}
1082+
1083+
BOOST_AUTO_TEST_CASE(bit_flipper__read_line__partial_match__handles_correctly)
1084+
{
1085+
const std::string value{ "\rabc\r\n" };
1086+
std::stringstream stream{ value };
1087+
flip::bits::iostream reader(stream);
1088+
BOOST_REQUIRE_EQUAL(reader.read_line(), "\rabc");
1089+
BOOST_REQUIRE(reader);
1090+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 6u);
1091+
}
1092+
1093+
BOOST_AUTO_TEST_CASE(bit_flipper__read_line__multiple_lines__returns_sequential)
1094+
{
1095+
const std::string value{ "line1\r\nline2\r\n" };
1096+
std::stringstream stream{ value };
1097+
flip::bits::iostream reader(stream);
1098+
BOOST_REQUIRE_EQUAL(reader.read_line(), "line1");
1099+
BOOST_REQUIRE(reader);
1100+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 7u);
1101+
BOOST_REQUIRE_EQUAL(reader.read_line(), "line2");
1102+
BOOST_REQUIRE(reader);
1103+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 14u);
1104+
}
1105+
10291106
#endif // BIT_FLIPPER_READER_STRINGS
10301107

10311108
#endif // BIT_FLIPPER_READER

test/stream/streamers/bit_reader.cpp

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ BC_PUSH_WARNING(NO_CASTS_FOR_ARITHMETIC_CONVERSION)
2626
// Failed get/peek reads are populated with 0x00 by the reader.
2727
constexpr uint8_t pad = 0x00;
2828

29-
// Exact copy of the byte_reader tests, replaced by read::bits::istream.
29+
// Exact copy of the bit_reader tests, replaced by read::bits::istream.
3030
#define BIT_READER_CONTEXT
3131
#define BIT_READER_BIG_ENDIAN
3232
#define BIT_READER_LITTLE_ENDIAN
@@ -1001,6 +1001,83 @@ BOOST_AUTO_TEST_CASE(bit_reader__read_string_buffer__partial_embedded_null__trun
10011001
BOOST_REQUIRE_EQUAL(stream.get(), '*');
10021002
}
10031003

1004+
// read_line
1005+
1006+
BOOST_AUTO_TEST_CASE(bit_reader__read_line__no_terminator__invalidates)
1007+
{
1008+
const std::string value{ "hello" };
1009+
std::istringstream stream{ value };
1010+
read::bits::istream reader(stream);
1011+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1012+
BOOST_REQUIRE(!reader);
1013+
}
1014+
1015+
BOOST_AUTO_TEST_CASE(bit_reader__read_line__custom_terminator__expected)
1016+
{
1017+
const std::string value{ "test|done" };
1018+
std::istringstream stream{ value };
1019+
read::bits::istream reader(stream);
1020+
BOOST_REQUIRE_EQUAL(reader.read_line("|"), "test");
1021+
BOOST_REQUIRE(reader);
1022+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 5u);
1023+
}
1024+
1025+
BOOST_AUTO_TEST_CASE(bit_reader__read_line__terminator_only__expected_position_empty)
1026+
{
1027+
const std::string value{ "\r\n" };
1028+
std::istringstream stream{ value };
1029+
read::bits::istream reader(stream);
1030+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1031+
BOOST_REQUIRE(reader);
1032+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 2u);
1033+
}
1034+
1035+
BOOST_AUTO_TEST_CASE(bit_reader__read_line__multiple_terminators__expected_positions_empty)
1036+
{
1037+
const std::string value{ "\r\n\r\n" };
1038+
std::istringstream stream{ value };
1039+
read::bits::istream reader(stream);
1040+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1041+
BOOST_REQUIRE(reader);
1042+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 2u);
1043+
BOOST_REQUIRE_EQUAL(reader.read_line(), "");
1044+
BOOST_REQUIRE(reader);
1045+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 4u);
1046+
}
1047+
1048+
BOOST_AUTO_TEST_CASE(bit_reader__read_line__text_before_terminator__returns_text)
1049+
{
1050+
const std::string value{ "hello\r\n" };
1051+
std::istringstream stream{ value };
1052+
read::bits::istream reader(stream);
1053+
BOOST_REQUIRE_EQUAL(reader.read_line(), "hello");
1054+
BOOST_REQUIRE(reader);
1055+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 7u);
1056+
}
1057+
1058+
BOOST_AUTO_TEST_CASE(bit_reader__read_line__partial_match__handles_correctly)
1059+
{
1060+
const std::string value{ "\rabc\r\n" };
1061+
std::istringstream stream{ value };
1062+
read::bits::istream reader(stream);
1063+
BOOST_REQUIRE_EQUAL(reader.read_line(), "\rabc");
1064+
BOOST_REQUIRE(reader);
1065+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 6u);
1066+
}
1067+
1068+
BOOST_AUTO_TEST_CASE(bit_reader__read_line__multiple_lines__returns_sequential)
1069+
{
1070+
const std::string value{ "line1\r\nline2\r\n" };
1071+
std::istringstream stream{ value };
1072+
read::bits::istream reader(stream);
1073+
BOOST_REQUIRE_EQUAL(reader.read_line(), "line1");
1074+
BOOST_REQUIRE(reader);
1075+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 7u);
1076+
BOOST_REQUIRE_EQUAL(reader.read_line(), "line2");
1077+
BOOST_REQUIRE(reader);
1078+
BOOST_REQUIRE_EQUAL(reader.get_read_position(), 14u);
1079+
}
1080+
10041081
#endif // BIT_READER_STRINGS
10051082

10061083
BC_POP_WARNING()

0 commit comments

Comments
 (0)