From 04e854bcc5b88d777e75481fd788caa1929382cc Mon Sep 17 00:00:00 2001 From: Graham Kelly Date: Fri, 29 Aug 2025 15:42:18 +0000 Subject: [PATCH 1/3] Support `no_std` --- Cargo.toml | 3 +++ src/deserializer/header.rs | 3 ++- src/deserializer/iter.rs | 7 ++++++- src/deserializer/string.rs | 4 +++- src/deserializer/typedstream.rs | 2 ++ src/error.rs | 14 +++++++------- src/lib.rs | 8 +++++++- src/models/archived.rs | 2 ++ src/models/types.rs | 2 +- 9 files changed, 33 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8522dbc..085ea7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ keywords = ["typedstream", "macos", "ios", "apple"] categories = ["parsing", "parser-implementations", "database"] [dependencies] + +[features] +std=[] \ No newline at end of file diff --git a/src/deserializer/header.rs b/src/deserializer/header.rs index fa78079..75109f3 100644 --- a/src/deserializer/header.rs +++ b/src/deserializer/header.rs @@ -57,7 +57,8 @@ pub fn validate_header(data: &[u8]) -> Result> { #[cfg(test)] mod header_tests { - use std::{env::current_dir, fs::File, io::Read}; + use std::{env::current_dir, fs::File, io::Read,println}; + use alloc::{vec}; use crate::deserializer::{constants::I_16, header::validate_header}; diff --git a/src/deserializer/iter.rs b/src/deserializer/iter.rs index f51c4c0..f40c97d 100644 --- a/src/deserializer/iter.rs +++ b/src/deserializer/iter.rs @@ -1,6 +1,8 @@ //! Iterators for resolving properties in an [`Archived::Object`] -use std::slice::Iter; +use core::slice::Iter; + +use alloc::vec::Vec; use crate::models::{archived::Archived, class::Class, output_data::OutputData, types::Type}; @@ -250,6 +252,7 @@ impl<'a, 'b: 'a> Iterator for PropertyIterator<'a, 'b> { /// Group: /// Primitive: SignedInteger(0) /// ``` +#[cfg(feature = "std")] pub fn print_resolved(iter: PropertyIterator<'_, '_>, indent: usize) { print_resolved_with_limits(iter, indent, 100, 1000000); } @@ -262,12 +265,14 @@ pub fn print_resolved(iter: PropertyIterator<'_, '_>, indent: usize) { /// * `indent` - Number of spaces to indent each level /// * `max_depth` - Maximum depth to traverse (prevents infinite recursion on cycles) /// * `max_items` - Maximum total items to print (prevents runaway output) +#[cfg(feature = "std")] fn print_resolved_with_limits( iter: PropertyIterator<'_, '_>, indent: usize, max_depth: usize, max_items: usize, ) { + use std::{println}; // Use an explicit stack to avoid recursion and potential stack overflow let mut stack: Vec<(Property<'_, '_>, usize)> = Vec::new(); let mut items_printed = 0; diff --git a/src/deserializer/string.rs b/src/deserializer/string.rs index b3287cd..58a246b 100644 --- a/src/deserializer/string.rs +++ b/src/deserializer/string.rs @@ -28,7 +28,7 @@ use crate::{ pub fn read_string(data: &[u8]) -> Result> { let length = read_unsigned_int(data)?; Ok(Consumed::new( - std::str::from_utf8( + core::str::from_utf8( data.get(length.bytes_consumed..(length.bytes_consumed + length.value as usize)) .ok_or({ crate::error::TypedStreamError::OutOfBounds(length.value as usize, data.len()) @@ -40,6 +40,8 @@ pub fn read_string(data: &[u8]) -> Result> { #[cfg(test)] mod string_tests { + use alloc::vec::Vec; + use crate::deserializer::{ constants::{I_16, I_32}, string::read_string, diff --git a/src/deserializer/typedstream.rs b/src/deserializer/typedstream.rs index d29d974..8caf5be 100644 --- a/src/deserializer/typedstream.rs +++ b/src/deserializer/typedstream.rs @@ -4,6 +4,8 @@ A writeup about the reverse engineering of `typedstream` can be found [here](https://chrissardegna.com/blog/reverse-engineering-apples-typedstream-format/). */ +use alloc::{vec::Vec,vec}; + use crate::{ deserializer::{ constants::{EMPTY, END, START}, diff --git a/src/error.rs b/src/error.rs index b84e930..92be42e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ //! Error types and result alias for `typedstream` deserialization -use std::{array::TryFromSliceError, fmt::Display}; +use core::{array::TryFromSliceError, fmt::Display}; /// A specialized [`Result`] type for `typedstream` operations. /// @@ -15,7 +15,7 @@ use std::{array::TryFromSliceError, fmt::Display}; /// deserializer.oxidize() /// } /// ``` -pub type Result = std::result::Result; +pub type Result = core::result::Result; /// Errors that can occur while deserializing a `typedstream`. #[derive(Debug)] @@ -27,7 +27,7 @@ pub enum TypedStreamError { /// Error converting a slice into an array of fixed size. SliceError(TryFromSliceError), /// Error parsing a string as UTF-8. - StringParseError(std::str::Utf8Error), + StringParseError(core::str::Utf8Error), /// The `typedstream` header was invalid. InvalidHeader, /// Encountered an invalid pointer value. @@ -39,7 +39,7 @@ pub enum TypedStreamError { } impl Display for TypedStreamError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { TypedStreamError::InvalidObject => { write!(f, "Invalid object encountered in typedstream!") @@ -72,10 +72,10 @@ impl From for TypedStreamError { } } -impl From for TypedStreamError { - fn from(error: std::str::Utf8Error) -> Self { +impl From for TypedStreamError { + fn from(error: core::str::Utf8Error) -> Self { TypedStreamError::StringParseError(error) } } -impl std::error::Error for TypedStreamError {} +impl core::error::Error for TypedStreamError {} diff --git a/src/lib.rs b/src/lib.rs index b98bb51..d0d57fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,10 @@ #![forbid(unsafe_code)] #![deny(missing_docs)] #![doc = include_str!("../README.md")] +#![no_std] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; pub mod deserializer; pub mod error; @@ -11,7 +15,9 @@ pub use models::{archived::Archived, output_data::OutputData}; #[cfg(test)] mod test_typedstream_deserializer { - use std::{env::current_dir, fs::File, io::Read}; + extern crate std; + use alloc::vec; + use std::{env::current_dir, fs::File, io::Read, println}; use crate::{ deserializer::{iter::print_resolved, typedstream::TypedStreamDeserializer}, diff --git a/src/models/archived.rs b/src/models/archived.rs index 0510fbf..e2e9557 100644 --- a/src/models/archived.rs +++ b/src/models/archived.rs @@ -1,5 +1,7 @@ //! Types that can be archived into a `typedstream` +use alloc::vec::Vec; + use crate::models::{class::Class, output_data::OutputData}; /// Types of data that can be archived into the `typedstream` diff --git a/src/models/types.rs b/src/models/types.rs index 622ecaf..7f3e723 100644 --- a/src/models/types.rs +++ b/src/models/types.rs @@ -1,11 +1,11 @@ //! Type tags that denote the type of data stored in a `typedstream` - use crate::{ deserializer::{ constants::ARRAY, consumed::Consumed, number::read_unsigned_int, read::read_exact_bytes, }, error::{Result, TypedStreamError}, }; +use alloc::{vec, vec::Vec}; /// Represents primitive types of data that can be stored in a `typedstream` /// From af8dddd8f26e22b48ce8f6a997dcb7d9ff3ebee7 Mon Sep 17 00:00:00 2001 From: Graham Kelly Date: Fri, 29 Aug 2025 16:29:04 +0000 Subject: [PATCH 2/3] a --- src/deserializer/consumed.rs | 4 ++-- src/models/output_data.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/deserializer/consumed.rs b/src/deserializer/consumed.rs index 393183e..55b1b19 100644 --- a/src/deserializer/consumed.rs +++ b/src/deserializer/consumed.rs @@ -50,7 +50,7 @@ impl Consumed { } } -impl std::ops::Deref for Consumed { +impl core::ops::Deref for Consumed { type Target = T; fn deref(&self) -> &Self::Target { @@ -58,7 +58,7 @@ impl std::ops::Deref for Consumed { } } -impl std::ops::DerefMut for Consumed { +impl core::ops::DerefMut for Consumed { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } diff --git a/src/models/output_data.rs b/src/models/output_data.rs index 565dcb9..0551d78 100644 --- a/src/models/output_data.rs +++ b/src/models/output_data.rs @@ -178,8 +178,8 @@ impl<'a> OutputData<'a> { } // Implement Display for human-friendly formatting -impl std::fmt::Display for OutputData<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for OutputData<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { OutputData::String(s) => write!(f, "{s}"), OutputData::SignedInteger(i) => write!(f, "{i}"), From b0f0f4eb39e4b0621634c26dbdac9750b9733edb Mon Sep 17 00:00:00 2001 From: Graham Kelly Date: Fri, 29 Aug 2025 16:32:48 +0000 Subject: [PATCH 3/3] fix tests 1 --- src/deserializer/header.rs | 1 + src/deserializer/iter.rs | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/deserializer/header.rs b/src/deserializer/header.rs index 75109f3..d087621 100644 --- a/src/deserializer/header.rs +++ b/src/deserializer/header.rs @@ -57,6 +57,7 @@ pub fn validate_header(data: &[u8]) -> Result> { #[cfg(test)] mod header_tests { + extern crate std; use std::{env::current_dir, fs::File, io::Read,println}; use alloc::{vec}; diff --git a/src/deserializer/iter.rs b/src/deserializer/iter.rs index f40c97d..61d8aa7 100644 --- a/src/deserializer/iter.rs +++ b/src/deserializer/iter.rs @@ -252,7 +252,7 @@ impl<'a, 'b: 'a> Iterator for PropertyIterator<'a, 'b> { /// Group: /// Primitive: SignedInteger(0) /// ``` -#[cfg(feature = "std")] +#[cfg(any(feature = "std", test))] pub fn print_resolved(iter: PropertyIterator<'_, '_>, indent: usize) { print_resolved_with_limits(iter, indent, 100, 1000000); } @@ -265,14 +265,15 @@ pub fn print_resolved(iter: PropertyIterator<'_, '_>, indent: usize) { /// * `indent` - Number of spaces to indent each level /// * `max_depth` - Maximum depth to traverse (prevents infinite recursion on cycles) /// * `max_items` - Maximum total items to print (prevents runaway output) -#[cfg(feature = "std")] +#[cfg(any(feature = "std", test))] fn print_resolved_with_limits( iter: PropertyIterator<'_, '_>, indent: usize, max_depth: usize, max_items: usize, ) { - use std::{println}; + extern crate std; + use std::println; // Use an explicit stack to avoid recursion and potential stack overflow let mut stack: Vec<(Property<'_, '_>, usize)> = Vec::new(); let mut items_printed = 0;