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