Skip to content

Commit 4dbb673

Browse files
committed
refactor(writer): avoid some intermediate allocations
This doesn't change the public interface at all, but should also make it easier to avoid intermediate allocations in the future. Signed-off-by: Ellen Εμιλία Άννα Zscheile <fogti+devel@ytrizja.de>
1 parent ef5bd73 commit 4dbb673

File tree

1 file changed

+67
-19
lines changed

1 file changed

+67
-19
lines changed

src/writer.rs

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use alloc::string::String;
1010
use alloc::vec::Vec;
1111
use core::cmp::{Ord, Ordering};
1212
use core::convert::TryInto;
13+
use core::ffi::CStr;
1314
use core::fmt;
1415
use core::mem::size_of_val;
1516
#[cfg(feature = "std")]
@@ -216,21 +217,28 @@ fn node_name_valid_first_char(c: char) -> bool {
216217
// Check if `name` is a valid property name.
217218
// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
218219
fn property_name_valid(name: &str) -> bool {
219-
if name.is_empty() || name.len() > PROPERTY_NAME_MAX_LEN {
220-
return false;
221-
}
222-
223-
if name.contains(|c: char| !property_name_valid_char(c)) {
224-
return false;
225-
}
220+
!(name.is_empty()
221+
|| name.len() > PROPERTY_NAME_MAX_LEN
222+
|| name.contains(|c| !property_name_valid_char(c)))
223+
}
226224

227-
true
225+
// Check if `name` is a valid property name.
226+
// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
227+
fn property_cstr_name_valid(name: &CStr) -> bool {
228+
let name_bytes = name.to_bytes();
229+
!(name_bytes.is_empty()
230+
|| name_bytes.len() > PROPERTY_NAME_MAX_LEN
231+
|| name_bytes.iter().any(|&c| !property_name_valid_byte(c)))
228232
}
229233

230234
fn property_name_valid_char(c: char) -> bool {
231235
matches!(c, '0'..='9' | 'a'..='z' | 'A'..='Z' | ',' | '.' | '_' | '+' | '?' | '#' | '-')
232236
}
233237

238+
fn property_name_valid_byte(c: u8) -> bool {
239+
matches!(c, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b',' | b'.' | b'_' | b'+' | b'?' | b'#' | b'-')
240+
}
241+
234242
/// Handle to an open node created by `FdtWriter::begin_node`.
235243
///
236244
/// This must be passed back to `FdtWriter::end_node` to close the nodes.
@@ -390,19 +398,19 @@ impl FdtWriter {
390398

391399
// Find an existing instance of a string `s`, or add it to the strings block.
392400
// Returns the offset into the strings block.
393-
fn intern_string(&mut self, s: CString) -> Result<u32> {
394-
if let Some(off) = self.string_offsets.get(&s) {
395-
Ok(*off)
401+
fn intern_string(&mut self, s: &CStr) -> Result<u32> {
402+
Ok(if let Some(off) = self.string_offsets.get(s) {
403+
*off
396404
} else {
397405
let off = self
398406
.strings
399407
.len()
400408
.try_into()
401409
.map_err(|_| Error::TotalSizeTooLarge)?;
402410
self.strings.extend_from_slice(s.to_bytes_with_nul());
403-
self.string_offsets.insert(s, off);
404-
Ok(off)
405-
}
411+
self.string_offsets.insert(s.to_owned(), off);
412+
off
413+
})
406414
}
407415

408416
/// Write a property.
@@ -431,11 +439,45 @@ impl FdtWriter {
431439
.try_into()
432440
.map_err(|_| Error::PropertyValueTooLarge)?;
433441

434-
let nameoff = self.intern_string(name_cstr)?;
442+
let nameoff = self.intern_string(&name_cstr)?;
443+
self.append_u32(FDT_PROP);
444+
self.append_u32(len);
445+
self.append_u32(nameoff);
446+
self.data.extend_from_slice(val);
447+
self.align(4);
448+
Ok(())
449+
}
450+
451+
/// Write a property, but add a NUL byte after `val` and make sure that `val` doesn't contain NUL bytes.
452+
/// Otherwise identical to [`FdtWriter::property`].
453+
///
454+
/// The purpose of this is to avoid unnecessary intermediate allocations.
455+
fn property_and_nul(&mut self, name: &CStr, val: &[u8]) -> Result<()> {
456+
if self.node_ended {
457+
return Err(Error::PropertyAfterEndNode);
458+
}
459+
460+
if self.node_depth == 0 {
461+
return Err(Error::PropertyBeforeBeginNode);
462+
}
463+
464+
if !property_cstr_name_valid(name) {
465+
return Err(Error::InvalidPropertyName);
466+
}
467+
if val.contains(&0x00) {
468+
return Err(Error::InvalidString);
469+
}
470+
471+
let len = (val.len() + 1)
472+
.try_into()
473+
.map_err(|_| Error::PropertyValueTooLarge)?;
474+
475+
let nameoff = self.intern_string(name)?;
435476
self.append_u32(FDT_PROP);
436477
self.append_u32(len);
437478
self.append_u32(nameoff);
438479
self.data.extend_from_slice(val);
480+
self.data.push(0x00);
439481
self.align(4);
440482
Ok(())
441483
}
@@ -447,16 +489,22 @@ impl FdtWriter {
447489

448490
/// Write a string property.
449491
pub fn property_string(&mut self, name: &str, val: &str) -> Result<()> {
450-
let cstr_value = CString::new(val).map_err(|_| Error::InvalidString)?;
451-
self.property(name, cstr_value.to_bytes_with_nul())
492+
self.property_and_nul(
493+
&CString::new(name).map_err(|_| Error::InvalidString)?,
494+
val.as_bytes(),
495+
)
452496
}
453497

454498
/// Write a stringlist property.
455499
pub fn property_string_list(&mut self, name: &str, values: Vec<String>) -> Result<()> {
456500
let mut bytes = Vec::new();
457501
for s in values {
458-
let cstr = CString::new(s).map_err(|_| Error::InvalidString)?;
459-
bytes.extend_from_slice(cstr.to_bytes_with_nul());
502+
let s = s.as_bytes();
503+
if s.contains(&0x00) {
504+
return Err(Error::InvalidString);
505+
}
506+
bytes.extend_from_slice(s);
507+
bytes.push(0x00);
460508
}
461509
self.property(name, &bytes)
462510
}

0 commit comments

Comments
 (0)