Skip to content

Commit

Permalink
feat: 构造遍历上下文类型,集成所有遍历中的信息传递
Browse files Browse the repository at this point in the history
Signed-off-by: YdrMaster <[email protected]>
  • Loading branch information
YdrMaster committed Jul 18, 2022
1 parent e694aa3 commit 5351f45
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 105 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Change

- 删除 `Path`,增加 `Context` 类型集中处理所有从父节点向子节点传递的信息。

---

- Removes `Path` Type, and add `Context` to handle all information passed from parent node to child node.

## [0.2.0-alpha.2](https://github.com/YdrMaster/dtb-walker/releases/tag/0.2.0-alpha.2) - 2022-07-15

### Fixed
Expand Down
98 changes: 98 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use crate::{tree_on_stack::Node, Str};
use core::fmt;

/// 遍历上下文。
pub struct Context<'a>(Node<'a, Inner<'a>>);

struct Inner<'a> {
name: Str<'a>,
cells: Cells,
}

impl Context<'_> {
pub(crate) const ROOT: Self = Context(Node::root(Inner {
name: Str(b""),
cells: Cells::DEFAULT,
}));

/// 返回路径层数。定义根节点的子节点层数为 0。
#[inline]
pub fn level(&self) -> usize {
self.0.level()
}

/// 如果这是根节点的路径则返回 `true`。
#[inline]
pub fn is_root(&self) -> bool {
self.0.is_root()
}

/// 返回路径最后一级的节点名。
#[inline]
pub fn name(&self) -> Str {
self.0.as_ref().name
}

#[inline]
pub(crate) fn cells(&self) -> Cells {
self.0.as_ref().cells
}

/// 将路径字符串格式化到 `buf` 中。
///
/// 如果返回 `Ok(n)`,表示字符串长度为 `n`(`n` 不大于 `buf.len()`)。
/// 如果返回 `Err(n)`,表示缓冲区长度无法存放整个字符串,实现保证 `n` 等于 `buf.len()`。
pub fn fmt_path(&self, buf: &mut [u8]) -> Result<usize, usize> {
self.0.fold(0, |len, inner| match buf.len() - len {
0 => Err(buf.len()),
mut rest => {
let bytes = inner.name.as_bytes();
buf[len] = b'/';
rest -= 1;
if bytes.len() > rest {
buf[len + 1..].copy_from_slice(&bytes[..rest]);
Err(buf.len())
} else {
buf[len + 1..][..bytes.len()].copy_from_slice(bytes);
Ok(len + bytes.len() + 1)
}
}
})
}
}

impl<'a> Context<'a> {
#[inline]
pub(crate) fn grow(&'a self, name: Str<'a>, cells: Cells) -> Self {
Self(self.0.grow(Inner { name, cells }))
}
}

impl fmt::Display for Context<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fold((), |(), inner| {
'/'.fmt(f)?;
unsafe { inner.name.as_str_unchecked() }.fmt(f)
})
}
}

#[derive(Clone, Copy)]
pub(crate) struct Cells {
pub address: u32,
pub size: u32,
pub interrupt: u32,
}

impl Cells {
pub const DEFAULT: Self = Self {
address: 2,
size: 1,
interrupt: 1,
};

#[inline]
pub fn reg_size(&self) -> usize {
(self.address + self.size) as _
}
}
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@
#![no_std]
#![deny(warnings, unstable_features, missing_docs)] // cancel this line during developing

mod context;
mod header;
mod indent;
mod path;
mod property;
mod str;
mod structure_block;
mod tree_on_stack;
mod walker;

pub use self::str::Str;
pub use path::Path;
pub use property::{PHandle, Property, Reg, StrList};
pub mod utils {
//! 用于设备树解析、格式化的工具集。
pub use crate::indent::indent;
}
pub use context::Context;
pub use header::HeaderError;

use core::{fmt, mem, slice};
Expand Down Expand Up @@ -129,7 +129,7 @@ impl Dtb<'_> {
}

