OpenTTD Source 20251213-master-g1091fa6071
string_consumer.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../stdafx.h"
11#include "string_consumer.hpp"
12
13#include "utf8.hpp"
14#include "string_builder.hpp"
15
16#include "../string_func.h"
17
18#if defined(STRGEN) || defined(SETTINGSGEN)
19#include "../error_func.h"
20#else
21#include "../debug.h"
22#endif
23
24#include "../safeguards.h"
25
26/* static */ const std::string_view StringConsumer::WHITESPACE_NO_NEWLINE = "\t\v\f\r ";
27/* static */ const std::string_view StringConsumer::WHITESPACE_OR_NEWLINE = "\t\n\v\f\r ";
28
29/* static */ void StringConsumer::LogError(std::string &&msg)
30{
31#if defined(STRGEN) || defined(SETTINGSGEN)
32 FatalErrorI(std::move(msg));
33#else
34 DebugPrint("misc", 0, std::move(msg));
35#endif
36}
37
38std::optional<uint8_t> StringConsumer::PeekUint8() const
39{
40 if (this->GetBytesLeft() < 1) return std::nullopt;
41 return static_cast<uint8_t>(this->src[this->position]);
42}
43
44std::optional<uint16_t> StringConsumer::PeekUint16LE() const
45{
46 if (this->GetBytesLeft() < 2) return std::nullopt;
47 return static_cast<uint8_t>(this->src[this->position]) |
48 static_cast<uint8_t>(this->src[this->position + 1]) << 8;
49}
50
51std::optional<uint32_t> StringConsumer::PeekUint32LE() const
52{
53 if (this->GetBytesLeft() < 4) return std::nullopt;
54 return static_cast<uint8_t>(this->src[this->position]) |
55 static_cast<uint8_t>(this->src[this->position + 1]) << 8 |
56 static_cast<uint8_t>(this->src[this->position + 2]) << 16 |
57 static_cast<uint8_t>(this->src[this->position + 3]) << 24;
58}
59
60std::optional<uint64_t> StringConsumer::PeekUint64LE() const
61{
62 if (this->GetBytesLeft() < 8) return std::nullopt;
63 return static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position])) |
64 static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 1])) << 8 |
65 static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 2])) << 16 |
66 static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 3])) << 24 |
67 static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 4])) << 32 |
68 static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 5])) << 40 |
69 static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 6])) << 48 |
70 static_cast<uint64_t>(static_cast<uint8_t>(this->src[this->position + 7])) << 56;
71}
72
73std::optional<char> StringConsumer::PeekChar() const
74{
75 auto result = this->PeekUint8();
76 if (!result.has_value()) return {};
77 return static_cast<char>(*result);
78}
79
80std::pair<StringConsumer::size_type, char32_t> StringConsumer::PeekUtf8() const
81{
82 auto buf = this->src.substr(this->position);
83 return DecodeUtf8(buf);
84}
85
86std::string_view StringConsumer::Peek(size_type len) const
87{
88 auto buf = this->src.substr(this->position);
89 if (len == std::string_view::npos) {
90 len = buf.size();
91 } else if (len > buf.size()) {
92 len = buf.size();
93 }
94 return buf.substr(0, len);
95}
96
97void StringConsumer::Skip(size_type len)
98{
99 if (len == std::string_view::npos) {
100 this->position = this->src.size();
101 } else if (size_type max_len = GetBytesLeft(); len > max_len) {
102 LogError(fmt::format("Source buffer too short: {} > {}", len, max_len));
103 this->position = this->src.size();
104 } else {
105 this->position += len;
106 }
107}
108
109StringConsumer::size_type StringConsumer::Find(std::string_view str) const
110{
111 assert(!str.empty());
112 auto buf = this->src.substr(this->position);
113 return buf.find(str);
114}
115
116StringConsumer::size_type StringConsumer::FindUtf8(char32_t c) const
117{
118 auto [data, len] = EncodeUtf8(c);
119 return this->Find({data, len});
120}
121
122StringConsumer::size_type StringConsumer::FindCharIn(std::string_view chars) const
123{
124 assert(!chars.empty());
125 auto buf = this->src.substr(this->position);
126 return buf.find_first_of(chars);
127}
128
129StringConsumer::size_type StringConsumer::FindCharNotIn(std::string_view chars) const
130{
131 assert(!chars.empty());
132 auto buf = this->src.substr(this->position);
133 return buf.find_first_not_of(chars);
134}
135
136std::string_view StringConsumer::PeekUntil(std::string_view str, SeparatorUsage sep) const
137{
138 assert(!str.empty());
139 auto buf = this->src.substr(this->position);
140 auto len = buf.find(str);
141 if (len != std::string_view::npos) {
142 switch (sep) {
144 if (buf.compare(len, str.size(), str) == 0) len += str.size();
145 break;
147 while (buf.compare(len, str.size(), str) == 0) len += str.size();
148 break;
149 default:
150 break;
151 }
152 }
153 return buf.substr(0, len);
154}
155
156std::string_view StringConsumer::PeekUntilUtf8(char32_t c, SeparatorUsage sep) const
157{
158 auto [data, len] = EncodeUtf8(c);
159 return PeekUntil({data, len}, sep);
160}
161
162std::string_view StringConsumer::ReadUntilUtf8(char32_t c, SeparatorUsage sep)
163{
164 auto [data, len] = EncodeUtf8(c);
165 return ReadUntil({data, len}, sep);
166}
167
169{
170 auto [data, len] = EncodeUtf8(c);
171 return SkipUntil({data, len}, sep);
172}
173
175{
176 this->SkipIf("-");
177 if (base == 0) {
178 if (this->ReadIf("0x") || this->ReadIf("0X")) { // boolean short-circuit ensures only one prefix is read
179 base = 16;
180 } else {
181 base = 10;
182 }
183 }
184 switch (base) {
185 default:
186 assert(false);
187 break;
188 case 8:
189 this->SkipUntilCharNotIn("01234567");
190 break;
191 case 10:
192 this->SkipUntilCharNotIn("0123456789");
193 break;
194 case 16:
195 this->SkipUntilCharNotIn("0123456789abcdefABCDEF");
196 break;
197 }
198}
std::string_view Peek(size_type len) const
Peek the next 'len' bytes.
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.
std::optional< uint32_t > PeekUint32LE() const
Peek binary uint32 using little endian.
size_type GetBytesLeft() const noexcept
Get number of bytes left to read.
std::string_view PeekUntil(std::string_view str, SeparatorUsage sep) const
Peek data until the first occurrence of 'str'.
static const std::string_view WHITESPACE_OR_NEWLINE
ASCII whitespace characters, including new-line.
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'.
static const std::string_view WHITESPACE_NO_NEWLINE
ASCII whitespace characters, excluding new-line.
std::optional< char > PeekChar() const
Peek 8-bit character.
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.
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.
void SkipIntegerBase(int base)
Skip an integer in number 'base'.
void Skip(size_type len)
Discard some bytes.
size_type Find(std::string_view str) const
Find first occurrence of 'str'.
std::optional< uint8_t > PeekUint8() const
Peek binary uint8.
void SkipUntilUtf8(char32_t c, SeparatorUsage sep)
Skip data until the first occurrence of UTF-8 char 'c'.
std::string_view ReadUntilUtf8(char32_t c, SeparatorUsage sep)
Read data until the first occurrence of UTF-8 char 'c', and advance reader.
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'.
size_type FindUtf8(char32_t c) const
Find first occurrence of UTF-8 char 'c'.
std::pair< size_t, char32_t > DecodeUtf8(std::string_view buf)
Decode a character from UTF-8.
Definition utf8.cpp:48
std::pair< char[4], size_t > EncodeUtf8(char32_t c)
Encode a character to UTF-8.
Definition utf8.cpp:21
void DebugPrint(std::string_view category, int level, std::string &&message)
Internal function for outputting the debug line.
Definition debug.cpp:110
void FatalErrorI(const std::string &str)
Error handling for fatal non-user errors.
Definition openttd.cpp:140
Compose strings from textual and binary data.
Parse strings.
Handling of UTF-8 encoded data.