From 0d886b1752c0b8e7a8b89261a1afe20ec949348c Mon Sep 17 00:00:00 2001 From: Ygg01 Date: Thu, 3 Sep 2026 02:50:19 +0200 Subject: [PATCH 1/3] Make value-trait mostly no_std --- Cargo.toml | 8 +++-- src/array.rs | 4 ++- src/base.rs | 10 +++--- src/derived.rs | 14 ++++----- src/impls.rs | 12 +++---- src/lib.rs | 74 ++++++++++++++++++++++++++++++++++++++++--- src/node.rs | 4 +-- src/node/cmp.rs | 1 + src/object.rs | 83 +++---------------------------------------------- 9 files changed, 105 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3c95f6a..754d1f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,14 +18,18 @@ halfbrown = { version = "0.4", optional = true } indexmap = { version = "2.0", optional = true } float-cmp = "0.10" ordered-float = { version = "5", optional = true } -hashbrown = { version = "0.16", optional = true } +hashbrown = { version = "0.16" } abi_stable = { version = "0.11.0", optional = true, default-features = false } +embedded-io = "0.7.1" +core_detect = "1.0.0" [features] -default = ["custom-types", "halfbrown", "runtime-detection"] +default = ["custom-types", "runtime-detection"] # Support for 128 bit integers 128bit = [] +std = [] + # Support for custom types custom-types = [] diff --git a/src/array.rs b/src/array.rs index df538d4..48fd301 100644 --- a/src/array.rs +++ b/src/array.rs @@ -1,4 +1,6 @@ -use std::slice::SliceIndex; +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::slice::SliceIndex; /// Trait for indexing into an array pub trait Indexed { diff --git a/src/base.rs b/src/base.rs index 9f2ee2d..5ff8390 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,6 +1,8 @@ -use std::io::{self, Write}; +use crate::io::{Write}; -use crate::{TryTypeError, ValueType, array::Array, object::Object}; +use alloc::string::String; + +use crate::{array::Array, object::Object, IoResult, TryTypeError, ValueType}; /// Type information on a value pub trait TypedValue { @@ -244,7 +246,7 @@ pub trait Writable { /// # Errors /// /// Will return `Err` if an IO error is encountered - fn write<'writer, W>(&self, w: &mut W) -> io::Result<()> + fn write<'writer, W>(&self, w: &mut W) -> IoResult<()> where W: 'writer + Write; @@ -253,7 +255,7 @@ pub trait Writable { /// # Errors /// /// Will return `Err` if an IO error is encountered. - fn write_pp<'writer, W>(&self, w: &mut W) -> io::Result<()> + fn write_pp<'writer, W>(&self, w: &mut W) -> IoResult<()> where W: 'writer + Write; } diff --git a/src/derived.rs b/src/derived.rs index 4f715a6..11230a2 100644 --- a/src/derived.rs +++ b/src/derived.rs @@ -1,5 +1,5 @@ -use std::{borrow::Borrow, hash::Hash}; - +use core::borrow::Borrow; +use core::hash::Hash; use crate::{AccessError, TryTypeError, array::Array, object::Object}; /// `try_as_*` access to scalar value types @@ -99,7 +99,7 @@ pub trait TypedScalarValue { #[must_use] fn is_float(&self) -> bool; - /// returns true if the current value a integer number + /// returns true if the current value an integer number #[must_use] fn is_integer(&self) -> bool; @@ -559,7 +559,7 @@ pub trait MutableObject { &mut self, k: K, v: V, - ) -> std::result::Result, AccessError> + ) -> Result, AccessError> where Self::Key: From + Hash + Eq, V: Into; @@ -583,7 +583,7 @@ pub trait MutableObject { /// # Errors /// /// Will return `Err` if `self` is not an Object. - fn remove(&mut self, k: &Q) -> std::result::Result, AccessError> + fn remove(&mut self, k: &Q) -> Result, AccessError> where Self::Key: Borrow, Q: ?Sized + Hash + Eq + Ord; @@ -634,7 +634,7 @@ pub trait MutableArray { /// # Errors /// /// Will return `Err` if `self` is not an array. - fn push(&mut self, v: V) -> std::result::Result<(), AccessError> + fn push(&mut self, v: V) -> Result<(), AccessError> where V: Into; @@ -656,7 +656,7 @@ pub trait MutableArray { /// # Errors /// /// Will return `Err` if `self` is not an array. - fn pop(&mut self) -> std::result::Result, AccessError>; + fn pop(&mut self) -> Result, AccessError>; /// Tries to pop from a `Value` as an `Array`. /// if the `Value` is any other type `None` will diff --git a/src/impls.rs b/src/impls.rs index e9dd46e..5c5bf81 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,5 +1,5 @@ -use std::{borrow::Borrow, hash::Hash}; - +use core::borrow::Borrow; +use core::hash::Hash; use crate::{ AccessError, ExtendedValueType, TryTypeError, ValueType, array::{self, ArrayMut, Indexed, IndexedMut}, @@ -860,7 +860,7 @@ where type Object = T::Object; #[inline] - fn insert(&mut self, k: K, v: V) -> std::result::Result, AccessError> + fn insert(&mut self, k: K, v: V) -> Result, AccessError> where Self::Key: From + Hash + Eq, V: Into, @@ -871,7 +871,7 @@ where } #[inline] - fn remove(&mut self, k: &Q) -> std::result::Result, AccessError> + fn remove(&mut self, k: &Q) -> Result, AccessError> where Self::Key: Borrow, Q: ?Sized + Hash + Eq + Ord, @@ -898,7 +898,7 @@ where type Target = ::Element; #[inline] - fn push(&mut self, v: V) -> std::result::Result<(), AccessError> + fn push(&mut self, v: V) -> Result<(), AccessError> where V: Into, { @@ -915,7 +915,7 @@ where /// /// Will return `Err` if `self` is not an array. #[inline] - fn pop(&mut self) -> std::result::Result, AccessError> { + fn pop(&mut self) -> Result, AccessError> { self.as_array_mut() .ok_or(AccessError::NotAnArray) .map(ArrayMut::pop) diff --git a/src/lib.rs b/src/lib.rs index 74443ca..c8daccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), no_std)] //! A crate providing generalised value traits for working with //! `JSONesque` values. #![warn(unused_extern_crates)] @@ -12,6 +13,16 @@ // We might want to revisit inline_always #![allow(clippy::module_name_repetitions)] +extern crate alloc; + +#[cfg(not(feature = "std"))] +pub(crate) use hashbrown::HashMap; +#[cfg(feature = "std")] +pub(crate) use std::collections::HashMap; + +use alloc::borrow::Cow; +use alloc::string::String; + #[cfg(all(feature = "128bit", feature = "c-abi"))] compile_error!( "Combining the features `128bit` and `c-abi` is impossible because i128's \ @@ -28,8 +39,7 @@ compile_error!( "Combining the features `c-abi` and `ordered-float` is impossible because ordered_float::OrderedFloat does not implement `StableAbi`" ); -use std::borrow::Cow; -use std::fmt; +use core::fmt; mod array; /// Traits for serializing JSON @@ -70,7 +80,61 @@ impl fmt::Display for AccessError { } } } -impl std::error::Error for AccessError {} + +#[cfg(feature = "std")] +use std::error; +#[cfg(not(feature = "std"))] +use core::error; +use core::fmt::{Display, Error, Formatter}; +use core::str::Utf8Error; +#[cfg(feature = "std")] +use std::io; +#[cfg(not(feature = "std"))] +use embedded_io as io; + +#[cfg(feature = "std")] +use std::is_x86_feature_detected; +#[cfg(not(feature = "std"))] +use core_detect::is_x86_feature_detected; +use embedded_io::ErrorKind; + +pub type IoResult = Result; + +#[derive(Debug, )] +pub enum IoError { + Formatting, + InvalidUtf8, +} + +impl error::Error for IoError {} +impl io::Error for IoError { + fn kind(&self) -> ErrorKind { + match self { + IoError::Formatting => ErrorKind::Other, + IoError::InvalidUtf8 => ErrorKind::InvalidData, + } + } +} + +impl From for IoError { + fn from(_value: Error) -> Self { + IoError::Formatting + } +} + +impl From for IoError { + fn from(_value: Utf8Error) -> Self { + IoError::InvalidUtf8 + } +} + +impl Display for IoError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("An I/O error occurred") + } +} + +impl error::Error for AccessError {} /// Extended types that have no native representation in JSON #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] @@ -213,13 +277,13 @@ pub struct TryTypeError { pub got: ValueType, } -impl std::fmt::Display for TryTypeError { +impl fmt::Display for TryTypeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Expected type {}, got {}", self.expected, self.got) } } -impl std::error::Error for TryTypeError {} +impl error::Error for TryTypeError {} // /// The `Value` exposes common interface for values, this allows using both/// `BorrowedValue` and `OwnedValue` nearly interchangeable // pub trait Value: diff --git a/src/node.rs b/src/node.rs index a11ccac..93e5b0f 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,8 +1,8 @@ use crate::base::{TypedValue, ValueAsScalar}; use super::{ValueType, fmt}; -use std::convert::TryFrom; -use std::ops::{Index, IndexMut}; +use core::convert::TryFrom; +use core::ops::{Index, IndexMut}; #[cfg(feature = "ordered-float")] use ordered_float::OrderedFloat; diff --git a/src/node/cmp.rs b/src/node/cmp.rs index 469716c..2c1da9a 100644 --- a/src/node/cmp.rs +++ b/src/node/cmp.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use crate::{base::ValueAsScalar, derived::TypedScalarValue}; use super::StaticNode; diff --git a/src/object.rs b/src/object.rs index e41d4cf..641d455 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,12 +1,12 @@ +use alloc::boxed::Box; +use core::borrow::Borrow; +use core::hash::{BuildHasher, Hash}; #[cfg(feature = "halfbrown")] use halfbrown::HashMap as Halfbrown; -#[cfg(feature = "hashbrown")] -use hashbrown::HashMap as Hashbrown; #[cfg(feature = "indexmap")] use indexmap::IndexMap; -use std::collections::HashMap; -use std::hash::Hash; -use std::{borrow::Borrow, hash::BuildHasher}; + +use crate::HashMap; /// A trait for the minimal common functionality of a vale object pub trait Object { @@ -301,79 +301,6 @@ where } } -#[cfg(feature = "hashbrown")] -impl Object for Hashbrown -where - MapK: Hash + Eq, -{ - type Key = MapK; - type Element = MapE; - - #[inline] - fn get(&self, k: &Q) -> Option<&Self::Element> - where - Self::Key: Borrow, - Q: ?Sized + Hash + Eq + Ord, - { - Hashbrown::get(self, k) - } - - #[inline] - fn iter<'i>(&'i self) -> Box + 'i> { - Box::new(Hashbrown::iter(self)) - } - - #[inline] - fn keys<'i>(&'i self) -> Box + 'i> { - Box::new(Hashbrown::keys(self)) - } - - #[inline] - fn values<'i>(&'i self) -> Box + 'i> { - Box::new(Hashbrown::values(self)) - } - - #[inline] - fn len(&self) -> usize { - Hashbrown::len(self) - } -} -#[cfg(feature = "hashbrown")] -impl ObjectMut for Hashbrown -where - MapK: Hash + Eq, -{ - type Key = MapK; - type Element = MapE; - - #[inline] - fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> - where - Self::Key: Borrow, - Q: ?Sized + Hash + Eq + Ord, - { - Hashbrown::get_mut(self, k) - } - - #[inline] - fn insert(&mut self, k: K, v: V) -> Option - where - K: Into, - V: Into, - Self::Key: Hash + Eq, - { - Hashbrown::insert(self, k.into(), v.into()) - } - - #[inline] - fn remove(&mut self, k: &Q) -> Option - where - Self::Key: Borrow, - Q: ?Sized + Hash + Eq + Ord, - { - Hashbrown::remove(self, k) - } -} #[cfg(feature = "c-abi")] impl Object From c902499df333def691931a5813b16ce3ee430b7c Mon Sep 17 00:00:00 2001 From: Ygg01 Date: Thu, 3 Sep 2026 09:19:45 +0200 Subject: [PATCH 2/3] Major changes to the generator trait - Makes it stop relying on `io::Read` (breaking change) by introducing a convenience trait. - Replace `stry!` macro with `?` operator --- src/generator.rs | 160 ++++++++++++++++++++++------------------------- src/lib.rs | 39 +++++++++++- 2 files changed, 113 insertions(+), 86 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 20895e3..fbbc040 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -4,18 +4,9 @@ // // https://github.com/maciejhirsz/json-rust/blob/master/src/codegen.rs -macro_rules! stry { - ($e:expr) => { - match $e { - ::std::result::Result::Ok(val) => val, - ::std::result::Result::Err(err) => return ::std::result::Result::Err(err), - } - }; -} - -use std::io; -use std::io::Write; -use std::ptr; +use alloc::string::String; +use alloc::vec::Vec; +use crate::{IoResult, BufferWriter}; const QU: u8 = b'"'; const BS: u8 = b'\\'; @@ -51,9 +42,9 @@ pub(crate) static ESCAPED: [u8; 256] = [ // taken from https://github.com/serde-rs/json/blob/4354fc3eb2232ee0ba9a9a23acce107a980a6dc0/src/ser.rs#L1790 // This is called rarely #[inline(never)] -fn u_encode(w: &mut W, byte: u8) -> io::Result<()> +fn u_encode(w: &mut W, byte: u8) -> IoResult<()> where - W: Write, + W: BufferWriter, { static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef"; let bytes = [ @@ -70,7 +61,7 @@ where /// Base generator trait pub trait BaseGenerator { /// The writer - type T: Write; + type T : BufferWriter; /// returns the writer fn get_writer(&mut self) -> &mut Self::T; @@ -79,7 +70,7 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write(&mut self, slice: &[u8]) -> io::Result<()> { + fn write(&mut self, slice: &[u8]) -> IoResult { self.get_writer().write_all(slice) } @@ -87,20 +78,20 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write_char(&mut self, ch: u8) -> io::Result<()> { + fn write_char(&mut self, ch: u8) -> IoResult { self.get_writer().write_all(&[ch]) } /// write with minimum /// # Errors /// if the write fails - fn write_min(&mut self, slice: &[u8], min: u8) -> io::Result<()>; + fn write_min(&mut self, slice: &[u8], min: u8) -> IoResult; /// writes new line /// # Errors /// if the write fails #[inline] - fn new_line(&mut self) -> io::Result<()> { + fn new_line(&mut self) -> IoResult<()> { Ok(()) } @@ -116,9 +107,9 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write_string(&mut self, string: &str) -> io::Result<()> { - stry!(self.write_char(b'"')); - stry!(self.write_string_content(string)); + fn write_string(&mut self, string: &str) -> IoResult<()> { + self.write_char(b'"')?; + self.write_string_content(string)?; self.write_char(b'"') } @@ -126,14 +117,14 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write_string_content(&mut self, string: &str) -> io::Result<()> { + fn write_string_content(&mut self, string: &str) -> IoResult { let mut string = string.as_bytes(); unsafe { // Looking at the table above the lower 5 bits are entirely // quote characters that gives us a bitmask of 0x1f for that // region, only quote (`"`) and backslash (`\`) are not in // this range. - stry!(self.write_str_simd(&mut string)); + self.write_str_simd(&mut string)?; } write_string_rust(self.get_writer(), &mut string) } @@ -144,7 +135,7 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write_simple_string(&mut self, string: &str) -> io::Result<()> { + fn write_simple_string(&mut self, string: &str) -> IoResult<()> { self.write(br#"""#)?; write_string_rust(self.get_writer(), &mut string.as_bytes())?; self.write(br#"""#) @@ -155,7 +146,7 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write_simple_str_content(&mut self, string: &str) -> io::Result<()> { + fn write_simple_str_content(&mut self, string: &str) -> IoResult<()> { let mut string = string.as_bytes(); // Legacy code to handle the remainder of the code write_string_rust(self.get_writer(), &mut string) @@ -165,7 +156,7 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write_float(&mut self, num: f64) -> io::Result<()> { + fn write_float(&mut self, num: f64) -> IoResult<()> { let mut buffer = ryu::Buffer::new(); let s = buffer.format_finite(num); self.get_writer().write_all(s.as_bytes()) @@ -175,7 +166,7 @@ pub trait BaseGenerator { /// # Errors /// if the write fails #[inline] - fn write_int(&mut self, num: I) -> io::Result<()> { + fn write_int(&mut self, num: I) -> IoResult<()> { let mut buffer = itoa::Buffer::new(); let s = buffer.format(num); self.get_writer().write_all(s.as_bytes()) @@ -189,7 +180,7 @@ pub trait BaseGenerator { feature = "runtime-detection", any(target_arch = "x86_64", target_arch = "x86"), ))] - unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { + unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> IoResult { unsafe { write_str_simd_fastest(self.get_writer(), string) } } #[cfg(all(target_feature = "avx2", not(feature = "runtime-detection")))] @@ -199,7 +190,7 @@ pub trait BaseGenerator { /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails - unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { + unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> IoResult<()> { write_str_simd_avx2(self.get_writer(), string) } @@ -214,7 +205,7 @@ pub trait BaseGenerator { /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails - unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { + unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> IoResult<()> { write_str_simd_sse2(self.get_writer(), string) } @@ -235,7 +226,7 @@ pub trait BaseGenerator { /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails - unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { + unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> IoResult<()> { write_string_rust(self.get_writer(), string) } @@ -246,7 +237,7 @@ pub trait BaseGenerator { /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails - unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { + unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> IoResult<()> { use std::arch::aarch64::{ uint8x16_t, vandq_u8, vceqq_u8, vdupq_n_u8, veorq_u8, vgetq_lane_u16, vld1q_u8, vorrq_u8, vpaddq_u8, vreinterpretq_u16_u8, @@ -326,7 +317,7 @@ pub trait BaseGenerator { /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails - unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { + unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> IoResult<()> { let writer = self.get_writer(); use std::arch::wasm32::{ u8x16_bitmask, u8x16_eq, u8x16_splat, v128, v128_and, v128_load, v128_or, v128_xor, @@ -375,15 +366,15 @@ pub trait BaseGenerator { } #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] -unsafe fn write_str_simd_fastest(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +unsafe fn write_str_simd_fastest(writer: &mut W, string: &mut &[u8]) -> IoResult<()> where - W: Write, + W: BufferWriter, { // This is not possible right now since we can't get `W` to be part of the static // use std::sync::atomic::{AtomicPtr, Ordering}; // type FnRaw = *mut (); - // type WriteStrFn = for<'a, 'b, 'c> unsafe fn(&'a mut T, &'b mut &'c [u8]) -> io::Result<()>; + // type WriteStrFn = for<'a, 'b, 'c> unsafe fn(&'a mut T, &'b mut &'c [u8]) -> IoResult<()>; // static FN: AtomicPtr<()> = AtomicPtr::new(get_fastest:: as FnRaw); // #[inline] // fn get_fastest_available_implementation() -> WriteStrFn @@ -400,7 +391,7 @@ where // } // #[inline] - // unsafe fn get_fastest<'invoke, 'de, W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()> + // unsafe fn get_fastest<'invoke, 'de, W>(writer: &mut W, string: &mut &[u8]) -> IoResult<()> // where // W: Write, // 'de: 'invoke, @@ -412,9 +403,9 @@ where // let fun = FN.load(Ordering::Relaxed); // mem::transmute::(fun)(writer, string) - if std::is_x86_feature_detected!("avx2") { + if core_detect::is_x86_feature_detected!("avx2") { unsafe { write_str_simd_avx2(writer, string) } - } else if std::is_x86_feature_detected!("sse2") { + } else if core_detect::is_x86_feature_detected!("sse2") { unsafe { write_str_simd_sse2(writer, string) } } else { #[cfg(not(feature = "portable"))] @@ -424,20 +415,20 @@ where } } #[inline] -fn write_string_container(writer: &mut W, string: &[u8], mut start: usize) -> io::Result<()> +fn write_string_container(writer: &mut W, string: &[u8], mut start: usize) -> IoResult where - W: Write, + W: BufferWriter, { - stry!(writer.write_all(&string[..start])); + writer.write_all(&string[..start])?; for (index, ch) in string.iter().enumerate().skip(start) { let escape = ESCAPED[*ch as usize]; if escape > 0 { - stry!(writer.write_all(&string[start..index])); + writer.write_all(&string[start..index])?; if escape == b'u' { - stry!(u_encode(writer, *ch)); + u_encode(writer, *ch)?; } else { - stry!(writer.write_all(&[b'\\', escape])); + writer.write_all(&[b'\\', escape])?; } start = index + 1; } @@ -446,9 +437,9 @@ where } #[inline] -fn write_string_rust(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +fn write_string_rust(writer: &mut W, string: &mut &[u8]) -> IoResult<()> where - W: Write, + W: BufferWriter, { // Legacy code to handle the remainder of the code for (index, ch) in string.iter().enumerate() { @@ -470,7 +461,7 @@ where /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails -unsafe fn write_str_simd_portable(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +unsafe fn write_str_simd_portable(writer: &mut W, string: &mut &[u8]) -> IoResult<()> where W: Write, { @@ -523,17 +514,17 @@ where /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails -unsafe fn write_str_simd_avx2(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +unsafe fn write_str_simd_avx2(writer: &mut W, string: &mut &[u8]) -> IoResult<()> where - W: Write, + W: BufferWriter, { #[cfg(target_arch = "x86")] - use std::arch::x86::{ + use core::arch::x86::{ __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8, _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256, }; #[cfg(target_arch = "x86_64")] - use std::arch::x86_64::{ + use core::arch::x86_64::{ __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8, _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256, }; @@ -567,19 +558,19 @@ where idx += 32; } else { let quote_dist = quote_bits.trailing_zeros() as usize; - stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist))); + writer.write_all(string.get_unchecked(0..idx + quote_dist))?; let ch = string[idx + quote_dist]; match ESCAPED[ch as usize] { - b'u' => stry!(u_encode(writer, ch)), - escape => stry!(writer.write_all(&[b'\\', escape])), + b'u' => u_encode(writer, ch)?, + escape => writer.write_all(&[b'\\', escape])?, } *string = string.get_unchecked(idx + quote_dist + 1..); idx = 0; } } - stry!(writer.write_all(&string[0..idx])); + writer.write_all(&string[0..idx])?; *string = string.get_unchecked(idx..); } @@ -594,17 +585,17 @@ where /// This function is unsafe because it uses simd instructions /// # Errors /// if the write fails -unsafe fn write_str_simd_sse2(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +unsafe fn write_str_simd_sse2(writer: &mut W, string: &mut &[u8]) -> IoResult<()> where - W: Write, + W: BufferWriter, { #[cfg(target_arch = "x86")] - use std::arch::x86::{ + use core::arch::x86::{ __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128, _mm_set1_epi8, _mm_xor_si128, }; #[cfg(target_arch = "x86_64")] - use std::arch::x86_64::{ + use core::arch::x86_64::{ __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128, _mm_set1_epi8, _mm_xor_si128, }; @@ -636,19 +627,19 @@ where idx += 16; } else { let quote_dist = quote_bits.trailing_zeros() as usize; - stry!(writer.write_all(&string[0..idx + quote_dist])); + writer.write_all(&string[0..idx + quote_dist])?; let ch = string[idx + quote_dist]; match ESCAPED[ch as usize] { - b'u' => stry!(u_encode(writer, ch)), - escape => stry!(writer.write_all(&[b'\\', escape])), + b'u' => u_encode(writer, ch)?, + escape => writer.write_all(&[b'\\', escape])?, } *string = &string[idx + quote_dist + 1..]; idx = 0; } } - stry!(writer.write_all(&string[0..idx])); + writer.write_all(&string[0..idx])?; *string = &string[idx..]; } @@ -688,12 +679,12 @@ impl BaseGenerator for DumpGenerator { type T = Vec; #[inline] - fn write(&mut self, slice: &[u8]) -> io::Result<()> { + fn write(&mut self, slice: &[u8]) -> IoResult<()> { extend_from_slice(&mut self.code, slice); Ok(()) } #[inline] - fn write_char(&mut self, ch: u8) -> io::Result<()> { + fn write_char(&mut self, ch: u8) -> IoResult<()> { self.code.push(ch); Ok(()) } @@ -704,7 +695,7 @@ impl BaseGenerator for DumpGenerator { } #[inline] - fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> { + fn write_min(&mut self, _: &[u8], min: u8) -> IoResult<()> { self.code.push(min); Ok(()) } @@ -738,13 +729,13 @@ impl PrettyGenerator { impl BaseGenerator for PrettyGenerator { type T = Vec; #[inline] - fn write(&mut self, slice: &[u8]) -> io::Result<()> { + fn write(&mut self, slice: &[u8]) -> IoResult<()> { extend_from_slice(&mut self.code, slice); Ok(()) } #[inline] - fn write_char(&mut self, ch: u8) -> io::Result<()> { + fn write_char(&mut self, ch: u8) -> IoResult<()> { self.code.push(ch); Ok(()) } @@ -755,12 +746,12 @@ impl BaseGenerator for PrettyGenerator { } #[inline] - fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> { + fn write_min(&mut self, slice: &[u8], _: u8) -> IoResult<()> { extend_from_slice(&mut self.code, slice); Ok(()) } - fn new_line(&mut self) -> io::Result<()> { + fn new_line(&mut self) -> IoResult<()> { self.code.push(b'\n'); self.code.resize( self.code.len() + (self.dent * self.spaces_per_indent) as usize, @@ -779,13 +770,13 @@ impl BaseGenerator for PrettyGenerator { } /// Writer Generator -pub struct WriterGenerator<'w, W: 'w + Write> { +pub struct WriterGenerator<'w, W: 'w + BufferWriter> { writer: &'w mut W, } impl<'w, W> WriterGenerator<'w, W> where - W: 'w + Write, + W: 'w + BufferWriter, { /// Creates a new generator pub fn new(writer: &'w mut W) -> Self { @@ -795,7 +786,7 @@ where impl BaseGenerator for WriterGenerator<'_, W> where - W: Write, + W: BufferWriter, { type T = W; @@ -805,7 +796,7 @@ where } #[inline] - fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> { + fn write_min(&mut self, _: &[u8], min: u8) -> IoResult { self.writer.write_all(&[min]) } } @@ -813,7 +804,7 @@ where /// Pretty Writer Generator pub struct PrettyWriterGenerator<'w, W> where - W: 'w + Write, + W: 'w + BufferWriter, { writer: &'w mut W, dent: u16, @@ -822,7 +813,7 @@ where impl<'w, W> PrettyWriterGenerator<'w, W> where - W: 'w + Write, + W: 'w + BufferWriter, { /// Creates a new generator pub fn new(writer: &'w mut W, spaces_per_indent: u16) -> Self { @@ -836,7 +827,7 @@ where impl BaseGenerator for PrettyWriterGenerator<'_, W> where - W: Write, + W: BufferWriter, { type T = W; @@ -846,14 +837,14 @@ where } #[inline] - fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> { + fn write_min(&mut self, slice: &[u8], _: u8) -> IoResult<()> { self.writer.write_all(slice) } - fn new_line(&mut self) -> io::Result<()> { - stry!(self.write_char(b'\n')); + fn new_line(&mut self) -> IoResult<()> { + self.write_char(b'\n')?; for _ in 0..(self.dent * self.spaces_per_indent) { - stry!(self.write_char(b' ')); + self.write_char(b' ')?; } Ok(()) } @@ -880,13 +871,14 @@ pub(crate) fn extend_from_slice(dst: &mut Vec, src: &[u8]) { unsafe { // We would have failed if `reserve` overflowed\ - ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len); + core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len); dst.set_len(dst_len + src_len); } } #[cfg(test)] mod tests { + use alloc::vec::Vec; #[test] fn test_write_string_rust() { diff --git a/src/lib.rs b/src/lib.rs index c8daccd..4a6ca72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ pub(crate) use std::collections::HashMap; use alloc::borrow::Cow; use alloc::string::String; +use alloc::vec::Vec; #[cfg(all(feature = "128bit", feature = "c-abi"))] compile_error!( @@ -85,7 +86,7 @@ impl fmt::Display for AccessError { use std::error; #[cfg(not(feature = "std"))] use core::error; -use core::fmt::{Display, Error, Formatter}; +use core::fmt::{Display, Error, Formatter, Write}; use core::str::Utf8Error; #[cfg(feature = "std")] use std::io; @@ -98,11 +99,18 @@ use std::is_x86_feature_detected; use core_detect::is_x86_feature_detected; use embedded_io::ErrorKind; -pub type IoResult = Result; +#[doc(hidden)] +pub type IoResult = Result; +/// Represents I/O-related errors that can occur during operations. +/// +/// This enum categorizes different types of I/O errors that may arise, such as +/// formatting issues or invalid UTF-8 input. #[derive(Debug, )] pub enum IoError { + /// Formatting error. Formatting, + /// Passed string was non-valid UTF-8. InvalidUtf8, } @@ -134,6 +142,33 @@ impl Display for IoError { } } +/// A trait for objects which are byte-oriented sinks. +/// +/// Implementors of the `Write` trait are sometimes called 'writers'. +/// +/// Writers are defined by a required method, [`write_all`]: +/// +pub trait BufferWriter { + + /// Attempts to write an entire buffer into this writer. + /// + /// This method will continuously call write until + /// there is no more data to be written or an error of + /// non-[`ErrorKind::Interrupted`] kind is returned. + /// This method will not return until the entire buffer has been successfully + /// written or such an error occurs. The first error that is not of [`ErrorKind::Interrupted`] + /// kind generated from this method will be returned. + fn write_all(&mut self, ch: &[u8]) -> IoResult; +} + +impl BufferWriter for Vec { + fn write_all(&mut self, ch: &[u8]) -> IoResult{ + self.extend_from_slice(ch); + Ok(()) + } +} + + impl error::Error for AccessError {} /// Extended types that have no native representation in JSON From 8cbead3c870279c65764039514c579f9da613536 Mon Sep 17 00:00:00 2001 From: Ygg01 Date: Thu, 3 Sep 2026 10:05:07 +0200 Subject: [PATCH 3/3] Fix clippy lints --- src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a6ca72..c327294 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,6 @@ clippy::all, clippy::unwrap_used, clippy::unnecessary_unwrap, - clippy::pedantic, missing_docs )] // We might want to revisit inline_always @@ -86,7 +85,7 @@ impl fmt::Display for AccessError { use std::error; #[cfg(not(feature = "std"))] use core::error; -use core::fmt::{Display, Error, Formatter, Write}; +use core::fmt::{Display, Error, Formatter}; use core::str::Utf8Error; #[cfg(feature = "std")] use std::io; @@ -95,8 +94,7 @@ use embedded_io as io; #[cfg(feature = "std")] use std::is_x86_feature_detected; -#[cfg(not(feature = "std"))] -use core_detect::is_x86_feature_detected; + use embedded_io::ErrorKind; #[doc(hidden)] @@ -158,6 +156,9 @@ pub trait BufferWriter { /// This method will not return until the entire buffer has been successfully /// written or such an error occurs. The first error that is not of [`ErrorKind::Interrupted`] /// kind generated from this method will be returned. + /// + /// # Errors + /// Will emit errors that occur during writing. For more information, see [`IoError`]. fn write_all(&mut self, ch: &[u8]) -> IoResult; }