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