10#ifndef STRING_CONSUMER_HPP
11#define STRING_CONSUMER_HPP
50 static void LogError(std::string &&msg);
73 [[nodiscard]]
bool AnyBytesLeft() const noexcept {
return this->position < this->src.size(); }
84 [[nodiscard]]
bool AnyBytesRead() const noexcept {
return this->position > 0; }
95 [[nodiscard]] std::string_view
GetOrigData() const noexcept {
return this->src; }
100 [[nodiscard]] std::string_view
GetReadData() const noexcept {
return this->src.substr(0, this->position); }
105 [[nodiscard]] std::string_view
GetLeftData() const noexcept {
return this->src.substr(this->position); }
110 void SkipAll() { this->position = this->src.size(); }
116 [[nodiscard]] std::optional<uint8_t>
PeekUint8()
const;
124 if (value.has_value()) this->
SkipUint8();
136 return value.value_or(def);
150 if (!result.has_value())
return std::nullopt;
151 return static_cast<int8_t
>(*result);
160 if (value.has_value()) this->
SkipSint8();
172 return value.value_or(def);
183 [[nodiscard]] std::optional<uint16_t>
PeekUint16LE()
const;
204 return value.value_or(def);
219 if (!result.has_value())
return std::nullopt;
220 return static_cast<int16_t
>(*result);
242 return value.value_or(def);
254 [[nodiscard]] std::optional<uint32_t>
PeekUint32LE()
const;
275 return value.value_or(def);
290 if (!result.has_value())
return std::nullopt;
291 return static_cast<int32_t
>(*result);
313 return value.value_or(def);
325 [[nodiscard]] std::optional<uint64_t>
PeekUint64LE()
const;
346 return value.value_or(def);
361 if (!result.has_value())
return std::nullopt;
362 return static_cast<int64_t
>(*result);
384 return value.value_or(def);
396 [[nodiscard]] std::optional<char>
PeekChar()
const;
404 if (value.has_value()) this->
SkipChar();
415 return value.value_or(def);
426 [[nodiscard]] std::pair<size_type, char32_t>
PeekUtf8()
const;
433 auto [len, value] = this->
PeekUtf8();
434 if (len == 0)
return std::nullopt;
444 [[nodiscard]]
char32_t ReadUtf8(
char32_t def =
'?')
446 auto [len, value] = this->
PeekUtf8();
447 this->
Skip(len > 0 ? len : 1);
448 return len > 0 ? value : def;
460 this->
Skip(len > 0 ? len : 1);
468 [[nodiscard]]
bool PeekIf(std::string_view str)
const
470 return this->src.compare(this->position, str.size(), str) == 0;
477 [[nodiscard]]
bool ReadIf(std::string_view str)
479 bool result = this->
PeekIf(str);
480 if (result) this->
Skip(str.size());
489 if (this->
PeekIf(str)) this->
Skip(str.size());
499 return this->
PeekIf({&c, 1});
508 return this->
ReadIf({&c, 1});
516 return this->
SkipIf({&c, 1});
526 auto [len, result] = this->
PeekUtf8();
527 return len > 0 && result == c;
536 auto [len, result] = this->
PeekUtf8();
537 if (len == 0 || result != c)
return false;
547 auto [len, result] = this->
PeekUtf8();
548 if (len > 0 && result == c) {
566 auto result = this->
Peek(len);
567 if (len !=
npos && len != result.size()) {
568 LogError(fmt::format(
"Source buffer too short: {} > {}", len, result.size()));
570 this->
Skip(result.size());
592 return this->
Find({&c, 1});
619 [[nodiscard]] std::optional<char>
PeekCharIfIn(std::string_view chars)
const
621 assert(!chars.empty());
622 std::optional<char> c = this->
PeekChar();
623 if (c.has_value() && chars.find(*c) != std::string_view::npos)
return c;
634 if (result.has_value()) this->
Skip(1);
644 if (result.has_value()) this->
Skip(1);
654 assert(!chars.empty());
655 std::optional<char> c = this->
PeekChar();
656 if (c.has_value() && chars.find(*c) == std::string_view::npos)
return c;
667 if (result.has_value()) this->
Skip(1);
677 if (result.has_value()) this->
Skip(1);
688 return this->
Peek(len);
698 return this->
Read(len);
718 return this->
Peek(len);
728 return this->
Read(len);
766 assert(!str.empty());
768 this->
Skip(result.size());
776 while (this->
ReadIf(str)) {}
788 assert(!str.empty());
799 while (this->
ReadIf(str)) {}
866 [[nodiscard]]
static std::pair<size_type, T>
ParseIntegerBase(std::string_view
src,
int base,
bool clamp,
bool log_errors)
870 if (
src.starts_with(
"0x") ||
src.starts_with(
"0X")) {
872 if (len == 0)
return {};
873 return {len + 2, value};
877 if (std::is_signed_v<T> && (
src.starts_with(
"-0x") ||
src.starts_with(
"-0X"))) {
878 using Unsigned = std::make_unsigned_t<T>;
880 if (len == 0)
return {};
881 T value =
static_cast<T
>(0 - uvalue);
884 if (log_errors)
LogError(fmt::format(
"Integer out of range: '{}'",
src.substr(0, len + 3)));
887 value = std::numeric_limits<T>::lowest();
889 return {len + 3, value};
897 assert(base == 8 || base == 10 || base == 16);
898 auto result = std::from_chars(
src.data(),
src.data() +
src.size(), value, base);
899 auto len = result.ptr -
src.data();
900 if (result.ec == std::errc::result_out_of_range) {
902 if (log_errors)
LogError(fmt::format(
"Integer out of range: '{}'+'{}'",
src.substr(0, len),
src.substr(len, 4)));
905 if (
src.starts_with(
"-")) {
906 value = std::numeric_limits<T>::lowest();
908 value = std::numeric_limits<T>::max();
910 }
else if (result.ec != std::errc{}) {
911 if (log_errors)
LogError(fmt::format(
"Cannot parse integer: '{}'+'{}'",
src.substr(0, len),
src.substr(len, 4)));
928 [[nodiscard]] std::pair<size_type, T>
PeekIntegerBase(
int base,
bool clamp =
false)
const
945 if (len == 0)
return std::nullopt;
963 auto [len, value] =
ParseIntegerBase<T>(this->src.substr(this->position), base, clamp,
true);
965 return len > 0 ? value : def;
987template <
class T = u
int32_t>
988static inline std::optional<T>
ParseInteger(std::string_view arg,
int base = 10,
bool clamp =
false)
Parse data from a string / buffer.
bool ReadCharIf(char c)
Check whether the next 8-bit char matches 'c', and skip it.
std::string_view Peek(size_type len) const
Peek the next 'len' bytes.
std::optional< T > TryReadIntegerBase(int base, bool clamp=false)
Try to read and parse an integer in number 'base', and then advance the reader.
void SkipUint64LE()
Skip binary uint64, and advance reader.
bool PeekUtf8If(char32_t c) const
Check whether the next UTF-8 char matches 'c'.
std::string_view GetOrigData() const noexcept
Get the original data, as passed to the constructor.
void SkipCharIfIn(std::string_view chars)
If the next 8-bit char is in 'chars', skip it.
std::optional< uint32_t > TryReadUint32LE()
Try to read binary uint32, and then advance reader.
char32_t ReadUtf8(char32_t def='?')
Read UTF-8 character, and advance reader.
void SkipCharIf(char c)
If the next data matches the 8-bit char 'c', then skip it.
std::string_view ReadUntilChar(char c, SeparatorUsage sep)
Read data until the first occurrence of 8-bit char 'c', and advance reader.
size_type FindChar(char c) const
Find first occurrence of 8-bit char 'c'.
void SkipUntilChar(char c, SeparatorUsage sep)
Skip data until the first occurrence of 8-bit char 'c'.
SeparatorUsage
Treatment of separator characters.
@ READ_ALL_SEPARATORS
Read all consecutive separators, and include them all in the result.
@ READ_ONE_SEPARATOR
Read one separator, and include it in the result.
@ SKIP_ALL_SEPARATORS
Read and discard all consecutive separators, do not include any in the result.
@ SKIP_ONE_SEPARATOR
Read and discard one separator, do not include it in the result.
@ KEEP_SEPARATOR
Keep the separator in the data as next value to be read.
int16_t ReadSint16LE(int16_t def=0)
Read binary int16 using little endian, and advance reader.
void SkipUtf8()
Skip UTF-8 character, and advance reader.
std::optional< char > PeekCharIfIn(std::string_view chars) const
Check whether the next 8-bit char is in 'chars'.
StringConsumer(std::string_view src)
Construct parser with data from string.
char ReadChar(char def='?')
Read 8-bit character, and advance reader.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
uint8_t ReadUint8(uint8_t def=0)
Read binary uint8, and advance reader.
bool PeekCharIf(char c) const
Check whether the next 8-bit char matches 'c'.
std::optional< uint32_t > PeekUint32LE() const
Peek binary uint32 using little endian.
std::optional< uint16_t > TryReadUint16LE()
Try to read binary uint16, and then advance reader.
size_type GetBytesLeft() const noexcept
Get number of bytes left to read.
static std::pair< size_type, T > ParseIntegerBase(std::string_view src, int base, bool clamp, bool log_errors)
Parse an integer from the given string.
std::string_view ReadUntilCharNotIn(std::string_view chars)
Read 8-bit chars, while they are in 'chars', until they are not; and advance reader.
std::string_view PeekUntil(std::string_view str, SeparatorUsage sep) const
Peek data until the first occurrence of 'str'.
std::optional< char > PeekCharIfNotIn(std::string_view chars) const
Check whether the next 8-bit char is not in 'chars'.
void SkipUntilCharIn(std::string_view chars)
Skip 8-bit chars, while they are not in 'chars', until they are.
std::string_view::size_type size_type
The type of the size of our strings.
int8_t ReadSint8(int8_t def=0)
Read binary int8, and advance reader.
void SkipUint16LE()
Skip binary uint16, and advance reader.
std::optional< int8_t > PeekSint8() const
Peek binary int8.
uint16_t ReadUint16LE(uint16_t def=0)
Read binary uint16 using little endian, and advance reader.
void SkipChar()
Skip 8-bit character, and advance reader.
static const std::string_view WHITESPACE_OR_NEWLINE
ASCII whitespace characters, including new-line.
void SkipSint8()
Skip binary int8.
void SkipUint32LE()
Skip binary uint32, and advance reader.
std::string_view ReadUntil(std::string_view str, SeparatorUsage sep)
Read data until the first occurrence of 'str', and advance reader.
void SkipUntil(std::string_view str, SeparatorUsage sep)
Skip data until the first occurrence of 'str'.
int64_t ReadSint64LE(int64_t def=0)
Read binary int64 using little endian, and advance reader.
static const std::string_view WHITESPACE_NO_NEWLINE
ASCII whitespace characters, excluding new-line.
std::optional< char > PeekChar() const
Peek 8-bit character.
static void LogError(std::string &&msg)
Log an error in the processing (too small buffer, integer out of range, etc.).
std::optional< int16_t > PeekSint16LE() const
Peek binary int16 using little endian.
std::pair< size_type, T > PeekIntegerBase(int base, bool clamp=false) const
Peek and parse an integer in number 'base'.
std::optional< char > ReadCharIfIn(std::string_view chars)
Read next 8-bit char, check whether it is in 'chars', and advance reader.
std::optional< char > TryReadChar()
Try to read a 8-bit character, and then advance reader.
bool ReadIf(std::string_view str)
Check whether the next data matches 'str', and skip it.
std::string_view PeekUntilUtf8(char32_t c, SeparatorUsage sep) const
Peek data until the first occurrence of UTF-8 char 'c'.
std::optional< uint64_t > PeekUint64LE() const
Peek binary uint64 using little endian.
void SkipUntilCharNotIn(std::string_view chars)
Skip 8-bit chars, while they are in 'chars', until they are not.
bool AnyBytesRead() const noexcept
Check whether any bytes were already read.
std::optional< int32_t > TryReadSint32LE()
Try to read binary int32, and then advance reader.
void SkipUint8()
Skip binary uint8.
void SkipAll()
Discard all remaining data.
StringConsumer(const std::string &src)
Construct parser with data from string.
std::string_view ReadUntilCharIn(std::string_view chars)
Read 8-bit chars, while they are not in 'chars', until they are; and advance reader.
uint32_t ReadUint32LE(uint32_t def=0)
Read binary uint32 using little endian, and advance reader.
std::optional< int32_t > PeekSint32LE() const
Peek binary int32 using little endian.
std::optional< char32_t > TryReadUtf8()
Try to read a UTF-8 character, and then advance reader.
int32_t ReadSint32LE(int32_t def=0)
Read binary int32 using little endian, and advance reader.
void SkipCharIfNotIn(std::string_view chars)
If the next 8-bit char is not in 'chars', skip it.
size_type FindCharNotIn(std::string_view chars) const
Find first occurrence of any 8-bit char not in 'chars'.
void SkipIf(std::string_view str)
If the next data matches 'str', then skip it.
std::optional< uint16_t > PeekUint16LE() const
Peek binary uint16 using little endian.
std::optional< uint64_t > TryReadUint64LE()
Try to read binary uint64, and then advance reader.
size_type position
The current parsing position in the string.
std::optional< uint8_t > TryReadUint8()
Try to read binary uint8, and then advance reader.
T ReadIntegerBase(int base, T def=0, bool clamp=false)
Read and parse an integer in number 'base', and advance the reader.
std::string_view Read(size_type len)
Read the next 'len' bytes, and advance reader.
std::string_view src
The string to parse.
bool ReadUtf8If(char32_t c)
Check whether the next UTF-8 char matches 'c', and skip it.
void SkipIntegerBase(int base)
Skip an integer in number 'base'.
void SkipSint64LE()
Skip binary int64, and advance reader.
std::string_view GetReadData() const noexcept
Get already read data.
std::optional< int64_t > PeekSint64LE() const
Peek binary int64 using little endian.
static constexpr size_type npos
Special value for "end of data".
std::string_view GetLeftData() const noexcept
Get data left to read.
size_type GetBytesRead() const noexcept
Get number of already read bytes.
void Skip(size_type len)
Discard some bytes.
size_type Find(std::string_view str) const
Find first occurrence of 'str'.
bool PeekIf(std::string_view str) const
Check whether the next data matches 'str'.
std::optional< uint8_t > PeekUint8() const
Peek binary uint8.
std::string_view PeekUntilChar(char c, SeparatorUsage sep) const
Peek data until the first occurrence of 8-bit char 'c'.
void SkipUtf8If(char32_t c)
If the next data matches the UTF-8 char 'c', then skip it.
uint64_t ReadUint64LE(uint64_t def=0)
Read binary uint64 using little endian, and advance reader.
std::optional< int64_t > TryReadSint64LE()
Try to read binary int64, and then advance reader.
void SkipSint32LE()
Skip binary int32, and advance reader.
std::string_view PeekUntilCharIn(std::string_view chars) const
Peek 8-bit chars, while they are not in 'chars', until they are.
std::optional< char > ReadCharIfNotIn(std::string_view chars)
Read next 8-bit char, check whether it is not in 'chars', and advance reader.
void SkipUntilUtf8(char32_t c, SeparatorUsage sep)
Skip data until the first occurrence of UTF-8 char 'c'.
std::optional< int8_t > TryReadSint8()
Try to read binary int8, and then advance reader.
void SkipSint16LE()
Skip binary int16, and advance reader.
StringConsumer(std::span< const char > src)
Construct parser with data from span.
std::string_view ReadUntilUtf8(char32_t c, SeparatorUsage sep)
Read data until the first occurrence of UTF-8 char 'c', and advance reader.
std::string_view PeekUntilCharNotIn(std::string_view chars) const
Peek 8-bit chars, while they are in 'chars', until they are not.
std::pair< size_type, char32_t > PeekUtf8() const
Peek UTF-8 character.
size_type FindCharIn(std::string_view chars) const
Find first occurrence of any 8-bit char in 'chars'.
std::optional< int16_t > TryReadSint16LE()
Try to read binary int16, and then advance reader.
size_type FindUtf8(char32_t c) const
Find first occurrence of UTF-8 char 'c'.
static std::optional< T > ParseInteger(std::string_view arg, int base=10, bool clamp=false)
Change a string into its number representation.