• 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/common/supplement_data.h"
16 
17 #include <pw_assert/check.h>
18 #include <pw_preprocessor/compiler.h>
19 
20 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
21 
22 namespace bt {
23 
ParseUuids(const BufferView & data,UUIDElemSize uuid_size,UuidFunction func)24 bool ParseUuids(const BufferView& data,
25                 UUIDElemSize uuid_size,
26                 UuidFunction func) {
27   PW_CHECK(func);
28 
29   if (data.size() % uuid_size) {
30     return false;
31   }
32 
33   size_t uuid_count = data.size() / uuid_size;
34   for (size_t i = 0; i < uuid_count; i++) {
35     const BufferView uuid_bytes(data.data() + (i * uuid_size), uuid_size);
36     UUID uuid;
37     if (!UUID::FromBytes(uuid_bytes, &uuid) || !func(uuid)) {
38       return false;
39     }
40   }
41 
42   return true;
43 }
44 
SizeForType(DataType type)45 UUIDElemSize SizeForType(DataType type) {
46   PW_MODIFY_DIAGNOSTICS_PUSH();
47   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
48   switch (type) {
49     case DataType::kIncomplete16BitServiceUuids:
50     case DataType::kComplete16BitServiceUuids:
51     case DataType::kServiceData16Bit:
52     case DataType::kSolicitationUuid16Bit:
53       return UUIDElemSize::k16Bit;
54     case DataType::kIncomplete32BitServiceUuids:
55     case DataType::kComplete32BitServiceUuids:
56     case DataType::kServiceData32Bit:
57     case DataType::kSolicitationUuid32Bit:
58       return UUIDElemSize::k32Bit;
59     case DataType::kIncomplete128BitServiceUuids:
60     case DataType::kComplete128BitServiceUuids:
61     case DataType::kServiceData128Bit:
62     case DataType::kSolicitationUuid128Bit:
63       return UUIDElemSize::k128Bit;
64     default:
65       break;
66   };
67   PW_MODIFY_DIAGNOSTICS_POP();
68 
69   PW_CRASH("called SizeForType with non-UUID DataType %du",
70            static_cast<uint8_t>(type));
71   return UUIDElemSize::k16Bit;
72 }
73 
SupplementDataReader(const ByteBuffer & data)74 SupplementDataReader::SupplementDataReader(const ByteBuffer& data)
75     : is_valid_(true), remaining_(data) {
76   if (!remaining_.size()) {
77     is_valid_ = false;
78     return;
79   }
80 
81   // Do a validity check.
82   BufferView tmp(remaining_);
83   while (tmp.size()) {
84     size_t tlv_len = tmp[0];
85 
86     // A struct can have 0 as its length. In that case its valid to terminate.
87     if (!tlv_len)
88       break;
89 
90     // The full struct includes the length octet itself.
91     size_t struct_size = tlv_len + 1;
92     if (struct_size > tmp.size()) {
93       is_valid_ = false;
94       break;
95     }
96 
97     tmp = tmp.view(struct_size);
98   }
99 }
100 
GetNextField(DataType * out_type,BufferView * out_data)101 bool SupplementDataReader::GetNextField(DataType* out_type,
102                                         BufferView* out_data) {
103   PW_DCHECK(out_type);
104   PW_DCHECK(out_data);
105 
106   if (!HasMoreData())
107     return false;
108 
109   size_t tlv_len = remaining_[0];
110   size_t cur_struct_size = tlv_len + 1;
111   PW_DCHECK(cur_struct_size <= remaining_.size());
112 
113   *out_type = static_cast<DataType>(remaining_[1]);
114   *out_data = remaining_.view(2, tlv_len - 1);
115 
116   // Update |remaining_|.
117   remaining_ = remaining_.view(cur_struct_size);
118   return true;
119 }
120 
HasMoreData() const121 bool SupplementDataReader::HasMoreData() const {
122   if (!is_valid_ || !remaining_.size())
123     return false;
124 
125   // If the buffer is valid and has remaining bytes but the length of the next
126   // segment is zero, then we terminate.
127   return !!remaining_[0];
128 }
129 
SupplementDataWriter(MutableByteBuffer * buffer)130 SupplementDataWriter::SupplementDataWriter(MutableByteBuffer* buffer)
131     : buffer_(buffer), bytes_written_(0u) {
132   PW_DCHECK(buffer_);
133 }
134 
WriteField(DataType type,const ByteBuffer & data)135 bool SupplementDataWriter::WriteField(DataType type, const ByteBuffer& data) {
136   size_t next_size = data.size() + 2;  // 2 bytes for [length][type].
137   if (bytes_written_ + next_size > buffer_->size() || next_size > 255)
138     return false;
139 
140   (*buffer_)[bytes_written_++] = static_cast<uint8_t>(next_size) - 1;
141   (*buffer_)[bytes_written_++] = static_cast<uint8_t>(type);
142 
143   // Get a view into the offset we want to write to.
144   auto target = buffer_->mutable_view(bytes_written_);
145 
146   // Copy the data into the view.
147   data.Copy(&target);
148 
149   bytes_written_ += data.size();
150 
151   return true;
152 }
153 
154 }  // namespace bt
155