SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
KV1.h
Go to the documentation of this file.
1#pragma once
2
3#include <concepts>
4#include <string>
5#include <string_view>
6#include <vector>
7
8#include <BufferStream.h>
10#include <sourcepp/String.h>
11
12namespace kvpp {
13
14template<typename V>
15concept KV1ValueType = std::convertible_to<V, std::string_view>
16 || std::same_as<V, bool>
17 || std::same_as<V, int32_t>
18 || std::same_as<V, int64_t>
19 || std::same_as<V, float>;
20
21template<typename S = std::string_view>
22requires std::convertible_to<S, std::string_view>
24public:
26 [[nodiscard]] std::string_view getKey() const {
27 return this->key;
28 }
29
31 [[nodiscard]] std::string_view getValue() const {
32 return this->value;
33 }
34
36 template<KV1ValueType V>
37 [[nodiscard]] V getValue() const {
38 if constexpr (std::convertible_to<V, std::string_view>) {
39 return this->value;
40 } else if constexpr (std::same_as<V, bool>) {
41 return static_cast<bool>(this->getValue<int32_t>());
42 } else if constexpr (std::same_as<V, int32_t> || std::same_as<V, int64_t>) {
43 V out = 0;
44 if (this->value.length() == 10 && this->value.starts_with("0x") && sourcepp::parser::text::isNumber(this->value.substr(2))) {
45 sourcepp::string::toInt(this->value.substr(2), out, 16);
46 } else {
48 }
49 return out;
50 } else if constexpr (std::same_as<V, float>) {
51 float out = 0.f;
53 return out;
54 }
55 return V{};
56 }
57
59 [[nodiscard]] std::string_view getConditional() const {
60 return this->conditional;
61 }
62
64 [[nodiscard]] bool hasChild(std::string_view childKey) const {
65 return !this->operator[](childKey).isInvalid();
66 }
67
69 [[nodiscard]] uint64_t getChildCount() const {
70 return this->children.size();
71 }
72
74 [[nodiscard]] uint64_t getChildCount(std::string_view childKey) const {
75 uint64_t count = 0;
76 for (const auto& element : this->children) {
77 if (sourcepp::string::iequals(element.key, childKey)) {
78 ++count;
79 }
80 }
81 return count;
82 }
83
85 [[nodiscard]] const std::vector<KV1ElementReadable>& getChildren() const {
86 return this->children;
87 }
88
89 using iterator = std::vector<KV1ElementReadable>::iterator;
90
91 [[nodiscard]] constexpr iterator begin() {
92 return this->children.begin();
93 }
94
95 [[nodiscard]] constexpr iterator end() {
96 return this->children.end();
97 }
98
99 using const_iterator = std::vector<KV1ElementReadable>::const_iterator;
100
101 [[nodiscard]] constexpr const_iterator begin() const {
102 return this->children.begin();
103 }
104
105 [[nodiscard]] constexpr const_iterator end() const {
106 return this->children.end();
107 }
108
109 [[nodiscard]] constexpr const_iterator cbegin() const {
110 return this->children.cbegin();
111 }
112
113 [[nodiscard]] constexpr const_iterator cend() const {
114 return this->children.cend();
115 }
116
118 [[nodiscard]] const KV1ElementReadable& operator[](unsigned int n) const {
119 return this->children.at(n);
120 }
121
123 [[nodiscard]] const KV1ElementReadable& operator[](std::string_view childKey) const {
124 return this->operator()(childKey);
125 }
126
128 [[nodiscard]] const KV1ElementReadable& operator()(std::string_view childKey) const {
129 for (const auto& element : this->children) {
130 if (sourcepp::string::iequals(element.getKey(), childKey)) {
131 return element;
132 }
133 }
134 return getInvalid();
135 }
136
138 [[nodiscard]] const KV1ElementReadable& operator()(std::string_view childKey, unsigned int n) const {
139 unsigned int count = 0;
140 for (const auto& element : this->children) {
141 if (sourcepp::string::iequals(element.getKey(), childKey)) {
142 if (count == n) {
143 return element;
144 }
145 if (++count > n) {
146 break;
147 }
148 }
149 }
150 return getInvalid();
151 }
152
154 [[nodiscard]] bool isInvalid() const {
155 return this == &getInvalid();
156 }
157
159 static KV1ElementReadable element;
160 return element;
161 }
162
163 [[nodiscard]] explicit operator bool() const {
164 return !this->isInvalid();
165 }
166
167protected:
169
170 static void read(BufferStreamReadOnly& stream, BufferStream& backing, std::vector<KV1ElementReadable>& elements, const sourcepp::parser::text::EscapeSequenceMap& escapeSequences) {
171 using namespace sourcepp;
172 while (true) {
173 // Check if the block is over
175 if (stream.peek<char>() == '}') {
176 stream.skip();
177 break;
178 }
179 // Read key
180 {
181 elements.push_back(KV1ElementReadable{});
182 elements.back().key = parser::text::readStringToBuffer(stream, backing, parser::text::DEFAULT_STRING_START, parser::text::DEFAULT_STRING_END, escapeSequences);
184 }
185 // Read value
186 if (stream.peek<char>() != '{') {
187 elements.back().value = parser::text::readStringToBuffer(stream, backing, parser::text::DEFAULT_STRING_START, parser::text::DEFAULT_STRING_END, escapeSequences);
189 }
190 // Read conditional
191 if (stream.peek<char>() == '[') {
192 elements.back().conditional = parser::text::readStringToBuffer(stream, backing, "[", "]", escapeSequences);
194 }
195 // Read block
196 if (stream.peek<char>() == '{') {
197 stream.skip();
199 if (stream.peek<char>() != '}') {
200 KV1ElementReadable::read(stream, backing, elements.back().children, escapeSequences);
201 } else {
202 stream.skip();
203 }
204 }
205 }
206 }
207
208 S key = ""; // NOLINT(*-redundant-string-init)
209 S value = ""; // NOLINT(*-redundant-string-init)
210 S conditional = ""; // NOLINT(*-redundant-string-init)
211 std::vector<KV1ElementReadable> children;
212};
213
214template<typename S = std::string_view>
215requires std::convertible_to<S, std::string_view>
216class KV1 : public KV1ElementReadable<S> {
217public:
218 explicit KV1(std::string_view kv1Data, bool useEscapeSequences_ = false)
219 : KV1ElementReadable<S>()
220 , useEscapeSequences(useEscapeSequences_) {
221 if (kv1Data.empty()) {
222 return;
223 }
224 BufferStreamReadOnly stream{kv1Data};
225
226 // Multiply by 2 to ensure buffer will have enough space (very generous)
227 this->backingData.resize(kv1Data.size() * 2);
228 BufferStream backing{this->backingData, false};
229 try {
230 KV1ElementReadable<S>::read(stream, backing, this->children, sourcepp::parser::text::getDefaultEscapeSequencesOrNone(this->useEscapeSequences));
231 } catch (const std::overflow_error&) {}
232 }
233
234protected:
238
239 std::string backingData;
241};
242
243namespace literals {
244
245inline KV1<> operator""_kv1(const char* str, const std::size_t len) {
246 return KV1{std::string_view{str, len}};
247}
248
249} // namespace literals
250
251} // namespace kvpp
std::string_view getConditional() const
Get the conditional associated with the element.
Definition KV1.h:59
const KV1ElementReadable & operator[](std::string_view childKey) const
Get the first child element of the element with the given key.
Definition KV1.h:123
constexpr const_iterator cend() const
Definition KV1.h:113
V getValue() const
Get the value associated with the element as the given type.
Definition KV1.h:37
const KV1ElementReadable & operator()(std::string_view childKey, unsigned int n) const
Get the nth child element of the element with the given key.
Definition KV1.h:138
const KV1ElementReadable & operator()(std::string_view childKey) const
Get the first child element of the element with the given key.
Definition KV1.h:128
std::vector< KV1ElementReadable > children
Definition KV1.h:211
std::string_view getKey() const
Get the key associated with the element.
Definition KV1.h:26
bool isInvalid() const
Check if the given element is invalid.
Definition KV1.h:154
constexpr const_iterator cbegin() const
Definition KV1.h:109
static const KV1ElementReadable & getInvalid()
Definition KV1.h:158
uint64_t getChildCount() const
Get the number of child elements.
Definition KV1.h:69
bool hasChild(std::string_view childKey) const
Check if the element has one or more children with the given key.
Definition KV1.h:64
std::vector< KV1ElementReadable >::const_iterator const_iterator
Definition KV1.h:99
static void read(BufferStreamReadOnly &stream, BufferStream &backing, std::vector< KV1ElementReadable > &elements, const sourcepp::parser::text::EscapeSequenceMap &escapeSequences)
Definition KV1.h:170
std::vector< KV1ElementReadable >::iterator iterator
Definition KV1.h:89
constexpr const_iterator end() const
Definition KV1.h:105
const std::vector< KV1ElementReadable > & getChildren() const
Get the child elements of the element.
Definition KV1.h:85
uint64_t getChildCount(std::string_view childKey) const
Get the number of child elements with the given key.
Definition KV1.h:74
constexpr iterator end()
Definition KV1.h:95
constexpr const_iterator begin() const
Definition KV1.h:101
constexpr iterator begin()
Definition KV1.h:91
std::string_view getValue() const
Get the value associated with the element.
Definition KV1.h:31
const KV1ElementReadable & operator[](unsigned int n) const
Get the child element of the element at the given index.
Definition KV1.h:118
bool useEscapeSequences
Definition KV1.h:240
KV1(std::string_view kv1Data, bool useEscapeSequences_=false)
Definition KV1.h:218
std::string backingData
Definition KV1.h:239
Definition DMX.h:13
std::unordered_map< char, char > EscapeSequenceMap
Definition Text.h:17
void eatWhitespaceAndSingleLineComments(BufferStream &stream, std::string_view singleLineCommentStart=DEFAULT_SINGLE_LINE_COMMENT_START)
Eat all whitespace and single line comments after the current stream position.
Definition Text.cpp:108
constexpr std::string_view DEFAULT_STRING_END
Definition Text.h:15
constexpr std::string_view DEFAULT_STRING_START
Definition Text.h:14
std::string_view readStringToBuffer(BufferStream &stream, BufferStream &backing, std::string_view start=DEFAULT_STRING_START, std::string_view end=DEFAULT_STRING_END, const EscapeSequenceMap &escapeSequences=getDefaultEscapeSequences())
Read a string starting at the current stream position.
Definition Text.cpp:180
bool isNumber(char c)
If a char is a numerical character (0-9).
Definition Text.cpp:48
std::from_chars_result toFloat(std::string_view number, std::floating_point auto &out)
Definition String.h:85
std::from_chars_result toInt(std::string_view number, std::integral auto &out, int base=10)
Definition String.h:81
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:61