1 /*
2 * Copyright (c) 2019, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <cstdint>
20 #include <functional>
21 #include <iterator>
22 #include <memory>
23 #include <numeric>
24 #include <string>
25 #include <vector>
26
27 namespace cppbor {
28
29 enum MajorType : uint8_t {
30 UINT = 0 << 5,
31 NINT = 1 << 5,
32 BSTR = 2 << 5,
33 TSTR = 3 << 5,
34 ARRAY = 4 << 5,
35 MAP = 5 << 5,
36 SEMANTIC = 6 << 5,
37 SIMPLE = 7 << 5,
38 };
39
40 enum SimpleType {
41 BOOLEAN,
42 NULL_T, // Only two supported, as yet.
43 };
44
45 enum SpecialAddlInfoValues : uint8_t {
46 FALSE = 20,
47 TRUE = 21,
48 NULL_V = 22,
49 ONE_BYTE_LENGTH = 24,
50 TWO_BYTE_LENGTH = 25,
51 FOUR_BYTE_LENGTH = 26,
52 EIGHT_BYTE_LENGTH = 27,
53 };
54
55 class Item;
56 class Uint;
57 class Nint;
58 class Int;
59 class Tstr;
60 class Bstr;
61 class Simple;
62 class Bool;
63 class Array;
64 class Map;
65 class Null;
66 class Semantic;
67
68 /**
69 * Returns the size of a CBOR header that contains the additional info value addlInfo.
70 */
71 size_t headerSize(uint64_t addlInfo);
72
73 /**
74 * Encodes a CBOR header with the specified type and additional info into the range [pos, end).
75 * Returns a pointer to one past the last byte written, or nullptr if there isn't sufficient space
76 * to write the header.
77 */
78 uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end);
79
80 using EncodeCallback = std::function<void(uint8_t)>;
81
82 /**
83 * Encodes a CBOR header with the specified type and additional info, passing each byte in turn to
84 * encodeCallback.
85 */
86 void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback);
87
88 /**
89 * Encodes a CBOR header with the specified type and additional info, writing each byte to the
90 * provided OutputIterator.
91 */
92 template <typename OutputIterator,
93 typename = std::enable_if_t<std::is_base_of_v<
94 std::output_iterator_tag,
95 typename std::iterator_traits<OutputIterator>::iterator_category>>>
encodeHeader(MajorType type,uint64_t addlInfo,OutputIterator iter)96 void encodeHeader(MajorType type, uint64_t addlInfo, OutputIterator iter) {
97 return encodeHeader(type, addlInfo, [&](uint8_t v) { *iter++ = v; });
98 }
99
100 /**
101 * Item represents a CBOR-encodeable data item. Item is an abstract interface with a set of virtual
102 * methods that allow encoding of the item or conversion to the appropriate derived type.
103 */
104 class Item {
105 public:
~Item()106 virtual ~Item() {}
107
108 /**
109 * Returns the CBOR type of the item.
110 */
111 virtual MajorType type() const = 0;
112
113 // These methods safely downcast an Item to the appropriate subclass.
asInt()114 virtual const Int* asInt() const { return nullptr; }
asUint()115 virtual const Uint* asUint() const { return nullptr; }
asNint()116 virtual const Nint* asNint() const { return nullptr; }
asTstr()117 virtual const Tstr* asTstr() const { return nullptr; }
asBstr()118 virtual const Bstr* asBstr() const { return nullptr; }
asSimple()119 virtual const Simple* asSimple() const { return nullptr; }
asMap()120 virtual const Map* asMap() const { return nullptr; }
asArray()121 virtual const Array* asArray() const { return nullptr; }
asSemantic()122 virtual const Semantic* asSemantic() const { return nullptr; }
123
124 /**
125 * Returns true if this is a "compound" item, i.e. one that contains one or more other items.
126 */
isCompound()127 virtual bool isCompound() const { return false; }
128
129 bool operator==(const Item& other) const&;
130 bool operator!=(const Item& other) const& { return !(*this == other); }
131
132 /**
133 * Returns the number of bytes required to encode this Item into CBOR. Note that if this is a
134 * complex Item, calling this method will require walking the whole tree.
135 */
136 virtual size_t encodedSize() const = 0;
137
138 /**
139 * Encodes the Item into buffer referenced by range [*pos, end). Returns a pointer to one past
140 * the last position written. Returns nullptr if there isn't enough space to encode.
141 */
142 virtual uint8_t* encode(uint8_t* pos, const uint8_t* end) const = 0;
143
144 /**
145 * Encodes the Item by passing each encoded byte to encodeCallback.
146 */
147 virtual void encode(EncodeCallback encodeCallback) const = 0;
148
149 /**
150 * Clones the Item
151 */
152 virtual std::unique_ptr<Item> clone() const = 0;
153
154 /**
155 * Encodes the Item into the provided OutputIterator.
156 */
157 template <typename OutputIterator,
158 typename = typename std::iterator_traits<OutputIterator>::iterator_category>
encode(OutputIterator i)159 void encode(OutputIterator i) const {
160 return encode([&](uint8_t v) { *i++ = v; });
161 }
162
163 /**
164 * Encodes the Item into a new std::vector<uint8_t>.
165 */
encode()166 std::vector<uint8_t> encode() const {
167 std::vector<uint8_t> retval;
168 retval.reserve(encodedSize());
169 encode(std::back_inserter(retval));
170 return retval;
171 }
172
173 /**
174 * Encodes the Item into a new std::string.
175 */
toString()176 std::string toString() const {
177 std::string retval;
178 retval.reserve(encodedSize());
179 encode([&](uint8_t v) { retval.push_back(v); });
180 return retval;
181 }
182
183 /**
184 * Encodes only the header of the Item.
185 */
encodeHeader(uint64_t addlInfo,uint8_t * pos,const uint8_t * end)186 inline uint8_t* encodeHeader(uint64_t addlInfo, uint8_t* pos, const uint8_t* end) const {
187 return ::cppbor::encodeHeader(type(), addlInfo, pos, end);
188 }
189
190 /**
191 * Encodes only the header of the Item.
192 */
encodeHeader(uint64_t addlInfo,EncodeCallback encodeCallback)193 inline void encodeHeader(uint64_t addlInfo, EncodeCallback encodeCallback) const {
194 ::cppbor::encodeHeader(type(), addlInfo, encodeCallback);
195 }
196 };
197
198 /**
199 * Int is an abstraction that allows Uint and Nint objects to be manipulated without caring about
200 * the sign.
201 */
202 class Int : public Item {
203 public:
204 bool operator==(const Int& other) const& { return value() == other.value(); }
205
206 virtual int64_t value() const = 0;
207
asInt()208 const Int* asInt() const override { return this; }
209 };
210
211 /**
212 * Uint is a concrete Item that implements CBOR major type 0.
213 */
214 class Uint : public Int {
215 public:
216 static constexpr MajorType kMajorType = UINT;
217
Uint(uint64_t v)218 explicit Uint(uint64_t v) : mValue(v) {}
219
220 bool operator==(const Uint& other) const& { return mValue == other.mValue; }
221
type()222 MajorType type() const override { return kMajorType; }
asUint()223 const Uint* asUint() const override { return this; }
224
encodedSize()225 size_t encodedSize() const override { return headerSize(mValue); }
226
value()227 int64_t value() const override { return mValue; }
unsignedValue()228 uint64_t unsignedValue() const { return mValue; }
229
230 using Item::encode;
encode(uint8_t * pos,const uint8_t * end)231 uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
232 return encodeHeader(mValue, pos, end);
233 }
encode(EncodeCallback encodeCallback)234 void encode(EncodeCallback encodeCallback) const override {
235 encodeHeader(mValue, encodeCallback);
236 }
237
clone()238 virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Uint>(mValue); }
239
240 private:
241 uint64_t mValue;
242 };
243
244 /**
245 * Nint is a concrete Item that implements CBOR major type 1.
246
247 * Note that it is incapable of expressing the full range of major type 1 values, becaue it can only
248 * express values that fall into the range [std::numeric_limits<int64_t>::min(), -1]. It cannot
249 * express values in the range [std::numeric_limits<int64_t>::min() - 1,
250 * -std::numeric_limits<uint64_t>::max()].
251 */
252 class Nint : public Int {
253 public:
254 static constexpr MajorType kMajorType = NINT;
255
256 explicit Nint(int64_t v);
257
258 bool operator==(const Nint& other) const& { return mValue == other.mValue; }
259
type()260 MajorType type() const override { return kMajorType; }
asNint()261 const Nint* asNint() const override { return this; }
encodedSize()262 size_t encodedSize() const override { return headerSize(addlInfo()); }
263
value()264 int64_t value() const override { return mValue; }
265
266 using Item::encode;
encode(uint8_t * pos,const uint8_t * end)267 uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
268 return encodeHeader(addlInfo(), pos, end);
269 }
encode(EncodeCallback encodeCallback)270 void encode(EncodeCallback encodeCallback) const override {
271 encodeHeader(addlInfo(), encodeCallback);
272 }
273
clone()274 virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Nint>(mValue); }
275
276 private:
addlInfo()277 uint64_t addlInfo() const { return -1ll - mValue; }
278
279 int64_t mValue;
280 };
281
282 /**
283 * Bstr is a concrete Item that implements major type 2.
284 */
285 class Bstr : public Item {
286 public:
287 static constexpr MajorType kMajorType = BSTR;
288
289 // Construct from a vector
Bstr(std::vector<uint8_t> v)290 explicit Bstr(std::vector<uint8_t> v) : mValue(std::move(v)) {}
291
292 // Construct from a string
Bstr(const std::string & v)293 explicit Bstr(const std::string& v)
294 : mValue(reinterpret_cast<const uint8_t*>(v.data()),
295 reinterpret_cast<const uint8_t*>(v.data()) + v.size()) {}
296
297 // Construct from a pointer/size pair
Bstr(const std::pair<const uint8_t *,size_t> & buf)298 explicit Bstr(const std::pair<const uint8_t*, size_t>& buf)
299 : mValue(buf.first, buf.first + buf.second) {}
300
301 // Construct from a pair of iterators
302 template <typename I1, typename I2,
303 typename = typename std::iterator_traits<I1>::iterator_category,
304 typename = typename std::iterator_traits<I2>::iterator_category>
Bstr(const std::pair<I1,I2> & pair)305 explicit Bstr(const std::pair<I1, I2>& pair) : mValue(pair.first, pair.second) {}
306
307 // Construct from an iterator range.
308 template <typename I1, typename I2,
309 typename = typename std::iterator_traits<I1>::iterator_category,
310 typename = typename std::iterator_traits<I2>::iterator_category>
Bstr(I1 begin,I2 end)311 Bstr(I1 begin, I2 end) : mValue(begin, end) {}
312
313 bool operator==(const Bstr& other) const& { return mValue == other.mValue; }
314
type()315 MajorType type() const override { return kMajorType; }
asBstr()316 const Bstr* asBstr() const override { return this; }
encodedSize()317 size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); }
318 using Item::encode;
319 uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
encode(EncodeCallback encodeCallback)320 void encode(EncodeCallback encodeCallback) const override {
321 encodeHeader(mValue.size(), encodeCallback);
322 encodeValue(encodeCallback);
323 }
324
value()325 const std::vector<uint8_t>& value() const { return mValue; }
326
clone()327 virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Bstr>(mValue); }
328
329 private:
330 void encodeValue(EncodeCallback encodeCallback) const;
331
332 std::vector<uint8_t> mValue;
333 };
334
335 /**
336 * Bstr is a concrete Item that implements major type 3.
337 */
338 class Tstr : public Item {
339 public:
340 static constexpr MajorType kMajorType = TSTR;
341
342 // Construct from a string
Tstr(std::string v)343 explicit Tstr(std::string v) : mValue(std::move(v)) {}
344
345 // Construct from a string_view
Tstr(const std::string_view & v)346 explicit Tstr(const std::string_view& v) : mValue(v) {}
347
348 // Construct from a C string
Tstr(const char * v)349 explicit Tstr(const char* v) : mValue(std::string(v)) {}
350
351 // Construct from a pair of iterators
352 template <typename I1, typename I2,
353 typename = typename std::iterator_traits<I1>::iterator_category,
354 typename = typename std::iterator_traits<I2>::iterator_category>
Tstr(const std::pair<I1,I2> & pair)355 explicit Tstr(const std::pair<I1, I2>& pair) : mValue(pair.first, pair.second) {}
356
357 // Construct from an iterator range
358 template <typename I1, typename I2,
359 typename = typename std::iterator_traits<I1>::iterator_category,
360 typename = typename std::iterator_traits<I2>::iterator_category>
Tstr(I1 begin,I2 end)361 Tstr(I1 begin, I2 end) : mValue(begin, end) {}
362
363 bool operator==(const Tstr& other) const& { return mValue == other.mValue; }
364
type()365 MajorType type() const override { return kMajorType; }
asTstr()366 const Tstr* asTstr() const override { return this; }
encodedSize()367 size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); }
368 using Item::encode;
369 uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
encode(EncodeCallback encodeCallback)370 void encode(EncodeCallback encodeCallback) const override {
371 encodeHeader(mValue.size(), encodeCallback);
372 encodeValue(encodeCallback);
373 }
374
value()375 const std::string& value() const { return mValue; }
376
clone()377 virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Tstr>(mValue); }
378
379 private:
380 void encodeValue(EncodeCallback encodeCallback) const;
381
382 std::string mValue;
383 };
384
385 /**
386 * CompoundItem is an abstract Item that provides common functionality for Items that contain other
387 * items, i.e. Arrays (CBOR type 4) and Maps (CBOR type 5).
388 */
389 class CompoundItem : public Item {
390 public:
391 bool operator==(const CompoundItem& other) const&;
392
size()393 virtual size_t size() const { return mEntries.size(); }
394
isCompound()395 bool isCompound() const override { return true; }
396
encodedSize()397 size_t encodedSize() const override {
398 return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()),
399 [](size_t sum, auto& entry) { return sum + entry->encodedSize(); });
400 }
401
402 using Item::encode; // Make base versions visible.
403 uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
404 void encode(EncodeCallback encodeCallback) const override;
405
406 virtual uint64_t addlInfo() const = 0;
407
408 protected:
409 std::vector<std::unique_ptr<Item>> mEntries;
410 };
411
412 /*
413 * Array is a concrete Item that implements CBOR major type 4.
414 *
415 * Note that Arrays are not copyable. This is because copying them is expensive and making them
416 * move-only ensures that they're never copied accidentally. If you actually want to copy an Array,
417 * use the clone() method.
418 */
419 class Array : public CompoundItem {
420 public:
421 static constexpr MajorType kMajorType = ARRAY;
422
423 Array() = default;
424 Array(const Array& other) = delete;
425 Array(Array&&) = default;
426 Array& operator=(const Array&) = delete;
427 Array& operator=(Array&&) = default;
428
429 /**
430 * Construct an Array from a variable number of arguments of different types. See
431 * details::makeItem below for details on what types may be provided. In general, this accepts
432 * all of the types you'd expect and doest the things you'd expect (integral values are addes as
433 * Uint or Nint, std::string and char* are added as Tstr, bools are added as Bool, etc.).
434 */
435 template <typename... Args, typename Enable>
436 Array(Args&&... args);
437
438 /**
439 * Append a single element to the Array, of any compatible type.
440 */
441 template <typename T>
442 Array& add(T&& v) &;
443 template <typename T>
444 Array&& add(T&& v) &&;
445
446 const std::unique_ptr<Item>& operator[](size_t index) const { return mEntries[index]; }
447 std::unique_ptr<Item>& operator[](size_t index) { return mEntries[index]; }
448
type()449 MajorType type() const override { return kMajorType; }
asArray()450 const Array* asArray() const override { return this; }
451
452 virtual std::unique_ptr<Item> clone() const override;
453
addlInfo()454 uint64_t addlInfo() const override { return size(); }
455 };
456
457 /*
458 * Map is a concrete Item that implements CBOR major type 5.
459 *
460 * Note that Maps are not copyable. This is because copying them is expensive and making them
461 * move-only ensures that they're never copied accidentally. If you actually want to copy a
462 * Map, use the clone() method.
463 */
464 class Map : public CompoundItem {
465 public:
466 static constexpr MajorType kMajorType = MAP;
467
468 Map() = default;
469 Map(const Map& other) = delete;
470 Map(Map&&) = default;
471 Map& operator=(const Map& other) = delete;
472 Map& operator=(Map&&) = default;
473
474 /**
475 * Construct a Map from a variable number of arguments of different types. An even number of
476 * arguments must be provided (this is verified statically). See details::makeItem below for
477 * details on what types may be provided. In general, this accepts all of the types you'd
478 * expect and doest the things you'd expect (integral values are addes as Uint or Nint,
479 * std::string and char* are added as Tstr, bools are added as Bool, etc.).
480 */
481 template <typename... Args, typename Enable>
482 Map(Args&&... args);
483
484 /**
485 * Append a key/value pair to the Map, of any compatible types.
486 */
487 template <typename Key, typename Value>
488 Map& add(Key&& key, Value&& value) &;
489 template <typename Key, typename Value>
490 Map&& add(Key&& key, Value&& value) &&;
491
size()492 size_t size() const override {
493 assertInvariant();
494 return mEntries.size() / 2;
495 }
496
497 template <typename Key, typename Enable>
498 std::pair<std::unique_ptr<Item>&, bool> get(Key key);
499
500 std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> operator[](
501 size_t index) const {
502 assertInvariant();
503 return {mEntries[index * 2], mEntries[index * 2 + 1]};
504 }
505
506 std::pair<std::unique_ptr<Item>&, std::unique_ptr<Item>&> operator[](size_t index) {
507 assertInvariant();
508 return {mEntries[index * 2], mEntries[index * 2 + 1]};
509 }
510
type()511 MajorType type() const override { return kMajorType; }
asMap()512 const Map* asMap() const override { return this; }
513
514 virtual std::unique_ptr<Item> clone() const override;
515
addlInfo()516 uint64_t addlInfo() const override { return size(); }
517
518 private:
519 void assertInvariant() const;
520 };
521
522 class Semantic : public CompoundItem {
523 public:
524 static constexpr MajorType kMajorType = SEMANTIC;
525
526 template <typename T>
527 explicit Semantic(uint64_t value, T&& child);
528
529 Semantic(const Semantic& other) = delete;
530 Semantic(Semantic&&) = default;
531 Semantic& operator=(const Semantic& other) = delete;
532 Semantic& operator=(Semantic&&) = default;
533
size()534 size_t size() const override {
535 assertInvariant();
536 return 1;
537 }
538
encodedSize()539 size_t encodedSize() const override {
540 return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(mValue),
541 [](size_t sum, auto& entry) { return sum + entry->encodedSize(); });
542 }
543
type()544 MajorType type() const override { return kMajorType; }
asSemantic()545 const Semantic* asSemantic() const override { return this; }
546
child()547 const std::unique_ptr<Item>& child() const {
548 assertInvariant();
549 return mEntries[0];
550 }
551
child()552 std::unique_ptr<Item>& child() {
553 assertInvariant();
554 return mEntries[0];
555 }
556
value()557 uint64_t value() const { return mValue; }
558
addlInfo()559 uint64_t addlInfo() const override { return value(); }
560
clone()561 virtual std::unique_ptr<Item> clone() const override {
562 assertInvariant();
563 return std::make_unique<Semantic>(mValue, mEntries[0]->clone());
564 }
565
566 protected:
567 Semantic() = default;
Semantic(uint64_t value)568 Semantic(uint64_t value) : mValue(value) {}
569 uint64_t mValue;
570
571 private:
572 void assertInvariant() const;
573 };
574
575 /**
576 * Simple is abstract Item that implements CBOR major type 7. It is intended to be subclassed to
577 * create concrete Simple types. At present only Bool is provided.
578 */
579 class Simple : public Item {
580 public:
581 static constexpr MajorType kMajorType = SIMPLE;
582
583 bool operator==(const Simple& other) const&;
584
585 virtual SimpleType simpleType() const = 0;
type()586 MajorType type() const override { return kMajorType; }
587
asSimple()588 const Simple* asSimple() const override { return this; }
589
asBool()590 virtual const Bool* asBool() const { return nullptr; };
asNull()591 virtual const Null* asNull() const { return nullptr; };
592 };
593
594 /**
595 * Bool is a concrete type that implements CBOR major type 7, with additional item values for TRUE
596 * and FALSE.
597 */
598 class Bool : public Simple {
599 public:
600 static constexpr SimpleType kSimpleType = BOOLEAN;
601
Bool(bool v)602 explicit Bool(bool v) : mValue(v) {}
603
604 bool operator==(const Bool& other) const& { return mValue == other.mValue; }
605
simpleType()606 SimpleType simpleType() const override { return kSimpleType; }
asBool()607 const Bool* asBool() const override { return this; }
608
encodedSize()609 size_t encodedSize() const override { return 1; }
610
611 using Item::encode;
encode(uint8_t * pos,const uint8_t * end)612 uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
613 return encodeHeader(mValue ? TRUE : FALSE, pos, end);
614 }
encode(EncodeCallback encodeCallback)615 void encode(EncodeCallback encodeCallback) const override {
616 encodeHeader(mValue ? TRUE : FALSE, encodeCallback);
617 }
618
value()619 bool value() const { return mValue; }
620
clone()621 virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Bool>(mValue); }
622
623 private:
624 bool mValue;
625 };
626
627 /**
628 * Null is a concrete type that implements CBOR major type 7, with additional item value for NULL
629 */
630 class Null : public Simple {
631 public:
632 static constexpr SimpleType kSimpleType = NULL_T;
633
Null()634 explicit Null() {}
635
simpleType()636 SimpleType simpleType() const override { return kSimpleType; }
asNull()637 const Null* asNull() const override { return this; }
638
encodedSize()639 size_t encodedSize() const override { return 1; }
640
641 using Item::encode;
encode(uint8_t * pos,const uint8_t * end)642 uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
643 return encodeHeader(NULL_V, pos, end);
644 }
encode(EncodeCallback encodeCallback)645 void encode(EncodeCallback encodeCallback) const override {
646 encodeHeader(NULL_V, encodeCallback);
647 }
648
clone()649 virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Null>(); }
650 };
651
652 template <typename T>
downcastItem(std::unique_ptr<Item> && v)653 std::unique_ptr<T> downcastItem(std::unique_ptr<Item>&& v) {
654 static_assert(std::is_base_of_v<Item, T> && !std::is_abstract_v<T>,
655 "returned type is not an Item or is an abstract class");
656 if (v && T::kMajorType == v->type()) {
657 if constexpr (std::is_base_of_v<Simple, T>) {
658 if (T::kSimpleType != v->asSimple()->simpleType()) {
659 return nullptr;
660 }
661 }
662 return std::unique_ptr<T>(static_cast<T*>(v.release()));
663 } else {
664 return nullptr;
665 }
666 }
667
668 /**
669 * Details. Mostly you shouldn't have to look below, except perhaps at the docstring for makeItem.
670 */
671 namespace details {
672
673 template <typename T, typename V, typename Enable = void>
674 struct is_iterator_pair_over : public std::false_type {};
675
676 template <typename I1, typename I2, typename V>
677 struct is_iterator_pair_over<
678 std::pair<I1, I2>, V,
679 typename std::enable_if_t<std::is_same_v<V, typename std::iterator_traits<I1>::value_type>>>
680 : public std::true_type {};
681
682 template <typename T, typename V, typename Enable = void>
683 struct is_unique_ptr_of_subclass_of_v : public std::false_type {};
684
685 template <typename T, typename P>
686 struct is_unique_ptr_of_subclass_of_v<T, std::unique_ptr<P>,
687 typename std::enable_if_t<std::is_base_of_v<T, P>>>
688 : public std::true_type {};
689
690 /* check if type is one of std::string (1), std::string_view (2), null-terminated char* (3) or pair
691 * of iterators (4)*/
692 template <typename T, typename Enable = void>
693 struct is_text_type_v : public std::false_type {};
694
695 template <typename T>
696 struct is_text_type_v<
697 T, typename std::enable_if_t<
698 /* case 1 */ //
699 std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::string>
700 /* case 2 */ //
701 || std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::string_view>
702 /* case 3 */ //
703 || std::is_same_v<std::remove_cv_t<std::decay_t<T>>, char*> //
704 || std::is_same_v<std::remove_cv_t<std::decay_t<T>>, const char*>
705 /* case 4 */
706 || details::is_iterator_pair_over<T, char>::value>> : public std::true_type {};
707
708 /**
709 * Construct a unique_ptr<Item> from many argument types. Accepts:
710 *
711 * (a) booleans;
712 * (b) integers, all sizes and signs;
713 * (c) text strings, as defined by is_text_type_v above;
714 * (d) byte strings, as std::vector<uint8_t>(d1), pair of iterators (d2) or pair<uint8_t*, size_T>
715 * (d3); and
716 * (e) Item subclass instances, including Array and Map. Items may be provided by naked pointer
717 * (e1), unique_ptr (e2), reference (e3) or value (e3). If provided by reference or value, will
718 * be moved if possible. If provided by pointer, ownership is taken.
719 * (f) null pointer;
720 */
721 template <typename T>
722 std::unique_ptr<Item> makeItem(T v) {
723 Item* p = nullptr;
724 if constexpr (/* case a */ std::is_same_v<T, bool>) {
725 p = new Bool(v);
726 } else if constexpr (/* case b */ std::is_integral_v<T>) { // b
727 if (v < 0) {
728 p = new Nint(v);
729 } else {
730 p = new Uint(static_cast<uint64_t>(v));
731 }
732 } else if constexpr (/* case c */ //
733 details::is_text_type_v<T>::value) {
734 p = new Tstr(v);
735 } else if constexpr (/* case d1 */ //
736 std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
737 std::vector<uint8_t>>
738 /* case d2 */ //
739 || details::is_iterator_pair_over<T, uint8_t>::value
740 /* case d3 */ //
741 || std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
742 std::pair<uint8_t*, size_t>>) {
743 p = new Bstr(v);
744 } else if constexpr (/* case e1 */ //
745 std::is_pointer_v<T> &&
746 std::is_base_of_v<Item, std::remove_pointer_t<T>>) {
747 p = v;
748 } else if constexpr (/* case e2 */ //
749 details::is_unique_ptr_of_subclass_of_v<Item, T>::value) {
750 p = v.release();
751 } else if constexpr (/* case e3 */ //
752 std::is_base_of_v<Item, T>) {
753 p = new T(std::move(v));
754 } else if constexpr (/* case f */ std::is_null_pointer_v<T>) {
755 p = new Null();
756 } else {
757 // It's odd that this can't be static_assert(false), since it shouldn't be evaluated if one
758 // of the above ifs matches. But static_assert(false) always triggers.
759 static_assert(std::is_same_v<T, bool>, "makeItem called with unsupported type");
760 }
761 return std::unique_ptr<Item>(p);
762 }
763
764 } // namespace details
765
766 template <typename... Args,
767 /* Prevent use as copy ctor */ typename = std::enable_if_t<
768 (sizeof...(Args)) != 1 ||
769 !(std::is_same_v<Array, std::remove_cv_t<std::remove_reference_t<Args>>> || ...)>>
770 Array::Array(Args&&... args) {
771 mEntries.reserve(sizeof...(args));
772 (mEntries.push_back(details::makeItem(std::forward<Args>(args))), ...);
773 }
774
775 template <typename T>
776 Array& Array::add(T&& v) & {
777 mEntries.push_back(details::makeItem(std::forward<T>(v)));
778 return *this;
779 }
780
781 template <typename T>
782 Array&& Array::add(T&& v) && {
783 mEntries.push_back(details::makeItem(std::forward<T>(v)));
784 return std::move(*this);
785 }
786
787 template <typename... Args,
788 /* Prevent use as copy ctor */ typename = std::enable_if_t<(sizeof...(Args)) != 1>>
789 Map::Map(Args&&... args) {
790 static_assert((sizeof...(Args)) % 2 == 0, "Map must have an even number of entries");
791 mEntries.reserve(sizeof...(args));
792 (mEntries.push_back(details::makeItem(std::forward<Args>(args))), ...);
793 }
794
795 template <typename Key, typename Value>
796 Map& Map::add(Key&& key, Value&& value) & {
797 mEntries.push_back(details::makeItem(std::forward<Key>(key)));
798 mEntries.push_back(details::makeItem(std::forward<Value>(value)));
799 return *this;
800 }
801
802 template <typename Key, typename Value>
803 Map&& Map::add(Key&& key, Value&& value) && {
804 this->add(std::forward<Key>(key), std::forward<Value>(value));
805 return std::move(*this);
806 }
807
808 template <typename Key, typename = std::enable_if_t<std::is_integral_v<Key> ||
809 details::is_text_type_v<Key>::value>>
810 std::pair<std::unique_ptr<Item>&, bool> Map::get(Key key) {
811 assertInvariant();
812 auto keyItem = details::makeItem(key);
813 for (size_t i = 0; i < mEntries.size(); i += 2) {
814 if (*keyItem == *mEntries[i]) {
815 return {mEntries[i + 1], true};
816 }
817 }
818 return {keyItem, false};
819 }
820
821 template <typename T>
822 Semantic::Semantic(uint64_t value, T&& child) : mValue(value) {
823 mEntries.reserve(1);
824 mEntries.push_back(details::makeItem(std::forward<T>(child)));
825 }
826
827 } // namespace cppbor
828