Branch data Line data Source code
1 : : // Copyright (c) 2026 M. A. Chatterjee
2 : : // SPDX-License-Identifier: BSD-2-Clause
3 : :
4 : : #include "triepack/triepack.hpp"
5 : :
6 : : #include <cstdlib>
7 : : #include <cstring>
8 : :
9 : : extern "C" {
10 : : #include "triepack/triepack.h"
11 : : }
12 : :
13 : : namespace triepack
14 : : {
15 : :
16 : : const size_t kMaxAlphabetSize = TP_MAX_ALPHABET_SIZE;
17 : :
18 : 3 : const char *message(Status status)
19 : : {
20 : 3 : return tp_result_str(static_cast<tp_result>(status));
21 : : }
22 : :
23 : 1 : VersionInfo version()
24 : : {
25 : : /* Same numbers the C library reports; only the implementation differs. */
26 [ + - ]: 1 : tp_version_info c = tp_version();
27 : : VersionInfo v;
28 : 1 : v.name = c.name;
29 : 1 : v.implementation = "c++";
30 : 1 : v.version = c.version;
31 : 1 : v.version_major = c.version_major;
32 : 1 : v.version_minor = c.version_minor;
33 : 1 : v.version_patch = c.version_patch;
34 : 1 : v.format_version_major = c.format_version_major;
35 : 1 : v.format_version_minor = c.format_version_minor;
36 : 1 : v.max_alphabet_size = c.max_alphabet_size;
37 : 2 : return v;
38 : : }
39 : :
40 : : namespace
41 : : {
42 : :
43 : 412 : Status to_status(tp_result rc)
44 : : {
45 : 412 : return static_cast<Status>(rc);
46 : : }
47 : :
48 : 49 : Type to_type(tp_value_type t)
49 : : {
50 [ + + + + : 49 : switch (t) {
+ + + + ]
51 : 4 : case TP_BOOL:
52 : 4 : return Type::Bool;
53 : 3 : case TP_INT:
54 : 3 : return Type::Int;
55 : 27 : case TP_UINT:
56 : 27 : return Type::UInt;
57 : 1 : case TP_FLOAT32:
58 : 1 : return Type::Float32;
59 : 3 : case TP_FLOAT64:
60 : 3 : return Type::Float64;
61 : 3 : case TP_STRING:
62 : 3 : return Type::String;
63 : 3 : case TP_BLOB:
64 : 3 : return Type::Blob;
65 : 5 : default:
66 : 5 : return Type::Null;
67 : : }
68 : : }
69 : :
70 : : /// Convert a decoded C value into an owning Value.
71 : 49 : Value from_c(const tp_value &v)
72 : : {
73 [ + + + + : 49 : switch (to_type(v.type)) {
+ + + + ]
74 : 4 : case Type::Bool:
75 : 4 : return Value::boolean(v.data.bool_val);
76 : 3 : case Type::Int:
77 : 3 : return Value::integer(v.data.int_val);
78 : 27 : case Type::UInt:
79 : 27 : return Value::unsigned_integer(v.data.uint_val);
80 : 1 : case Type::Float32:
81 : 1 : return Value::float32(v.data.float32_val);
82 : 3 : case Type::Float64:
83 : 3 : return Value::float64(v.data.float64_val);
84 : 3 : case Type::String:
85 [ + - + - ]: 6 : return Value::string(std::string(v.data.string_val.str, v.data.string_val.str_len));
86 : 3 : case Type::Blob:
87 : 3 : return Value::blob(v.data.blob_val.data, v.data.blob_val.len);
88 : 5 : default:
89 : 5 : return Value::null();
90 : : }
91 : : }
92 : :
93 : : /// Build a C value borrowing this Value's storage; it must outlive the call.
94 : 319 : tp_value to_c(const Value &v)
95 : : {
96 [ + + + + : 319 : switch (v.type()) {
+ + + + ]
97 : 2 : case Type::Bool:
98 : 2 : return tp_value_bool(v.as_bool());
99 : 3 : case Type::Int:
100 : 3 : return tp_value_int(v.as_int());
101 : 46 : case Type::UInt:
102 : 46 : return tp_value_uint(v.as_uint());
103 : 1 : case Type::Float32:
104 : 1 : return tp_value_float32(v.as_float32());
105 : 2 : case Type::Float64:
106 : 2 : return tp_value_float64(v.as_float64());
107 : 2 : case Type::String:
108 : 2 : return tp_value_string_n(v.as_string().data(), v.as_string().size());
109 : 2 : case Type::Blob:
110 [ - + ]: 4 : return tp_value_blob(v.as_blob().empty() ? reinterpret_cast<const uint8_t *>("")
111 : 2 : : v.as_blob().data(),
112 : 4 : v.as_blob().size());
113 : 261 : default:
114 : 261 : return tp_value_null();
115 : : }
116 : : }
117 : :
118 : 3 : const std::string &empty_string()
119 : : {
120 [ + + + - ]: 3 : static const std::string s;
121 : 3 : return s;
122 : : }
123 : :
124 : 1 : const std::vector<uint8_t> &empty_blob()
125 : : {
126 [ + - + - ]: 1 : static const std::vector<uint8_t> b;
127 : 1 : return b;
128 : : }
129 : :
130 : : } // namespace
131 : :
132 : : // ---------------------------------------------------------------------------
133 : : // Value
134 : : // ---------------------------------------------------------------------------
135 : :
136 : 463 : Value::Value() : type_(Type::Null), bool_(false), int_(0), uint_(0), f32_(0.0f), f64_(0.0) {}
137 : :
138 : 300 : Value Value::null()
139 : : {
140 : 300 : return Value();
141 : : }
142 : :
143 : 9 : Value Value::boolean(bool v)
144 : : {
145 : 9 : Value out;
146 : 9 : out.type_ = Type::Bool;
147 : 9 : out.bool_ = v;
148 : 9 : return out;
149 : : }
150 : :
151 : 14 : Value Value::integer(int64_t v)
152 : : {
153 : 14 : Value out;
154 : 14 : out.type_ = Type::Int;
155 : 14 : out.int_ = v;
156 : 14 : return out;
157 : : }
158 : :
159 : 76 : Value Value::unsigned_integer(uint64_t v)
160 : : {
161 : 76 : Value out;
162 : 76 : out.type_ = Type::UInt;
163 : 76 : out.uint_ = v;
164 : 76 : return out;
165 : : }
166 : :
167 : 4 : Value Value::float32(float v)
168 : : {
169 : 4 : Value out;
170 : 4 : out.type_ = Type::Float32;
171 : 4 : out.f32_ = v;
172 : 4 : return out;
173 : : }
174 : :
175 : 9 : Value Value::float64(double v)
176 : : {
177 : 9 : Value out;
178 : 9 : out.type_ = Type::Float64;
179 : 9 : out.f64_ = v;
180 : 9 : return out;
181 : : }
182 : :
183 : 11 : Value Value::string(const std::string &v)
184 : : {
185 : 11 : Value out;
186 : 11 : out.type_ = Type::String;
187 [ + - ]: 11 : out.str_ = v;
188 : 11 : return out;
189 : 0 : }
190 : :
191 : 11 : Value Value::blob(const std::vector<uint8_t> &v)
192 : : {
193 : 11 : Value out;
194 : 11 : out.type_ = Type::Blob;
195 [ + - ]: 11 : out.blob_ = v;
196 : 11 : return out;
197 : 0 : }
198 : :
199 : 3 : Value Value::blob(const uint8_t *data, size_t size)
200 : : {
201 : 3 : Value out;
202 : 3 : out.type_ = Type::Blob;
203 [ + - + - ]: 3 : if (data && size > 0)
204 [ + - ]: 3 : out.blob_.assign(data, data + size);
205 : 3 : return out;
206 : 0 : }
207 : :
208 : 7 : bool Value::as_bool() const
209 : : {
210 [ + + + - ]: 7 : return type_ == Type::Bool ? bool_ : false;
211 : : }
212 : :
213 : 7 : int64_t Value::as_int() const
214 : : {
215 [ + + ]: 7 : return type_ == Type::Int ? int_ : 0;
216 : : }
217 : :
218 : 57 : uint64_t Value::as_uint() const
219 : : {
220 [ + + ]: 57 : return type_ == Type::UInt ? uint_ : 0;
221 : : }
222 : :
223 : 2 : float Value::as_float32() const
224 : : {
225 [ + - ]: 2 : return type_ == Type::Float32 ? f32_ : 0.0f;
226 : : }
227 : :
228 : 5 : double Value::as_float64() const
229 : : {
230 [ + - ]: 5 : return type_ == Type::Float64 ? f64_ : 0.0;
231 : : }
232 : :
233 : 10 : const std::string &Value::as_string() const
234 : : {
235 [ + + ]: 10 : return type_ == Type::String ? str_ : empty_string();
236 : : }
237 : :
238 : 10 : const std::vector<uint8_t> &Value::as_blob() const
239 : : {
240 [ + + ]: 10 : return type_ == Type::Blob ? blob_ : empty_blob();
241 : : }
242 : :
243 : 16 : bool Value::operator==(const Value &other) const
244 : : {
245 [ + + ]: 16 : if (type_ != other.type_)
246 : 2 : return false;
247 [ + + + + : 14 : switch (type_) {
+ + + +
- ]
248 : 1 : case Type::Null:
249 : 1 : return true;
250 : 1 : case Type::Bool:
251 : 1 : return bool_ == other.bool_;
252 : 2 : case Type::Int:
253 : 2 : return int_ == other.int_;
254 : 1 : case Type::UInt:
255 : 1 : return uint_ == other.uint_;
256 : 1 : case Type::Float32:
257 : : /* Bit pattern, so -0.0 differs from 0.0 and NaN payloads are kept. */
258 : 1 : return std::memcmp(&f32_, &other.f32_, sizeof(f32_)) == 0;
259 : 2 : case Type::Float64:
260 : 2 : return std::memcmp(&f64_, &other.f64_, sizeof(f64_)) == 0;
261 : 2 : case Type::String:
262 : 2 : return str_ == other.str_;
263 : 4 : case Type::Blob:
264 : 4 : return blob_ == other.blob_;
265 : : }
266 : 0 : return false;
267 : : }
268 : :
269 : : // ---------------------------------------------------------------------------
270 : : // Encoder
271 : : // ---------------------------------------------------------------------------
272 : :
273 : 25 : Encoder::Encoder() : handle_(nullptr)
274 : : {
275 : 25 : tp_encoder_create(&handle_);
276 : 25 : }
277 : :
278 : 27 : Encoder::~Encoder()
279 : : {
280 : 27 : tp_encoder_destroy(&handle_);
281 : 27 : }
282 : :
283 : 2 : Encoder::Encoder(Encoder &&other) noexcept : handle_(other.handle_)
284 : : {
285 : 2 : other.handle_ = nullptr;
286 : 2 : }
287 : :
288 : 2 : Encoder &Encoder::operator=(Encoder &&other) noexcept
289 : : {
290 [ + + ]: 2 : if (this != &other) {
291 : 1 : tp_encoder_destroy(&handle_);
292 : 1 : handle_ = other.handle_;
293 : 1 : other.handle_ = nullptr;
294 : : }
295 : 2 : return *this;
296 : : }
297 : :
298 : 319 : Status Encoder::add(const std::string &key, const Value &value)
299 : : {
300 : 319 : return add(key.data(), key.size(), value);
301 : : }
302 : :
303 : 322 : Status Encoder::add(const char *key, size_t key_len, const Value &value)
304 : : {
305 [ + + + + ]: 322 : if (!handle_ || !key)
306 : 3 : return Status::InvalidParam;
307 [ + - ]: 319 : tp_value v = to_c(value);
308 [ + - ]: 319 : return to_status(tp_encoder_add_n(handle_, key, key_len, &v));
309 : : }
310 : :
311 : 8 : size_t Encoder::count() const
312 : : {
313 [ + - ]: 8 : return handle_ ? tp_encoder_count(handle_) : 0;
314 : : }
315 : :
316 : 19 : Status Encoder::build(std::vector<uint8_t> &out)
317 : : {
318 : 19 : out.clear();
319 [ + + ]: 19 : if (!handle_)
320 : 1 : return Status::InvalidParam;
321 : :
322 : 18 : uint8_t *buf = nullptr;
323 : 18 : size_t len = 0;
324 [ + - ]: 18 : tp_result rc = tp_encoder_build(handle_, &buf, &len);
325 [ + + ]: 18 : if (rc != TP_OK)
326 : 1 : return to_status(rc);
327 : :
328 [ + - ]: 17 : out.assign(buf, buf + len);
329 : 17 : free(buf);
330 : 17 : return Status::Ok;
331 : : }
332 : :
333 : 2 : Status Encoder::reset()
334 : : {
335 [ + + ]: 2 : if (!handle_)
336 : 1 : return Status::InvalidParam;
337 : 1 : return to_status(tp_encoder_reset(handle_));
338 : : }
339 : :
340 : : // ---------------------------------------------------------------------------
341 : : // Dict
342 : : // ---------------------------------------------------------------------------
343 : :
344 : 2 : Dict::Dict() : handle_(nullptr), status_(Status::InvalidParam) {}
345 : :
346 : 16 : Dict::Dict(const uint8_t *data, size_t size) : handle_(nullptr), status_(Status::InvalidParam)
347 : : {
348 : 16 : open(data, size);
349 : 16 : }
350 : :
351 : 20 : Dict::~Dict()
352 : : {
353 : 20 : tp_dict_close(&handle_);
354 : 20 : }
355 : :
356 : 2 : Dict::Dict(Dict &&other) noexcept : handle_(other.handle_), status_(other.status_)
357 : : {
358 : 2 : other.handle_ = nullptr;
359 : 2 : other.status_ = Status::InvalidParam;
360 : 2 : }
361 : :
362 : 2 : Dict &Dict::operator=(Dict &&other) noexcept
363 : : {
364 [ + + ]: 2 : if (this != &other) {
365 : 1 : tp_dict_close(&handle_);
366 : 1 : handle_ = other.handle_;
367 : 1 : status_ = other.status_;
368 : 1 : other.handle_ = nullptr;
369 : 1 : other.status_ = Status::InvalidParam;
370 : : }
371 : 2 : return *this;
372 : : }
373 : :
374 : 16 : Status Dict::open(const uint8_t *data, size_t size)
375 : : {
376 : 16 : tp_dict_close(&handle_);
377 : 16 : status_ = to_status(tp_dict_open(&handle_, data, size));
378 [ + + ]: 16 : if (status_ != Status::Ok)
379 : 1 : handle_ = nullptr;
380 : 16 : return status_;
381 : : }
382 : :
383 : 20 : Status Dict::lookup(const std::string &key, Value &out) const
384 : : {
385 : 20 : return lookup(key.data(), key.size(), out);
386 : : }
387 : :
388 : 21 : Status Dict::lookup(const char *key, size_t key_len, Value &out) const
389 : : {
390 : 21 : out = Value::null();
391 [ + + - + ]: 21 : if (!handle_ || !key)
392 : 1 : return Status::InvalidParam;
393 : : tp_value v;
394 [ + - ]: 20 : tp_result rc = tp_dict_lookup_n(handle_, key, key_len, &v);
395 [ + + ]: 20 : if (rc == TP_OK)
396 [ + - ]: 17 : out = from_c(v);
397 : 20 : return to_status(rc);
398 : : }
399 : :
400 : 2 : bool Dict::contains(const std::string &key) const
401 : : {
402 : 2 : Value ignored;
403 [ + - ]: 4 : return lookup(key, ignored) == Status::Ok;
404 : 2 : }
405 : :
406 : 5 : size_t Dict::size() const
407 : : {
408 [ + + ]: 5 : return handle_ ? tp_dict_count(handle_) : 0;
409 : : }
410 : :
411 : : // ---------------------------------------------------------------------------
412 : : // Iterator
413 : : // ---------------------------------------------------------------------------
414 : :
415 : 8 : Iterator::Iterator(const Dict &dict) : handle_(nullptr), status_(Status::InvalidParam)
416 : : {
417 [ + - ]: 8 : status_ = to_status(tp_dict_iterate(dict.handle(), &handle_));
418 : 8 : }
419 : :
420 : 4 : Iterator::Iterator(const Dict &dict, const std::string &prefix)
421 : 4 : : handle_(nullptr), status_(Status::InvalidParam)
422 : : {
423 [ + - ]: 4 : status_ = to_status(tp_dict_find_prefix(dict.handle(), prefix.c_str(), &handle_));
424 : 4 : }
425 : :
426 : 14 : Iterator::~Iterator()
427 : : {
428 : 14 : tp_iter_destroy(&handle_);
429 : 14 : }
430 : :
431 : 2 : Iterator::Iterator(Iterator &&other) noexcept
432 : 2 : : handle_(other.handle_), status_(other.status_), key_(std::move(other.key_)),
433 : 2 : value_(std::move(other.value_))
434 : : {
435 : 2 : other.handle_ = nullptr;
436 : 2 : other.status_ = Status::InvalidParam;
437 : 2 : }
438 : :
439 : 2 : Iterator &Iterator::operator=(Iterator &&other) noexcept
440 : : {
441 [ + + ]: 2 : if (this != &other) {
442 : 1 : tp_iter_destroy(&handle_);
443 : 1 : handle_ = other.handle_;
444 : 1 : status_ = other.status_;
445 : 1 : key_ = std::move(other.key_);
446 : 1 : value_ = std::move(other.value_);
447 : 1 : other.handle_ = nullptr;
448 : 1 : other.status_ = Status::InvalidParam;
449 : : }
450 : 2 : return *this;
451 : : }
452 : :
453 : 43 : bool Iterator::next()
454 : : {
455 [ + + ]: 43 : if (!handle_) {
456 : 1 : return false;
457 : : }
458 : 42 : const char *key = nullptr;
459 : 42 : size_t key_len = 0;
460 : : tp_value val;
461 [ + - ]: 42 : tp_result rc = tp_iter_next(handle_, &key, &key_len, &val);
462 : 42 : status_ = to_status(rc);
463 [ + + ]: 42 : if (rc != TP_OK) {
464 : 10 : key_.clear();
465 : 10 : value_ = Value::null();
466 : 10 : return false;
467 : : }
468 : : /* The C iterator's key buffer is reused, so copy it out. */
469 [ + - ]: 32 : key_.assign(key, key_len);
470 [ + - ]: 32 : value_ = from_c(val);
471 : 32 : return true;
472 : : }
473 : :
474 : 2 : Status Iterator::reset()
475 : : {
476 [ + + ]: 2 : if (!handle_)
477 : 1 : return Status::InvalidParam;
478 : 1 : key_.clear();
479 : 1 : value_ = Value::null();
480 : 1 : status_ = to_status(tp_iter_reset(handle_));
481 : 1 : return status_;
482 : : }
483 : :
484 : : } // namespace triepack
|