• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/internal/host/sdp/data_element.h"
16 
17 #include <cpp-string/string_printf.h>
18 #include <endian.h>
19 
20 #include <algorithm>
21 #include <set>
22 #include <vector>
23 
24 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
25 
26 // Returns true if |url| is a valid URI.
IsValidUrl(const std::string & url)27 bool IsValidUrl(const std::string& url) {
28   // Pulled from [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986).
29   // See Section 2.2 for the set of reserved characters.
30   // See Section 2.3 for the set of unreserved characters.
31   constexpr char kValidUrlChars[] =
32       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!#$&'("
33       ")*+,/:;=?@[]";
34   return url.find_first_not_of(kValidUrlChars) == std::string::npos;
35 }
36 
37 namespace bt::sdp {
38 
39 namespace {
40 
41 // Size Descriptor occupies the lowest 3 bits of the header byte.
42 // v5.0, Vol 3, Part B, Sec 3.3.
43 constexpr uint8_t kDataElementSizeTypeMask = 0x07;
44 
SizeToSizeType(size_t size)45 DataElement::Size SizeToSizeType(size_t size) {
46   switch (size) {
47     case 1:
48       return DataElement::Size::kOneByte;
49     case 2:
50       return DataElement::Size::kTwoBytes;
51     case 4:
52       return DataElement::Size::kFourBytes;
53     case 8:
54       return DataElement::Size::kEightBytes;
55     case 16:
56       return DataElement::Size::kSixteenBytes;
57     default:
58       BT_PANIC("invalid data element size: %zu", size);
59   }
60   return DataElement::Size::kNextFour;
61 }
62 
AggregateSize(const std::vector<DataElement> & aggregate)63 size_t AggregateSize(const std::vector<DataElement>& aggregate) {
64   size_t total_size = 0;
65   for (const auto& elem : aggregate) {
66     total_size += elem.WriteSize();
67   }
68   return total_size;
69 }
70 
WriteLength(MutableByteBuffer * buf,size_t length)71 size_t WriteLength(MutableByteBuffer* buf, size_t length) {
72   if (length <= std::numeric_limits<uint8_t>::max()) {
73     uint8_t val = static_cast<uint8_t>(length);
74     buf->Write(&val, sizeof(val));
75     return sizeof(uint8_t);
76   }
77 
78   if (length <= std::numeric_limits<uint16_t>::max()) {
79     buf->WriteObj(htobe16(static_cast<uint16_t>(length)));
80     return sizeof(uint16_t);
81   }
82 
83   if (length <= std::numeric_limits<uint32_t>::max()) {
84     buf->WriteObj(htobe32(static_cast<uint32_t>(length)));
85     return sizeof(uint32_t);
86   }
87 
88   return 0;
89 }
90 
91 }  // namespace
92 
DataElement()93 DataElement::DataElement() : type_(Type::kNull), size_(Size::kOneByte) {}
94 
DataElement(const DataElement & other)95 DataElement::DataElement(const DataElement& other)
96     : type_(other.type_), size_(other.size_) {
97   switch (type_) {
98     case Type::kNull:
99       return;
100     case Type::kUnsignedInt:
101       uint_value_ = other.uint_value_;
102       return;
103     case Type::kBoolean:
104     case Type::kSignedInt:
105       int_value_ = other.int_value_;
106       return;
107     case Type::kUuid:
108       uuid_ = other.uuid_;
109       return;
110     case Type::kString:
111     case Type::kUrl:
112       bytes_ = DynamicByteBuffer(other.bytes_);
113       return;
114     case Type::kSequence:
115     case Type::kAlternative:
116       for (const auto& it : other.aggregate_) {
117         aggregate_.emplace_back(DataElement(it));
118       }
119       return;
120   }
121 }
122 
123 template <>
Set(uint8_t value)124 void DataElement::Set<uint8_t>(uint8_t value) {
125   type_ = Type::kUnsignedInt;
126   size_ = SizeToSizeType(sizeof(uint8_t));
127   uint_value_ = value;
128 }
129 
130 template <>
Set(uint16_t value)131 void DataElement::Set<uint16_t>(uint16_t value) {
132   type_ = Type::kUnsignedInt;
133   size_ = SizeToSizeType(sizeof(uint16_t));
134   uint_value_ = value;
135 }
136 
137 template <>
Set(uint32_t value)138 void DataElement::Set<uint32_t>(uint32_t value) {
139   type_ = Type::kUnsignedInt;
140   size_ = SizeToSizeType(sizeof(uint32_t));
141   uint_value_ = value;
142 }
143 
144 template <>
Set(uint64_t value)145 void DataElement::Set<uint64_t>(uint64_t value) {
146   type_ = Type::kUnsignedInt;
147   size_ = SizeToSizeType(sizeof(uint64_t));
148   uint_value_ = value;
149 }
150 
151 template <>
Set(int8_t value)152 void DataElement::Set<int8_t>(int8_t value) {
153   type_ = Type::kSignedInt;
154   size_ = SizeToSizeType(sizeof(int8_t));
155   int_value_ = value;
156 }
157 
158 template <>
Set(int16_t value)159 void DataElement::Set<int16_t>(int16_t value) {
160   type_ = Type::kSignedInt;
161   size_ = SizeToSizeType(sizeof(int16_t));
162   int_value_ = value;
163 }
164 
165 template <>
Set(int32_t value)166 void DataElement::Set<int32_t>(int32_t value) {
167   type_ = Type::kSignedInt;
168   size_ = SizeToSizeType(sizeof(int32_t));
169   int_value_ = value;
170 }
171 
172 template <>
Set(int64_t value)173 void DataElement::Set<int64_t>(int64_t value) {
174   type_ = Type::kSignedInt;
175   size_ = SizeToSizeType(sizeof(int64_t));
176   int_value_ = value;
177 }
178 
179 template <>
Set(bool value)180 void DataElement::Set<bool>(bool value) {
181   type_ = Type::kBoolean;
182   size_ = Size::kOneByte;
183   int_value_ = (value ? 1 : 0);
184 }
185 
186 template <>
Set(std::nullptr_t)187 void DataElement::Set<std::nullptr_t>(std::nullptr_t) {
188   type_ = Type::kNull;
189   size_ = Size::kOneByte;
190 }
191 
192 template <>
Set(UUID value)193 void DataElement::Set<UUID>(UUID value) {
194   type_ = Type::kUuid;
195   size_ = SizeToSizeType(value.CompactSize());
196   uuid_ = value;
197 }
198 
Set(const bt::DynamicByteBuffer & value)199 void DataElement::Set(const bt::DynamicByteBuffer& value) {
200   type_ = Type::kString;
201   SetVariableSize(value.size());
202   bytes_ = DynamicByteBuffer(value);
203 }
204 
Set(const std::string & value)205 void DataElement::Set(const std::string& value) {
206   type_ = Type::kString;
207   SetVariableSize(value.size());
208   bytes_ = DynamicByteBuffer(value);
209 }
210 
Set(std::vector<DataElement> && value)211 void DataElement::Set(std::vector<DataElement>&& value) {
212   type_ = Type::kSequence;
213   aggregate_ = std::move(value);
214   SetVariableSize(AggregateSize(aggregate_));
215 }
216 
SetUrl(const std::string & url)217 void DataElement::SetUrl(const std::string& url) {
218   if (!IsValidUrl(url)) {
219     bt_log(WARN, "sdp", "Invalid URL in SetUrl: %s", url.c_str());
220     return;
221   }
222 
223   type_ = Type::kUrl;
224   SetVariableSize(url.size());
225   bytes_ = DynamicByteBuffer(url);
226 }
227 
SetAlternative(std::vector<DataElement> && items)228 void DataElement::SetAlternative(std::vector<DataElement>&& items) {
229   type_ = Type::kAlternative;
230   aggregate_ = std::move(items);
231   SetVariableSize(AggregateSize(aggregate_));
232 }
233 
234 template <>
Get() const235 std::optional<uint8_t> DataElement::Get<uint8_t>() const {
236   if (type_ == Type::kUnsignedInt && size_ == SizeToSizeType(sizeof(uint8_t))) {
237     return static_cast<uint8_t>(uint_value_);
238   }
239 
240   return std::nullopt;
241 }
242 
243 template <>
Get() const244 std::optional<uint16_t> DataElement::Get<uint16_t>() const {
245   if (type_ == Type::kUnsignedInt &&
246       size_ == SizeToSizeType(sizeof(uint16_t))) {
247     return static_cast<uint16_t>(uint_value_);
248   }
249 
250   return std::nullopt;
251 }
252 
253 template <>
Get() const254 std::optional<uint32_t> DataElement::Get<uint32_t>() const {
255   if (type_ == Type::kUnsignedInt &&
256       size_ == SizeToSizeType(sizeof(uint32_t))) {
257     return static_cast<uint32_t>(uint_value_);
258   }
259 
260   return std::nullopt;
261 }
262 
263 template <>
Get() const264 std::optional<uint64_t> DataElement::Get<uint64_t>() const {
265   if (type_ == Type::kUnsignedInt &&
266       size_ == SizeToSizeType(sizeof(uint64_t))) {
267     return uint_value_;
268   }
269 
270   return std::nullopt;
271 }
272 
273 template <>
Get() const274 std::optional<int8_t> DataElement::Get<int8_t>() const {
275   if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int8_t))) {
276     return static_cast<int8_t>(int_value_);
277   }
278 
279   return std::nullopt;
280 }
281 
282 template <>
Get() const283 std::optional<int16_t> DataElement::Get<int16_t>() const {
284   if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int16_t))) {
285     return static_cast<int16_t>(int_value_);
286   }
287 
288   return std::nullopt;
289 }
290 
291 template <>
Get() const292 std::optional<int32_t> DataElement::Get<int32_t>() const {
293   if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int32_t))) {
294     return static_cast<int32_t>(int_value_);
295   }
296 
297   return std::nullopt;
298   ;
299 }
300 
301 template <>
Get() const302 std::optional<int64_t> DataElement::Get<int64_t>() const {
303   if (type_ == Type::kSignedInt && size_ == SizeToSizeType(sizeof(int64_t))) {
304     return static_cast<int64_t>(int_value_);
305   }
306 
307   return std::nullopt;
308 }
309 
310 template <>
Get() const311 std::optional<bool> DataElement::Get<bool>() const {
312   if (type_ != Type::kBoolean) {
313     return std::nullopt;
314   }
315 
316   return (int_value_ == 1);
317 }
318 
319 template <>
Get() const320 std::optional<std::nullptr_t> DataElement::Get<std::nullptr_t>() const {
321   if (type_ != Type::kNull) {
322     return std::nullopt;
323   }
324 
325   return nullptr;
326 }
327 
328 template <>
Get() const329 std::optional<bt::DynamicByteBuffer> DataElement::Get<bt::DynamicByteBuffer>()
330     const {
331   if (type_ != Type::kString) {
332     return std::nullopt;
333   }
334 
335   return DynamicByteBuffer(bytes_);
336 }
337 
338 template <>
Get() const339 std::optional<std::string> DataElement::Get<std::string>() const {
340   if (type_ != Type::kString) {
341     return std::nullopt;
342   }
343 
344   return std::string(reinterpret_cast<const char*>(bytes_.data()),
345                      bytes_.size());
346 }
347 
348 template <>
Get() const349 std::optional<UUID> DataElement::Get<UUID>() const {
350   if (type_ != Type::kUuid) {
351     return std::nullopt;
352   }
353 
354   return uuid_;
355 }
356 
357 template <>
358 std::optional<std::vector<DataElement>>
Get() const359 DataElement::Get<std::vector<DataElement>>() const {
360   if (type_ != Type::kSequence) {
361     return std::nullopt;
362   }
363 
364   std::vector<DataElement> aggregate_copy;
365   for (const auto& it : aggregate_) {
366     aggregate_copy.emplace_back(it.Clone());
367   }
368 
369   return aggregate_copy;
370 }
371 
GetUrl() const372 std::optional<std::string> DataElement::GetUrl() const {
373   if (type_ != Type::kUrl) {
374     return std::nullopt;
375   }
376 
377   return std::string(reinterpret_cast<const char*>(bytes_.data()),
378                      bytes_.size());
379 }
380 
SetVariableSize(size_t length)381 void DataElement::SetVariableSize(size_t length) {
382   if (length <= std::numeric_limits<uint8_t>::max()) {
383     size_ = Size::kNextOne;
384   } else if (length <= std::numeric_limits<uint16_t>::max()) {
385     size_ = Size::kNextTwo;
386   } else {
387     size_ = Size::kNextFour;
388   }
389 }
390 
Read(DataElement * elem,const ByteBuffer & buffer)391 size_t DataElement::Read(DataElement* elem, const ByteBuffer& buffer) {
392   if (buffer.size() == 0) {
393     return 0;
394   }
395   Type type_desc = static_cast<Type>(buffer[0] & kTypeMask);
396   Size size_desc = static_cast<Size>(buffer[0] & kDataElementSizeTypeMask);
397   size_t data_bytes = 0;
398   size_t bytes_read = 1;
399   BufferView cursor = buffer.view(bytes_read);
400   switch (size_desc) {
401     case DataElement::Size::kOneByte:
402     case DataElement::Size::kTwoBytes:
403     case DataElement::Size::kFourBytes:
404     case DataElement::Size::kEightBytes:
405     case DataElement::Size::kSixteenBytes:
406       if (type_desc != Type::kNull) {
407         data_bytes = (1 << static_cast<uint8_t>(size_desc));
408       } else {
409         data_bytes = 0;
410       }
411       break;
412     case DataElement::Size::kNextOne: {
413       if (cursor.size() < sizeof(uint8_t)) {
414         return 0;
415       }
416       data_bytes = cursor.To<uint8_t>();
417       bytes_read += sizeof(uint8_t);
418       break;
419     }
420     case DataElement::Size::kNextTwo: {
421       if (cursor.size() < sizeof(uint16_t)) {
422         return 0;
423       }
424       data_bytes = be16toh(cursor.To<uint16_t>());
425       bytes_read += sizeof(uint16_t);
426       break;
427     }
428     case DataElement::Size::kNextFour: {
429       if (cursor.size() < sizeof(uint32_t)) {
430         return 0;
431       }
432       data_bytes = be32toh(cursor.To<uint32_t>());
433       bytes_read += sizeof(uint32_t);
434       break;
435     }
436   }
437   cursor = buffer.view(bytes_read);
438   if (cursor.size() < data_bytes) {
439     return 0;
440   }
441 
442   switch (type_desc) {
443     case Type::kNull: {
444       if (size_desc != Size::kOneByte) {
445         return 0;
446       }
447       elem->Set(nullptr);
448       return bytes_read + data_bytes;
449     }
450     case Type::kBoolean: {
451       if (size_desc != Size::kOneByte) {
452         return 0;
453       }
454       elem->Set(cursor.To<uint8_t>() != 0);
455       return bytes_read + data_bytes;
456     }
457     case Type::kUnsignedInt: {
458       if (size_desc == Size::kOneByte) {
459         elem->Set(cursor.To<uint8_t>());
460       } else if (size_desc == Size::kTwoBytes) {
461         elem->Set(be16toh(cursor.To<uint16_t>()));
462       } else if (size_desc == Size::kFourBytes) {
463         elem->Set(be32toh(cursor.To<uint32_t>()));
464       } else if (size_desc == Size::kEightBytes) {
465         elem->Set(be64toh(cursor.To<uint64_t>()));
466       } else {
467         // TODO(fxbug.dev/42078670): support 128-bit uints
468         // Invalid size.
469         return 0;
470       }
471       return bytes_read + data_bytes;
472     }
473     case Type::kSignedInt: {
474       if (size_desc == Size::kOneByte) {
475         elem->Set(cursor.To<int8_t>());
476       } else if (size_desc == Size::kTwoBytes) {
477         elem->Set(be16toh(cursor.To<int16_t>()));
478       } else if (size_desc == Size::kFourBytes) {
479         elem->Set(be32toh(cursor.To<int32_t>()));
480       } else if (size_desc == Size::kEightBytes) {
481         elem->Set(be64toh(cursor.To<int64_t>()));
482       } else {
483         // TODO(fxbug.dev/42078670): support 128-bit uints
484         // Invalid size.
485         return 0;
486       }
487       return bytes_read + data_bytes;
488     }
489     case Type::kUuid: {
490       if (size_desc == Size::kTwoBytes) {
491         elem->Set(UUID(be16toh(cursor.To<uint16_t>())));
492       } else if (size_desc == Size::kFourBytes) {
493         elem->Set(UUID(be32toh(cursor.To<uint32_t>())));
494       } else if (size_desc == Size::kSixteenBytes) {
495         StaticByteBuffer<16> uuid_bytes;
496         // UUID expects these to be in little-endian order.
497         cursor.Copy(&uuid_bytes, 0, 16);
498         std::reverse(uuid_bytes.mutable_data(), uuid_bytes.mutable_data() + 16);
499         UUID uuid(uuid_bytes);
500         elem->Set(uuid);
501       } else {
502         return 0;
503       }
504       return bytes_read + data_bytes;
505     }
506     case Type::kString: {
507       if (static_cast<uint8_t>(size_desc) < 5) {
508         return 0;
509       }
510       bt::DynamicByteBuffer str(data_bytes);
511       str.Write(cursor.data(), data_bytes);
512       elem->Set(str);
513       return bytes_read + data_bytes;
514     }
515     case Type::kSequence:
516     case Type::kAlternative: {
517       if (static_cast<uint8_t>(size_desc) < 5) {
518         return 0;
519       }
520       BufferView sequence_buf = cursor.view(0, data_bytes);
521       size_t remaining = data_bytes;
522       BT_DEBUG_ASSERT(sequence_buf.size() == data_bytes);
523 
524       std::vector<DataElement> seq;
525       while (remaining > 0) {
526         DataElement next;
527         size_t used = Read(&next, sequence_buf.view(data_bytes - remaining));
528         if (used == 0 || used > remaining) {
529           return 0;
530         }
531         seq.push_back(std::move(next));
532         remaining -= used;
533       }
534       BT_DEBUG_ASSERT(remaining == 0);
535       if (type_desc == Type::kAlternative) {
536         elem->SetAlternative(std::move(seq));
537       } else {
538         elem->Set(std::move(seq));
539       }
540       return bytes_read + data_bytes;
541     }
542     case Type::kUrl: {
543       if (static_cast<uint8_t>(size_desc) < 5) {
544         return 0;
545       }
546       std::string str(cursor.data(), cursor.data() + data_bytes);
547       elem->SetUrl(str);
548       return bytes_read + data_bytes;
549     }
550   }
551   return 0;
552 }
553 
WriteSize() const554 size_t DataElement::WriteSize() const {
555   switch (type_) {
556     case Type::kNull:
557       return 1;
558     case Type::kBoolean:
559       return 2;
560     case Type::kUnsignedInt:
561     case Type::kSignedInt:
562     case Type::kUuid:
563       return 1 + (1 << static_cast<uint8_t>(size_));
564     case Type::kString:
565     case Type::kUrl:
566       return 1 + (1 << (static_cast<uint8_t>(size_) - 5)) + bytes_.size();
567     case Type::kSequence:
568     case Type::kAlternative:
569       return 1 + (1 << (static_cast<uint8_t>(size_) - 5)) +
570              AggregateSize(aggregate_);
571   }
572 }
573 
Write(MutableByteBuffer * buffer) const574 size_t DataElement::Write(MutableByteBuffer* buffer) const {
575   if (buffer->size() < WriteSize()) {
576     bt_log(TRACE,
577            "sdp",
578            "not enough space in buffer (%zu < %zu)",
579            buffer->size(),
580            WriteSize());
581     return 0;
582   }
583 
584   uint8_t type_and_size =
585       static_cast<uint8_t>(type_) | static_cast<uint8_t>(size_);
586   buffer->Write(&type_and_size, 1);
587   size_t pos = 1;
588 
589   MutableBufferView cursor = buffer->mutable_view(pos);
590 
591   switch (type_) {
592     case Type::kNull: {
593       return pos;
594     }
595     case Type::kBoolean: {
596       uint8_t val = int_value_ != 0 ? 1 : 0;
597       cursor.Write(&val, sizeof(val));
598       pos += 1;
599       return pos;
600     }
601     case Type::kUnsignedInt: {
602       if (size_ == Size::kOneByte) {
603         uint8_t val = static_cast<uint8_t>(uint_value_);
604         cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
605         pos += sizeof(val);
606       } else if (size_ == Size::kTwoBytes) {
607         cursor.WriteObj(htobe16(static_cast<uint16_t>(uint_value_)));
608         pos += sizeof(uint16_t);
609       } else if (size_ == Size::kFourBytes) {
610         cursor.WriteObj(htobe32(static_cast<uint32_t>(uint_value_)));
611         pos += sizeof(uint32_t);
612       } else if (size_ == Size::kEightBytes) {
613         uint64_t val = htobe64(uint_value_);
614         cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
615         pos += sizeof(val);
616       }
617       return pos;
618     }
619     case Type::kSignedInt: {
620       if (size_ == Size::kOneByte) {
621         int8_t val = static_cast<int8_t>(int_value_);
622         cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
623         pos += sizeof(val);
624       } else if (size_ == Size::kTwoBytes) {
625         cursor.WriteObj(htobe16(static_cast<int16_t>(int_value_)));
626         pos += sizeof(uint16_t);
627       } else if (size_ == Size::kFourBytes) {
628         cursor.WriteObj(htobe32(static_cast<int32_t>(int_value_)));
629         pos += sizeof(uint32_t);
630       } else if (size_ == Size::kEightBytes) {
631         int64_t val = htobe64(static_cast<int64_t>(int_value_));
632         cursor.Write(reinterpret_cast<uint8_t*>(&val), sizeof(val));
633         pos += sizeof(val);
634       }
635       return pos;
636     }
637     case Type::kUuid: {
638       size_t written = uuid_.ToBytes(&cursor);
639       BT_DEBUG_ASSERT(written);
640       // SDP is big-endian, so reverse.
641       std::reverse(cursor.mutable_data(), cursor.mutable_data() + written);
642       pos += written;
643       return pos;
644     }
645     case Type::kString:
646     case Type::kUrl: {
647       size_t used = WriteLength(&cursor, bytes_.size());
648       BT_DEBUG_ASSERT(used);
649       pos += used;
650       cursor.Write(bytes_.data(), bytes_.size(), used);
651       pos += bytes_.size();
652       return pos;
653     }
654     case Type::kSequence:
655     case Type::kAlternative: {
656       size_t used = WriteLength(&cursor, AggregateSize(aggregate_));
657       BT_DEBUG_ASSERT(used);
658       pos += used;
659       cursor = cursor.mutable_view(used);
660       for (const auto& elem : aggregate_) {
661         used = elem.Write(&cursor);
662         BT_DEBUG_ASSERT(used);
663         pos += used;
664         cursor = cursor.mutable_view(used);
665       }
666       return pos;
667     }
668   }
669   return 0;
670 }
671 
At(size_t idx) const672 const DataElement* DataElement::At(size_t idx) const {
673   if ((type_ != Type::kSequence && type_ != Type::kAlternative) ||
674       (idx >= aggregate_.size())) {
675     return nullptr;
676   }
677   return &aggregate_[idx];
678 }
679 
ToString() const680 std::string DataElement::ToString() const {
681   switch (type_) {
682     case Type::kNull:
683       return std::string("Null");
684     case Type::kBoolean:
685       return bt_lib_cpp_string::StringPrintf("Boolean(%s)",
686                                              int_value_ ? "true" : "false");
687     case Type::kUnsignedInt:
688       return bt_lib_cpp_string::StringPrintf(
689           "UnsignedInt:%zu(%lu)", WriteSize() - 1, uint_value_);
690     case Type::kSignedInt:
691       return bt_lib_cpp_string::StringPrintf(
692           "SignedInt:%zu(%ld)", WriteSize() - 1, int_value_);
693     case Type::kUuid:
694       return bt_lib_cpp_string::StringPrintf("UUID(%s)",
695                                              uuid_.ToString().c_str());
696     case Type::kString:
697       return bt_lib_cpp_string::StringPrintf(
698           "String(%s)", bytes_.Printable(0, bytes_.size()).c_str());
699     case Type::kUrl:
700       return bt_lib_cpp_string::StringPrintf(
701           "Url(%s)", bytes_.Printable(0, bytes_.size()).c_str());
702     case Type::kSequence: {
703       std::string str;
704       for (const auto& it : aggregate_) {
705         str += it.ToString() + " ";
706       }
707       return bt_lib_cpp_string::StringPrintf("Sequence { %s}", str.c_str());
708     }
709     case Type::kAlternative: {
710       std::string str;
711       for (const auto& it : aggregate_) {
712         str += it.ToString() + " ";
713       }
714       return bt_lib_cpp_string::StringPrintf("Alternatives { %s}", str.c_str());
715     }
716     default:
717       bt_log(TRACE,
718              "sdp",
719              "unhandled type (%hhu) in ToString()",
720              static_cast<unsigned char>(type_));
721       // Fallthrough to unknown.
722   }
723 
724   return "(unknown)";
725 }
726 }  // namespace bt::sdp
727