1use std::marker::PhantomData;
2use std::ops::Range;
3use std::{iter, mem, slice};
4
5use structbuf::Unpack;
6use tracing::{debug, warn};
7
8pub use builder::*;
9
10use crate::gap::{Uuid, Uuid16, UuidType, UuidVec};
11
12use super::*;
13
14mod builder;
15
16type Idx = u16;
18
19#[derive(Clone, Debug, Default)]
24pub struct Db {
25 attr: Box<[Attr]>,
27 data: Box<[u8]>,
30}
31
32impl Db {
33 #[inline(always)]
35 #[must_use]
36 pub fn build() -> Builder<Self> {
37 Builder::new()
38 }
39
40 #[inline(always)]
42 #[must_use]
43 pub const fn hash(&self) -> u128 {
44 let (p, i) = (self.data.as_ptr(), self.data.len() - mem::size_of::<u128>());
45 u128::from_le_bytes(unsafe { *p.add(i).cast() })
48 }
49
50 #[inline]
52 pub fn iter(&self) -> impl Iterator<Item = (Handle, Uuid, &[u8])> {
53 (self.attr.iter()).map(|at| (at.hdl, self.typ(at), self.value(at)))
54 }
55
56 #[inline]
60 #[must_use]
61 pub fn get(&self, hdl: Handle) -> Option<(Uuid, &[u8])> {
62 (self.try_get(hdl).ok()).map(|at| (self.typ(at), self.value(at)))
63 }
64
65 #[inline]
69 #[must_use]
70 pub(super) fn get_characteristic(&self, hdl: Handle) -> Option<CharInfo> {
71 (self.try_get(hdl).ok()).and_then(|at| self.characteristic_for_attr(at))
72 }
73
74 #[inline]
77 pub fn primary_services(
78 &self,
79 start: Handle,
80 uuid: Option<Uuid>,
81 ) -> impl Iterator<Item = DbEntry<ServiceDef>> {
82 let i = self.try_get(start).map_or_else(|i| i, |at| self.index(at));
83 let uuid = uuid.map_or_else(UuidVec::default, UuidVec::new);
84 GroupIter::new(self, unsafe { self.attr.get_unchecked(i..) }, move |at| {
86 at.is_primary_service() && (uuid.is_empty() || self.value(at) == uuid.as_ref())
87 })
88 }
89
90 pub fn includes(&self, hdls: HandleRange) -> impl Iterator<Item = DbEntry<IncludeDef>> {
93 (self.service_attrs(hdls).iter())
94 .map_while(|at| at.is_include().then(|| DbEntry::new(self, at, at.hdl)))
95 }
96
97 pub fn characteristics(
100 &self,
101 hdls: HandleRange,
102 ) -> impl Iterator<Item = DbEntry<CharacteristicDef>> {
103 GroupIter::new(self, self.service_attrs(hdls), Attr::is_char)
104 }
105
106 pub fn descriptors(&self, hdls: HandleRange) -> impl Iterator<Item = DbEntry<DescriptorDef>> {
109 let attr = self.subset(hdls).and_then(|s| {
110 use private::Group;
111 let decl = unsafe { self.attr.get_unchecked(..s.off).iter() }
113 .rfind(|&at| Attr::is_char(at))?;
114 (value_handle(self.value(decl)) < s.first().hdl
117 && !(s.attr.iter()).any(|at| CharacteristicDef::is_next_group(at.typ)))
118 .then_some(s.attr)
119 });
120 (attr.unwrap_or_default().iter()).map(|at| DbEntry::new(self, at, at.hdl))
121 }
122
123 #[inline]
125 pub fn try_access(&self, req: Request, hdl: Handle) -> RspResult<Handle> {
126 self.try_multi_access(req, [hdl]).map(|_| hdl)
127 }
128
129 #[inline]
131 pub fn try_multi_access<T: AsRef<[Handle]>>(&self, req: Request, hdls: T) -> RspResult<T> {
132 let v = hdls.as_ref();
133 if v.is_empty() {
134 return req.op.err(ErrorCode::InvalidPdu); }
136 for &hdl in v {
138 let Ok(at) = self.try_get(hdl) else {
139 warn!("Denied {} for invalid {hdl}", req.op);
140 return req.op.hdl_err(ErrorCode::InvalidHandle, hdl);
141 };
142 self.access_check(req, at)?;
143 }
144 Ok(hdls)
145 }
146
147 #[inline]
150 pub fn try_range_access(
151 &self,
152 req: Request,
153 hdls: HandleRange,
154 uuid: Uuid,
155 ) -> RspResult<Vec<Handle>> {
156 let attr = self.subset(hdls).map_or_else(Default::default, |s| s.attr);
157 let mut it = (attr.iter())
158 .filter_map(|at| (self.typ(at) == uuid).then(|| self.access_check(req, at)))
159 .peekable();
160 match it.peek() {
162 None => return req.op.hdl_err(ErrorCode::AttributeNotFound, hdls.start()),
163 Some(&r) => {
164 r?;
165 }
166 }
167 Ok(it.map_while(RspResult::ok).collect())
168 }
169
170 pub fn dump(&self) {
176 use Declaration::*;
177 macro_rules! log {
178 ($at:ident, $fmt:expr$(, $($args:tt)*)?) => {
179 ::tracing::debug!("[{:#06X}] {}", u16::from($at.hdl), format_args!($fmt$(, $($args)*)?))
180 };
181 }
182 let mut vhdl = Handle::MIN;
183 let mut last_char_hdl = Handle::MIN;
184 let mut cont = ' ';
185 debug!("GATT database:");
186 for at in self.attr.iter() {
187 let v = self.value(at);
188 let mut v = v.unpack();
189 if let Some(uuid16) = at.typ {
190 match uuid16.typ() {
191 UuidType::Declaration(d) => match d {
192 PrimaryService | SecondaryService => {
193 last_char_hdl = (self.service_group(at.hdl).unwrap().attr.iter())
194 .rfind(|at| at.is_char())
195 .map_or(Handle::MIN, |at| at.hdl);
196 let sec = ((!at.is_primary_service()).then_some("Secondary"))
197 .unwrap_or_default();
198 let uuid = Uuid::try_from(v.as_ref()).unwrap();
199 if let UuidType::Service(_) = uuid.typ() {
200 log!(at, "{sec}{uuid} <{uuid:?}>");
201 } else {
202 log!(at, "{sec}Service <{uuid:?}>");
203 }
204 }
205 Include => log!(at, "|__ [Include {:#06X}..={:#06X}]", v.u16(), v.u16()),
206 Characteristic => {
207 cont = if at.hdl < last_char_hdl { '|' } else { ' ' };
208 let _prop = Prop::from_bits(v.u8()).unwrap(); vhdl = Handle::new(v.u16()).unwrap();
210 let uuid = Uuid::try_from(v.as_ref()).unwrap();
211 if let UuidType::Characteristic(_) = uuid.typ() {
212 log!(at, "|__ {uuid} <{uuid:?}>");
213 } else {
214 log!(at, "|__ Characteristic <{uuid:?}>");
215 }
216 }
217 _ => unreachable!(),
218 },
219 UuidType::Characteristic(_) => log!(at, "{cont} |__ [Value <{uuid16:?}>]"),
220 UuidType::Descriptor(_) => log!(at, "{cont} |__ {uuid16} <{uuid16:?}>"),
221 typ => log!(at, "Unexpected {typ}"),
222 }
223 } else {
224 let uuid = self.typ(at);
225 if at.hdl <= vhdl {
226 log!(at, "{cont} |__ [Value <{uuid:?}>]");
227 } else {
228 log!(at, "{cont} |__ Descriptor <{uuid:?}>");
229 }
230 }
231 }
232 }
233
234 fn service_attrs(&self, hdls: HandleRange) -> &[Attr] {
237 let attr = self.subset(hdls).and_then(|s| {
238 let attr = if s.first().is_service() {
239 unsafe { s.attr.get_unchecked(1..) }
241 } else {
242 s.attr
243 };
244 (!attr.iter().any(Attr::is_service)).then_some(attr)
246 });
247 attr.unwrap_or_default()
248 }
249
250 fn access_check(&self, req: Request, at: &Attr) -> RspResult<Handle> {
252 use Opcode::*;
253 let (op, hdl) = (req.op, at.hdl);
254 if let Err(e) = at.perms.test(req.ac) {
256 warn!("Denied {op} to {hdl} due to {e}");
257 return op.hdl_err(e, hdl);
258 }
259 let Some(ch) = self.characteristic_for_attr(at) else {
260 return Ok(hdl); };
262 if hdl != ch.vhdl {
263 if req.ac.typ() == Access::WRITE
265 && matches!(at.typ, Some(Descriptor::CHARACTERISTIC_USER_DESCRIPTION))
266 && !(ch.ext_props).map_or(false, |p| p.contains(ExtProp::WRITABLE_AUX))
267 {
268 warn!("Denied {op} to {hdl} because WRITABLE_AUX bit is not set");
269 return op.hdl_err(ErrorCode::WriteNotPermitted, hdl);
270 }
271 return Ok(hdl); }
273 let bit = match op {
275 ReadReq | ReadByTypeReq | ReadBlobReq | ReadMultipleReq | ReadMultipleVariableReq => Prop::READ, WriteCmd => Prop::WRITE_CMD, WriteReq | PrepareWriteReq => Prop::WRITE, SignedWriteCmd => Prop::SIGNED_WRITE_CMD, _ => {
285 warn!("Denied non-read/write {op} for {hdl}");
286 return op.hdl_err(ErrorCode::RequestNotSupported, hdl);
287 }
288 };
289 if !ch.props.contains(bit) {
290 let e = if req.ac.typ() == Access::READ {
291 ErrorCode::ReadNotPermitted
292 } else {
293 ErrorCode::WriteNotPermitted
294 };
295 warn!("Denied {op} for {hdl} due to {e} by properties");
296 return op.hdl_err(e, hdl);
297 }
298 if matches!(op, PrepareWriteReq)
300 && !(ch.ext_props).map_or(true, |p| p.contains(ExtProp::RELIABLE_WRITE))
301 {
302 warn!("Denied {op} for {hdl} because RELIABLE_WRITE bit is not set");
308 return op.hdl_err(ErrorCode::WriteNotPermitted, hdl);
309 }
310 Ok(hdl) }
312
313 fn characteristic_for_attr(&self, at: &Attr) -> Option<CharInfo> {
315 use private::Group;
316 let i = self.index(at);
317 let decl = unsafe { self.attr.get_unchecked(..=i).iter() }.rposition(Attr::is_char)?;
319 let end = unsafe { self.attr.get_unchecked(decl + 1..).iter() }
321 .position(|at| CharacteristicDef::is_next_group(at.typ))
322 .map_or(self.attr.len(), |j| decl + 1 + j);
323 if end <= i {
324 return None; }
326 let dval = self.value(unsafe { self.attr.get_unchecked(decl) });
328 let vhdl = value_handle(dval);
329 let val = unsafe { self.attr.get_unchecked(decl + 1..end).iter() }
331 .position(|at| at.hdl == vhdl)
332 .map(|j| decl + 1 + j)
333 .expect("invalid characteristic");
334 let desc = unsafe { self.attr.get_unchecked(val + 1..end) };
336 let props = Prop::from_bits_retain(unsafe { *dval.get_unchecked(0) });
338 let ext_props = props.contains(Prop::EXT_PROPS).then(|| {
339 (desc.iter().find(|&at| Attr::is_ext_props(at))).map_or(ExtProp::empty(), |at| {
340 ExtProp::from_bits_truncate(self.value(at).unpack().u16())
341 })
342 });
343 let at = unsafe { self.attr.get_unchecked(val) };
345 Some(CharInfo {
346 props,
347 ext_props,
348 vhdl: at.hdl,
349 uuid: self.typ(at),
350 _desc: desc,
351 })
352 }
353
354 fn subset(&self, hdls: HandleRange) -> Option<Subset> {
357 let i = self.try_get(hdls.start()).map_or_else(
358 |i| (i < self.attr.len()).then_some(i),
359 |at| Some(self.index(at)),
360 )?;
361 let j = (self.try_get(hdls.end()))
362 .map_or_else(|j| (j > 0).then_some(j), |j| Some(self.index(j) + 1))?;
363 Some(Subset::new(&self.attr, i..j))
364 }
365
366 #[inline]
368 fn typ(&self, at: &Attr) -> Uuid {
369 at.typ.map_or_else(
370 || unsafe {
372 let i = usize::from(at.val.0) - 16;
373 Uuid::new_unchecked(u128::from_le_bytes(*self.data.as_ptr().add(i).cast()))
374 },
375 Uuid16::as_uuid,
376 )
377 }
378}
379
380trait CommonOps {
382 fn attr(&self) -> &[Attr];
384
385 #[must_use]
387 fn data(&self) -> &[u8];
388
389 #[inline]
392 fn try_get(&self, hdl: Handle) -> std::result::Result<&Attr, usize> {
393 fn search(attr: &[Attr], hdl: Handle) -> std::result::Result<&Attr, usize> {
394 attr.binary_search_by(|at| at.hdl.cmp(&hdl))
395 .map(|i| unsafe { attr.get_unchecked(i) })
397 }
398 let i = usize::from(hdl) - 1;
399 let prior = match self.attr().get(i) {
402 Some(at) if at.hdl == hdl => return Ok(unsafe { self.attr().get_unchecked(i) }),
404 Some(_) => unsafe { self.attr().get_unchecked(..i) },
406 None => self.attr(),
407 };
408 search(prior, hdl)
409 }
410
411 #[inline(always)]
413 fn index(&self, at: &Attr) -> usize {
414 unsafe {
418 usize::try_from((at as *const Attr).offset_from(self.attr().as_ptr()))
419 .unwrap_unchecked()
420 }
421 }
422
423 #[inline(always)]
425 #[must_use]
426 fn value(&self, at: &Attr) -> &[u8] {
427 unsafe { (self.data()).get_unchecked(usize::from(at.val.0)..usize::from(at.val.1)) }
429 }
430
431 fn service_group(&self, hdl: Handle) -> Option<Subset> {
434 let Ok(at) = self.try_get(hdl) else { return None };
435 at.is_service().then(|| {
436 let i = self.index(at);
437 let j = unsafe { self.attr().get_unchecked(i + 1..).iter() }
439 .position(Attr::is_service)
440 .map_or(self.attr().len(), |j| i + 1 + j);
441 Subset::new(self.attr(), i..j)
442 })
443 }
444}
445
446impl CommonOps for Db {
447 #[inline(always)]
448 fn attr(&self) -> &[Attr] {
449 &self.attr
450 }
451
452 #[inline(always)]
453 fn data(&self) -> &[u8] {
454 &self.data
455 }
456}
457
458pub trait Group: private::Group {}
460
461impl Group for ServiceDef {}
462impl Group for CharacteristicDef {}
463
464#[derive(Clone, Copy, Debug)]
466pub struct DbEntry<'a, T> {
467 hdls: HandleRange,
468 typ: Uuid,
469 val: &'a [u8],
470 _marker: PhantomData<T>,
471}
472
473impl<'a, T> DbEntry<'a, T> {
474 #[inline(always)]
476 #[must_use]
477 fn new(db: &'a Db, at: &Attr, end_hdl: Handle) -> Self {
478 Self {
479 hdls: HandleRange::new(at.hdl, end_hdl),
480 typ: db.typ(at),
481 val: db.value(at),
482 _marker: PhantomData,
483 }
484 }
485
486 #[inline(always)]
488 #[must_use]
489 pub const fn handle(&self) -> Handle {
490 self.hdls.start()
491 }
492
493 #[inline(always)]
495 #[must_use]
496 pub const fn value(&self) -> &'a [u8] {
497 self.val
498 }
499}
500
501impl<T: Group> DbEntry<'_, T> {
502 #[inline(always)]
504 pub const fn handle_range(&self) -> HandleRange {
505 self.hdls
511 }
512
513 #[inline]
515 #[must_use]
516 pub fn uuid(&self) -> Uuid {
517 Uuid::try_from(unsafe { self.val.get_unchecked(T::UUID_OFF..) })
519 .map_or_else(|_| unreachable!("corrupt database"), |u| u)
520 }
521}
522
523impl DbEntry<'_, CharacteristicDef> {
524 #[inline]
526 #[must_use]
527 pub fn properties(&self) -> Prop {
528 Prop::from_bits_retain(self.val.unpack().u8())
529 }
530
531 #[inline]
533 #[must_use]
534 pub fn value_handle(&self) -> Handle {
535 value_handle(self.val)
536 }
537}
538
539impl DbEntry<'_, DescriptorDef> {
540 #[inline(always)]
542 #[must_use]
543 pub const fn uuid(&self) -> Uuid {
544 self.typ
545 }
546}
547
548impl<'a, T> AsRef<[u8]> for DbEntry<'a, T> {
549 #[inline(always)]
550 fn as_ref(&self) -> &'a [u8] {
551 self.val
552 }
553}
554
555#[derive(Clone, Copy, Debug)]
557pub(super) struct CharInfo<'a> {
558 pub props: Prop,
559 pub ext_props: Option<ExtProp>,
560 pub vhdl: Handle,
561 pub uuid: Uuid,
562 _desc: &'a [Attr], }
564
565#[derive(Clone, Copy, Debug)]
569#[must_use]
570struct Attr {
571 hdl: Handle,
572 typ: Option<Uuid16>,
573 val: (Idx, Idx),
574 perms: Perms,
575}
576
577impl Attr {
578 #[inline(always)]
580 const fn is_service(&self) -> bool {
581 matches!(
582 self.typ,
583 Some(Declaration::PRIMARY_SERVICE | Declaration::SECONDARY_SERVICE)
584 )
585 }
586
587 #[inline(always)]
589 const fn is_primary_service(&self) -> bool {
590 matches!(self.typ, Some(Declaration::PRIMARY_SERVICE))
591 }
592
593 #[inline(always)]
595 const fn is_include(&self) -> bool {
596 matches!(self.typ, Some(Declaration::INCLUDE))
597 }
598
599 #[inline(always)]
601 const fn is_char(&self) -> bool {
602 matches!(self.typ, Some(Declaration::CHARACTERISTIC))
603 }
604
605 #[inline(always)]
607 const fn is_ext_props(&self) -> bool {
608 matches!(
609 self.typ,
610 Some(Descriptor::CHARACTERISTIC_EXTENDED_PROPERTIES)
611 )
612 }
613
614 #[inline(always)]
616 const fn len(&self) -> usize {
617 self.val.1 as usize - self.val.0 as usize
618 }
619}
620
621#[derive(Clone, Copy, Debug)]
623struct Subset<'a> {
624 off: usize,
625 attr: &'a [Attr],
626}
627
628impl<'a> Subset<'a> {
629 #[inline(always)]
631 fn new(attr: &[Attr], r: Range<usize>) -> Subset {
632 debug_assert!(!r.is_empty() && r.end <= attr.len());
633 Subset {
634 off: r.start,
635 attr: unsafe { attr.get_unchecked(r) },
637 }
638 }
639
640 #[inline(always)]
642 fn first(&self) -> &'a Attr {
643 unsafe { self.attr.get_unchecked(0) }
645 }
646
647 #[inline(always)]
649 fn last(&self) -> &'a Attr {
650 unsafe { self.attr.get_unchecked(self.attr.len() - 1) }
652 }
653}
654
655struct GroupIter<'a, T, F> {
656 db: &'a Db,
657 it: iter::Peekable<slice::Iter<'a, Attr>>,
658 is_start: F,
659 _marker: PhantomData<T>,
660}
661
662impl<'a, T: Group, F: Fn(&Attr) -> bool> GroupIter<'a, T, F> {
663 #[inline(always)]
665 #[must_use]
666 fn new(db: &'a Db, it: &'a [Attr], is_start: F) -> Self {
667 Self {
668 db,
669 it: it.iter().peekable(),
670 is_start,
671 _marker: PhantomData,
672 }
673 }
674}
675
676impl<'a, T: Group, F: Fn(&Attr) -> bool> Iterator for GroupIter<'a, T, F> {
677 type Item = DbEntry<'a, T>;
678
679 fn next(&mut self) -> Option<Self::Item> {
680 let decl = self.it.find(|at| (self.is_start)(at))?;
681 let mut end = decl.hdl;
682 while !self.it.peek().map_or(true, |at| T::is_next_group(at.typ)) {
683 end = unsafe { self.it.next().unwrap_unchecked().hdl };
685 }
686 Some(DbEntry::new(self.db, decl, end))
687 }
688
689 #[inline]
690 fn size_hint(&self) -> (usize, Option<usize>) {
691 self.it.size_hint()
692 }
693}
694
695impl<T: Group, F: Fn(&Attr) -> bool> iter::FusedIterator for GroupIter<'_, T, F> {}
696
697#[inline]
700fn value_handle(decl: &[u8]) -> Handle {
701 Handle::new(decl.unpack().split_at(1).1.u16()).unwrap_or(Handle::MAX)
702}
703
704mod private {
705 use super::*;
706
707 pub trait Group {
709 const UUID_OFF: usize = 0;
711
712 #[inline(always)]
715 #[must_use]
716 fn is_next_group(typ: Option<Uuid16>) -> bool {
717 matches!(
718 typ,
719 Some(Declaration::PRIMARY_SERVICE | Declaration::SECONDARY_SERVICE)
720 )
721 }
722 }
723
724 impl Group for ServiceDef {}
725
726 impl Group for CharacteristicDef {
727 const UUID_OFF: usize = 3;
728
729 #[inline(always)]
730 fn is_next_group(typ: Option<Uuid16>) -> bool {
731 matches!(
733 typ,
734 Some(
735 Declaration::PRIMARY_SERVICE
736 | Declaration::SECONDARY_SERVICE
737 | Declaration::INCLUDE
738 | Declaration::CHARACTERISTIC
739 )
740 )
741 }
742 }
743}