From f7a887c1d29cba942e99bd89abe64c564edfcdb1 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sun, 23 Apr 2023 14:53:48 +0000 Subject: [PATCH] Remove allocations from Base38 and QR calc --- matter/Cargo.toml | 2 +- matter/src/codec/base38.rs | 215 ++++++++++++++++++++----------------- matter/src/core.rs | 13 ++- matter/src/group_keys.rs | 30 ------ matter/src/pairing/mod.rs | 6 +- matter/src/pairing/qr.rs | 215 ++++++++++++++++++------------------- 6 files changed, 236 insertions(+), 245 deletions(-) diff --git a/matter/Cargo.toml b/matter/Cargo.toml index fc38d45..2769010 100644 --- a/matter/Cargo.toml +++ b/matter/Cargo.toml @@ -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"] diff --git a/matter/src/codec/base38.rs b/matter/src/codec/base38.rs index 7b7e758..14114e6 100644 --- a/matter/src/codec/base38.rs +++ b/matter/src/codec/base38.rs @@ -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) -> String { - let mut offset = 0; - let mut result = String::new(); - - // 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); - - while offset < length { - let remaining = length - offset; - match remaining.cmp(&2) { - core::cmp::Ordering::Greater => { - result.push_str(&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( - ((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; - } - } +pub fn encode_string(bytes: &[u8]) -> Result, Error> { + let mut string = heapless::String::new(); + for c in encode(bytes) { + string.push(c).map_err(|_| Error::NoSpace)?; } - result + Ok(string) } -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 encode(bytes: &[u8]) -> impl Iterator + '_ { + (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, + ) + }) + .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, + ), + 1 => encode_base38(bytes[offset] as u32, 2), + _ => encode_base38(0, 0), + } + }), + ) +} + +fn encode_base38(mut value: u32, repeat: usize) -> impl Iterator { + (0..repeat).map(move |_| { + let remainder = value % RADIX; + let c = BASE38_CHARS[remainder as usize]; + + value = (value - remainder) / RADIX; + + c + }) +} + +pub fn decode_vec(base38_str: &str) -> Result, 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, 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> + '_ { + 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); - } - - let mut value = 0u32; - - 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())?; - - value = value * RADIX + v as u32; - } - - decoded_base38_characters += base38_characters_in_chunk; - base38_characters_number -= base38_characters_in_chunk; - - for _i in 0..bytes_in_decoded_chunk { - result.push(value as u8); - value >>= 8; - } - - 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) + (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_char(c: char) -> Result { - let c = c as u8; +fn decode_base38(chars: &[u8]) -> impl Iterator> { + let mut value = 0u32; + let mut cerr = None; + + let repeat = match chars.len() { + 5 => 3, + 4 => 2, + 2 => 1, + 0 => 0, + _ => -1, + }; + + 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) + } + + (0..repeat) + .map(move |_| { + if let Some(err) = cerr { + Err(err) + } else { + let byte = (value & 0xff) as u8; + + value >>= 8; + + Ok(byte) + } + }) + .take_while(Result::is_ok) +} + +fn decode_char(c: u8) -> Result { 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 + ); } } diff --git a/matter/src/core.rs b/matter/src/core.rs index 7b853b9..1c3eb25 100644 --- a/matter/src/core.rs +++ b/matter/src/core.rs @@ -84,10 +84,19 @@ impl<'a> Matter<'a> { self.dev_det } - pub fn start(&mut self, dev_comm: CommissioningData) -> Result<(), Error> { + pub fn start( + &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::( + self.dev_det, + &dev_comm, + DiscoveryCapabilities::default(), + buf, + ); self.pase_mgr.borrow_mut().enable_pase_session( dev_comm.verifier, diff --git a/matter/src/group_keys.rs b/matter/src/group_keys.rs index c4dfaaf..1dc1c40 100644 --- a/matter/src/group_keys.rs +++ b/matter/src/group_keys.rs @@ -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>> = None; -static INIT: Once = Once::new(); - -impl GroupKeys { - fn new() -> Self { - Self {} - } - - pub fn get() -> Result>, 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], diff --git a/matter/src/pairing/mod.rs b/matter/src/pairing/mod.rs index d75e8f6..ee5aaff 100644 --- a/matter/src/pairing/mod.rs +++ b/matter/src/pairing/mod.rs @@ -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( 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::(&qr_code_data, buf).expect("Failed to encode"); pretty_print_pairing_code(&pairing_code); print_qr_code(&data_str); diff --git a/matter/src/pairing/qr.rs b/matter/src/pairing/qr.rs index f1d844a..70b668d 100644 --- a/matter/src/pairing/qr.rs +++ b/matter/src/pairing/qr.rs @@ -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, + // The vec is ordered by the tag of OptionalQRCodeInfo + optional_data: heapless::Vec, } 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)?; - - Ok(()) } - pub fn get_all_optional_data(&self) -> &FnvIndexMap { + 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); + + 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) -> &[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, - data: Option>, -} +pub(super) fn payload_base38_representation( + payload: &QrSetupPayload, + buf: &mut [u8], +) -> Result, 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 { - 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 { + (buf, None) + }; + + payload_base38_representation_with_tlv(payload, bits_buf, tlv_buf) } else { - (vec![0; TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES], None) - }; - - if !payload.is_valid() { - return Err(Error::InvalidArgument); + Err(Error::InvalidArgument) } - - payload_base38_representation_with_tlv(payload, &mut bits, tlv_data) } -fn estimate_buffer_size(payload: &QrSetupPayload) -> Result { +pub fn estimate_buffer_size(payload: &QrSetupPayload) -> Result { // 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 { 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( payload: &QrSetupPayload, - bits: &mut [u8], - mut tlv_data: Option, -) -> Result { - 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, 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 = "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, -) -> Result { - 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) } }