Remove allocations from Base38 and QR calc
This commit is contained in:
parent
26fb6b01c5
commit
f7a887c1d2
6 changed files with 236 additions and 245 deletions
|
@ -15,7 +15,7 @@ name = "matter"
|
|||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = ["std", "crypto_mbedtls", "nightly"]
|
||||
default = ["std", "crypto_mbedtls"]
|
||||
std = []
|
||||
nightly = []
|
||||
crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"]
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
|
||||
//! Base38 encoding and decoding functions.
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
const BASE38_CHARS: [char; 38] = [
|
||||
|
@ -81,60 +77,68 @@ const DECODE_BASE38: [u8; 46] = [
|
|||
35, // 'Z', =90
|
||||
];
|
||||
|
||||
const BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK: [u8; 3] = [2, 4, 5];
|
||||
const RADIX: u32 = BASE38_CHARS.len() as u32;
|
||||
|
||||
/// Encode a byte array into a base38 string.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `bytes` - byte array to encode
|
||||
/// * `length` - optional length of the byte array to encode. If not specified, the entire byte array is encoded.
|
||||
pub fn encode(bytes: &[u8], length: Option<usize>) -> String {
|
||||
let mut offset = 0;
|
||||
let mut result = String::new();
|
||||
pub fn encode_string<const N: usize>(bytes: &[u8]) -> Result<heapless::String<N>, Error> {
|
||||
let mut string = heapless::String::new();
|
||||
for c in encode(bytes) {
|
||||
string.push(c).map_err(|_| Error::NoSpace)?;
|
||||
}
|
||||
|
||||
// if length is specified, use it, otherwise use the length of the byte array
|
||||
// if length is specified but is greater than the length of the byte array, use the length of the byte array
|
||||
let b_len = bytes.len();
|
||||
let length = length.map(|l| l.min(b_len)).unwrap_or(b_len);
|
||||
Ok(string)
|
||||
}
|
||||
|
||||
while offset < length {
|
||||
let remaining = length - offset;
|
||||
match remaining.cmp(&2) {
|
||||
core::cmp::Ordering::Greater => {
|
||||
result.push_str(&encode_base38(
|
||||
pub fn encode(bytes: &[u8]) -> impl Iterator<Item = char> + '_ {
|
||||
(0..bytes.len() / 3)
|
||||
.flat_map(move |index| {
|
||||
let offset = index * 3;
|
||||
|
||||
encode_base38(
|
||||
((bytes[offset + 2] as u32) << 16)
|
||||
| ((bytes[offset + 1] as u32) << 8)
|
||||
| (bytes[offset] as u32),
|
||||
5,
|
||||
));
|
||||
offset += 3;
|
||||
}
|
||||
core::cmp::Ordering::Equal => {
|
||||
result.push_str(&encode_base38(
|
||||
)
|
||||
})
|
||||
.chain(
|
||||
core::iter::once(bytes.len() % 3).flat_map(move |remainder| {
|
||||
let offset = bytes.len() / 3 * 3;
|
||||
|
||||
match remainder {
|
||||
2 => encode_base38(
|
||||
((bytes[offset + 1] as u32) << 8) | (bytes[offset] as u32),
|
||||
4,
|
||||
));
|
||||
break;
|
||||
}
|
||||
core::cmp::Ordering::Less => {
|
||||
result.push_str(&encode_base38(bytes[offset] as u32, 2));
|
||||
break;
|
||||
}
|
||||
),
|
||||
1 => encode_base38(bytes[offset] as u32, 2),
|
||||
_ => encode_base38(0, 0),
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
result
|
||||
fn encode_base38(mut value: u32, repeat: usize) -> impl Iterator<Item = char> {
|
||||
(0..repeat).map(move |_| {
|
||||
let remainder = value % RADIX;
|
||||
let c = BASE38_CHARS[remainder as usize];
|
||||
|
||||
value = (value - remainder) / RADIX;
|
||||
|
||||
c
|
||||
})
|
||||
}
|
||||
|
||||
fn encode_base38(mut value: u32, char_count: u8) -> String {
|
||||
let mut result = String::new();
|
||||
for _ in 0..char_count {
|
||||
let remainder = value % 38;
|
||||
result.push(BASE38_CHARS[remainder as usize]);
|
||||
value = (value - remainder) / 38;
|
||||
pub fn decode_vec<const N: usize>(base38_str: &str) -> Result<heapless::Vec<u8, N>, Error> {
|
||||
let mut vec = heapless::Vec::new();
|
||||
|
||||
for byte in decode(base38_str) {
|
||||
vec.push(byte?).map_err(|_| Error::NoSpace)?;
|
||||
}
|
||||
result
|
||||
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
/// Decode a base38-encoded string into a byte slice
|
||||
|
@ -142,57 +146,64 @@ fn encode_base38(mut value: u32, char_count: u8) -> String {
|
|||
/// # Arguments
|
||||
/// * `base38_str` - base38-encoded string to decode
|
||||
///
|
||||
/// Fails if the string contains invalid characters
|
||||
pub fn decode(base38_str: &str) -> Result<Vec<u8>, Error> {
|
||||
let mut result = Vec::new();
|
||||
let mut base38_characters_number: usize = base38_str.len();
|
||||
let mut decoded_base38_characters: usize = 0;
|
||||
/// Fails if the string contains invalid characters or if the supplied buffer is too small to fit the decoded data
|
||||
pub fn decode(base38_str: &str) -> impl Iterator<Item = Result<u8, Error>> + '_ {
|
||||
let stru = base38_str.as_bytes();
|
||||
|
||||
while base38_characters_number > 0 {
|
||||
let base38_characters_in_chunk: usize;
|
||||
let bytes_in_decoded_chunk: usize;
|
||||
|
||||
if base38_characters_number >= BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[2] as usize {
|
||||
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[2] as usize;
|
||||
bytes_in_decoded_chunk = 3;
|
||||
} else if base38_characters_number == BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[1] as usize {
|
||||
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[1] as usize;
|
||||
bytes_in_decoded_chunk = 2;
|
||||
} else if base38_characters_number == BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[0] as usize {
|
||||
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[0] as usize;
|
||||
bytes_in_decoded_chunk = 1;
|
||||
} else {
|
||||
return Err(Error::InvalidData);
|
||||
(0..stru.len() / 5)
|
||||
.flat_map(move |index| {
|
||||
let offset = index * 5;
|
||||
decode_base38(&stru[offset..offset + 5])
|
||||
})
|
||||
.chain({
|
||||
let offset = stru.len() / 5 * 5;
|
||||
decode_base38(&stru[offset..])
|
||||
})
|
||||
.take_while(Result::is_ok)
|
||||
}
|
||||
|
||||
fn decode_base38(chars: &[u8]) -> impl Iterator<Item = Result<u8, Error>> {
|
||||
let mut value = 0u32;
|
||||
let mut cerr = None;
|
||||
|
||||
for i in (1..=base38_characters_in_chunk).rev() {
|
||||
let mut base38_chars = base38_str.chars();
|
||||
let v = decode_char(base38_chars.nth(decoded_base38_characters + i - 1).unwrap())?;
|
||||
let repeat = match chars.len() {
|
||||
5 => 3,
|
||||
4 => 2,
|
||||
2 => 1,
|
||||
0 => 0,
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
value = value * RADIX + v as u32;
|
||||
if repeat >= 0 {
|
||||
for c in chars.iter().rev() {
|
||||
match decode_char(*c) {
|
||||
Ok(v) => value = value * RADIX + v as u32,
|
||||
Err(err) => {
|
||||
cerr = Some(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cerr = Some(Error::InvalidData)
|
||||
}
|
||||
|
||||
decoded_base38_characters += base38_characters_in_chunk;
|
||||
base38_characters_number -= base38_characters_in_chunk;
|
||||
(0..repeat)
|
||||
.map(move |_| {
|
||||
if let Some(err) = cerr {
|
||||
Err(err)
|
||||
} else {
|
||||
let byte = (value & 0xff) as u8;
|
||||
|
||||
for _i in 0..bytes_in_decoded_chunk {
|
||||
result.push(value as u8);
|
||||
value >>= 8;
|
||||
|
||||
Ok(byte)
|
||||
}
|
||||
})
|
||||
.take_while(Result::is_ok)
|
||||
}
|
||||
|
||||
if value > 0 {
|
||||
// encoded value is too big to represent a correct chunk of size 1, 2 or 3 bytes
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn decode_char(c: char) -> Result<u8, Error> {
|
||||
let c = c as u8;
|
||||
fn decode_char(c: u8) -> Result<u8, Error> {
|
||||
if !(45..=90).contains(&c) {
|
||||
return Err(Error::InvalidData);
|
||||
}
|
||||
|
@ -215,15 +226,17 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_base38_encode() {
|
||||
assert_eq!(encode(&DECODED, None), ENCODED);
|
||||
assert_eq!(encode(&DECODED, Some(11)), ENCODED);
|
||||
|
||||
// length is greater than the length of the byte array
|
||||
assert_eq!(encode(&DECODED, Some(12)), ENCODED);
|
||||
assert_eq!(
|
||||
encode_string::<{ ENCODED.len() }>(&DECODED).unwrap(),
|
||||
ENCODED
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_base38_decode() {
|
||||
assert_eq!(decode(ENCODED).expect("can not decode base38"), DECODED);
|
||||
assert_eq!(
|
||||
decode_vec::<{ DECODED.len() }>(ENCODED).expect("Cannot decode base38"),
|
||||
DECODED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,10 +84,19 @@ impl<'a> Matter<'a> {
|
|||
self.dev_det
|
||||
}
|
||||
|
||||
pub fn start(&mut self, dev_comm: CommissioningData) -> Result<(), Error> {
|
||||
pub fn start<const N: usize>(
|
||||
&mut self,
|
||||
dev_comm: CommissioningData,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
let open_comm_window = self.fabric_mgr.borrow().is_empty();
|
||||
if open_comm_window {
|
||||
print_pairing_code_and_qr(self.dev_det, &dev_comm, DiscoveryCapabilities::default());
|
||||
print_pairing_code_and_qr::<N>(
|
||||
self.dev_det,
|
||||
&dev_comm,
|
||||
DiscoveryCapabilities::default(),
|
||||
buf,
|
||||
);
|
||||
|
||||
self.pase_mgr.borrow_mut().enable_pase_session(
|
||||
dev_comm.verifier,
|
||||
|
|
|
@ -15,38 +15,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use std::sync::{Mutex, Once};
|
||||
|
||||
use crate::{crypto, error::Error};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
// This is just makeshift implementation for now, not used anywhere
|
||||
pub struct GroupKeys {}
|
||||
|
||||
static mut G_GRP_KEYS: Option<Arc<Mutex<GroupKeys>>> = None;
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
impl GroupKeys {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn get() -> Result<Arc<Mutex<Self>>, Error> {
|
||||
unsafe {
|
||||
INIT.call_once(|| {
|
||||
G_GRP_KEYS = Some(Arc::new(Mutex::new(GroupKeys::new())));
|
||||
});
|
||||
Ok(G_GRP_KEYS.as_ref().ok_or(Error::Invalid)?.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_key() -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct KeySet {
|
||||
pub epoch_key: [u8; crypto::SYMM_KEY_LEN_BYTES],
|
||||
|
|
|
@ -82,14 +82,16 @@ impl DiscoveryCapabilities {
|
|||
}
|
||||
|
||||
/// Prepares and prints the pairing code and the QR code for easy pairing.
|
||||
pub fn print_pairing_code_and_qr(
|
||||
pub fn print_pairing_code_and_qr<const N: usize>(
|
||||
dev_det: &BasicInfoConfig,
|
||||
comm_data: &CommissioningData,
|
||||
discovery_capabilities: DiscoveryCapabilities,
|
||||
buf: &mut [u8],
|
||||
) {
|
||||
let pairing_code = compute_pairing_code(comm_data);
|
||||
let qr_code_data = QrSetupPayload::new(dev_det, comm_data, discovery_capabilities);
|
||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
||||
let data_str =
|
||||
payload_base38_representation::<N>(&qr_code_data, buf).expect("Failed to encode");
|
||||
|
||||
pretty_print_pairing_code(&pairing_code);
|
||||
print_qr_code(&data_str);
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use heapless::FnvIndexMap;
|
||||
|
||||
use crate::{
|
||||
tlv::{TLVWriter, TagType},
|
||||
utils::writebuf::WriteBuf,
|
||||
|
@ -45,6 +43,7 @@ const TOTAL_PAYLOAD_DATA_SIZE_IN_BITS: usize = VERSION_FIELD_LENGTH_IN_BITS
|
|||
+ PAYLOAD_DISCRIMINATOR_FIELD_LENGTH_IN_BITS
|
||||
+ SETUP_PINCODE_FIELD_LENGTH_IN_BITS
|
||||
+ PADDING_FIELD_LENGTH_IN_BITS;
|
||||
|
||||
const TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES: usize = TOTAL_PAYLOAD_DATA_SIZE_IN_BITS / 8;
|
||||
|
||||
// Spec 5.1.4.2 CHIP-Common Reserved Tags
|
||||
|
@ -80,8 +79,8 @@ pub struct QrSetupPayload<'data> {
|
|||
discovery_capabilities: DiscoveryCapabilities,
|
||||
dev_det: &'data BasicInfoConfig<'data>,
|
||||
comm_data: &'data CommissioningData,
|
||||
// we use a BTreeMap to keep the order of the optional data stable
|
||||
optional_data: heapless::FnvIndexMap<u8, OptionalQRCodeInfo, 16>,
|
||||
// The vec is ordered by the tag of OptionalQRCodeInfo
|
||||
optional_data: heapless::Vec<OptionalQRCodeInfo, 16>,
|
||||
}
|
||||
|
||||
impl<'data> QrSetupPayload<'data> {
|
||||
|
@ -98,7 +97,7 @@ impl<'data> QrSetupPayload<'data> {
|
|||
discovery_capabilities,
|
||||
dev_det,
|
||||
comm_data,
|
||||
optional_data: FnvIndexMap::new(),
|
||||
optional_data: heapless::Vec::new(),
|
||||
};
|
||||
|
||||
if !dev_det.serial_no.is_empty() {
|
||||
|
@ -132,15 +131,11 @@ impl<'data> QrSetupPayload<'data> {
|
|||
/// * `tag` - tag number in the [0x80-0xFF] range
|
||||
/// * `data` - Data to add
|
||||
pub fn add_optional_vendor_data(&mut self, tag: u8, data: QRCodeInfoType) -> Result<(), Error> {
|
||||
if !is_vendor_tag(tag) {
|
||||
return Err(Error::InvalidArgument);
|
||||
if is_vendor_tag(tag) {
|
||||
self.add_optional_data(tag, data)
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
|
||||
self.optional_data
|
||||
.insert(tag, OptionalQRCodeInfo { tag, data })
|
||||
.map_err(|_| Error::NoSpace)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A function to add an optional QR Code info CHIP object
|
||||
|
@ -152,18 +147,26 @@ impl<'data> QrSetupPayload<'data> {
|
|||
tag: u8,
|
||||
data: QRCodeInfoType,
|
||||
) -> Result<(), Error> {
|
||||
if !is_common_tag(tag) {
|
||||
return Err(Error::InvalidArgument);
|
||||
if is_common_tag(tag) {
|
||||
self.add_optional_data(tag, data)
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
self.optional_data
|
||||
.insert(tag, OptionalQRCodeInfo { tag, data })
|
||||
.map_err(|_| Error::NoSpace)?;
|
||||
fn add_optional_data(&mut self, tag: u8, data: QRCodeInfoType) -> Result<(), Error> {
|
||||
let item = OptionalQRCodeInfo { tag, data };
|
||||
let index = self.optional_data.iter().position(|info| tag < info.tag);
|
||||
|
||||
Ok(())
|
||||
if let Some(index) = index {
|
||||
self.optional_data.insert(index, item)
|
||||
} else {
|
||||
self.optional_data.push(item)
|
||||
}
|
||||
.map_err(|_| Error::NoSpace)
|
||||
}
|
||||
|
||||
pub fn get_all_optional_data(&self) -> &FnvIndexMap<u8, OptionalQRCodeInfo, 16> {
|
||||
pub fn get_all_optional_data(&self) -> &[OptionalQRCodeInfo] {
|
||||
&self.optional_data
|
||||
}
|
||||
|
||||
|
@ -249,35 +252,26 @@ pub enum CommissionningFlowType {
|
|||
Custom = 2,
|
||||
}
|
||||
|
||||
struct TlvData {
|
||||
max_data_length_in_bytes: u32,
|
||||
data_length_in_bytes: Option<usize>,
|
||||
data: Option<Vec<u8>>,
|
||||
}
|
||||
pub(super) fn payload_base38_representation<const N: usize>(
|
||||
payload: &QrSetupPayload,
|
||||
buf: &mut [u8],
|
||||
) -> Result<heapless::String<N>, Error> {
|
||||
if payload.is_valid() {
|
||||
let (bits_buf, tlv_buf) = if payload.has_tlv() {
|
||||
let (bits_buf, tlv_buf) = buf.split_at_mut(buf.len() / 2);
|
||||
|
||||
pub(super) fn payload_base38_representation(payload: &QrSetupPayload) -> Result<String, Error> {
|
||||
let (mut bits, tlv_data) = if payload.has_tlv() {
|
||||
let buffer_size = estimate_buffer_size(payload)?;
|
||||
(
|
||||
vec![0; buffer_size],
|
||||
Some(TlvData {
|
||||
max_data_length_in_bytes: buffer_size as u32,
|
||||
data_length_in_bytes: None,
|
||||
data: None,
|
||||
}),
|
||||
)
|
||||
(bits_buf, Some(tlv_buf))
|
||||
} else {
|
||||
(vec![0; TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES], None)
|
||||
(buf, None)
|
||||
};
|
||||
|
||||
if !payload.is_valid() {
|
||||
return Err(Error::InvalidArgument);
|
||||
payload_base38_representation_with_tlv(payload, bits_buf, tlv_buf)
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
payload_base38_representation_with_tlv(payload, &mut bits, tlv_data)
|
||||
}
|
||||
|
||||
fn estimate_buffer_size(payload: &QrSetupPayload) -> Result<usize, Error> {
|
||||
pub fn estimate_buffer_size(payload: &QrSetupPayload) -> Result<usize, Error> {
|
||||
// Estimate the size of the needed buffer; initialize with the size of the standard payload.
|
||||
let mut estimate = TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES;
|
||||
|
||||
|
@ -298,10 +292,9 @@ fn estimate_buffer_size(payload: &QrSetupPayload) -> Result<usize, Error> {
|
|||
size
|
||||
};
|
||||
|
||||
let vendor_data = payload.get_all_optional_data();
|
||||
vendor_data.values().for_each(|data| {
|
||||
for data in payload.get_all_optional_data() {
|
||||
estimate += data_item_size_estimate(&data.data);
|
||||
});
|
||||
}
|
||||
|
||||
estimate = estimate_struct_overhead(estimate);
|
||||
|
||||
|
@ -372,70 +365,72 @@ fn populate_bits(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn payload_base38_representation_with_tlv(
|
||||
fn payload_base38_representation_with_tlv<const N: usize>(
|
||||
payload: &QrSetupPayload,
|
||||
bits: &mut [u8],
|
||||
mut tlv_data: Option<TlvData>,
|
||||
) -> Result<String, Error> {
|
||||
if let Some(tlv_data) = tlv_data.as_mut() {
|
||||
generate_tlv_from_optional_data(payload, tlv_data)?;
|
||||
bits_buf: &mut [u8],
|
||||
tlv_buf: Option<&mut [u8]>,
|
||||
) -> Result<heapless::String<N>, Error> {
|
||||
let tlv_data = if let Some(tlv_buf) = tlv_buf {
|
||||
Some(generate_tlv_from_optional_data(payload, tlv_buf)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let bits = generate_bit_set(payload, bits_buf, tlv_data)?;
|
||||
|
||||
let mut base38_encoded: heapless::String<N> = "MT:".into();
|
||||
|
||||
for c in base38::encode(bits) {
|
||||
base38_encoded.push(c).map_err(|_| Error::NoSpace)?;
|
||||
}
|
||||
|
||||
let bytes_written = generate_bit_set(payload, bits, tlv_data)?;
|
||||
let base38_encoded = base38::encode(&*bits, Some(bytes_written));
|
||||
Ok(format!("MT:{}", base38_encoded))
|
||||
Ok(base38_encoded)
|
||||
}
|
||||
|
||||
fn generate_tlv_from_optional_data(
|
||||
fn generate_tlv_from_optional_data<'a>(
|
||||
payload: &QrSetupPayload,
|
||||
tlv_data: &mut TlvData,
|
||||
) -> Result<(), Error> {
|
||||
let size_needed = tlv_data.max_data_length_in_bytes as usize;
|
||||
let mut tlv_buffer = vec![0u8; size_needed];
|
||||
let mut wb = WriteBuf::new(&mut tlv_buffer);
|
||||
tlv_buf: &'a mut [u8],
|
||||
) -> Result<&'a [u8], Error> {
|
||||
let mut wb = WriteBuf::new(tlv_buf);
|
||||
let mut tw = TLVWriter::new(&mut wb);
|
||||
|
||||
tw.start_struct(TagType::Anonymous)?;
|
||||
let data = payload.get_all_optional_data();
|
||||
|
||||
for (tag, value) in data {
|
||||
match &value.data {
|
||||
QRCodeInfoType::String(data) => tw.utf8(TagType::Context(*tag), data.as_bytes())?,
|
||||
QRCodeInfoType::Int32(data) => tw.i32(TagType::Context(*tag), *data)?,
|
||||
QRCodeInfoType::Int64(data) => tw.i64(TagType::Context(*tag), *data)?,
|
||||
QRCodeInfoType::UInt32(data) => tw.u32(TagType::Context(*tag), *data)?,
|
||||
QRCodeInfoType::UInt64(data) => tw.u64(TagType::Context(*tag), *data)?,
|
||||
for info in payload.get_all_optional_data() {
|
||||
match &info.data {
|
||||
QRCodeInfoType::String(data) => tw.utf8(TagType::Context(info.tag), data.as_bytes())?,
|
||||
QRCodeInfoType::Int32(data) => tw.i32(TagType::Context(info.tag), *data)?,
|
||||
QRCodeInfoType::Int64(data) => tw.i64(TagType::Context(info.tag), *data)?,
|
||||
QRCodeInfoType::UInt32(data) => tw.u32(TagType::Context(info.tag), *data)?,
|
||||
QRCodeInfoType::UInt64(data) => tw.u64(TagType::Context(info.tag), *data)?,
|
||||
}
|
||||
}
|
||||
|
||||
tw.end_container()?;
|
||||
tlv_data.data_length_in_bytes = Some(tw.get_tail());
|
||||
tlv_data.data = Some(tlv_buffer);
|
||||
|
||||
Ok(())
|
||||
let tail = tw.get_tail();
|
||||
|
||||
Ok(&tlv_buf[..tail])
|
||||
}
|
||||
|
||||
fn generate_bit_set(
|
||||
fn generate_bit_set<'a>(
|
||||
payload: &QrSetupPayload,
|
||||
bits: &mut [u8],
|
||||
tlv_data: Option<TlvData>,
|
||||
) -> Result<usize, Error> {
|
||||
let mut offset: usize = 0;
|
||||
bits_buf: &'a mut [u8],
|
||||
tlv_data: Option<&[u8]>,
|
||||
) -> Result<&'a [u8], Error> {
|
||||
let total_payload_size_in_bits =
|
||||
TOTAL_PAYLOAD_DATA_SIZE_IN_BITS + tlv_data.map(|tlv_data| tlv_data.len() * 8).unwrap_or(0);
|
||||
|
||||
let total_payload_size_in_bits = if let Some(tlv_data) = &tlv_data {
|
||||
TOTAL_PAYLOAD_DATA_SIZE_IN_BITS + (tlv_data.data_length_in_bytes.unwrap_or_default() * 8)
|
||||
} else {
|
||||
TOTAL_PAYLOAD_DATA_SIZE_IN_BITS
|
||||
};
|
||||
|
||||
if bits.len() * 8 < total_payload_size_in_bits {
|
||||
if bits_buf.len() * 8 < total_payload_size_in_bits {
|
||||
return Err(Error::BufferTooSmall);
|
||||
};
|
||||
|
||||
let passwd = passwd_from_comm_data(payload.comm_data);
|
||||
|
||||
let mut offset: usize = 0;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
payload.version as u64,
|
||||
VERSION_FIELD_LENGTH_IN_BITS,
|
||||
|
@ -443,7 +438,7 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
payload.dev_det.vid as u64,
|
||||
VENDOR_IDFIELD_LENGTH_IN_BITS,
|
||||
|
@ -451,7 +446,7 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
payload.dev_det.pid as u64,
|
||||
PRODUCT_IDFIELD_LENGTH_IN_BITS,
|
||||
|
@ -459,7 +454,7 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
payload.flow_type as u64,
|
||||
COMMISSIONING_FLOW_FIELD_LENGTH_IN_BITS,
|
||||
|
@ -467,7 +462,7 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
payload.discovery_capabilities.as_bits() as u64,
|
||||
RENDEZVOUS_INFO_FIELD_LENGTH_IN_BITS,
|
||||
|
@ -475,7 +470,7 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
payload.comm_data.discriminator as u64,
|
||||
PAYLOAD_DISCRIMINATOR_FIELD_LENGTH_IN_BITS,
|
||||
|
@ -483,7 +478,7 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
passwd as u64,
|
||||
SETUP_PINCODE_FIELD_LENGTH_IN_BITS,
|
||||
|
@ -491,7 +486,7 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
populate_bits(
|
||||
bits,
|
||||
bits_buf,
|
||||
&mut offset,
|
||||
0,
|
||||
PADDING_FIELD_LENGTH_IN_BITS,
|
||||
|
@ -499,26 +494,22 @@ fn generate_bit_set(
|
|||
)?;
|
||||
|
||||
if let Some(tlv_data) = tlv_data {
|
||||
populate_tlv_bits(bits, &mut offset, tlv_data, total_payload_size_in_bits)?;
|
||||
populate_tlv_bits(bits_buf, &mut offset, tlv_data, total_payload_size_in_bits)?;
|
||||
}
|
||||
|
||||
let bytes_written = (offset + 7) / 8;
|
||||
Ok(bytes_written)
|
||||
|
||||
Ok(&bits_buf[..bytes_written])
|
||||
}
|
||||
|
||||
fn populate_tlv_bits(
|
||||
bits: &mut [u8],
|
||||
bits_buf: &mut [u8],
|
||||
offset: &mut usize,
|
||||
tlv_data: TlvData,
|
||||
tlv_data: &[u8],
|
||||
total_payload_size_in_bits: usize,
|
||||
) -> Result<(), Error> {
|
||||
if let (Some(data), Some(data_length_in_bytes)) = (tlv_data.data, tlv_data.data_length_in_bytes)
|
||||
{
|
||||
for b in data.iter().take(data_length_in_bytes) {
|
||||
populate_bits(bits, offset, *b as u64, 8, total_payload_size_in_bits)?;
|
||||
}
|
||||
} else {
|
||||
return Err(Error::InvalidArgument);
|
||||
for b in tlv_data {
|
||||
populate_bits(bits_buf, offset, *b as u64, 8, total_payload_size_in_bits)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -555,7 +546,9 @@ mod tests {
|
|||
|
||||
let disc_cap = DiscoveryCapabilities::new(false, true, false);
|
||||
let qr_code_data = QrSetupPayload::new(&dev_det, &comm_data, disc_cap);
|
||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
||||
let mut buf = [0; 1024];
|
||||
let data_str = payload_base38_representation::<128>(&qr_code_data, &mut buf)
|
||||
.expect("Failed to encode");
|
||||
assert_eq!(data_str, QR_CODE)
|
||||
}
|
||||
|
||||
|
@ -576,7 +569,9 @@ mod tests {
|
|||
|
||||
let disc_cap = DiscoveryCapabilities::new(true, false, false);
|
||||
let qr_code_data = QrSetupPayload::new(&dev_det, &comm_data, disc_cap);
|
||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
||||
let mut buf = [0; 1024];
|
||||
let data_str = payload_base38_representation::<128>(&qr_code_data, &mut buf)
|
||||
.expect("Failed to encode");
|
||||
assert_eq!(data_str, QR_CODE)
|
||||
}
|
||||
|
||||
|
@ -620,7 +615,9 @@ mod tests {
|
|||
)
|
||||
.expect("Failed to add optional data");
|
||||
|
||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
||||
let mut buf = [0; 1024];
|
||||
let data_str = payload_base38_representation::<{ QR_CODE.len() }>(&qr_code_data, &mut buf)
|
||||
.expect("Failed to encode");
|
||||
assert_eq!(data_str, QR_CODE)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue