summaryrefslogtreecommitdiff
path: root/crates/parser
diff options
context:
space:
mode:
authorHenry <mail@henrygressmann.de>2026-04-05 20:49:40 +0200
committerHenry <mail@henrygressmann.de>2026-04-05 20:49:40 +0200
commit7d4dfd8ec65c78eb8154d89aa77dd71ac65015f6 (patch)
tree7db6f65b35553a9994a3790e885721bdcbddf1ca /crates/parser
parent66c9f7ab06dd67ac6e62321dd33943de7f9f9e57 (diff)
chore: cleanup
Diffstat (limited to 'crates/parser')
-rw-r--r--crates/parser/src/conversion.rs77
-rw-r--r--crates/parser/src/lib.rs81
-rw-r--r--crates/parser/src/module.rs89
-rw-r--r--crates/parser/src/visit.rs32
4 files changed, 96 insertions, 183 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index b62e670..570d0a1 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -70,38 +70,33 @@ pub(crate) fn convert_module_imports<'a, T: IntoIterator<Item = wasmparser::Resu
}
pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result<Import> {
- Ok(Import {
- module: import.module.to_string().into_boxed_str(),
- name: import.name.to_string().into_boxed_str(),
- kind: match import.ty {
- wasmparser::TypeRef::Func(ty) => ImportKind::Function(ty),
- wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType {
- element_type: convert_reftype(ty.element_type),
- size_initial: ty.initial.try_into().map_err(|_| {
- crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", ty.initial))
- })?,
- size_max: match ty.maximum {
- Some(max) => Some(max.try_into().map_err(|_| {
- crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}"))
- })?),
- None => None,
- },
- }),
- wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)),
- wasmparser::TypeRef::Global(ty) => {
- ImportKind::Global(GlobalType { mutable: ty.mutable, ty: convert_valtype(&ty.content_type) })
- }
- wasmparser::TypeRef::Tag(ty) => {
- return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {ty:?}")));
- }
- _ => {
- return Err(crate::ParseError::UnsupportedOperator(format!(
- "Unsupported import kind: {:?}",
- import.ty
- )));
- }
- },
- })
+ let kind = match import.ty {
+ wasmparser::TypeRef::Func(ty) => ImportKind::Function(ty),
+ wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType {
+ element_type: convert_reftype(ty.element_type),
+ size_initial: ty.initial.try_into().map_err(|_| {
+ crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", ty.initial))
+ })?,
+ size_max: match ty.maximum {
+ Some(max) => Some(max.try_into().map_err(|_| {
+ crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}"))
+ })?),
+ None => None,
+ },
+ }),
+ wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)),
+ wasmparser::TypeRef::Global(ty) => {
+ ImportKind::Global(GlobalType { mutable: ty.mutable, ty: convert_valtype(&ty.content_type) })
+ }
+ wasmparser::TypeRef::Tag(ty) => {
+ return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {ty:?}")));
+ }
+ _ => {
+ return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported import kind: {:?}", import.ty)));
+ }
+ };
+
+ Ok(Import { module: import.module.into(), name: import.name.into(), kind })
}
pub(crate) fn convert_module_memories<T: IntoIterator<Item = wasmparser::Result<wasmparser::MemoryType>>>(
@@ -130,21 +125,16 @@ pub(crate) fn convert_module_table(table: wasmparser::Table<'_>) -> Result<Table
crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", table.ty.initial))
})?;
- let size_max = match table.ty.maximum {
- Some(max) => Some(
- max.try_into()
- .map_err(|_| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {max}")))?,
- ),
- None => None,
- };
-
+ let size_max = table.ty.maximum.map(|max| max.try_into()).transpose();
+ let size_max =
+ size_max.map_err(|e| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {e}")))?;
Ok(TableType { element_type: convert_reftype(table.ty.element_type), size_initial, size_max })
}
pub(crate) fn convert_module_globals(
globals: wasmparser::SectionLimited<'_, wasmparser::Global<'_>>,
) -> Result<Vec<Global>> {
- let globals = globals
+ globals
.into_iter()
.map(|global| {
let global = global?;
@@ -152,8 +142,7 @@ pub(crate) fn convert_module_globals(
let ops = global.init_expr.get_operators_reader();
Ok(Global { init: process_const_operators(ops)?, ty: GlobalType { mutable: global.ty.mutable, ty } })
})
- .collect::<Result<Vec<_>>>()?;
- Ok(globals)
+ .collect::<Result<Vec<_>>>()
}
pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result<Export> {
@@ -215,7 +204,6 @@ pub(crate) fn convert_module_code(
pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType> {
let mut types = ty.types();
-
if types.len() != 1 {
return Err(crate::ParseError::UnsupportedOperator(
"Expected exactly one type in the type section".to_string(),
@@ -225,7 +213,6 @@ pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType>
let ty = types.next().unwrap().unwrap_func();
let params = ty.params().iter().map(convert_valtype).collect::<Vec<ValType>>().into_boxed_slice();
let results = ty.results().iter().map(convert_valtype).collect::<Vec<ValType>>().into_boxed_slice();
-
Ok(FuncType { params, results })
}
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 2d46857..a0c0c7e 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -36,7 +36,7 @@ mod optimize;
mod visit;
pub use error::*;
use module::ModuleReader;
-use wasmparser::{Validator, WasmFeaturesInflated};
+use wasmparser::{Validator, WasmFeatures};
pub use tinywasm_types::TinyWasmModule;
@@ -79,56 +79,32 @@ impl Parser {
}
fn create_validator(_options: ParserOptions) -> Validator {
- let features = WasmFeaturesInflated {
- bulk_memory: true,
- floats: true,
- multi_value: true,
- mutable_global: true,
- reference_types: true,
- sign_extension: true,
- saturating_float_to_int: true,
- function_references: true,
- tail_call: true,
- multi_memory: true,
- simd: true,
- memory64: true,
- custom_page_sizes: true,
- bulk_memory_opt: true,
- call_indirect_overlong: true,
- wide_arithmetic: true,
- relaxed_simd: true,
-
- compact_imports: false,
- cm_map: false,
- custom_descriptors: false,
- cm_threading: false,
- extended_const: false,
- gc_types: true,
- stack_switching: false,
- component_model: false,
- exceptions: false,
- gc: false,
- memory_control: false,
- threads: false,
- shared_everything_threads: false,
- legacy_exceptions: false,
- cm_async: false,
- cm_async_builtins: false,
- cm_async_stackful: false,
- cm_nested_names: false,
- cm_values: false,
- cm_error_context: false,
- cm_fixed_length_lists: false,
- cm_gc: false,
- };
- Validator::new_with_features(features.into())
+ let features = WasmFeatures::CALL_INDIRECT_OVERLONG
+ | WasmFeatures::BULK_MEMORY_OPT
+ | WasmFeatures::RELAXED_SIMD
+ | WasmFeatures::GC_TYPES
+ | WasmFeatures::REFERENCE_TYPES
+ | WasmFeatures::MUTABLE_GLOBAL
+ | WasmFeatures::MULTI_VALUE
+ | WasmFeatures::FLOATS
+ | WasmFeatures::BULK_MEMORY
+ | WasmFeatures::SATURATING_FLOAT_TO_INT
+ | WasmFeatures::SIGN_EXTENSION
+ | WasmFeatures::FUNCTION_REFERENCES
+ | WasmFeatures::TAIL_CALL
+ | WasmFeatures::MULTI_MEMORY
+ | WasmFeatures::SIMD
+ | WasmFeatures::MEMORY64
+ | WasmFeatures::CUSTOM_PAGE_SIZES
+ | WasmFeatures::WIDE_ARITHMETIC;
+ Validator::new_with_features(features)
}
/// Parse a [`TinyWasmModule`] from bytes
pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result<TinyWasmModule> {
let wasm = wasm.as_ref();
let mut validator = Self::create_validator(self.options.clone());
- let mut reader = ModuleReader::new();
+ let mut reader = ModuleReader::default();
for payload in wasmparser::Parser::new(0).parse_all(wasm) {
reader.process_payload(payload?, &mut validator)?;
@@ -144,21 +120,16 @@ impl Parser {
#[cfg(feature = "std")]
/// Parse a [`TinyWasmModule`] from a file. Requires `std` feature.
pub fn parse_module_file(&self, path: impl AsRef<crate::std::path::Path> + Clone) -> Result<TinyWasmModule> {
- use alloc::format;
- let f = crate::std::fs::File::open(&path)
- .map_err(|e| ParseError::Other(format!("Error opening file {:?}: {}", path.as_ref(), e)))?;
-
- let mut reader = crate::std::io::BufReader::new(f);
- self.parse_module_stream(&mut reader)
+ let file = crate::std::fs::File::open(&path)
+ .map_err(|e| ParseError::Other(alloc::format!("Error opening file {:?}: {}", path.as_ref(), e)))?;
+ self.parse_module_stream(&mut crate::std::io::BufReader::new(file))
}
#[cfg(feature = "std")]
/// Parse a [`TinyWasmModule`] from a stream. Requires `std` feature.
pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result<TinyWasmModule> {
- use alloc::format;
-
let mut validator = Self::create_validator(self.options.clone());
- let mut reader = ModuleReader::new();
+ let mut reader = ModuleReader::default();
let mut buffer = alloc::vec::Vec::new();
let mut parser = wasmparser::Parser::new(0);
let mut eof = false;
@@ -170,7 +141,7 @@ impl Parser {
buffer.extend((0..hint).map(|_| 0u8));
let read_bytes = stream
.read(&mut buffer[len..])
- .map_err(|e| ParseError::Other(format!("Error reading from stream: {e}")))?;
+ .map_err(|e| ParseError::Other(alloc::format!("Error reading from stream: {e}")))?;
buffer.truncate(len + read_bytes);
eof = read_bytes == 0;
}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index 29e556c..cb29e21 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -2,10 +2,7 @@ use crate::log::debug;
use crate::{ParseError, ParserOptions, Result, conversion, optimize};
use alloc::string::ToString;
use alloc::{format, vec::Vec};
-use tinywasm_types::{
- ArcSlice, Data, Element, Export, FuncType, Global, Import, ImportKind, Instruction, MemoryType, TableType,
- TinyWasmModule, ValueCounts, ValueCountsSmall, WasmFunction, WasmFunctionData,
-};
+use tinywasm_types::*;
use wasmparser::{FuncValidatorAllocations, Payload, Validator};
pub(crate) type Code = (Vec<Instruction>, WasmFunctionData, ValueCounts);
@@ -30,23 +27,16 @@ pub(crate) struct ModuleReader {
}
impl ModuleReader {
- pub(crate) fn new() -> Self {
- Self::default()
- }
-
pub(crate) fn process_payload(&mut self, payload: Payload<'_>, validator: &mut Validator) -> Result<()> {
- use wasmparser::Payload::*;
-
match payload {
- Version { num, encoding, range } => {
+ Payload::Version { num, encoding, range } => {
validator.version(num, encoding, &range)?;
self.version = Some(num);
- match encoding {
- wasmparser::Encoding::Module => {}
- wasmparser::Encoding::Component => return Err(ParseError::InvalidEncoding(encoding)),
+ if let wasmparser::Encoding::Component = encoding {
+ return Err(ParseError::InvalidEncoding(encoding));
}
}
- StartSection { func, range } => {
+ Payload::StartSection { func, range } => {
if self.start_func.is_some() {
return Err(ParseError::DuplicateSection("Start section".into()));
}
@@ -55,7 +45,7 @@ impl ModuleReader {
validator.start_section(func, &range)?;
self.start_func = Some(func);
}
- TypeSection(reader) => {
+ Payload::TypeSection(reader) => {
if !self.func_types.is_empty() {
return Err(ParseError::DuplicateSection("Type section".into()));
}
@@ -68,7 +58,7 @@ impl ModuleReader {
.collect::<Result<Vec<FuncType>>>()?;
}
- GlobalSection(reader) => {
+ Payload::GlobalSection(reader) => {
if !self.globals.is_empty() {
return Err(ParseError::DuplicateSection("Global section".into()));
}
@@ -77,7 +67,7 @@ impl ModuleReader {
validator.global_section(&reader)?;
self.globals = conversion::convert_module_globals(reader)?;
}
- TableSection(reader) => {
+ Payload::TableSection(reader) => {
if !self.table_types.is_empty() {
return Err(ParseError::DuplicateSection("Table section".into()));
}
@@ -85,7 +75,7 @@ impl ModuleReader {
validator.table_section(&reader)?;
self.table_types = conversion::convert_module_tables(reader)?;
}
- MemorySection(reader) => {
+ Payload::MemorySection(reader) => {
if !self.memory_types.is_empty() {
return Err(ParseError::DuplicateSection("Memory section".into()));
}
@@ -94,12 +84,12 @@ impl ModuleReader {
validator.memory_section(&reader)?;
self.memory_types = conversion::convert_module_memories(reader)?;
}
- ElementSection(reader) => {
+ Payload::ElementSection(reader) => {
debug!("Found element section");
validator.element_section(&reader)?;
self.elements = conversion::convert_module_elements(reader)?;
}
- DataSection(reader) => {
+ Payload::DataSection(reader) => {
if !self.data.is_empty() {
return Err(ParseError::DuplicateSection("Data section".into()));
}
@@ -108,14 +98,14 @@ impl ModuleReader {
validator.data_section(&reader)?;
self.data = conversion::convert_module_data_sections(reader)?;
}
- DataCountSection { count, range } => {
+ Payload::DataCountSection { count, range } => {
debug!("Found data count section");
if !self.data.is_empty() {
return Err(ParseError::DuplicateSection("Data count section".into()));
}
validator.data_count_section(count, &range)?;
}
- FunctionSection(reader) => {
+ Payload::FunctionSection(reader) => {
if !self.code_type_addrs.is_empty() {
return Err(ParseError::DuplicateSection("Function section".into()));
}
@@ -124,7 +114,7 @@ impl ModuleReader {
validator.function_section(&reader)?;
self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::<Result<Vec<_>>>()?;
}
- CodeSectionStart { count, range, .. } => {
+ Payload::CodeSectionStart { count, range, .. } => {
debug!("Found code section ({count} functions)");
if !self.code.is_empty() {
return Err(ParseError::DuplicateSection("Code section".into()));
@@ -132,7 +122,7 @@ impl ModuleReader {
self.code.reserve(count as usize);
validator.code_section_start(&range)?;
}
- CodeSectionEntry(function) => {
+ Payload::CodeSectionEntry(function) => {
debug!("Found code section entry");
let v = validator.code_section_entry(&function)?;
let func_validator = v.into_validator(self.func_validator_allocations.take().unwrap_or_default());
@@ -140,7 +130,7 @@ impl ModuleReader {
self.code.push(code);
self.func_validator_allocations = Some(allocations);
}
- ImportSection(reader) => {
+ Payload::ImportSection(reader) => {
if !self.imports.is_empty() {
return Err(ParseError::DuplicateSection("Import section".into()));
}
@@ -149,7 +139,7 @@ impl ModuleReader {
validator.import_section(&reader)?;
self.imports = conversion::convert_module_imports(reader.into_imports())?;
}
- ExportSection(reader) => {
+ Payload::ExportSection(reader) => {
if !self.exports.is_empty() {
return Err(ParseError::DuplicateSection("Export section".into()));
}
@@ -159,7 +149,7 @@ impl ModuleReader {
self.exports =
reader.into_iter().map(|e| conversion::convert_module_export(e?)).collect::<Result<Vec<_>>>()?;
}
- End(offset) => {
+ Payload::End(offset) => {
debug!("Reached end of module");
if self.end_reached {
return Err(ParseError::DuplicateSection("End section".into()));
@@ -168,14 +158,13 @@ impl ModuleReader {
validator.end(offset)?;
self.end_reached = true;
}
- CustomSection(_reader) => {
+ Payload::CustomSection(_reader) => {
debug!("Found custom section");
debug!("Skipping custom section: {:?}", _reader.name());
}
- UnknownSection { .. } => return Err(ParseError::UnsupportedSection("Unknown section".into())),
+ Payload::UnknownSection { .. } => return Err(ParseError::UnsupportedSection("Unknown section".into())),
section => return Err(ParseError::UnsupportedSection(format!("Unsupported section: {section:?}"))),
};
-
Ok(())
}
@@ -188,38 +177,22 @@ impl ModuleReader {
return Err(ParseError::Other("Code and code type address count mismatch".to_string()));
}
- let imported_func_count =
- self.imports.iter().filter(|i| matches!(&i.kind, ImportKind::Function(_))).count() as u32;
-
- let funcs = self
- .code
- .into_iter()
- .zip(self.code_type_addrs)
- .enumerate()
- .map(|(func_idx, ((instructions, mut data, locals), ty_idx))| {
+ let imported_func_count = self.imports.iter().filter(|i| matches!(&i.kind, ImportKind::Function(_))).count();
+ let funcs = self.code.into_iter().zip(self.code_type_addrs).enumerate().map(
+ |(func_idx, ((instructions, mut data, locals), ty_idx))| {
let ty = self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone();
- let params = ValueCountsSmall::from(&ty.params);
- let locals = ValueCountsSmall {
- c32: u16::try_from(locals.c32).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- c64: u16::try_from(locals.c64).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- c128: u16::try_from(locals.c128).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- cref: u16::try_from(locals.cref).unwrap_or_else(|_| unreachable!("local count exceeds u16")),
- };
- let self_func_addr = imported_func_count + func_idx as u32;
- let instructions = optimize::optimize_instructions(instructions, &mut data, self_func_addr, options);
-
+ let params = ValueCounts::from(&ty.params);
+ let self_func = (imported_func_count + func_idx) as u32;
+ let instructions = optimize::optimize_instructions(instructions, &mut data, self_func, options);
WasmFunction { instructions: ArcSlice::from(instructions), data, locals, params, ty }
- })
- .collect::<Vec<_>>();
-
- let globals = self.globals;
- let table_types = self.table_types;
+ },
+ );
Ok(TinyWasmModule {
- funcs: funcs.into(),
+ funcs: funcs.collect(),
func_types: self.func_types.into(),
- globals: globals.into(),
- table_types: table_types.into(),
+ globals: self.globals.into(),
+ table_types: self.table_types.into(),
imports: self.imports.into(),
start_func: self.start_func,
data: self.data.into(),
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index 076f7c1..8313d9f 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -75,7 +75,7 @@ impl<R: WasmModuleResources> VisitSimdOperator<'_> for ValidateThenVisit<'_, R>
pub(crate) fn process_operators_and_validate<R: WasmModuleResources>(
validator: FuncValidator<R>,
body: FunctionBody<'_>,
- local_addr_map: Vec<u32>,
+ local_addr_map: Vec<u16>,
) -> Result<(Vec<Instruction>, WasmFunctionData, FuncValidatorAllocations)> {
let mut reader = body.get_operators_reader()?;
let remaining = reader.get_binary_reader().bytes_remaining();
@@ -148,7 +148,7 @@ pub(crate) struct FunctionBuilder<R: WasmModuleResources> {
instructions: Vec<Instruction>,
data: FunctionDataBuilder,
ctx_stack: Vec<LoweringCtx>,
- local_addr_map: Vec<u32>,
+ local_addr_map: Vec<u16>,
errors: Vec<crate::ParseError>,
}
@@ -244,13 +244,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_local_get(&mut self, idx: u32) -> Self::Output {
- let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else {
- self.errors.push(crate::ParseError::UnsupportedOperator(
- "Local index is too large, tinywasm does not support local indexes that large".to_string(),
- ));
- return;
- };
-
+ let resolved_idx = self.local_addr_map[idx as usize];
if let Some(t) = self.validator.get_local_type(idx) {
match t {
wasmparser::ValType::I32 | wasmparser::ValType::F32 => {
@@ -270,13 +264,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_local_set(&mut self, idx: u32) -> Self::Output {
- let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else {
- self.errors.push(crate::ParseError::UnsupportedOperator(
- "Local index is too large, tinywasm does not support local indexes that large".to_string(),
- ));
- return;
- };
-
+ let resolved_idx = self.local_addr_map[idx as usize];
if let Some(Some(t)) = self.validator.get_operand_type(0) {
self.instructions.push(match t {
wasmparser::ValType::I32 => Instruction::LocalSet32(resolved_idx),
@@ -290,13 +278,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_local_tee(&mut self, idx: u32) -> Self::Output {
- let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else {
- self.errors.push(crate::ParseError::UnsupportedOperator(
- "Local index is too large, tinywasm does not support local indexes that large".to_string(),
- ));
- return;
- };
-
+ let resolved_idx = self.local_addr_map[idx as usize];
if let Some(Some(t)) = self.validator.get_operand_type(0) {
self.instructions.push(match t {
wasmparser::ValType::I32 => Instruction::LocalTee32(resolved_idx),
@@ -472,7 +454,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
fn visit_typed_select_multi(&mut self, tys: Vec<wasmparser::ValType>) -> Self::Output {
let (c32, c64, c128, cref) = Self::label_keep_counts(&tys);
- self.instructions.push(Instruction::SelectMulti(tinywasm_types::ValueCountsSmall { c32, c64, c128, cref }));
+ self.instructions.push(Instruction::SelectMulti(tinywasm_types::ValueCounts { c32, c64, c128, cref }));
}
fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output {
@@ -588,7 +570,7 @@ impl<R: WasmModuleResources> FunctionBuilder<R> {
self.validator.simd_visitor(offset)
}
- pub(crate) fn new(instr_capacity: usize, validator: FuncValidator<R>, local_addr_map: Vec<u32>) -> Self {
+ pub(crate) fn new(instr_capacity: usize, validator: FuncValidator<R>, local_addr_map: Vec<u16>) -> Self {
Self {
validator,
local_addr_map,