1 //! This module extracts the common logic in filtering attributes by type +
2 //! length, used in READ_BY_TYPE_REQ and READ_BY_GROUP_TYPE_REQ
3
4 use crate::{
5 core::uuid::Uuid,
6 gatt::server::att_database::{AttAttribute, StableAttDatabase},
7 packets::{AttAttributeDataChild, AttErrorCode, Serializable},
8 };
9
10 use super::truncate_att_data::truncate_att_data;
11
12 /// An attribute and the value
13 #[derive(Debug, PartialEq, Eq)]
14 pub struct AttributeWithValue {
15 /// The attribute
16 pub attr: AttAttribute,
17 pub value: AttAttributeDataChild,
18 }
19
20 /// Takes a StableAttDatabase, a range of handles, a target type, and a
21 /// size limit.
22 ///
23 /// Returns an iterator of attributes in the range and matching the type,
24 /// with the max number of elements such that each attribute has the same
25 /// size.
26 ///
27 /// Attributes are truncated to the attr_size limit before size comparison.
28 /// If an error occurs while reading, do not output further attributes.
filter_read_attributes_by_size_type( db: &impl StableAttDatabase, attrs: impl Iterator<Item = AttAttribute>, target: Uuid, size_limit: usize, ) -> Result<impl Iterator<Item = AttributeWithValue>, AttErrorCode>29 pub async fn filter_read_attributes_by_size_type(
30 db: &impl StableAttDatabase,
31 attrs: impl Iterator<Item = AttAttribute>,
32 target: Uuid,
33 size_limit: usize,
34 ) -> Result<impl Iterator<Item = AttributeWithValue>, AttErrorCode> {
35 let target_attrs = attrs.filter(|attr| attr.type_ == target);
36
37 let mut out = vec![];
38 let mut curr_elem_size = None;
39
40 for attr @ AttAttribute { handle, .. } in target_attrs {
41 match db.read_attribute(handle).await {
42 Ok(value) => {
43 let value = truncate_att_data(value, size_limit);
44 let value_size = value.size_in_bits().unwrap_or(0);
45 if let Some(curr_elem_size) = curr_elem_size {
46 if curr_elem_size != value_size {
47 // no more attributes of the same size
48 break;
49 }
50 } else {
51 curr_elem_size = Some(value_size)
52 }
53
54 out.push(AttributeWithValue { attr, value });
55 }
56 Err(err) => {
57 if out.is_empty() {
58 return Err(err);
59 }
60 break;
61 }
62 }
63 }
64
65 Ok(out.into_iter())
66 }
67
68 #[cfg(test)]
69 mod test {
70 use super::*;
71
72 use crate::{
73 core::uuid::Uuid,
74 gatt::{
75 ids::AttHandle,
76 server::{
77 att_database::{AttAttribute, AttDatabase, StableAttDatabase},
78 gatt_database::AttPermissions,
79 test::test_att_db::TestAttDatabase,
80 },
81 },
82 packets::AttAttributeDataChild,
83 };
84
85 const UUID: Uuid = Uuid::new(1234);
86 const ANOTHER_UUID: Uuid = Uuid::new(2345);
87
88 #[test]
test_single_matching_attr()89 fn test_single_matching_attr() {
90 // arrange
91 let db = TestAttDatabase::new(vec![(
92 AttAttribute {
93 handle: AttHandle(3),
94 type_: UUID,
95 permissions: AttPermissions::READABLE,
96 },
97 vec![4, 5],
98 )]);
99
100 // act
101 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
102 &db,
103 db.list_attributes().into_iter(),
104 UUID,
105 31,
106 ))
107 .unwrap();
108
109 // assert
110 assert_eq!(
111 response.collect::<Vec<_>>(),
112 vec![AttributeWithValue {
113 attr: db.find_attribute(AttHandle(3)).unwrap(),
114 value: AttAttributeDataChild::RawData([4, 5].into())
115 }]
116 )
117 }
118
119 #[test]
test_skip_mismatching_attrs()120 fn test_skip_mismatching_attrs() {
121 // arrange
122 let db = TestAttDatabase::new(vec![
123 (
124 AttAttribute {
125 handle: AttHandle(3),
126 type_: UUID,
127 permissions: AttPermissions::READABLE,
128 },
129 vec![4, 5],
130 ),
131 (
132 AttAttribute {
133 handle: AttHandle(5),
134 type_: ANOTHER_UUID,
135 permissions: AttPermissions::READABLE,
136 },
137 vec![5, 6],
138 ),
139 (
140 AttAttribute {
141 handle: AttHandle(6),
142 type_: UUID,
143 permissions: AttPermissions::READABLE,
144 },
145 vec![6, 7],
146 ),
147 ]);
148
149 // act
150 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
151 &db,
152 db.list_attributes().into_iter(),
153 UUID,
154 31,
155 ))
156 .unwrap();
157
158 // assert
159 assert_eq!(
160 response.collect::<Vec<_>>(),
161 vec![
162 AttributeWithValue {
163 attr: db.find_attribute(AttHandle(3)).unwrap(),
164 value: AttAttributeDataChild::RawData([4, 5].into())
165 },
166 AttributeWithValue {
167 attr: db.find_attribute(AttHandle(6)).unwrap(),
168 value: AttAttributeDataChild::RawData([6, 7].into())
169 }
170 ]
171 );
172 }
173
174 #[test]
test_stop_once_length_changes()175 fn test_stop_once_length_changes() {
176 // arrange
177 let db = TestAttDatabase::new(vec![
178 (
179 AttAttribute {
180 handle: AttHandle(3),
181 type_: UUID,
182 permissions: AttPermissions::READABLE,
183 },
184 vec![4, 5],
185 ),
186 (
187 AttAttribute {
188 handle: AttHandle(5),
189 type_: UUID,
190 permissions: AttPermissions::READABLE,
191 },
192 vec![5],
193 ),
194 (
195 AttAttribute {
196 handle: AttHandle(6),
197 type_: UUID,
198 permissions: AttPermissions::READABLE,
199 },
200 vec![6, 7],
201 ),
202 ]);
203
204 // act
205 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
206 &db,
207 db.list_attributes().into_iter(),
208 UUID,
209 31,
210 ))
211 .unwrap();
212
213 // assert
214 assert_eq!(
215 response.collect::<Vec<_>>(),
216 vec![AttributeWithValue {
217 attr: db.find_attribute(AttHandle(3)).unwrap(),
218 value: AttAttributeDataChild::RawData([4, 5].into())
219 },]
220 );
221 }
222
223 #[test]
test_truncate_to_mtu()224 fn test_truncate_to_mtu() {
225 // arrange: attr with data of length 3
226 let db = TestAttDatabase::new(vec![(
227 AttAttribute {
228 handle: AttHandle(3),
229 type_: UUID,
230 permissions: AttPermissions::READABLE,
231 },
232 vec![4, 5, 6],
233 )]);
234
235 // act: read the attribute with max_size = 2
236 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
237 &db,
238 db.list_attributes().into_iter(),
239 UUID,
240 2,
241 ))
242 .unwrap();
243
244 // assert: the length of the read attribute is 2
245 assert_eq!(
246 response.collect::<Vec<_>>(),
247 vec![AttributeWithValue {
248 attr: db.find_attribute(AttHandle(3)).unwrap(),
249 value: AttAttributeDataChild::RawData([4, 5].into())
250 },]
251 );
252 }
253
254 #[test]
test_no_results()255 fn test_no_results() {
256 // arrange: an empty database
257 let db = TestAttDatabase::new(vec![]);
258
259 // act
260 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
261 &db,
262 db.list_attributes().into_iter(),
263 UUID,
264 31,
265 ))
266 .unwrap();
267
268 // assert: no results
269 assert_eq!(response.count(), 0)
270 }
271
272 #[test]
test_read_failure_on_first_attr()273 fn test_read_failure_on_first_attr() {
274 // arrange: put a non-readable attribute in the db with the right type
275 let db = TestAttDatabase::new(vec![(
276 AttAttribute {
277 handle: AttHandle(3),
278 type_: UUID,
279 permissions: AttPermissions::empty(),
280 },
281 vec![4, 5, 6],
282 )]);
283
284 // act
285 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
286 &db,
287 db.list_attributes().into_iter(),
288 UUID,
289 31,
290 ));
291
292 // assert: got READ_NOT_PERMITTED
293 assert!(matches!(response, Err(AttErrorCode::READ_NOT_PERMITTED)));
294 }
295
296 #[test]
test_read_failure_on_subsequent_attr()297 fn test_read_failure_on_subsequent_attr() {
298 // arrange: put a non-readable attribute in the db with the right
299 // type
300 let db = TestAttDatabase::new(vec![
301 (
302 AttAttribute {
303 handle: AttHandle(3),
304 type_: UUID,
305 permissions: AttPermissions::READABLE,
306 },
307 vec![4, 5, 6],
308 ),
309 (
310 AttAttribute {
311 handle: AttHandle(4),
312 type_: UUID,
313 permissions: AttPermissions::empty(),
314 },
315 vec![5, 6, 7],
316 ),
317 (
318 AttAttribute {
319 handle: AttHandle(5),
320 type_: UUID,
321 permissions: AttPermissions::READABLE,
322 },
323 vec![8, 9, 10],
324 ),
325 ]);
326
327 // act
328 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
329 &db,
330 db.list_attributes().into_iter(),
331 UUID,
332 31,
333 ))
334 .unwrap();
335
336 // assert: we reply with the first attribute, but not the second or third
337 // (since we stop on the first failure)
338 assert_eq!(
339 response.collect::<Vec<_>>(),
340 vec![AttributeWithValue {
341 attr: db.find_attribute(AttHandle(3)).unwrap(),
342 value: AttAttributeDataChild::RawData([4, 5, 6].into())
343 },]
344 );
345 }
346
347 #[test]
test_skip_unreadable_mismatching_attr()348 fn test_skip_unreadable_mismatching_attr() {
349 // arrange: put a non-readable attribute in the db with the wrong type
350 // between two attributes of interest
351 let db = TestAttDatabase::new(vec![
352 (
353 AttAttribute {
354 handle: AttHandle(3),
355 type_: UUID,
356 permissions: AttPermissions::READABLE,
357 },
358 vec![4, 5, 6],
359 ),
360 (
361 AttAttribute {
362 handle: AttHandle(4),
363 type_: ANOTHER_UUID,
364 permissions: AttPermissions::empty(),
365 },
366 vec![5, 6, 7],
367 ),
368 (
369 AttAttribute {
370 handle: AttHandle(5),
371 type_: UUID,
372 permissions: AttPermissions::READABLE,
373 },
374 vec![6, 7, 8],
375 ),
376 ]);
377
378 // act
379 let response = tokio_test::block_on(filter_read_attributes_by_size_type(
380 &db,
381 db.list_attributes().into_iter(),
382 UUID,
383 31,
384 ))
385 .unwrap();
386
387 // assert: we reply with the first and third attributes, but not the second
388 assert_eq!(
389 response.collect::<Vec<_>>(),
390 vec![
391 AttributeWithValue {
392 attr: db.find_attribute(AttHandle(3)).unwrap(),
393 value: AttAttributeDataChild::RawData([4, 5, 6].into())
394 },
395 AttributeWithValue {
396 attr: db.find_attribute(AttHandle(5)).unwrap(),
397 value: AttAttributeDataChild::RawData([6, 7, 8].into())
398 }
399 ]
400 );
401 }
402 }
403