From 4710a0cf1001500904201a7b2081fdc8ea67069a Mon Sep 17 00:00:00 2001 From: Woshiluo Luo Date: Tue, 30 Sep 2025 18:38:10 +0800 Subject: [PATCH 1/2] refactor(ser): split to_dtb Signed-off-by: Woshiluo Luo --- src/ser/mod.rs | 70 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 0bc404e..45a2ce9 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -9,6 +9,25 @@ use crate::ser::patch::Patch; // TODO: set reverse map const RSVMAP_LEN: usize = 16; +/// Make dtb header with structure block and string block length. +fn make_header<'se>(writer: &'se mut [u8], structure_length: u32, string_block_length: u32) { + let (header, _) = writer.split_at_mut(HEADER_LEN as usize); + let header = unsafe { &mut *(header.as_mut_ptr() as *mut Header) }; + header.magic = u32::from_be(DEVICE_TREE_MAGIC); + header.total_size = u32::from_be( + HEADER_PADDING_LEN + RSVMAP_LEN as u32 + structure_length + string_block_length, + ); + assert_eq!(header.total_size % 8, 0); + header.off_dt_struct = u32::from_be(HEADER_PADDING_LEN + RSVMAP_LEN as u32); + header.off_dt_strings = u32::from_be(HEADER_PADDING_LEN + RSVMAP_LEN as u32 + structure_length); + header.off_mem_rsvmap = u32::from_be(HEADER_PADDING_LEN); + header.version = u32::from_be(SUPPORTED_VERSION); + header.last_comp_version = u32::from_be(SUPPORTED_VERSION); // TODO: maybe 16 + header.boot_cpuid_phys = 0; // TODO + header.size_dt_strings = u32::from_be(string_block_length as u32); + header.size_dt_struct = u32::from_be(structure_length as u32); +} + /// Serialize the data to dtb, with a list fof Patch, write to the `writer`. /// /// We do run-twice on convert, first time to generate string block, second time todo real @@ -37,16 +56,23 @@ where }; offset }; + list.iter().for_each(|patch| patch.init()); - // Write from bottom to top, to avoid overlap. - for i in (0..string_block_length).rev() { - writer[writer.len() - string_block_length + i] = writer[i]; - writer[i] = 0; + let bottom_string_block_start = writer.len() - string_block_length; + // Write to bottom, avoid overlap. + unsafe { + core::ptr::copy( + core::ptr::addr_of!(writer[0]), + core::ptr::addr_of_mut!(writer[bottom_string_block_start]), + string_block_length, + ); } + writer[0..string_block_length].fill(0); - let struct_len = { + let structure_length = { let (data_block, string_block) = writer.split_at_mut(writer.len() - string_block_length); let (_, data_block) = data_block.split_at_mut(HEADER_PADDING_LEN as usize + RSVMAP_LEN); + let mut patch_list = crate::ser::patch::PatchList::new(list); let mut temp_length = string_block_length; let mut block = crate::ser::string_block::StringBlock::new(string_block, &mut temp_length); @@ -60,31 +86,19 @@ where struct_len }; - // Align to 8-bytes. - for i in 0..string_block_length { - writer[HEADER_PADDING_LEN as usize + RSVMAP_LEN + struct_len + i] = - writer[writer.len() - string_block_length + i]; - writer[writer.len() - string_block_length + i] = 0; - } - - // Make header - { - let (header, _) = writer.split_at_mut(HEADER_LEN as usize); - let header = unsafe { &mut *(header.as_mut_ptr() as *mut Header) }; - header.magic = u32::from_be(DEVICE_TREE_MAGIC); - header.total_size = u32::from_be( - HEADER_PADDING_LEN + (RSVMAP_LEN + struct_len + string_block_length) as u32, + unsafe { + core::ptr::copy( + core::ptr::addr_of!(writer[writer.len() - string_block_length]), + core::ptr::addr_of_mut!( + writer[HEADER_PADDING_LEN as usize + RSVMAP_LEN + structure_length] + ), + string_block_length, ); - assert_eq!(header.total_size % 8, 0); - header.off_dt_struct = u32::from_be(HEADER_PADDING_LEN + RSVMAP_LEN as u32); - header.off_dt_strings = u32::from_be(HEADER_PADDING_LEN + (RSVMAP_LEN + struct_len) as u32); - header.off_mem_rsvmap = u32::from_be(HEADER_PADDING_LEN); - header.version = u32::from_be(SUPPORTED_VERSION); - header.last_comp_version = u32::from_be(SUPPORTED_VERSION); // TODO: maybe 16 - header.boot_cpuid_phys = 0; // TODO - header.size_dt_strings = u32::from_be(string_block_length as u32); - header.size_dt_struct = u32::from_be(struct_len as u32); } + writer[bottom_string_block_start..].fill(0); + + make_header(writer, structure_length as u32, string_block_length as u32); + Ok(()) } From 6c5884603694434158e7f319d9446de0ef98b4e4 Mon Sep 17 00:00:00 2001 From: Woshiluo Luo Date: Tue, 30 Sep 2025 23:56:54 +0800 Subject: [PATCH 2/2] feat(ser): add probe length feat(ser): now to dtb will return total size Signed-off-by: Woshiluo Luo --- Cargo.toml | 2 +- src/de.rs | 2 - src/ser/mod.rs | 114 +++++++++++++++++++++++----------------- src/ser/serializer.rs | 35 ++++++++++++ src/ser/string_block.rs | 66 +++++++++++++++++------ 5 files changed, 151 insertions(+), 68 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d160a2e..a20e6b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] } dyn_serde = { version = "=1.1.2", default-features = false, optional = true } [features] -default = ["std", "ser"] +default = ["std", "ser", "alloc"] ser = ["dep:dyn_serde"] std = ["serde/std"] diff --git a/src/de.rs b/src/de.rs index 93ed378..edcdd12 100644 --- a/src/de.rs +++ b/src/de.rs @@ -518,8 +518,6 @@ mod tests { use alloc::format; #[cfg(any(feature = "std", feature = "alloc"))] use serde::Deserialize; - #[cfg(feature = "std")] - use std::format; #[cfg(any(feature = "std", feature = "alloc"))] #[test] diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 45a2ce9..4872a0b 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -1,6 +1,7 @@ pub mod patch; pub mod pointer; pub mod serializer; + pub mod string_block; use crate::common::*; @@ -9,14 +10,26 @@ use crate::ser::patch::Patch; // TODO: set reverse map const RSVMAP_LEN: usize = 16; +#[inline(always)] +fn get_structure_padding(length: usize) -> usize { + let rem = length & (ALIGN - 1); + ALIGN - rem +} + /// Make dtb header with structure block and string block length. -fn make_header<'se>(writer: &'se mut [u8], structure_length: u32, string_block_length: u32) { +fn make_header<'se>( + writer: &'se mut [u8], + structure_length: u32, + string_block_length: u32, +) -> usize { let (header, _) = writer.split_at_mut(HEADER_LEN as usize); let header = unsafe { &mut *(header.as_mut_ptr() as *mut Header) }; + let total_size = + HEADER_PADDING_LEN + RSVMAP_LEN as u32 + structure_length + string_block_length; + let padding = get_structure_padding(total_size as usize); + let total_size = total_size + padding as u32; header.magic = u32::from_be(DEVICE_TREE_MAGIC); - header.total_size = u32::from_be( - HEADER_PADDING_LEN + RSVMAP_LEN as u32 + structure_length + string_block_length, - ); + header.total_size = u32::from_be(total_size); assert_eq!(header.total_size % 8, 0); header.off_dt_struct = u32::from_be(HEADER_PADDING_LEN + RSVMAP_LEN as u32); header.off_dt_strings = u32::from_be(HEADER_PADDING_LEN + RSVMAP_LEN as u32 + structure_length); @@ -26,80 +39,83 @@ fn make_header<'se>(writer: &'se mut [u8], structure_length: u32, string_block_l header.boot_cpuid_phys = 0; // TODO header.size_dt_strings = u32::from_be(string_block_length as u32); header.size_dt_struct = u32::from_be(structure_length as u32); + + total_size as usize } /// Serialize the data to dtb, with a list fof Patch, write to the `writer`. /// /// We do run-twice on convert, first time to generate string block, second time todo real /// structure. -pub fn to_dtb<'se, T>(data: &T, list: &'se [Patch<'se>], writer: &'se mut [u8]) -> Result<(), Error> +pub fn to_dtb<'se, T>( + data: &T, + list: &'se [Patch<'se>], + writer: &'se mut [u8], +) -> Result where T: serde::ser::Serialize, { - writer.iter_mut().for_each(|x| *x = 0); + writer.fill(0); - let string_block_length = { + let (string_block_length, structure_length) = { let mut offset: usize = 0; - { - let mut dst = crate::ser::pointer::Pointer::new(None); - let mut patch_list = crate::ser::patch::PatchList::new(list); - let mut block = crate::ser::string_block::StringBlock::new(writer, &mut offset); - let mut ser = - crate::ser::serializer::SerializerInner::new(&mut dst, &mut block, &mut patch_list); - let ser = crate::ser::serializer::Serializer::new(&mut ser); - data.serialize(ser)?; - offset - }; - { - let mut block = crate::ser::string_block::StringBlock::new(writer, &mut offset); - block.align(); - }; - offset + let mut block = crate::ser::string_block::StringBlock::new(Some(writer), &mut offset); + let mut dst = crate::ser::pointer::Pointer::new(None); + let mut patch_list = crate::ser::patch::PatchList::new(list); + let mut ser = + crate::ser::serializer::SerializerInner::new(&mut dst, &mut block, &mut patch_list); + let ser = crate::ser::serializer::Serializer::new(&mut ser); + let structure_length = data.serialize(ser)?.1; + (offset, structure_length) }; - list.iter().for_each(|patch| patch.init()); - let bottom_string_block_start = writer.len() - string_block_length; - // Write to bottom, avoid overlap. - unsafe { - core::ptr::copy( - core::ptr::addr_of!(writer[0]), - core::ptr::addr_of_mut!(writer[bottom_string_block_start]), - string_block_length, - ); - } + // Clear string block writer[0..string_block_length].fill(0); + list.iter().for_each(|patch| patch.init()); + let string_block_start = HEADER_PADDING_LEN as usize + RSVMAP_LEN + structure_length; - let structure_length = { - let (data_block, string_block) = writer.split_at_mut(writer.len() - string_block_length); + { + let (data_block, string_block) = writer.split_at_mut(string_block_start); let (_, data_block) = data_block.split_at_mut(HEADER_PADDING_LEN as usize + RSVMAP_LEN); let mut patch_list = crate::ser::patch::PatchList::new(list); - let mut temp_length = string_block_length; - let mut block = crate::ser::string_block::StringBlock::new(string_block, &mut temp_length); + let mut temp_length = 0; + let mut block = + crate::ser::string_block::StringBlock::new(Some(string_block), &mut temp_length); let mut dst = crate::ser::pointer::Pointer::new(Some(data_block)); let mut ser = crate::ser::serializer::SerializerInner::new(&mut dst, &mut block, &mut patch_list); let ser = crate::ser::serializer::Serializer::new(&mut ser); let struct_len = data.serialize(ser)?.1; - assert_eq!(struct_len % 4, 0); // As spec, structure block align with 4 bytes. assert_eq!(temp_length, string_block_length); // StringBlock should be same with first run. struct_len }; - unsafe { - core::ptr::copy( - core::ptr::addr_of!(writer[writer.len() - string_block_length]), - core::ptr::addr_of_mut!( - writer[HEADER_PADDING_LEN as usize + RSVMAP_LEN + structure_length] - ), - string_block_length, - ); - } - writer[bottom_string_block_start..].fill(0); + let result = make_header(writer, structure_length as u32, string_block_length as u32); + + Ok(result) +} - make_header(writer, structure_length as u32, string_block_length as u32); +#[cfg(feature = "alloc")] +pub fn probe_dtb_length<'se, T>(data: &T, list: &'se [Patch<'se>]) -> Result +where + T: serde::ser::Serialize, +{ + let mut offset: usize = 0; + let structure_length = { + let mut dst = crate::ser::pointer::Pointer::new(None); + let mut patch_list = crate::ser::patch::PatchList::new(list); + let mut block = crate::ser::string_block::StringBlock::new(None, &mut offset); + let mut ser = + crate::ser::serializer::SerializerInner::new(&mut dst, &mut block, &mut patch_list); + let ser = crate::ser::serializer::Serializer::new(&mut ser); + data.serialize(ser)?.1 + }; - Ok(()) + let total_size = HEADER_PADDING_LEN as usize + RSVMAP_LEN + structure_length + offset; + let padding = get_structure_padding(total_size); + let total_size = total_size + padding; + Ok(total_size) } #[derive(Debug)] diff --git a/src/ser/serializer.rs b/src/ser/serializer.rs index 39b03d1..4560e7a 100644 --- a/src/ser/serializer.rs +++ b/src/ser/serializer.rs @@ -763,4 +763,39 @@ mod tests { // println!("{:x?}", buf1); // assert!(false); } + #[test] + #[cfg(feature = "alloc")] + fn probe_length() { + #[derive(Serialize)] + struct Base { + pub hello: u32, + pub base1: Base1, + pub hello2: u32, + pub base2: Base1, + } + #[derive(Serialize)] + struct Base1 { + pub hello: &'static str, + } + let mut buf1 = [0u8; MAX_SIZE]; + + let new_base = Base1 { hello: "added" }; + let patch = crate::ser::patch::Patch::new( + "/base3", + &new_base as _, + crate::ser::serializer::ValueType::Node, + ); + let list = [patch]; + let base = Base { + hello: 0xdeedbeef, + base1: Base1 { + hello: "Hello, World!", + }, + hello2: 0x11223344, + base2: Base1 { hello: "Roger" }, + }; + let probe_legnth = crate::ser::probe_dtb_length(&base, &list).unwrap(); + let result = crate::ser::to_dtb(&base, &list, &mut buf1).unwrap(); + assert_eq!(probe_legnth, result); + } } diff --git a/src/ser/string_block.rs b/src/ser/string_block.rs index 672b7d3..0688c24 100644 --- a/src/ser/string_block.rs +++ b/src/ser/string_block.rs @@ -1,8 +1,13 @@ +#[cfg(feature = "alloc")] +use alloc::collections::BTreeMap; + /// StringBlock /// As spec said, dtb have a block called string block for saving prop names. pub struct StringBlock<'se> { end: &'se mut usize, - data: &'se mut [u8], + data: Option<&'se mut [u8]>, + #[cfg(feature = "alloc")] + tree: BTreeMap<&'se str, usize>, } impl<'se> StringBlock<'se> { @@ -10,8 +15,13 @@ impl<'se> StringBlock<'se> { /// /// For get how long is string block, we make `end` as a mut ref. #[inline(always)] - pub fn new(dst: &'se mut [u8], end: &'se mut usize) -> StringBlock<'se> { - StringBlock { data: dst, end } + pub fn new(dst: Option<&'se mut [u8]>, end: &'se mut usize) -> StringBlock<'se> { + StringBlock { + data: dst, + #[cfg(feature = "alloc")] + tree: BTreeMap::new(), + end, + } } // TODO: show as error @@ -24,26 +34,39 @@ impl<'se> StringBlock<'se> { if offset > *self.end { panic!("invalid read"); } - let current_slice = &self.data[offset..]; - let pos = current_slice - .iter() - .position(|&x| x == b'\0') - .unwrap_or(self.data.len()); - let (a, _) = current_slice.split_at(pos + 1); - let result = unsafe { core::str::from_utf8_unchecked(&a[..a.len() - 1]) }; - (result, pos + offset + 1) + if let Some(data) = &self.data { + let current_slice = &data[offset..]; + let pos = current_slice + .iter() + .position(|&x| x == b'\0') + .unwrap_or(data.len()); + let (a, _) = current_slice.split_at(pos + 1); + let result = unsafe { core::str::from_utf8_unchecked(&a[..a.len() - 1]) }; + (result, pos + offset + 1) + } else { + panic!("must have writer when no alloc"); + } + } + + #[inline(always)] + fn write_u8(&mut self, index: usize, data: u8) { + if let Some(buffer) = &mut self.data { + buffer[index] = data; + } } #[inline(always)] fn insert_u8(&mut self, data: u8) { - self.data[*self.end] = data; + self.write_u8(*self.end, data); *self.end += 1; } /// Return the start offset of inserted string. #[inline(always)] - pub fn insert_str(&mut self, name: &str) -> usize { + pub fn insert_str(&mut self, name: &'se str) -> usize { let result = *self.end; + #[cfg(feature = "alloc")] + self.tree.insert(name, result); name.bytes().for_each(|x| { self.insert_u8(x); }); @@ -55,14 +78,14 @@ impl<'se> StringBlock<'se> { #[inline(always)] pub fn align(&mut self) { while (*self.end & 0b111) != 0 { - self.data[*self.end] = 0; - *self.end += 1; + self.insert_u8(0); } } /// Find a string. If not found, insert it. #[inline(always)] - pub fn find_or_insert(&mut self, name: &str) -> usize { + #[cfg(not(feature = "alloc"))] + pub fn find_or_insert(&mut self, name: &'se str) -> usize { let mut current_pos = 0; while current_pos < *self.end { let (result, new_pos) = self.get_str_by_offset(current_pos); @@ -74,4 +97,15 @@ impl<'se> StringBlock<'se> { self.insert_str(name) } + + #[inline(always)] + #[cfg(feature = "alloc")] + pub fn find_or_insert(&mut self, name: &'se str) -> usize { + let result = self.tree.get(name); + if result.is_some() { + *result.unwrap() + } else { + self.insert_str(name) + } + } }