xref: /aosp_15_r20/system/libcppbor/src/cppbor.cpp (revision 0963554132e37a14524024fa04dc9e883c7a8221)
1 /*
2  * Copyright 2019 Google LLC
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  *     https://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 #include "cppbor.h"
18 
19 #include <inttypes.h>
20 #include <openssl/sha.h>
21 #include <cstdint>
22 #include <cstdio>
23 
24 #include "cppbor_parse.h"
25 
26 using std::string;
27 using std::vector;
28 
29 #ifndef __TRUSTY__
30 #include <android-base/logging.h>
31 #define LOG_TAG "CppBor"
32 #else
33 #define CHECK(x) (void)(x)
34 #endif
35 
36 namespace cppbor {
37 
38 namespace {
39 
40 template <typename T, typename Iterator, typename = std::enable_if<std::is_unsigned<T>::value>>
41 Iterator writeBigEndian(T value, Iterator pos) {
42     for (unsigned i = 0; i < sizeof(value); ++i) {
43         *pos++ = static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1)));
44         value = static_cast<T>(value << 8);
45     }
46     return pos;
47 }
48 
49 template <typename T, typename = std::enable_if<std::is_unsigned<T>::value>>
writeBigEndian(T value,std::function<void (uint8_t)> & cb)50 void writeBigEndian(T value, std::function<void(uint8_t)>& cb) {
51     for (unsigned i = 0; i < sizeof(value); ++i) {
52         cb(static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1))));
53         value = static_cast<T>(value << 8);
54     }
55 }
56 
cborAreAllElementsNonCompound(const Item * compoundItem)57 bool cborAreAllElementsNonCompound(const Item* compoundItem) {
58     if (compoundItem->type() == ARRAY) {
59         const Array* array = compoundItem->asArray();
60         for (size_t n = 0; n < array->size(); n++) {
61             const Item* entry = (*array)[n].get();
62             switch (entry->type()) {
63                 case ARRAY:
64                 case MAP:
65                     return false;
66                 default:
67                     break;
68             }
69         }
70     } else {
71         const Map* map = compoundItem->asMap();
72         for (auto& [keyEntry, valueEntry] : *map) {
73             switch (keyEntry->type()) {
74                 case ARRAY:
75                 case MAP:
76                     return false;
77                 default:
78                     break;
79             }
80             switch (valueEntry->type()) {
81                 case ARRAY:
82                 case MAP:
83                     return false;
84                 default:
85                     break;
86             }
87         }
88     }
89     return true;
90 }
91 
prettyPrintInternal(const Item * item,string & out,size_t indent,size_t maxBStrSize,const vector<string> & mapKeysToNotPrint)92 bool prettyPrintInternal(const Item* item, string& out, size_t indent, size_t maxBStrSize,
93                          const vector<string>& mapKeysToNotPrint) {
94     if (!item) {
95         out.append("<NULL>");
96         return false;
97     }
98 
99     char buf[80];
100 
101     string indentString(indent, ' ');
102 
103     size_t tagCount = item->semanticTagCount();
104     while (tagCount > 0) {
105         --tagCount;
106         snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", item->semanticTag(tagCount));
107         out.append(buf);
108     }
109 
110     switch (item->type()) {
111         case SEMANTIC:
112             // Handled above.
113             break;
114 
115         case UINT:
116             snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue());
117             out.append(buf);
118             break;
119 
120         case NINT:
121             snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value());
122             out.append(buf);
123             break;
124 
125         case BSTR: {
126             const uint8_t* valueData;
127             size_t valueSize;
128             const Bstr* bstr = item->asBstr();
129             if (bstr != nullptr) {
130                 const vector<uint8_t>& value = bstr->value();
131                 valueData = value.data();
132                 valueSize = value.size();
133             } else {
134                 const ViewBstr* viewBstr = item->asViewBstr();
135                 assert(viewBstr != nullptr);
136 
137                 valueData = viewBstr->view().data();
138                 valueSize = viewBstr->view().size();
139             }
140 
141             if (valueSize > maxBStrSize) {
142                 unsigned char digest[SHA_DIGEST_LENGTH];
143                 SHA_CTX ctx;
144                 SHA1_Init(&ctx);
145                 SHA1_Update(&ctx, valueData, valueSize);
146                 SHA1_Final(digest, &ctx);
147                 char buf2[SHA_DIGEST_LENGTH * 2 + 1];
148                 for (size_t n = 0; n < SHA_DIGEST_LENGTH; n++) {
149                     snprintf(buf2 + n * 2, 3, "%02x", digest[n]);
150                 }
151                 snprintf(buf, sizeof(buf), "<bstr size=%zd sha1=%s>", valueSize, buf2);
152                 out.append(buf);
153             } else {
154                 out.append("{");
155                 for (size_t n = 0; n < valueSize; n++) {
156                     if (n > 0) {
157                         out.append(", ");
158                     }
159                     snprintf(buf, sizeof(buf), "0x%02x", valueData[n]);
160                     out.append(buf);
161                 }
162                 out.append("}");
163             }
164         } break;
165 
166         case TSTR:
167             out.append("'");
168             {
169                 // TODO: escape "'" characters
170                 if (item->asTstr() != nullptr) {
171                     out.append(item->asTstr()->value().c_str());
172                 } else {
173                     const ViewTstr* viewTstr = item->asViewTstr();
174                     assert(viewTstr != nullptr);
175                     out.append(viewTstr->view());
176                 }
177             }
178             out.append("'");
179             break;
180 
181         case ARRAY: {
182             const Array* array = item->asArray();
183             if (array->size() == 0) {
184                 out.append("[]");
185             } else if (cborAreAllElementsNonCompound(array)) {
186                 out.append("[");
187                 for (size_t n = 0; n < array->size(); n++) {
188                     if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
189                                              mapKeysToNotPrint)) {
190                         return false;
191                     }
192                     out.append(", ");
193                 }
194                 out.append("]");
195             } else {
196                 out.append("[\n" + indentString);
197                 for (size_t n = 0; n < array->size(); n++) {
198                     out.append("  ");
199                     if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
200                                              mapKeysToNotPrint)) {
201                         return false;
202                     }
203                     out.append(",\n" + indentString);
204                 }
205                 out.append("]");
206             }
207         } break;
208 
209         case MAP: {
210             const Map* map = item->asMap();
211 
212             if (map->size() == 0) {
213                 out.append("{}");
214             } else {
215                 out.append("{\n" + indentString);
216                 for (auto& [map_key, map_value] : *map) {
217                     out.append("  ");
218 
219                     if (!prettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize,
220                                              mapKeysToNotPrint)) {
221                         return false;
222                     }
223                     out.append(" : ");
224                     if (map_key->type() == TSTR &&
225                         std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(),
226                                   map_key->asTstr()->value()) != mapKeysToNotPrint.end()) {
227                         out.append("<not printed>");
228                     } else {
229                         if (!prettyPrintInternal(map_value.get(), out, indent + 2, maxBStrSize,
230                                                  mapKeysToNotPrint)) {
231                             return false;
232                         }
233                     }
234                     out.append(",\n" + indentString);
235                 }
236                 out.append("}");
237             }
238         } break;
239 
240         case SIMPLE:
241             switch (item->asSimple()->simpleType()) {
242                 case BOOLEAN:
243                     out.append(item->asSimple()->asBool()->value() ? "true" : "false");
244                     break;
245                 case NULL_T:
246                     out.append("null");
247                     break;
248                 case FLOAT:
249 #if defined(__STDC_IEC_559__) || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
250                     snprintf(buf, sizeof(buf), "%f", item->asSimple()->asFloat()->value());
251                     out.append(buf);
252                     break;
253 #else
254 #ifndef __TRUSTY__
255                     LOG(ERROR) << "float not supported for this platform.";
256 #endif // __TRUSTY__
257                     return false;
258 #endif // __STDC_IEC_559__ || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
259                 case DOUBLE:
260 #if defined(__STDC_IEC_559__) || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
261                     snprintf(buf, sizeof(buf), "%f", item->asSimple()->asDouble()->value());
262                     out.append(buf);
263                     break;
264 #else
265 #ifndef __TRUSTY__
266                     LOG(ERROR) << "double not supported for this platform.";
267 #endif  // __TRUSTY__
268                     return false;
269 #endif  // __STDC_IEC_559__ || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
270                 default:
271 #ifndef __TRUSTY__
272                     LOG(ERROR) << "Only boolean/null/float/double is implemented for SIMPLE";
273 #endif  // __TRUSTY__
274                     return false;
275             }
276             break;
277     }
278 
279     return true;
280 }
281 
282 }  // namespace
283 
headerSize(uint64_t addlInfo)284 size_t headerSize(uint64_t addlInfo) {
285     if (addlInfo < ONE_BYTE_LENGTH) return 1;
286     if (addlInfo <= std::numeric_limits<uint8_t>::max()) return 2;
287     if (addlInfo <= std::numeric_limits<uint16_t>::max()) return 3;
288     if (addlInfo <= std::numeric_limits<uint32_t>::max()) return 5;
289     return 9;
290 }
291 
encodeHeader(MajorType type,uint64_t addlInfo,uint8_t * pos,const uint8_t * end)292 uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end) {
293     size_t sz = headerSize(addlInfo);
294     if (end - pos < static_cast<ssize_t>(sz)) return nullptr;
295     switch (sz) {
296         case 1:
297             *pos++ = type | static_cast<uint8_t>(addlInfo);
298             return pos;
299         case 2:
300             *pos++ = type | static_cast<MajorType>(ONE_BYTE_LENGTH);
301             *pos++ = static_cast<uint8_t>(addlInfo);
302             return pos;
303         case 3:
304             *pos++ = type | static_cast<MajorType>(TWO_BYTE_LENGTH);
305             return writeBigEndian(static_cast<uint16_t>(addlInfo), pos);
306         case 5:
307             *pos++ = type | static_cast<MajorType>(FOUR_BYTE_LENGTH);
308             return writeBigEndian(static_cast<uint32_t>(addlInfo), pos);
309         case 9:
310             *pos++ = type | static_cast<MajorType>(EIGHT_BYTE_LENGTH);
311             return writeBigEndian(addlInfo, pos);
312         default:
313             CHECK(false);  // Impossible to get here.
314             return nullptr;
315     }
316 }
317 
encodeHeader(MajorType type,uint64_t addlInfo,EncodeCallback encodeCallback)318 void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback) {
319     size_t sz = headerSize(addlInfo);
320     switch (sz) {
321         case 1:
322             encodeCallback(type | static_cast<uint8_t>(addlInfo));
323             break;
324         case 2:
325             encodeCallback(type | static_cast<MajorType>(ONE_BYTE_LENGTH));
326             encodeCallback(static_cast<uint8_t>(addlInfo));
327             break;
328         case 3:
329             encodeCallback(type | static_cast<MajorType>(TWO_BYTE_LENGTH));
330             writeBigEndian(static_cast<uint16_t>(addlInfo), encodeCallback);
331             break;
332         case 5:
333             encodeCallback(type | static_cast<MajorType>(FOUR_BYTE_LENGTH));
334             writeBigEndian(static_cast<uint32_t>(addlInfo), encodeCallback);
335             break;
336         case 9:
337             encodeCallback(type | static_cast<MajorType>(EIGHT_BYTE_LENGTH));
338             writeBigEndian(addlInfo, encodeCallback);
339             break;
340         default:
341             CHECK(false);  // Impossible to get here.
342     }
343 }
344 
operator ==(const Item & other) const345 bool Item::operator==(const Item& other) const& {
346     if (type() != other.type()) return false;
347     switch (type()) {
348         case UINT:
349             return *asUint() == *(other.asUint());
350         case NINT:
351             return *asNint() == *(other.asNint());
352         case BSTR:
353             if (asBstr() != nullptr && other.asBstr() != nullptr) {
354                 return *asBstr() == *(other.asBstr());
355             }
356             if (asViewBstr() != nullptr && other.asViewBstr() != nullptr) {
357                 return *asViewBstr() == *(other.asViewBstr());
358             }
359             // Interesting corner case: comparing a Bstr and ViewBstr with
360             // identical contents. The function currently returns false for
361             // this case.
362             // TODO: if it should return true, this needs a deep comparison
363             return false;
364         case TSTR:
365             if (asTstr() != nullptr && other.asTstr() != nullptr) {
366                 return *asTstr() == *(other.asTstr());
367             }
368             if (asViewTstr() != nullptr && other.asViewTstr() != nullptr) {
369                 return *asViewTstr() == *(other.asViewTstr());
370             }
371             // Same corner case as Bstr
372             return false;
373         case ARRAY:
374             return *asArray() == *(other.asArray());
375         case MAP:
376             return *asMap() == *(other.asMap());
377         case SIMPLE:
378             return *asSimple() == *(other.asSimple());
379         case SEMANTIC:
380             return *asSemanticTag() == *(other.asSemanticTag());
381         default:
382             CHECK(false);  // Impossible to get here.
383             return false;
384     }
385 }
386 
Nint(int64_t v)387 Nint::Nint(int64_t v) : mValue(v) {
388     CHECK(v < 0);
389 }
390 
operator ==(const Simple & other) const391 bool Simple::operator==(const Simple& other) const& {
392     if (simpleType() != other.simpleType()) return false;
393 
394     switch (simpleType()) {
395         case BOOLEAN:
396             return *asBool() == *(other.asBool());
397         case NULL_T:
398             return true;
399 #if defined(__STDC_IEC_559__) || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
400         case FLOAT:
401             return *asFloat() == *(other.asFloat());
402 #endif  // __STDC_IEC_559__ || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24
403 #if defined(__STDC_IEC_559__) || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
404         case DOUBLE:
405             return *asDouble() == *(other.asDouble());
406 #endif  // __STDC_IEC_559__ || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53
407         default:
408             CHECK(false);  // Impossible to get here.
409             return false;
410     }
411 }
412 
encode(uint8_t * pos,const uint8_t * end) const413 uint8_t* Bstr::encode(uint8_t* pos, const uint8_t* end) const {
414     pos = encodeHeader(mValue.size(), pos, end);
415     if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
416     return std::copy(mValue.begin(), mValue.end(), pos);
417 }
418 
encodeValue(EncodeCallback encodeCallback) const419 void Bstr::encodeValue(EncodeCallback encodeCallback) const {
420     for (auto c : mValue) {
421         encodeCallback(c);
422     }
423 }
424 
encode(uint8_t * pos,const uint8_t * end) const425 uint8_t* ViewBstr::encode(uint8_t* pos, const uint8_t* end) const {
426     pos = encodeHeader(mView.size(), pos, end);
427     if (!pos || end - pos < static_cast<ptrdiff_t>(mView.size())) return nullptr;
428     return std::copy(mView.begin(), mView.end(), pos);
429 }
430 
encodeValue(EncodeCallback encodeCallback) const431 void ViewBstr::encodeValue(EncodeCallback encodeCallback) const {
432     for (auto c : mView) {
433         encodeCallback(static_cast<uint8_t>(c));
434     }
435 }
436 
encode(uint8_t * pos,const uint8_t * end) const437 uint8_t* Tstr::encode(uint8_t* pos, const uint8_t* end) const {
438     pos = encodeHeader(mValue.size(), pos, end);
439     if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
440     return std::copy(mValue.begin(), mValue.end(), pos);
441 }
442 
encodeValue(EncodeCallback encodeCallback) const443 void Tstr::encodeValue(EncodeCallback encodeCallback) const {
444     for (auto c : mValue) {
445         encodeCallback(static_cast<uint8_t>(c));
446     }
447 }
448 
encode(uint8_t * pos,const uint8_t * end) const449 uint8_t* ViewTstr::encode(uint8_t* pos, const uint8_t* end) const {
450     pos = encodeHeader(mView.size(), pos, end);
451     if (!pos || end - pos < static_cast<ptrdiff_t>(mView.size())) return nullptr;
452     return std::copy(mView.begin(), mView.end(), pos);
453 }
454 
encodeValue(EncodeCallback encodeCallback) const455 void ViewTstr::encodeValue(EncodeCallback encodeCallback) const {
456     for (auto c : mView) {
457         encodeCallback(static_cast<uint8_t>(c));
458     }
459 }
460 
operator ==(const Array & other) const461 bool Array::operator==(const Array& other) const& {
462     return size() == other.size()
463            // Can't use vector::operator== because the contents are pointers.  std::equal lets us
464            // provide a predicate that does the dereferencing.
465            && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(),
466                          [](auto& a, auto& b) -> bool { return *a == *b; });
467 }
468 
encode(uint8_t * pos,const uint8_t * end) const469 uint8_t* Array::encode(uint8_t* pos, const uint8_t* end) const {
470     pos = encodeHeader(size(), pos, end);
471     if (!pos) return nullptr;
472     for (auto& entry : mEntries) {
473         pos = entry->encode(pos, end);
474         if (!pos) return nullptr;
475     }
476     return pos;
477 }
478 
encode(EncodeCallback encodeCallback) const479 void Array::encode(EncodeCallback encodeCallback) const {
480     encodeHeader(size(), encodeCallback);
481     for (auto& entry : mEntries) {
482         entry->encode(encodeCallback);
483     }
484 }
485 
clone() const486 std::unique_ptr<Item> Array::clone() const {
487     auto res = std::make_unique<Array>();
488     for (size_t i = 0; i < mEntries.size(); i++) {
489         res->add(mEntries[i]->clone());
490     }
491     return res;
492 }
493 
operator ==(const Map & other) const494 bool Map::operator==(const Map& other) const& {
495     return size() == other.size()
496            // Can't use vector::operator== because the contents are pairs of pointers.  std::equal
497            // lets us provide a predicate that does the dereferencing.
498            && std::equal(begin(), end(), other.begin(), [](auto& a, auto& b) {
499                   return *a.first == *b.first && *a.second == *b.second;
500               });
501 }
502 
encode(uint8_t * pos,const uint8_t * end) const503 uint8_t* Map::encode(uint8_t* pos, const uint8_t* end) const {
504     pos = encodeHeader(size(), pos, end);
505     if (!pos) return nullptr;
506     for (auto& entry : mEntries) {
507         pos = entry.first->encode(pos, end);
508         if (!pos) return nullptr;
509         pos = entry.second->encode(pos, end);
510         if (!pos) return nullptr;
511     }
512     return pos;
513 }
514 
encode(EncodeCallback encodeCallback) const515 void Map::encode(EncodeCallback encodeCallback) const {
516     encodeHeader(size(), encodeCallback);
517     for (auto& entry : mEntries) {
518         entry.first->encode(encodeCallback);
519         entry.second->encode(encodeCallback);
520     }
521 }
522 
keyLess(const Item * a,const Item * b)523 bool Map::keyLess(const Item* a, const Item* b) {
524     // CBOR map canonicalization rules are:
525 
526     // 1. If two keys have different lengths, the shorter one sorts earlier.
527     if (a->encodedSize() < b->encodedSize()) return true;
528     if (a->encodedSize() > b->encodedSize()) return false;
529 
530     // 2. If two keys have the same length, the one with the lower value in (byte-wise) lexical
531     // order sorts earlier.  This requires encoding both items.
532     auto encodedA = a->encode();
533     auto encodedB = b->encode();
534 
535     return std::lexicographical_compare(encodedA.begin(), encodedA.end(),  //
536                                         encodedB.begin(), encodedB.end());
537 }
538 
recursivelyCanonicalize(std::unique_ptr<Item> & item)539 void recursivelyCanonicalize(std::unique_ptr<Item>& item) {
540     switch (item->type()) {
541         case UINT:
542         case NINT:
543         case BSTR:
544         case TSTR:
545         case SIMPLE:
546             return;
547 
548         case ARRAY:
549             std::for_each(item->asArray()->begin(), item->asArray()->end(),
550                           recursivelyCanonicalize);
551             return;
552 
553         case MAP:
554             item->asMap()->canonicalize(true /* recurse */);
555             return;
556 
557         case SEMANTIC:
558             // This can't happen.  SemanticTags delegate their type() method to the contained Item's
559             // type.
560             assert(false);
561             return;
562     }
563 }
564 
canonicalize(bool recurse)565 Map& Map::canonicalize(bool recurse) & {
566     if (recurse) {
567         for (auto& entry : mEntries) {
568             recursivelyCanonicalize(entry.first);
569             recursivelyCanonicalize(entry.second);
570         }
571     }
572 
573     if (size() < 2 || mCanonicalized) {
574         // Trivially or already canonical; do nothing.
575         return *this;
576     }
577 
578     std::sort(begin(), end(),
579               [](auto& a, auto& b) { return keyLess(a.first.get(), b.first.get()); });
580     mCanonicalized = true;
581     return *this;
582 }
583 
clone() const584 std::unique_ptr<Item> Map::clone() const {
585     auto res = std::make_unique<Map>();
586     for (auto& [key, value] : *this) {
587         res->add(key->clone(), value->clone());
588     }
589     res->mCanonicalized = mCanonicalized;
590     return res;
591 }
592 
clone() const593 std::unique_ptr<Item> SemanticTag::clone() const {
594     return std::make_unique<SemanticTag>(mValue, mTaggedItem->clone());
595 }
596 
encode(uint8_t * pos,const uint8_t * end) const597 uint8_t* SemanticTag::encode(uint8_t* pos, const uint8_t* end) const {
598     // Can't use the encodeHeader() method that calls type() to get the major type, since that will
599     // return the tagged Item's type.
600     pos = ::cppbor::encodeHeader(kMajorType, mValue, pos, end);
601     if (!pos) return nullptr;
602     return mTaggedItem->encode(pos, end);
603 }
604 
encode(EncodeCallback encodeCallback) const605 void SemanticTag::encode(EncodeCallback encodeCallback) const {
606     // Can't use the encodeHeader() method that calls type() to get the major type, since that will
607     // return the tagged Item's type.
608     ::cppbor::encodeHeader(kMajorType, mValue, encodeCallback);
609     mTaggedItem->encode(encodeCallback);
610 }
611 
semanticTagCount() const612 size_t SemanticTag::semanticTagCount() const {
613     size_t levelCount = 1;  // Count this level.
614     const SemanticTag* cur = this;
615     while (cur->mTaggedItem && (cur = cur->mTaggedItem->asSemanticTag()) != nullptr) ++levelCount;
616     return levelCount;
617 }
618 
semanticTag(size_t nesting) const619 uint64_t SemanticTag::semanticTag(size_t nesting) const {
620     // Getting the value of a specific nested tag is a bit tricky, because we start with the outer
621     // tag and don't know how many are inside.  We count the number of nesting levels to find out
622     // how many there are in total, then to get the one we want we have to walk down levelCount -
623     // nesting steps.
624     size_t levelCount = semanticTagCount();
625     if (nesting >= levelCount) return 0;
626 
627     levelCount -= nesting;
628     const SemanticTag* cur = this;
629     while (--levelCount > 0) cur = cur->mTaggedItem->asSemanticTag();
630 
631     return cur->mValue;
632 }
633 
prettyPrint(const Item * item,size_t maxBStrSize,const vector<string> & mapKeysToNotPrint)634 string prettyPrint(const Item* item, size_t maxBStrSize, const vector<string>& mapKeysToNotPrint) {
635     string out;
636     prettyPrintInternal(item, out, 0, maxBStrSize, mapKeysToNotPrint);
637     return out;
638 }
prettyPrint(const vector<uint8_t> & encodedCbor,size_t maxBStrSize,const vector<string> & mapKeysToNotPrint)639 string prettyPrint(const vector<uint8_t>& encodedCbor, size_t maxBStrSize,
640                    const vector<string>& mapKeysToNotPrint) {
641     auto [item, _, message] = parse(encodedCbor);
642     if (item == nullptr) {
643 #ifndef __TRUSTY__
644         LOG(ERROR) << "Data to pretty print is not valid CBOR: " << message;
645 #endif  // __TRUSTY__
646         return "";
647     }
648 
649     return prettyPrint(item.get(), maxBStrSize, mapKeysToNotPrint);
650 }
651 
652 }  // namespace cppbor
653