rs-matter/matter/src/tlv/parser.rs
2023-05-24 10:07:11 +00:00

1233 lines
41 KiB
Rust

/*
*
* Copyright (c) 2020-2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::error::{Error, ErrorCode};
use byteorder::{ByteOrder, LittleEndian};
use core::fmt;
use log::{error, info};
use super::{TagType, MAX_TAG_INDEX, TAG_MASK, TAG_SHIFT_BITS, TAG_SIZE_MAP, TYPE_MASK};
pub struct TLVList<'a> {
buf: &'a [u8],
}
impl<'a> TLVList<'a> {
pub fn new(buf: &'a [u8]) -> TLVList<'a> {
TLVList { buf }
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Pointer<'a> {
buf: &'a [u8],
current: usize,
left: usize,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ElementType<'a> {
S8(i8),
S16(i16),
S32(i32),
S64(i64),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
False,
True,
F32(f32),
F64(f64),
Utf8l(&'a [u8]),
Utf16l(&'a [u8]),
Utf32l,
Utf64l,
Str8l(&'a [u8]),
Str16l(&'a [u8]),
Str32l,
Str64l,
Null,
Struct(Pointer<'a>),
Array(Pointer<'a>),
List(Pointer<'a>),
EndCnt,
Last,
}
const MAX_VALUE_INDEX: usize = 25;
// This is a function that takes a TLVListIterator and returns the tag type
type ExtractTag = for<'a> fn(&TLVListIterator<'a>) -> TagType;
static TAG_EXTRACTOR: [ExtractTag; 8] = [
// Anonymous 0
|_t| TagType::Anonymous,
// Context 1
|t| TagType::Context(t.buf[t.current]),
// CommonPrf16 2
|t| TagType::CommonPrf16(LittleEndian::read_u16(&t.buf[t.current..])),
// CommonPrf32 3
|t| TagType::CommonPrf32(LittleEndian::read_u32(&t.buf[t.current..])),
// ImplPrf16 4
|t| TagType::ImplPrf16(LittleEndian::read_u16(&t.buf[t.current..])),
// ImplPrf32 5
|t| TagType::ImplPrf32(LittleEndian::read_u32(&t.buf[t.current..])),
// FullQual48 6
|t| TagType::FullQual48(LittleEndian::read_u48(&t.buf[t.current..])),
// FullQual64 7
|t| TagType::FullQual64(LittleEndian::read_u64(&t.buf[t.current..])),
];
// This is a function that takes a TLVListIterator and returns the element type
// Some elements (like strings), also consume additional size, than that mentioned
// if this is the case, the additional size is returned
type ExtractValue = for<'a> fn(&TLVListIterator<'a>) -> (usize, ElementType<'a>);
static VALUE_EXTRACTOR: [ExtractValue; MAX_VALUE_INDEX] = [
// S8 0
{ |t| (0, ElementType::S8(t.buf[t.current] as i8)) },
// S16 1
{
|t| {
(
0,
ElementType::S16(LittleEndian::read_i16(&t.buf[t.current..])),
)
}
},
// S32 2
{
|t| {
(
0,
ElementType::S32(LittleEndian::read_i32(&t.buf[t.current..])),
)
}
},
// S64 3
{
|t| {
(
0,
ElementType::S64(LittleEndian::read_i64(&t.buf[t.current..])),
)
}
},
// U8 4
{ |t| (0, ElementType::U8(t.buf[t.current])) },
// U16 5
{
|t| {
(
0,
ElementType::U16(LittleEndian::read_u16(&t.buf[t.current..])),
)
}
},
// U32 6
{
|t| {
(
0,
ElementType::U32(LittleEndian::read_u32(&t.buf[t.current..])),
)
}
},
// U64 7
{
|t| {
(
0,
ElementType::U64(LittleEndian::read_u64(&t.buf[t.current..])),
)
}
},
// False 8
{ |_t| (0, ElementType::False) },
// True 9
{ |_t| (0, ElementType::True) },
// F32 10
{ |_t| (0, ElementType::Last) },
// F64 11
{ |_t| (0, ElementType::Last) },
// Utf8l 12
{
|t| match read_length_value(1, t) {
Err(_) => (0, ElementType::Last),
Ok((size, string)) => (size, ElementType::Utf8l(string)),
}
},
// Utf16l 13
{
|t| match read_length_value(2, t) {
Err(_) => (0, ElementType::Last),
Ok((size, string)) => (size, ElementType::Utf16l(string)),
}
},
// Utf32l 14
{ |_t| (0, ElementType::Last) },
// Utf64l 15
{ |_t| (0, ElementType::Last) },
// Str8l 16
{
|t| match read_length_value(1, t) {
Err(_) => (0, ElementType::Last),
Ok((size, string)) => (size, ElementType::Str8l(string)),
}
},
// Str16l 17
{
|t| match read_length_value(2, t) {
Err(_) => (0, ElementType::Last),
Ok((size, string)) => (size, ElementType::Str16l(string)),
}
},
// Str32l 18
{ |_t| (0, ElementType::Last) },
// Str64l 19
{ |_t| (0, ElementType::Last) },
// Null 20
{ |_t| (0, ElementType::Null) },
// Struct 21
{
|t| {
(
0,
ElementType::Struct(Pointer {
buf: t.buf,
current: t.current,
left: t.left,
}),
)
}
},
// Array 22
{
|t| {
(
0,
ElementType::Array(Pointer {
buf: t.buf,
current: t.current,
left: t.left,
}),
)
}
},
// List 23
{
|t| {
(
0,
ElementType::List(Pointer {
buf: t.buf,
current: t.current,
left: t.left,
}),
)
}
},
// EndCnt 24
{ |_t| (0, ElementType::EndCnt) },
];
// The array indices here correspond to the numeric value of the Element Type as defined in the Matter Spec
static VALUE_SIZE_MAP: [usize; MAX_VALUE_INDEX] = [
1, // S8 0
2, // S16 1
4, // S32 2
8, // S64 3
1, // U8 4
2, // U16 5
4, // U32 6
8, // U64 7
0, // False 8
0, // True 9
4, // F32 10
8, // F64 11
1, // Utf8l 12
2, // Utf16l 13
4, // Utf32l 14
8, // Utf64l 15
1, // Str8l 16
2, // Str16l 17
4, // Str32l 18
8, // Str64l 19
0, // Null 20
0, // Struct 21
0, // Array 22
0, // List 23
0, // EndCnt 24
];
fn read_length_value<'a>(
size_of_length_field: usize,
t: &TLVListIterator<'a>,
) -> Result<(usize, &'a [u8]), Error> {
// The current offset is the string size
let length: usize = LittleEndian::read_uint(&t.buf[t.current..], size_of_length_field) as usize;
// We'll consume the current offset (len) + the entire string
if length + size_of_length_field > t.left {
// Return Error
Err(ErrorCode::NoSpace.into())
} else {
Ok((
// return the additional size only
length,
&t.buf[(t.current + size_of_length_field)..(t.current + size_of_length_field + length)],
))
}
}
#[derive(Debug, Copy, Clone)]
pub struct TLVElement<'a> {
tag_type: TagType,
element_type: ElementType<'a>,
}
impl<'a> PartialEq for TLVElement<'a> {
fn eq(&self, other: &Self) -> bool {
match self.element_type {
ElementType::Struct(a) | ElementType::Array(a) | ElementType::List(a) => {
let mut our_iter = TLVListIterator::from_pointer(a);
let mut their = match other.element_type {
ElementType::Struct(b) | ElementType::Array(b) | ElementType::List(b) => {
TLVListIterator::from_pointer(b)
}
_ => {
// If we are a container, the other must be a container, else this is a mismatch
return false;
}
};
let mut nest_level = 0_u8;
loop {
let ours = our_iter.next();
let theirs = their.next();
if core::mem::discriminant(&ours) != core::mem::discriminant(&theirs) {
// One of us reached end of list, but the other didn't, that's a mismatch
return false;
}
if ours.is_none() {
// End of list
break;
}
// guaranteed to work
let ours = ours.unwrap();
let theirs = theirs.unwrap();
if let ElementType::EndCnt = ours.element_type {
if nest_level == 0 {
break;
}
nest_level -= 1;
} else {
if is_container(ours.element_type) {
nest_level += 1;
// Only compare the discriminants in case of array/list/structures,
// instead of actual element values. Those will be subsets within this same
// list that will get validated anyway
if core::mem::discriminant(&ours.element_type)
!= core::mem::discriminant(&theirs.element_type)
{
return false;
}
} else if ours.element_type != theirs.element_type {
return false;
}
if ours.tag_type != theirs.tag_type {
return false;
}
}
}
true
}
_ => self.tag_type == other.tag_type && self.element_type == other.element_type,
}
}
}
impl<'a> TLVElement<'a> {
pub fn enter(&self) -> Option<TLVContainerIterator<'a>> {
let ptr = match self.element_type {
ElementType::Struct(a) | ElementType::Array(a) | ElementType::List(a) => a,
_ => return None,
};
let list_iter = TLVListIterator {
buf: ptr.buf,
current: ptr.current,
left: ptr.left,
};
Some(TLVContainerIterator {
list_iter,
prev_container: false,
iterator_consumed: false,
})
}
pub fn new(tag: TagType, value: ElementType<'a>) -> Self {
Self {
tag_type: tag,
element_type: value,
}
}
pub fn i8(&self) -> Result<i8, Error> {
match self.element_type {
ElementType::S8(a) => Ok(a),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn u8(&self) -> Result<u8, Error> {
match self.element_type {
ElementType::U8(a) => Ok(a),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn u16(&self) -> Result<u16, Error> {
match self.element_type {
ElementType::U8(a) => Ok(a.into()),
ElementType::U16(a) => Ok(a),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn u32(&self) -> Result<u32, Error> {
match self.element_type {
ElementType::U8(a) => Ok(a.into()),
ElementType::U16(a) => Ok(a.into()),
ElementType::U32(a) => Ok(a),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn u64(&self) -> Result<u64, Error> {
match self.element_type {
ElementType::U8(a) => Ok(a.into()),
ElementType::U16(a) => Ok(a.into()),
ElementType::U32(a) => Ok(a.into()),
ElementType::U64(a) => Ok(a),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn slice(&self) -> Result<&'a [u8], Error> {
match self.element_type {
ElementType::Str8l(s)
| ElementType::Utf8l(s)
| ElementType::Str16l(s)
| ElementType::Utf16l(s) => Ok(s),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn str(&self) -> Result<&'a str, Error> {
match self.element_type {
ElementType::Str8l(s)
| ElementType::Utf8l(s)
| ElementType::Str16l(s)
| ElementType::Utf16l(s) => {
Ok(core::str::from_utf8(s).map_err(|_| Error::from(ErrorCode::InvalidData))?)
}
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn bool(&self) -> Result<bool, Error> {
match self.element_type {
ElementType::False => Ok(false),
ElementType::True => Ok(true),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn null(&self) -> Result<(), Error> {
match self.element_type {
ElementType::Null => Ok(()),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn confirm_struct(&self) -> Result<TLVElement<'a>, Error> {
match self.element_type {
ElementType::Struct(_) => Ok(*self),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn confirm_array(&self) -> Result<TLVElement<'a>, Error> {
match self.element_type {
ElementType::Array(_) => Ok(*self),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn confirm_list(&self) -> Result<TLVElement<'a>, Error> {
match self.element_type {
ElementType::List(_) => Ok(*self),
_ => Err(ErrorCode::TLVTypeMismatch.into()),
}
}
pub fn find_tag(&self, tag: u32) -> Result<TLVElement<'a>, Error> {
let match_tag: TagType = TagType::Context(tag as u8);
let iter = self.enter().ok_or(ErrorCode::TLVTypeMismatch)?;
for a in iter {
if match_tag == a.tag_type {
return Ok(a);
}
}
Err(ErrorCode::NoTagFound.into())
}
pub fn get_tag(&self) -> TagType {
self.tag_type
}
pub fn check_ctx_tag(&self, tag: u8) -> bool {
if let TagType::Context(our_tag) = self.tag_type {
if our_tag == tag {
return true;
}
}
false
}
pub fn get_element_type(&self) -> ElementType {
self.element_type
}
}
impl<'a> fmt::Display for TLVElement<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.tag_type {
TagType::Anonymous => (),
TagType::Context(tag) => write!(f, "{}: ", tag)?,
_ => write!(f, "Other Context Tag")?,
}
match self.element_type {
ElementType::Struct(_) => write!(f, "{{"),
ElementType::Array(_) => write!(f, "["),
ElementType::List(_) => write!(f, "["),
ElementType::EndCnt => write!(f, ">"),
ElementType::True => write!(f, "True"),
ElementType::False => write!(f, "False"),
ElementType::Str8l(a)
| ElementType::Utf8l(a)
| ElementType::Str16l(a)
| ElementType::Utf16l(a) => {
if let Ok(s) = core::str::from_utf8(a) {
write!(f, "len[{}]\"{}\"", s.len(), s)
} else {
write!(f, "len[{}]{:x?}", a.len(), a)
}
}
_ => write!(f, "{:?}", self.element_type),
}
}
}
// This is a TLV List iterator, it only iterates over the individual TLVs in a TLV list
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TLVListIterator<'a> {
buf: &'a [u8],
current: usize,
left: usize,
}
impl<'a> TLVListIterator<'a> {
fn from_pointer(p: Pointer<'a>) -> Self {
Self {
buf: p.buf,
current: p.current,
left: p.left,
}
}
fn advance(&mut self, len: usize) {
self.current += len;
self.left -= len;
}
// Caller should ensure they are reading the _right_ tag at the _right_ place
fn read_this_tag(&mut self, tag_type: u8) -> Option<TagType> {
if tag_type as usize >= MAX_TAG_INDEX {
return None;
}
let tag_size = TAG_SIZE_MAP[tag_type as usize];
if tag_size > self.left {
return None;
}
let tag = (TAG_EXTRACTOR[tag_type as usize])(self);
self.advance(tag_size);
Some(tag)
}
fn read_this_value(&mut self, element_type: u8) -> Option<ElementType<'a>> {
if element_type as usize >= MAX_VALUE_INDEX {
return None;
}
let mut size = VALUE_SIZE_MAP[element_type as usize];
if size > self.left {
error!(
"Invalid value found: {} self {:?} size {}",
element_type, self, size
);
return None;
}
let (extra_size, element) = (VALUE_EXTRACTOR[element_type as usize])(self);
if element != ElementType::Last {
size += extra_size;
self.advance(size);
Some(element)
} else {
None
}
}
}
impl<'a> Iterator for TLVListIterator<'a> {
type Item = TLVElement<'a>;
/* Code for going to the next Element */
fn next(&mut self) -> Option<TLVElement<'a>> {
if self.left < 1 {
return None;
}
/* Read Control */
let control = self.buf[self.current];
let tag_type = (control & TAG_MASK) >> TAG_SHIFT_BITS;
let element_type = control & TYPE_MASK;
self.advance(1);
/* Consume Tag */
let tag_type = self.read_this_tag(tag_type)?;
/* Consume Value */
let element_type = self.read_this_value(element_type)?;
Some(TLVElement {
tag_type,
element_type,
})
}
}
impl<'a> TLVList<'a> {
pub fn iter(&self) -> TLVListIterator<'a> {
TLVListIterator {
current: 0,
left: self.buf.len(),
buf: self.buf,
}
}
}
fn is_container(element_type: ElementType) -> bool {
matches!(
element_type,
ElementType::Struct(_) | ElementType::Array(_) | ElementType::List(_)
)
}
// This is a Container iterator, it iterates over containers in a TLV list
#[derive(Debug, PartialEq)]
pub struct TLVContainerIterator<'a> {
list_iter: TLVListIterator<'a>,
prev_container: bool,
iterator_consumed: bool,
}
impl<'a> TLVContainerIterator<'a> {
fn skip_to_end_of_container(&mut self) -> Option<TLVElement<'a>> {
let mut nest_level = 0;
while let Some(element) = self.list_iter.next() {
// We know we are already in a container, we have to keep looking for end-of-container
// println!("Skip: element: {:x?} nest_level: {}", element, nest_level);
match element.element_type {
ElementType::EndCnt => {
if nest_level == 0 {
// Return the element following this element
// println!("Returning");
// The final next() may be the end of the top-level container itself, if so, we must return None
let last_elem = self.list_iter.next()?;
match last_elem.element_type {
ElementType::EndCnt => {
self.iterator_consumed = true;
return None;
}
_ => return Some(last_elem),
}
}
nest_level -= 1;
}
_ => {
if is_container(element.element_type) {
nest_level += 1;
}
}
}
}
None
}
}
impl<'a> Iterator for TLVContainerIterator<'a> {
type Item = TLVElement<'a>;
/* Code for going to the next Element */
fn next(&mut self) -> Option<TLVElement<'a>> {
// This iterator may be consumed, but the underlying might not. This protects it from such occurrences
if self.iterator_consumed {
return None;
}
let element: TLVElement = if self.prev_container {
// println!("Calling skip to end of container");
self.skip_to_end_of_container()?
} else {
self.list_iter.next()?
};
// println!("Found element: {:x?}", element);
/* If we found end of container, that means our own container is over */
if element.element_type == ElementType::EndCnt {
self.iterator_consumed = true;
return None;
}
self.prev_container = is_container(element.element_type);
Some(element)
}
}
pub fn get_root_node(b: &[u8]) -> Result<TLVElement, Error> {
Ok(TLVList::new(b)
.iter()
.next()
.ok_or(ErrorCode::InvalidData)?)
}
pub fn get_root_node_struct(b: &[u8]) -> Result<TLVElement, Error> {
TLVList::new(b)
.iter()
.next()
.ok_or(ErrorCode::InvalidData)?
.confirm_struct()
}
pub fn get_root_node_list(b: &[u8]) -> Result<TLVElement, Error> {
TLVList::new(b)
.iter()
.next()
.ok_or(ErrorCode::InvalidData)?
.confirm_list()
}
pub fn print_tlv_list(b: &[u8]) {
let tlvlist = TLVList::new(b);
const MAX_DEPTH: usize = 9;
info!("TLV list:");
let space_buf = " ";
let space: [&str; MAX_DEPTH] = [
&space_buf[0..0],
&space_buf[0..4],
&space_buf[0..8],
&space_buf[0..12],
&space_buf[0..16],
&space_buf[0..20],
&space_buf[0..24],
&space_buf[0..28],
&space_buf[0..32],
];
let mut stack: [char; MAX_DEPTH] = [' '; MAX_DEPTH];
let mut index = 0_usize;
let iter = tlvlist.iter();
for a in iter {
match a.element_type {
ElementType::Struct(_) => {
if index < MAX_DEPTH {
info!("{}{}", space[index], a);
stack[index] = '}';
index += 1;
} else {
error!("Too Deep");
}
}
ElementType::Array(_) | ElementType::List(_) => {
if index < MAX_DEPTH {
info!("{}{}", space[index], a);
stack[index] = ']';
index += 1;
} else {
error!("Too Deep");
}
}
ElementType::EndCnt => {
if index > 0 {
index -= 1;
info!("{}{}", space[index], stack[index]);
} else {
error!("Incorrect TLV List");
}
}
_ => info!("{}{}", space[index], a),
}
}
info!("---------");
}
#[cfg(test)]
mod tests {
use log::info;
use super::{
get_root_node_list, get_root_node_struct, ElementType, Pointer, TLVElement, TLVList,
TagType,
};
use crate::error::ErrorCode;
#[test]
fn test_short_length_tag() {
// The 0x36 is an array with a tag, but we leave out the tag field
let b = [0x15, 0x36];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(tlv_iter.next(), None);
}
#[test]
fn test_invalid_value_type() {
// The 0x24 is a a tagged integer, here we leave out the integer value
let b = [0x15, 0x1f, 0x0];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(tlv_iter.next(), None);
}
#[test]
fn test_short_length_value_immediate() {
// The 0x24 is a a tagged integer, here we leave out the integer value
let b = [0x15, 0x24, 0x0];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(tlv_iter.next(), None);
}
#[test]
fn test_short_length_value_string() {
// This is a tagged string, with tag 0 and length 0xb, but we only have 4 bytes in the string
let b = [0x15, 0x30, 0x00, 0x0b, 0x73, 0x6d, 0x61, 0x72];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(tlv_iter.next(), None);
}
#[test]
fn test_valid_tag() {
// The 0x36 is an array with a tag, here tag is 0
let b = [0x15, 0x36, 0x0];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(
tlv_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(0),
element_type: ElementType::Array(Pointer {
buf: &[21, 54, 0],
current: 3,
left: 0
}),
})
);
}
#[test]
fn test_valid_value_immediate() {
// The 0x24 is a a tagged integer, here the integer is 2
let b = [0x15, 0x24, 0x1, 0x2];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(
tlv_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(1),
element_type: ElementType::U8(2),
})
);
}
#[test]
fn test_valid_value_string() {
// This is a tagged string, with tag 0 and length 4, and we have 4 bytes in the string
let b = [0x15, 0x30, 0x5, 0x04, 0x73, 0x6d, 0x61, 0x72];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(
tlv_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(5),
element_type: ElementType::Str8l(&[0x73, 0x6d, 0x61, 0x72]),
})
);
}
#[test]
fn test_valid_value_string16() {
// This is a tagged string, with tag 0 and length 4, and we have 4 bytes in the string
let b = [
0x15, 0x31, 0x1, 0xd8, 0x1, 0x30, 0x82, 0x1, 0xd4, 0x30, 0x82, 0x1, 0x7a, 0xa0, 0x3,
0x2, 0x1, 0x2, 0x2, 0x8, 0x3e, 0x6c, 0xe6, 0x50, 0x9a, 0xd8, 0x40, 0xcd, 0x30, 0xa,
0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x4, 0x3, 0x2, 0x30, 0x30, 0x31, 0x18, 0x30,
0x16, 0x6, 0x3, 0x55, 0x4, 0x3, 0xc, 0xf, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20,
0x54, 0x65, 0x73, 0x74, 0x20, 0x50, 0x41, 0x41, 0x31, 0x14, 0x30, 0x12, 0x6, 0xa, 0x2b,
0x6, 0x1, 0x4, 0x1, 0x82, 0xa2, 0x7c, 0x2, 0x1, 0xc, 0x4, 0x46, 0x46, 0x46, 0x31, 0x30,
0x20, 0x17, 0xd, 0x32, 0x31, 0x30, 0x36, 0x32, 0x38, 0x31, 0x34, 0x32, 0x33, 0x34,
0x33, 0x5a, 0x18, 0xf, 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33,
0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x46, 0x31, 0x18, 0x30, 0x16, 0x6, 0x3, 0x55, 0x4,
0x3, 0xc, 0xf, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20,
0x50, 0x41, 0x49, 0x31, 0x14, 0x30, 0x12, 0x6, 0xa, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82,
0xa2, 0x7c, 0x2, 0x1, 0xc, 0x4, 0x46, 0x46, 0x46, 0x31, 0x31, 0x14, 0x30, 0x12, 0x6,
0xa, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0xa2, 0x7c, 0x2, 0x2, 0xc, 0x4, 0x38, 0x30, 0x30,
0x30, 0x30, 0x59, 0x30, 0x13, 0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6,
0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0x3, 0x42, 0x0, 0x4, 0x80, 0xdd,
0xf1, 0x1b, 0x22, 0x8f, 0x3e, 0x31, 0xf6, 0x3b, 0xcf, 0x57, 0x98, 0xda, 0x14, 0x62,
0x3a, 0xeb, 0xbd, 0xe8, 0x2e, 0xf3, 0x78, 0xee, 0xad, 0xbf, 0xb1, 0x8f, 0xe1, 0xab,
0xce, 0x31, 0xd0, 0x8e, 0xd4, 0xb2, 0x6, 0x4, 0xb6, 0xcc, 0xc6, 0xd9, 0xb5, 0xfa, 0xb6,
0x4e, 0x7d, 0xe1, 0xc, 0xb7, 0x4b, 0xe0, 0x17, 0xc9, 0xec, 0x15, 0x16, 0x5, 0x6d, 0x70,
0xf2, 0xcd, 0xb, 0x22, 0xa3, 0x66, 0x30, 0x64, 0x30, 0x12, 0x6, 0x3, 0x55, 0x1d, 0x13,
0x1, 0x1, 0xff, 0x4, 0x8, 0x30, 0x6, 0x1, 0x1, 0xff, 0x2, 0x1, 0x0, 0x30, 0xe, 0x6,
0x3, 0x55, 0x1d, 0xf, 0x1, 0x1, 0xff, 0x4, 0x4, 0x3, 0x2, 0x1, 0x6, 0x30, 0x1d, 0x6,
0x3, 0x55, 0x1d, 0xe, 0x4, 0x16, 0x4, 0x14, 0xaf, 0x42, 0xb7, 0x9, 0x4d, 0xeb, 0xd5,
0x15, 0xec, 0x6e, 0xcf, 0x33, 0xb8, 0x11, 0x15, 0x22, 0x5f, 0x32, 0x52, 0x88, 0x30,
0x1f, 0x6, 0x3, 0x55, 0x1d, 0x23, 0x4, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0xfd, 0x22,
0x77, 0x1f, 0x51, 0x1f, 0xec, 0xbf, 0x16, 0x41, 0x97, 0x67, 0x10, 0xdc, 0xdc, 0x31,
0xa1, 0x71, 0x7e, 0x30, 0xa, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x4, 0x3, 0x2,
0x3, 0x48, 0x0, 0x30, 0x45, 0x2, 0x21, 0x0, 0x96, 0xc9, 0xc8, 0xcf, 0x2e, 0x1, 0x88,
0x60, 0x5, 0xd8, 0xf5, 0xbc, 0x72, 0xc0, 0x7b, 0x75, 0xfd, 0x9a, 0x57, 0x69, 0x5a,
0xc4, 0x91, 0x11, 0x31, 0x13, 0x8b, 0xea, 0x3, 0x3c, 0xe5, 0x3, 0x2, 0x20, 0x25, 0x54,
0x94, 0x3b, 0xe5, 0x7d, 0x53, 0xd6, 0xc4, 0x75, 0xf7, 0xd2, 0x3e, 0xbf, 0xcf, 0xc2,
0x3, 0x6c, 0xd2, 0x9b, 0xa6, 0x39, 0x3e, 0xc7, 0xef, 0xad, 0x87, 0x14, 0xab, 0x71,
0x82, 0x19, 0x26, 0x2, 0x3e, 0x0, 0x0, 0x0,
];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(
tlv_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(1),
element_type: ElementType::Str16l(&[
0x30, 0x82, 0x1, 0xd4, 0x30, 0x82, 0x1, 0x7a, 0xa0, 0x3, 0x2, 0x1, 0x2, 0x2,
0x8, 0x3e, 0x6c, 0xe6, 0x50, 0x9a, 0xd8, 0x40, 0xcd, 0x30, 0xa, 0x6, 0x8, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x4, 0x3, 0x2, 0x30, 0x30, 0x31, 0x18, 0x30, 0x16, 0x6,
0x3, 0x55, 0x4, 0x3, 0xc, 0xf, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54,
0x65, 0x73, 0x74, 0x20, 0x50, 0x41, 0x41, 0x31, 0x14, 0x30, 0x12, 0x6, 0xa,
0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0xa2, 0x7c, 0x2, 0x1, 0xc, 0x4, 0x46, 0x46,
0x46, 0x31, 0x30, 0x20, 0x17, 0xd, 0x32, 0x31, 0x30, 0x36, 0x32, 0x38, 0x31,
0x34, 0x32, 0x33, 0x34, 0x33, 0x5a, 0x18, 0xf, 0x39, 0x39, 0x39, 0x39, 0x31,
0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x46, 0x31,
0x18, 0x30, 0x16, 0x6, 0x3, 0x55, 0x4, 0x3, 0xc, 0xf, 0x4d, 0x61, 0x74, 0x74,
0x65, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x50, 0x41, 0x49, 0x31, 0x14,
0x30, 0x12, 0x6, 0xa, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x82, 0xa2, 0x7c, 0x2, 0x1,
0xc, 0x4, 0x46, 0x46, 0x46, 0x31, 0x31, 0x14, 0x30, 0x12, 0x6, 0xa, 0x2b, 0x6,
0x1, 0x4, 0x1, 0x82, 0xa2, 0x7c, 0x2, 0x2, 0xc, 0x4, 0x38, 0x30, 0x30, 0x30,
0x30, 0x59, 0x30, 0x13, 0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6,
0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0x3, 0x42, 0x0, 0x4, 0x80,
0xdd, 0xf1, 0x1b, 0x22, 0x8f, 0x3e, 0x31, 0xf6, 0x3b, 0xcf, 0x57, 0x98, 0xda,
0x14, 0x62, 0x3a, 0xeb, 0xbd, 0xe8, 0x2e, 0xf3, 0x78, 0xee, 0xad, 0xbf, 0xb1,
0x8f, 0xe1, 0xab, 0xce, 0x31, 0xd0, 0x8e, 0xd4, 0xb2, 0x6, 0x4, 0xb6, 0xcc,
0xc6, 0xd9, 0xb5, 0xfa, 0xb6, 0x4e, 0x7d, 0xe1, 0xc, 0xb7, 0x4b, 0xe0, 0x17,
0xc9, 0xec, 0x15, 0x16, 0x5, 0x6d, 0x70, 0xf2, 0xcd, 0xb, 0x22, 0xa3, 0x66,
0x30, 0x64, 0x30, 0x12, 0x6, 0x3, 0x55, 0x1d, 0x13, 0x1, 0x1, 0xff, 0x4, 0x8,
0x30, 0x6, 0x1, 0x1, 0xff, 0x2, 0x1, 0x0, 0x30, 0xe, 0x6, 0x3, 0x55, 0x1d, 0xf,
0x1, 0x1, 0xff, 0x4, 0x4, 0x3, 0x2, 0x1, 0x6, 0x30, 0x1d, 0x6, 0x3, 0x55, 0x1d,
0xe, 0x4, 0x16, 0x4, 0x14, 0xaf, 0x42, 0xb7, 0x9, 0x4d, 0xeb, 0xd5, 0x15, 0xec,
0x6e, 0xcf, 0x33, 0xb8, 0x11, 0x15, 0x22, 0x5f, 0x32, 0x52, 0x88, 0x30, 0x1f,
0x6, 0x3, 0x55, 0x1d, 0x23, 0x4, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0xfd,
0x22, 0x77, 0x1f, 0x51, 0x1f, 0xec, 0xbf, 0x16, 0x41, 0x97, 0x67, 0x10, 0xdc,
0xdc, 0x31, 0xa1, 0x71, 0x7e, 0x30, 0xa, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x4, 0x3, 0x2, 0x3, 0x48, 0x0, 0x30, 0x45, 0x2, 0x21, 0x0, 0x96, 0xc9,
0xc8, 0xcf, 0x2e, 0x1, 0x88, 0x60, 0x5, 0xd8, 0xf5, 0xbc, 0x72, 0xc0, 0x7b,
0x75, 0xfd, 0x9a, 0x57, 0x69, 0x5a, 0xc4, 0x91, 0x11, 0x31, 0x13, 0x8b, 0xea,
0x3, 0x3c, 0xe5, 0x3, 0x2, 0x20, 0x25, 0x54, 0x94, 0x3b, 0xe5, 0x7d, 0x53,
0xd6, 0xc4, 0x75, 0xf7, 0xd2, 0x3e, 0xbf, 0xcf, 0xc2, 0x3, 0x6c, 0xd2, 0x9b,
0xa6, 0x39, 0x3e, 0xc7, 0xef, 0xad, 0x87, 0x14, 0xab, 0x71, 0x82, 0x19
]),
})
);
assert_eq!(
tlv_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(2),
element_type: ElementType::U32(62),
})
);
}
#[test]
fn test_no_iterator_for_int() {
// The 0x24 is a a tagged integer, here the integer is 2
let b = [0x15, 0x24, 0x1, 0x2];
let tlvlist = TLVList::new(&b);
let mut tlv_iter = tlvlist.iter();
// Skip the 0x15
tlv_iter.next();
assert_eq!(tlv_iter.next().unwrap().enter(), None);
}
#[test]
fn test_struct_iteration_with_mix_values() {
// This is a struct with 3 valid values
let b = [
0x15, 0x24, 0x0, 0x2, 0x26, 0x2, 0x4e, 0x10, 0x02, 0x00, 0x30, 0x3, 0x04, 0x73, 0x6d,
0x61, 0x72,
];
let mut root_iter = get_root_node_struct(&b).unwrap().enter().unwrap();
assert_eq!(
root_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(0),
element_type: ElementType::U8(2),
})
);
assert_eq!(
root_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(2),
element_type: ElementType::U32(135246),
})
);
assert_eq!(
root_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(3),
element_type: ElementType::Str8l(&[0x73, 0x6d, 0x61, 0x72]),
})
);
}
#[test]
fn test_struct_find_element_mix_values() {
// This is a struct with 3 valid values
let b = [
0x15, 0x30, 0x3, 0x04, 0x73, 0x6d, 0x61, 0x72, 0x24, 0x0, 0x2, 0x26, 0x2, 0x4e, 0x10,
0x02, 0x00,
];
let root = get_root_node_struct(&b).unwrap();
assert_eq!(
root.find_tag(0).unwrap(),
TLVElement {
tag_type: TagType::Context(0),
element_type: ElementType::U8(2),
}
);
assert_eq!(
root.find_tag(2).unwrap(),
TLVElement {
tag_type: TagType::Context(2),
element_type: ElementType::U32(135246),
}
);
assert_eq!(
root.find_tag(3).unwrap(),
TLVElement {
tag_type: TagType::Context(3),
element_type: ElementType::Str8l(&[0x73, 0x6d, 0x61, 0x72]),
}
);
}
#[test]
fn test_list_iteration_with_mix_values() {
// This is a list with 3 valid values
let b = [
0x17, 0x24, 0x0, 0x2, 0x26, 0x2, 0x4e, 0x10, 0x02, 0x00, 0x30, 0x3, 0x04, 0x73, 0x6d,
0x61, 0x72,
];
let mut root_iter = get_root_node_list(&b).unwrap().enter().unwrap();
assert_eq!(
root_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(0),
element_type: ElementType::U8(2),
})
);
assert_eq!(
root_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(2),
element_type: ElementType::U32(135246),
})
);
assert_eq!(
root_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(3),
element_type: ElementType::Str8l(&[0x73, 0x6d, 0x61, 0x72]),
})
);
}
#[test]
fn test_complex_structure_invoke_cmd() {
// This is what we typically get in an invoke command
let b = [
0x15, 0x36, 0x0, 0x15, 0x37, 0x0, 0x25, 0x0, 0x2, 0x0, 0x26, 0x1, 0x6, 0x0, 0x0, 0x0,
0x26, 0x2, 0x1, 0x0, 0x0, 0x0, 0x18, 0x35, 0x1, 0x18, 0x18, 0x18, 0x18,
];
let root = get_root_node_struct(&b).unwrap();
let mut cmd_list_iter = root
.find_tag(0)
.unwrap()
.confirm_array()
.unwrap()
.enter()
.unwrap();
info!("Command list iterator: {:?}", cmd_list_iter);
// This is an array of CommandDataIB, but we'll only use the first element
let cmd_data_ib = cmd_list_iter.next().unwrap();
let cmd_path = cmd_data_ib.find_tag(0).unwrap().confirm_list().unwrap();
assert_eq!(
cmd_path.find_tag(0).unwrap(),
TLVElement {
tag_type: TagType::Context(0),
element_type: ElementType::U16(2),
}
);
assert_eq!(
cmd_path.find_tag(1).unwrap(),
TLVElement {
tag_type: TagType::Context(1),
element_type: ElementType::U32(6),
}
);
assert_eq!(
cmd_path.find_tag(2).unwrap(),
TLVElement {
tag_type: TagType::Context(2),
element_type: ElementType::U32(1),
}
);
assert_eq!(
cmd_path.find_tag(3).map_err(|e| e.code()),
Err(ErrorCode::NoTagFound)
);
// This is the variable of the invoke command
assert_eq!(
cmd_data_ib.find_tag(1).unwrap().enter().unwrap().next(),
None
);
}
#[test]
fn test_read_past_end_of_container() {
let b = [0x15, 0x35, 0x0, 0x24, 0x1, 0x2, 0x18, 0x24, 0x0, 0x2, 0x18];
let mut sub_root_iter = get_root_node_struct(&b)
.unwrap()
.find_tag(0)
.unwrap()
.enter()
.unwrap();
assert_eq!(
sub_root_iter.next(),
Some(TLVElement {
tag_type: TagType::Context(1),
element_type: ElementType::U8(2),
})
);
assert_eq!(sub_root_iter.next(), None);
// Call next, even after the first next returns None
assert_eq!(sub_root_iter.next(), None);
assert_eq!(sub_root_iter.next(), None);
}
#[test]
fn test_basic_list_iterator() {
// This is the input we have
let b = [
0x15, 0x36, 0x0, 0x15, 0x37, 0x0, 0x24, 0x0, 0x2, 0x24, 0x2, 0x6, 0x24, 0x3, 0x1, 0x18,
0x35, 0x1, 0x18, 0x18, 0x18, 0x18,
];
let dummy_pointer = Pointer {
buf: &b,
current: 1,
left: 21,
};
// These are the decoded elements that we expect from this input
let verify_matrix: [(TagType, ElementType); 13] = [
(TagType::Anonymous, ElementType::Struct(dummy_pointer)),
(TagType::Context(0), ElementType::Array(dummy_pointer)),
(TagType::Anonymous, ElementType::Struct(dummy_pointer)),
(TagType::Context(0), ElementType::List(dummy_pointer)),
(TagType::Context(0), ElementType::U8(2)),
(TagType::Context(2), ElementType::U8(6)),
(TagType::Context(3), ElementType::U8(1)),
(TagType::Anonymous, ElementType::EndCnt),
(TagType::Context(1), ElementType::Struct(dummy_pointer)),
(TagType::Anonymous, ElementType::EndCnt),
(TagType::Anonymous, ElementType::EndCnt),
(TagType::Anonymous, ElementType::EndCnt),
(TagType::Anonymous, ElementType::EndCnt),
];
let mut list_iter = TLVList::new(&b).iter();
let mut index = 0;
loop {
let element = list_iter.next();
match element {
None => break,
Some(a) => {
assert_eq!(a.tag_type, verify_matrix[index].0);
assert_eq!(
core::mem::discriminant(&a.element_type),
core::mem::discriminant(&verify_matrix[index].1)
);
}
}
index += 1;
}
// After the end, purposefully try a few more next
assert_eq!(list_iter.next(), None);
assert_eq!(list_iter.next(), None);
}
}