1
0
mirror of https://github.com/OpenTTD/OpenTTD synced 2026-01-17 09:22:42 +01:00

Codechange: Add StringConsumer.

This commit is contained in:
frosch
2025-03-27 18:15:56 +01:00
committed by frosch
parent 4721a1f93e
commit 800d6e339d
7 changed files with 1583 additions and 0 deletions

View File

@@ -0,0 +1,196 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file string_consumer.cpp Implementation of string parsing. */
#include "../stdafx.h"
#include "string_consumer.hpp"
#include "bitmath_func.hpp"
#include "utf8.hpp"
#include "string_builder.hpp"
#include "../string_func.h"
#if defined(STRGEN) || defined(SETTINGSGEN)
#include "../error_func.h"
#else
#include "../debug.h"
#endif
#include "../safeguards.h"
const std::string_view StringConsumer::WHITESPACE_NO_NEWLINE = "\t\v\f\r ";
const std::string_view StringConsumer::WHITESPACE_OR_NEWLINE = "\t\n\v\f\r ";
/* static */ void StringConsumer::LogError(std::string &&msg)
{
#if defined(STRGEN) || defined(SETTINGSGEN)
FatalErrorI(std::move(msg));
#else
DebugPrint("misc", 0, std::move(msg));
#endif
}
std::optional<uint8_t> StringConsumer::PeekUint8() const
{
if (this->GetBytesLeft() < 1) return std::nullopt;
return static_cast<uint8_t>(this->src[this->position]);
}
std::optional<uint16_t> StringConsumer::PeekUint16LE() const
{
if (this->GetBytesLeft() < 2) return std::nullopt;
return static_cast<uint8_t>(this->src[this->position]) |
static_cast<uint8_t>(this->src[this->position + 1]) << 8;
}
std::optional<uint32_t> StringConsumer::PeekUint32LE() const
{
if (this->GetBytesLeft() < 4) return std::nullopt;
return static_cast<uint8_t>(this->src[this->position]) |
static_cast<uint8_t>(this->src[this->position + 1]) << 8 |
static_cast<uint8_t>(this->src[this->position + 2]) << 16 |
static_cast<uint8_t>(this->src[this->position + 3]) << 24;
}
std::optional<uint64_t> StringConsumer::PeekUint64LE() const
{
if (this->GetBytesLeft() < 8) return std::nullopt;
return static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position])) |
static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 1])) << 8 |
static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 2])) << 16 |
static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 3])) << 24 |
static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 4])) << 32 |
static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 5])) << 40 |
static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 6])) << 48 |
static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 7])) << 56;
}
std::optional<char> StringConsumer::PeekChar() const
{
auto result = this->PeekUint8();
if (!result.has_value()) return {};
return static_cast<char>(*result);
}
std::pair<StringConsumer::size_type, char32_t> StringConsumer::PeekUtf8() const
{
auto buf = this->src.substr(this->position);
return DecodeUtf8(buf);
}
std::string_view StringConsumer::Peek(size_type len) const
{
auto buf = this->src.substr(this->position);
if (len == std::string_view::npos) {
len = buf.size();
} else if (len > buf.size()) {
len = buf.size();
}
return buf.substr(0, len);
}
void StringConsumer::Skip(size_type len)
{
if (len == std::string_view::npos) {
this->position = this->src.size();
} else if (size_type max_len = GetBytesLeft(); len > max_len) {
LogError(fmt::format("Source buffer too short: {} > {}", len, max_len));
this->position = this->src.size();
} else {
this->position += len;
}
}
StringConsumer::size_type StringConsumer::Find(std::string_view str) const
{
assert(!str.empty());
auto buf = this->src.substr(this->position);
return buf.find(str);
}
StringConsumer::size_type StringConsumer::FindUtf8(char32_t c) const
{
auto [data, len] = EncodeUtf8(c);
return this->Find({data, len});
}
StringConsumer::size_type StringConsumer::FindCharIn(std::string_view chars) const
{
assert(!chars.empty());
auto buf = this->src.substr(this->position);
return buf.find_first_of(chars);
}
StringConsumer::size_type StringConsumer::FindCharNotIn(std::string_view chars) const
{
assert(!chars.empty());
auto buf = this->src.substr(this->position);
return buf.find_first_not_of(chars);
}
std::string_view StringConsumer::PeekUntil(std::string_view str, SeparatorUsage sep) const
{
assert(!str.empty());
auto buf = this->src.substr(this->position);
auto len = buf.find(str);
if (len != std::string_view::npos) {
switch (sep) {
case READ_ONE_SEPARATOR:
if (buf.compare(len, str.size(), str) == 0) len += str.size();
break;
case READ_ALL_SEPARATORS:
while (buf.compare(len, str.size(), str) == 0) len += str.size();
break;
default:
break;
}
}
return buf.substr(0, len);
}
std::string_view StringConsumer::PeekUntilUtf8(char32_t c, SeparatorUsage sep) const
{
auto [data, len] = EncodeUtf8(c);
return PeekUntil({data, len}, sep);
}
std::string_view StringConsumer::ReadUntilUtf8(char32_t c, SeparatorUsage sep)
{
auto [data, len] = EncodeUtf8(c);
return ReadUntil({data, len}, sep);
}
void StringConsumer::SkipUntilUtf8(char32_t c, SeparatorUsage sep)
{
auto [data, len] = EncodeUtf8(c);
return SkipUntil({data, len}, sep);
}
void StringConsumer::SkipIntegerBase(int base)
{
this->SkipIf("-");
if (base == 0) {
if (this->ReadIf("0x") || this->ReadIf("0X")) { // boolean short-circuit ensures only one prefix is read
base = 16;
} else {
base = 10;
}
}
switch (base) {
default:
assert(false);
break;
case 10:
this->SkipUntilCharNotIn("0123456789");
break;
case 16:
this->SkipUntilCharNotIn("0123456789abcdefABCDEF");
break;
}
}