/// 遍历。
pub fn walk(&self, mut f: impl FnMut(&Path<'_>, DtbObj) -> WalkOperation) {
pub fn walk(&self, mut f: impl FnMut(&Context<'_>, DtbObj) -> WalkOperation) {
let header = self.header();
let off_struct = header.off_dt_struct.into_u32() as usize;
let len_struct = header.size_dt_struct.into_u32() as usize;
Expand All @@ -147,7 +147,7 @@ impl Dtb<'_> {
},
strings: &self.0[off_strings..][..len_strings],
}
.walk_inner(&mut f, &Path::ROOT, RegCfg::DEFAULT, false);
.walk_inner(&mut f, Some(Context::ROOT));
}

#[inline]
Expand Down
63 changes: 0 additions & 63 deletions src/path.rs

This file was deleted.

12 changes: 0 additions & 12 deletions src/property/reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,3 @@ pub(crate) struct RegCfg {
pub address_cells: u32,
pub size_cells: u32,
}

impl RegCfg {
pub const DEFAULT: Self = Self {
address_cells: 2,
size_cells: 1,
};

#[inline]
pub(crate) fn item_size(&self) -> usize {
(self.address_cells + self.size_cells) as _
}
}
59 changes: 33 additions & 26 deletions src/walker.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::{DtbObj, Path, Property, Reg, RegCfg, Str, StructureBlock as Blk, WalkOperation};
use core::slice;
use crate::{
context::Cells, Context, DtbObj, Property, Reg, RegCfg, Str, StructureBlock as Blk,
WalkOperation,
};

/// 设备树递归结构。
pub(crate) struct Walker<'a> {
Expand All @@ -18,14 +20,12 @@ impl Walker<'_> {
/// 深度优先遍历。如果返回 `false`,取消所有后续的遍历。
pub fn walk_inner(
&mut self,
f: &mut impl FnMut(&Path<'_>, DtbObj) -> WalkOperation,
path: &Path<'_>,
reg_cfg: RegCfg,
mut escape: bool,
f: &mut impl FnMut(&Context<'_>, DtbObj) -> WalkOperation,
mut ctx: Option<Context>,
) -> bool {
use WalkOperation::*;

let mut sub_reg_cfg = RegCfg::DEFAULT;
let mut cells = Cells::DEFAULT;
loop {
match self.tail.split_first() {
// 子节点
Expand All @@ -34,29 +34,29 @@ impl Walker<'_> {
let name_len = tail.iter().position(Blk::is_end_of_str).unwrap() + 1;
let (name, tail) = tail.split_at(name_len);
self.tail = tail;
if escape {
// 如果当前子树已选跳过,不可能再选择终止
assert!(self.walk_inner(f, path, sub_reg_cfg, true));
} else {
if let Some(ctx_) = ctx.as_ref() {
// 正确舍弃尾 '\0'
let name = Str(unsafe {
slice::from_raw_parts(
core::slice::from_raw_parts(
name.as_ptr().cast::<u8>(),
name.len() * Blk::LEN - name.last().unwrap().str_tail_zero(),
)
});
let escape = match f(path, DtbObj::SubNode { name }) {
StepInto => false,
StepOver => true,
let ctx = match f(&ctx_, DtbObj::SubNode { name }) {
StepInto => Some(ctx_.grow(name, cells)),
StepOver => None,
StepOut => {
escape = true;
true
ctx = None;
None
}
Terminate => return false,
};
if !self.walk_inner(f, &path.grow(name), sub_reg_cfg, escape) {
if !self.walk_inner(f, ctx) {
return false;
}
} else {
// 如果当前子树已选跳过,不可能再选择终止
assert!(self.walk_inner(f, None));
}
}
// 当前节点结束
Expand All @@ -70,28 +70,35 @@ impl Walker<'_> {
let len = len.into_u32() as usize;
let (value, tail) = tail.split_at((len + Blk::LEN - 1) / Blk::LEN);
// 如果当前子树需要解析
if !escape {
if let Some(ctx_) = ctx.as_ref() {
let op = match self.prop_name(*nameoff) {
b"#address-cells" if value.len() == 1 => {
sub_reg_cfg.address_cells = value[0].into_u32();
cells.address = value[0].into_u32();
StepOver
}
b"#size-cells" if value.len() == 1 => {
sub_reg_cfg.size_cells = value[0].into_u32();
cells.size = value[0].into_u32();
StepOver
}
b"#interrupt-cells" if value.len() == 1 => {
cells.interrupt = value[0].into_u32();
StepOver
}
b"reg" if value.len() % reg_cfg.item_size() == 0 => f(
path,
b"reg" if value.len() % (ctx_.cells().reg_size()) == 0 => f(
&ctx_,
DtbObj::Property(Property::Reg(Reg {
buf: value,
cfg: reg_cfg,
cfg: RegCfg {
address_cells: ctx_.cells().address,
size_cells: ctx_.cells().size,
},
})),
),
name => f(path, DtbObj::Property(Property::new(name, value, len))),
name => f(&ctx_, DtbObj::Property(Property::new(name, value, len))),
};
match op {
StepInto | StepOver => {}
StepOut => escape = true,
StepOut => ctx = None,
Terminate => return false,
};
}
Expand Down

0 comments on commit 5351f45

Please sign in to comment.