diff --git a/Cargo.toml b/Cargo.toml index 574cab86..b56ca815 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ colored = { version = "2.0", optional = true } getopts = { version = "0.2", optional = true } jemallocator = { version = "0.5", optional = true } perfcnt = { version = "0.8", optional = true } +iex = { version = "0.2", optional = false } ref-cast = "1.0" diff --git a/src/impls/avx2/deser.rs b/src/impls/avx2/deser.rs index 1e4cc66b..e1ddc0c7 100644 --- a/src/impls/avx2/deser.rs +++ b/src/impls/avx2/deser.rs @@ -23,6 +23,7 @@ use crate::{ clippy::too_many_lines )] #[cfg_attr(not(feature = "no-inline"), inline)] +#[iex::iex] pub(crate) unsafe fn parse_str<'invoke, 'de>( input: SillyWrapper<'de>, data: &'invoke [u8], diff --git a/src/impls/native/deser.rs b/src/impls/native/deser.rs index b947a63b..61e1fcf8 100644 --- a/src/impls/native/deser.rs +++ b/src/impls/native/deser.rs @@ -5,6 +5,7 @@ use crate::{ }; #[allow(clippy::cast_possible_truncation)] +#[iex::iex] pub(crate) unsafe fn parse_str<'invoke, 'de>( input: SillyWrapper<'de>, data: &'invoke [u8], diff --git a/src/impls/neon/deser.rs b/src/impls/neon/deser.rs index 3782cf29..8bda50d8 100644 --- a/src/impls/neon/deser.rs +++ b/src/impls/neon/deser.rs @@ -42,6 +42,7 @@ fn find_bs_bits_and_quote_bits(v0: uint8x16_t, v1: uint8x16_t) -> (u32, u32) { #[allow(clippy::if_not_else, clippy::too_many_lines)] #[cfg_attr(not(feature = "no-inline"), inline)] +#[iex::iex] pub(crate) fn parse_str<'invoke, 'de>( input: SillyWrapper<'de>, data: &'invoke [u8], diff --git a/src/impls/portable/deser.rs b/src/impls/portable/deser.rs index 75b6b52a..be49901a 100644 --- a/src/impls/portable/deser.rs +++ b/src/impls/portable/deser.rs @@ -7,6 +7,7 @@ use crate::{ }; #[cfg_attr(not(feature = "no-inline"), inline)] +#[iex::iex] pub(crate) unsafe fn parse_str<'invoke, 'de>( input: SillyWrapper<'de>, data: &'invoke [u8], diff --git a/src/impls/simd128/deser.rs b/src/impls/simd128/deser.rs index af77e16b..5e9b11f0 100644 --- a/src/impls/simd128/deser.rs +++ b/src/impls/simd128/deser.rs @@ -14,6 +14,7 @@ use crate::{ clippy::too_many_lines )] #[cfg_attr(not(feature = "no-inline"), inline)] +#[iex::iex] pub(crate) fn parse_str<'invoke, 'de>( input: SillyWrapper<'de>, data: &'invoke [u8], diff --git a/src/impls/sse42/deser.rs b/src/impls/sse42/deser.rs index ebf21b57..bf4eab07 100644 --- a/src/impls/sse42/deser.rs +++ b/src/impls/sse42/deser.rs @@ -17,6 +17,7 @@ use arch::{ #[target_feature(enable = "sse4.2")] #[allow(clippy::if_not_else, clippy::cast_possible_wrap)] #[cfg_attr(not(feature = "no-inline"), inline)] +#[iex::iex] pub(crate) unsafe fn parse_str<'invoke, 'de>( input: SillyWrapper<'de>, data: &'invoke [u8], diff --git a/src/lib.rs b/src/lib.rs index 75e7faf9..fc4c6331 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,6 +139,7 @@ mod numberparse; mod safer_unchecked; mod stringparse; +use iex::Outcome; use safer_unchecked::GetSaferUnchecked; use stage2::StackState; @@ -547,6 +548,7 @@ impl<'de> Deserializer<'de> { feature = "runtime-detection", any(target_arch = "x86_64", target_arch = "x86"), ))] + #[iex::iex] pub(crate) unsafe fn parse_str_<'invoke>( input: *mut u8, data: &'invoke [u8], @@ -606,6 +608,7 @@ impl<'de> Deserializer<'de> { target_feature = "simd128", target_arch = "aarch64", )))] + #[iex::iex] pub(crate) unsafe fn parse_str_<'invoke>( input: *mut u8, data: &'invoke [u8], @@ -620,6 +623,7 @@ impl<'de> Deserializer<'de> { } #[cfg_attr(not(feature = "no-inline"), inline)] #[cfg(all(feature = "portable", not(feature = "runtime-detection")))] + #[iex::iex] pub(crate) unsafe fn parse_str_<'invoke>( input: *mut u8, data: &'invoke [u8], @@ -639,6 +643,7 @@ impl<'de> Deserializer<'de> { not(feature = "portable"), not(feature = "runtime-detection"), ))] + #[iex::iex] pub(crate) unsafe fn parse_str_<'invoke>( input: *mut u8, data: &'invoke [u8], @@ -656,6 +661,7 @@ impl<'de> Deserializer<'de> { not(feature = "runtime-detection"), not(feature = "portable"), ))] + #[iex::iex] pub(crate) unsafe fn parse_str_<'invoke>( input: *mut u8, data: &'invoke [u8], @@ -668,6 +674,7 @@ impl<'de> Deserializer<'de> { #[cfg_attr(not(feature = "no-inline"), inline)] #[cfg(all(target_arch = "aarch64", not(feature = "portable")))] + #[iex::iex] pub(crate) unsafe fn parse_str_<'invoke>( input: *mut u8, data: &'invoke [u8], @@ -679,6 +686,7 @@ impl<'de> Deserializer<'de> { } #[cfg_attr(not(feature = "no-inline"), inline)] #[cfg(all(target_feature = "simd128", not(feature = "portable")))] + #[iex::iex] pub(crate) unsafe fn parse_str_<'invoke>( input: *mut u8, data: &'invoke [u8], @@ -906,6 +914,7 @@ impl<'de> Deserializer<'de> { &mut buffer.stage2_stack, tape, ) + .into_result() } /// Creates a serializer from a mutable slice of bytes using a temporary diff --git a/src/numberparse/correct.rs b/src/numberparse/correct.rs index b7706256..57d41504 100644 --- a/src/numberparse/correct.rs +++ b/src/numberparse/correct.rs @@ -17,18 +17,17 @@ macro_rules! get { unsafe { *$buf.get_kinda_unchecked($idx as usize) } }; } -macro_rules! err { - ($idx:ident, $num:expr) => { - return Err(Error::new_c($idx, $num as char, ErrorType::InvalidNumber)) - }; -} macro_rules! check_overflow { ($overflowed:ident, $buf:ident, $idx:ident, $start_idx:ident, $end_index:ident) => { if $overflowed { #[cfg(not(feature = "big-int-as-float"))] { - err!($idx, get!($buf, $idx)) + return Err(Error::new_c( + $idx, + get!($buf, $idx) as char, + ErrorType::InvalidNumber, + )); } #[cfg(feature = "big-int-as-float")] { @@ -55,13 +54,18 @@ impl<'de> Deserializer<'de> { clippy::cast_possible_truncation, clippy::too_many_lines )] + #[iex::iex] pub(crate) fn parse_number(idx: usize, buf: &[u8], negative: bool) -> Result { let start_idx = idx; let mut idx = idx; if negative { idx += 1; if !is_integer(get!(buf, idx)) { - err!(idx, get!(buf, idx)) + return Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )); } } let mut start = idx; @@ -69,11 +73,19 @@ impl<'de> Deserializer<'de> { if get!(buf, idx) == b'0' { idx += 1; if is_not_structural_or_whitespace_or_exponent_or_decimal(get!(buf, idx)) { - err!(idx, get!(buf, idx)) + return Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )); } } else { if !is_integer(get!(buf, idx)) { - err!(idx, get!(buf, idx)) + return Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )); } num = u64::from(get!(buf, idx) - b'0'); idx += 1; @@ -96,7 +108,11 @@ impl<'de> Deserializer<'de> { .wrapping_add(u64::from(get!(buf, idx) - b'0')); idx += 1; } else { - err!(idx, get!(buf, idx)) + return Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )); } #[cfg(feature = "swar-number-parsing")] @@ -139,7 +155,11 @@ impl<'de> Deserializer<'de> { } } if !is_integer(get!(buf, idx)) { - err!(idx, get!(buf, idx)) + return Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )); } let mut exp_number = i64::from(get!(buf, idx) - b'0'); idx += 1; @@ -153,7 +173,11 @@ impl<'de> Deserializer<'de> { } while is_integer(get!(buf, idx)) { if exp_number > 0x0001_0000_0000 { - err!(idx, get!(buf, idx)) + return Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )); } exp_number = 10 * exp_number + i64::from(get!(buf, idx) - b'0'); idx += 1; @@ -170,33 +194,43 @@ impl<'de> Deserializer<'de> { } digit_count = digit_count.wrapping_sub(start.wrapping_sub(start_digits)); if digit_count >= 19 { - return f64_from_parts_slow( + let res = f64_from_parts_slow( unsafe { buf.get_kinda_unchecked(start_idx..idx) }, start_idx, - ); + )?; + return Ok(res); } } if is_structural_or_whitespace(get!(buf, idx)) == 0 { - err!(idx, get!(buf, idx)) + return Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )); } - f64_from_parts( + Ok(f64_from_parts( !negative, num, exponent as i32, unsafe { buf.get_kinda_unchecked(start_idx..idx) }, start_idx, - ) + )?) } else if unlikely!(digit_count >= 18) { - parse_large_integer(start_idx, buf, negative, idx) + Ok(parse_large_integer(start_idx, buf, negative, idx)?) } else if is_structural_or_whitespace(get!(buf, idx)) == 0 { - err!(idx, get!(buf, idx)) + Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )) } else { - Ok(if negative { + let res = if negative { StaticNode::I64(unsafe { static_cast_i64!(num.wrapping_neg()) }) // -(num as i64) } else { StaticNode::U64(num) - }) + }; + Ok(res) } } } @@ -204,6 +238,7 @@ impl<'de> Deserializer<'de> { #[cfg(not(feature = "128bit"))] #[cold] #[allow(clippy::cast_possible_wrap)] +#[iex::iex] fn parse_large_integer( start_idx: usize, buf: &[u8], @@ -237,7 +272,11 @@ fn parse_large_integer( } match (negative, num) { (true, 9_223_372_036_854_775_808) => Ok(StaticNode::I64(i64::MIN)), - (true, 9_223_372_036_854_775_809..=u64::MAX) => err!(idx, get!(buf, idx)), + (true, 9_223_372_036_854_775_809..=u64::MAX) => Err(Error::new_c( + idx, + get!(buf, idx) as char, + ErrorType::InvalidNumber, + )), (true, 0..=9_223_372_036_854_775_807) => Ok(StaticNode::I64(-(num as i64))), (false, _) => Ok(StaticNode::U64(num)), } @@ -246,6 +285,7 @@ fn parse_large_integer( #[cfg(feature = "128bit")] #[cold] #[allow(clippy::cast_possible_wrap)] +#[iex::iex] fn parse_large_integer( start_idx: usize, buf: &[u8], @@ -307,6 +347,7 @@ fn parse_large_integer( clippy::cast_precision_loss, clippy::cast_possible_wrap )] +#[iex::iex] fn f64_from_parts( positive: bool, significand: u64, @@ -321,10 +362,14 @@ fn f64_from_parts( } else { f *= get!(POW10, exponent); } - Ok(StaticNode::F64(if positive { f } else { -f })) - } else if significand == 0 { - Ok(StaticNode::F64(if positive { 0.0 } else { -0.0 })) - } else if (-325..=308).contains(&exponent) { + let res = StaticNode::F64(if positive { f } else { -f }); + return Ok(res); + } + if significand == 0 { + let res = StaticNode::F64(if positive { 0.0 } else { -0.0 }); + return Ok(res); + } + if (-325..=308).contains(&exponent) { let (factor_mantissa, factor_exponent) = get!(POW10_COMPONENTS, exponent + 325); let mut leading_zeroes = u64::from(significand.leading_zeros()); let f = significand << leading_zeroes; @@ -342,7 +387,8 @@ fn f64_from_parts( && product_high & 0x1FF == 0x1FF && product_low.wrapping_add(f) < product_low { - return f64_from_parts_slow(slice, offset); + let res = f64_from_parts_slow(slice, offset)?; + return Ok(res); } upper = product_high; lower = product_middle; @@ -352,7 +398,7 @@ fn f64_from_parts( leading_zeroes += 1 ^ upperbit; if lower == 0 && upper.trailing_zeros() >= 9 && mantissa & 3 == 1 { - return f64_from_parts_slow(slice, offset); + return Ok(f64_from_parts_slow(slice, offset)?); } mantissa += mantissa & 1; mantissa >>= 1; @@ -365,33 +411,51 @@ fn f64_from_parts( let real_exponent = (factor_exponent as u64).wrapping_sub(leading_zeroes); // we have to check that real_exponent is in range, otherwise we bail out if !(1..=2046).contains(&real_exponent) { - return f64_from_parts_slow(slice, offset); + return Ok(f64_from_parts_slow(slice, offset)?); } mantissa |= real_exponent.wrapping_shl(52); mantissa |= u64::from(!positive) << 63; let res = f64::from_bits(mantissa); if res.is_infinite() { - err!(offset, get!(slice, offset)) + Err(Error::new_c( + offset, + get!(slice, offset) as char, + ErrorType::InvalidNumber, + )) + } else { + let res = StaticNode::F64(res); + Ok(res) } - Ok(StaticNode::F64(res)) } else { - f64_from_parts_slow(slice, offset) + let res = f64_from_parts_slow(slice, offset)?; + Ok(res) } } #[cold] +#[iex::iex] fn f64_from_parts_slow(slice: &[u8], offset: usize) -> Result { // we already validated the content of the slice we only need to translate // the slice to a string and parse it as parse is not defined for a u8 slice match unsafe { std::str::from_utf8_unchecked(slice).parse::() } { Ok(val) => { if val.is_infinite() { - err!(offset, get!(slice, 0)) + return Err(Error::new_c( + offset, + get!(slice, 0) as char, + ErrorType::InvalidNumber, + )); } Ok(StaticNode::F64(val)) } - Err(_) => err!(offset, get!(slice, offset)), + Err(_) => { + return Err(Error::new_c( + offset, + get!(slice, offset) as char, + ErrorType::InvalidNumber, + )) + } } } diff --git a/src/stage2.rs b/src/stage2.rs index 40f48b55..1b44ac3a 100644 --- a/src/stage2.rs +++ b/src/stage2.rs @@ -5,6 +5,8 @@ use crate::value::tape::Node; use crate::{Deserializer, Error, ErrorType, InternalError, Result}; use value_trait::StaticNode; +use iex::Outcome; + #[cfg_attr(not(feature = "no-inline"), inline)] pub fn is_valid_true_atom(loc: &[u8]) -> bool { debug_assert!(loc.len() >= 8, "loc too short for a u64 read"); @@ -92,6 +94,7 @@ pub(crate) enum StackState { impl<'de> Deserializer<'de> { #[cfg_attr(not(feature = "no-inline"), inline)] #[allow(clippy::cognitive_complexity, clippy::too_many_lines, unused_unsafe)] + #[iex::iex(captures = "'de")] pub(crate) fn build_tape( input: &'de mut [u8], input2: &[u8], @@ -123,12 +126,6 @@ impl<'de> Deserializer<'de> { let mut i: usize = 0; let mut state; - macro_rules! insert_res { - ($t:expr) => { - res.push($t); - }; - } - macro_rules! update_char { () => { if i < structural_indexes.len() { @@ -148,19 +145,6 @@ impl<'de> Deserializer<'de> { }}; } - macro_rules! insert_str { - () => { - unsafe { - insert_res!(Node::String(Self::parse_str_( - input.as_mut_ptr(), - &input2, - buffer, - idx - )?)); - } - }; - } - // The continue cases are the most frequently called onces it's // worth pulling them out into a macro (aka inlining them) // Since we don't have a 'gogo' in rust. @@ -191,7 +175,11 @@ impl<'de> Deserializer<'de> { cnt += 1; update_char!(); if c == b'"' { - insert_str!(); + unsafe { + let n = Self::parse_str_(input.as_mut_ptr(), &input2, buffer, idx) + .into_result()?; + res.push(Node::String(n)); + } goto!(ObjectKey); } fail!(ErrorType::ExpectedObjectKey); @@ -222,7 +210,11 @@ impl<'de> Deserializer<'de> { update_char!(); match c { b'"' => { - insert_str!(); + unsafe { + let n = Self::parse_str_(input.as_mut_ptr(), &input2, buffer, idx) + .into_result()?; + res.push(Node::String(n)); + } goto!(ObjectKey) } b'}' => { @@ -258,7 +250,7 @@ impl<'de> Deserializer<'de> { } last_start = res.len(); - insert_res!(Node::Object { len: 0, count: 0 }); + res.push(Node::Object { len: 0, count: 0 }); depth += 1; cnt = 1; @@ -266,7 +258,10 @@ impl<'de> Deserializer<'de> { update_char!(); match c { b'"' => { - insert_str!(); + unsafe { + let n = Self::parse_str_(input.as_mut_ptr(), &input2, buffer, idx)?; + res.push(Node::String(n)); + } state = State::ObjectKey; } b'}' => { @@ -284,7 +279,7 @@ impl<'de> Deserializer<'de> { } last_start = res.len(); - insert_res!(Node::Array { len: 0, count: 0 }); + res.push(Node::Array { len: 0, count: 0 }); depth += 1; cnt = 1; @@ -303,7 +298,7 @@ impl<'de> Deserializer<'de> { fail!(ErrorType::ExpectedTrue); } }; - insert_res!(Node::Static(StaticNode::Bool(true))); + res.push(Node::Static(StaticNode::Bool(true))); if i == structural_indexes.len() { return Ok(()); } @@ -315,7 +310,7 @@ impl<'de> Deserializer<'de> { fail!(ErrorType::ExpectedFalse); } }; - insert_res!(Node::Static(StaticNode::Bool(false))); + res.push(Node::Static(StaticNode::Bool(false))); if i == structural_indexes.len() { return Ok(()); } @@ -327,21 +322,25 @@ impl<'de> Deserializer<'de> { fail!(ErrorType::ExpectedNull); } }; - insert_res!(Node::Static(StaticNode::Null)); + res.push(Node::Static(StaticNode::Null)); if i == structural_indexes.len() { return Ok(()); } fail!(ErrorType::TrailingData); } b'"' => { - insert_str!(); + unsafe { + let n = Self::parse_str_(input.as_mut_ptr(), &input2, buffer, idx)?; + res.push(Node::String(n)); + } if i == structural_indexes.len() { return Ok(()); } fail!(ErrorType::TrailingData); } b'-' => { - insert_res!(Node::Static(Self::parse_number(idx, input2, true)?)); + let n = Self::parse_number(idx, input2, true)?; + res.push(Node::Static(n)); if i == structural_indexes.len() { return Ok(()); @@ -349,7 +348,8 @@ impl<'de> Deserializer<'de> { fail!(ErrorType::TrailingData); } b'0'..=b'9' => { - insert_res!(Node::Static(Self::parse_number(idx, input2, false)?)); + let n = Self::parse_number(idx, input2, false)?; + res.push(Node::Static(n)); if i == structural_indexes.len() { return Ok(()); @@ -373,37 +373,42 @@ impl<'de> Deserializer<'de> { update_char!(); match c { b'"' => { - insert_str!(); + unsafe { + let n = Self::parse_str_(input.as_mut_ptr(), &input2, buffer, idx)?; + res.push(Node::String(n)); + } object_continue!(); } b't' => { - insert_res!(Node::Static(StaticNode::Bool(true))); + res.push(Node::Static(StaticNode::Bool(true))); if !is_valid_true_atom(get!(input2, idx..)) { fail!(ErrorType::ExpectedTrue); } object_continue!(); } b'f' => { - insert_res!(Node::Static(StaticNode::Bool(false))); + res.push(Node::Static(StaticNode::Bool(false))); if !is_valid_false_atom(get!(input2, idx..)) { fail!(ErrorType::ExpectedFalse); } object_continue!(); } b'n' => { - insert_res!(Node::Static(StaticNode::Null)); + res.push(Node::Static(StaticNode::Null)); if !is_valid_null_atom(get!(input2, idx..)) { fail!(ErrorType::ExpectedNull); } object_continue!(); } b'-' => { - insert_res!(Node::Static(Self::parse_number(idx, input2, true)?)); + let n = Self::parse_number(idx, input2, true)?; + res.push(Node::Static(n)); object_continue!(); } b'0'..=b'9' => { - insert_res!(Node::Static(Self::parse_number(idx, input2, false)?)); + let n = Self::parse_number(idx, input2, false)?; + res.push(Node::Static(n)); object_continue!(); } @@ -414,7 +419,7 @@ impl<'de> Deserializer<'de> { .write(StackState::Object { last_start, cnt }); } last_start = res.len(); - insert_res!(Node::Object { len: 0, count: 0 }); + res.push(Node::Object { len: 0, count: 0 }); depth += 1; cnt = 1; object_begin!(); @@ -426,7 +431,7 @@ impl<'de> Deserializer<'de> { .write(StackState::Object { last_start, cnt }); } last_start = res.len(); - insert_res!(Node::Array { len: 0, count: 0 }); + res.push(Node::Array { len: 0, count: 0 }); depth += 1; cnt = 1; array_begin!(); @@ -493,36 +498,41 @@ impl<'de> Deserializer<'de> { // on paths that can accept a close square brace (post-, and at start) match c { b'"' => { - insert_str!(); + unsafe { + let n = Self::parse_str_(input.as_mut_ptr(), &input2, buffer, idx)?; + res.push(Node::String(n)); + } array_continue!(); } b't' => { - insert_res!(Node::Static(StaticNode::Bool(true))); + res.push(Node::Static(StaticNode::Bool(true))); if !is_valid_true_atom(get!(input2, idx..)) { fail!(ErrorType::ExpectedTrue); } array_continue!(); } b'f' => { - insert_res!(Node::Static(StaticNode::Bool(false))); + res.push(Node::Static(StaticNode::Bool(false))); if !is_valid_false_atom(get!(input2, idx..)) { fail!(ErrorType::ExpectedFalse); } array_continue!(); } b'n' => { - insert_res!(Node::Static(StaticNode::Null)); + res.push(Node::Static(StaticNode::Null)); if !is_valid_null_atom(get!(input2, idx..)) { fail!(ErrorType::ExpectedNull); } array_continue!(); } b'-' => { - insert_res!(Node::Static(Self::parse_number(idx, input2, true)?)); + let n = Self::parse_number(idx, input2, true)?; + res.push(Node::Static(n)); array_continue!(); } b'0'..=b'9' => { - insert_res!(Node::Static(Self::parse_number(idx, input2, false)?)); + let n = Self::parse_number(idx, input2, false)?; + res.push(Node::Static(n)); array_continue!(); } b'{' => { @@ -532,7 +542,7 @@ impl<'de> Deserializer<'de> { .write(StackState::Array { last_start, cnt }); } last_start = res.len(); - insert_res!(Node::Object { len: 0, count: 0 }); + res.push(Node::Object { len: 0, count: 0 }); depth += 1; cnt = 1; object_begin!(); @@ -544,7 +554,7 @@ impl<'de> Deserializer<'de> { .write(StackState::Array { last_start, cnt }); } last_start = res.len(); - insert_res!(Node::Array { len: 0, count: 0 }); + res.push(Node::Array { len: 0, count: 0 }); depth += 1; cnt = 1; array_begin!(); @@ -561,6 +571,8 @@ impl<'de> Deserializer<'de> { #[cfg(test)] mod test { + use iex::Outcome; + use crate::SIMDJSON_PADDING; use super::*; @@ -638,7 +650,8 @@ mod test { let mut buffer = vec![0; 1024]; let s = unsafe { - Deserializer::parse_str_(input.as_mut_ptr(), &input2, buffer.as_mut_slice(), 0)? + Deserializer::parse_str_(input.as_mut_ptr(), &input2, buffer.as_mut_slice(), 0) + .into_result()? }; assert_eq!(r#"{"arg":"test"}"#, s); Ok(())