diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index c664b8b..ddef430 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -23,7 +23,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: "1.61.0" + toolchain: "1.64.0" override: true components: clippy, rustfmt - name: Validate cargo format @@ -66,7 +66,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: "1.61.0" + toolchain: "1.64.0" override: true components: llvm-tools-preview - name: Install cargo-llcm-cov diff --git a/Cargo.toml b/Cargo.toml index 2d4b0c3..68e89be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ description = "Traits to deal with JSONesque values" repository = "https://github.com/simd-lite/value-trait" readme = "README.md" documentation = "https://docs.rs/value-trait" -rust-version = "1.61" +rust-version = "1.64" [dependencies] @@ -20,7 +20,7 @@ hashbrown = { version = "0.14", optional = true } abi_stable = { version = "0.11.0", optional = true, default-features = false } [features] -default = ["custom-types", "halfbrown"] +default = ["custom-types", "halfbrown", "runtime-detection"] # Support for 128 bit integers 128bit = [] @@ -30,5 +30,8 @@ custom-types = [] # Support for abi-stable's `StableAbi` implementation c-abi = ["abi_stable"] -# Allow fallback to non simd CPUs -allow-non-simd = [] +# use runtime detection of the CPU features where possible instead of enforcing an instruction set +runtime-detection = [] + +# portable simd support (as of rust 1.73 nightly only) +portable = [] diff --git a/src/generator.rs b/src/generator.rs index 7b7bb00..000c193 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -15,11 +15,8 @@ macro_rules! stry { use std::io; use std::io::Write; -use std::marker::PhantomData; use std::ptr; -use crate::Value; - const QU: u8 = b'"'; const BS: u8 = b'\\'; const BB: u8 = b'b'; @@ -81,7 +78,7 @@ pub trait BaseGenerator { /// Write a slice /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write(&mut self, slice: &[u8]) -> io::Result<()> { self.get_writer().write_all(slice) } @@ -89,7 +86,7 @@ pub trait BaseGenerator { /// Write a char /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write_char(&mut self, ch: u8) -> io::Result<()> { self.get_writer().write_all(&[ch]) } @@ -102,45 +99,23 @@ pub trait BaseGenerator { /// writes new line /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn new_line(&mut self) -> io::Result<()> { Ok(()) } /// indents one step - #[inline(always)] + #[inline] fn indent(&mut self) {} /// dedents one step - #[inline(always)] + #[inline] fn dedent(&mut self) {} - /// Writes a string with escape sequences - /// # Errors - /// if the write fails - #[inline(never)] - fn write_string_complex(&mut self, string: &[u8], mut start: usize) -> io::Result<()> { - stry!(self.write(&string[..start])); - - for (index, ch) in string.iter().enumerate().skip(start) { - let escape = ESCAPED[*ch as usize]; - if escape > 0 { - stry!(self.write(&string[start..index])); - if escape == b'u' { - stry!(u_encode(self.get_writer(), *ch)); - } else { - stry!(self.write(&[b'\\', escape])); - }; - start = index + 1; - } - } - self.write(&string[start..]) - } - /// writes a string /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write_string(&mut self, string: &str) -> io::Result<()> { stry!(self.write_char(b'"')); stry!(self.write_string_content(string)); @@ -150,7 +125,7 @@ pub trait BaseGenerator { /// writes a string /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write_string_content(&mut self, string: &str) -> io::Result<()> { let mut string = string.as_bytes(); @@ -161,13 +136,8 @@ pub trait BaseGenerator { // this range. stry!(self.write_str_simd(&mut string)); } - // Legacy code to handle the remainder of the code - for (index, ch) in string.iter().enumerate() { - if ESCAPED[*ch as usize] > 0 { - return self.write_string_complex(string, index); - } - } - self.write(string) + + write_string_rust(self.get_writer(), &mut string) } /// writes a simple string (usually short and non escaped) @@ -175,35 +145,28 @@ pub trait BaseGenerator { /// expensive on short strings. /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write_simple_string(&mut self, string: &str) -> io::Result<()> { - stry!(self.write_char(b'"')); - - stry!(self.write_simple_str_content(string)); - self.write_char(b'"') + self.write(br#"""#)?; + write_string_rust(self.get_writer(), &mut string.as_bytes())?; + self.write(br#"""#) } - /// writes a simple string content (usually short and non escaped) /// This means we can skip the simd accelerated writing which is /// expensive on short strings. /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write_simple_str_content(&mut self, string: &str) -> io::Result<()> { - let string = string.as_bytes(); + let mut string = string.as_bytes(); // Legacy code to handle the remainder of the code - for (index, ch) in string.iter().enumerate() { - if ESCAPED[*ch as usize] > 0 { - return self.write_string_complex(string, index); - } - } - self.write(string) + write_string_rust(self.get_writer(), &mut string) } /// writes a float value /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write_float(&mut self, num: f64) -> io::Result<()> { let mut buffer = ryu::Buffer::new(); let s = buffer.format_finite(num); @@ -213,144 +176,59 @@ pub trait BaseGenerator { /// writes an integer value /// # Errors /// if the write fails - #[inline(always)] + #[inline] fn write_int(&mut self, num: I) -> io::Result<()> { let mut buffer = itoa::Buffer::new(); let s = buffer.format(num); self.get_writer().write_all(s.as_bytes()) } - #[cfg(target_feature = "avx2")] - #[inline(always)] - #[allow(clippy::cast_possible_wrap, clippy::cast_ptr_alignment)] - /// Writes a string with simd-acceleration /// # Safety - /// This function is unsafe because it uses simd instructions + /// This function is unsafe because it may use simd instructions /// # Errors /// if the write fails + #[cfg(all( + feature = "runtime-detection", + any(target_arch = "x86_64", target_arch = "x86"), + ))] unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { - #[cfg(target_arch = "x86")] - use std::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::{ - __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8, - _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256, - }; - - let writer = self.get_writer(); - let mut idx = 0; - let zero = _mm256_set1_epi8(0); - let lower_quote_range = _mm256_set1_epi8(0x1F_i8); - let quote = _mm256_set1_epi8(b'"' as i8); - let backslash = _mm256_set1_epi8(b'\\' as i8); - while string.len() - idx >= 32 { - // Load 32 bytes of data; - let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx).cast::<__m256i>()); - // Test the data against being backslash and quote. - let bs_or_quote = _mm256_or_si256( - _mm256_cmpeq_epi8(data, backslash), - _mm256_cmpeq_epi8(data, quote), - ); - // Now mask the data with the quote range (0x1F). - let in_quote_range = _mm256_and_si256(data, lower_quote_range); - // then test of the data is unchanged. aka: xor it with the - // Any field that was inside the quote range it will be zero - // now. - let is_unchanged = _mm256_xor_si256(data, in_quote_range); - let in_range = _mm256_cmpeq_epi8(is_unchanged, zero); - let quote_bits = _mm256_movemask_epi8(_mm256_or_si256(bs_or_quote, in_range)); - if quote_bits == 0 { - idx += 32; - } else { - let quote_dist = quote_bits.trailing_zeros() as usize; - stry!(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])), - }; - - *string = string.get_unchecked(idx + quote_dist + 1..); - idx = 0; - } - } - stry!(writer.write_all(&string[0..idx])); - *string = string.get_unchecked(idx..); - Ok(()) + write_str_simd_fastest(self.get_writer(), string) } - - #[cfg(all(not(target_feature = "avx2"), target_feature = "sse2"))] - #[inline(always)] - #[allow(clippy::cast_possible_wrap, clippy::cast_ptr_alignment)] + #[cfg(all(target_feature = "avx2", not(feature = "runtime-detection")))] + #[inline] /// Writes a string with simd-acceleration /// # Safety /// 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<()> { - #[cfg(target_arch = "x86")] - use std::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::{ - __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, - _mm_or_si128, _mm_set1_epi8, _mm_xor_si128, - }; - - let writer = self.get_writer(); - let mut idx = 0; - let zero = _mm_set1_epi8(0); - let lower_quote_range = _mm_set1_epi8(0x1F_i8); - let quote = _mm_set1_epi8(b'"' as i8); - let backslash = _mm_set1_epi8(b'\\' as i8); - while string.len() - idx > 16 { - // Load 16 bytes of data; - let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx).cast::<__m128i>()); - // Test the data against being backslash and quote. - let bs_or_quote = - _mm_or_si128(_mm_cmpeq_epi8(data, backslash), _mm_cmpeq_epi8(data, quote)); - // Now mask the data with the quote range (0x1F). - let in_quote_range = _mm_and_si128(data, lower_quote_range); - // then test of the data is unchanged. aka: xor it with the - // Any field that was inside the quote range it will be zero - // now. - let is_unchanged = _mm_xor_si128(data, in_quote_range); - let in_range = _mm_cmpeq_epi8(is_unchanged, zero); - let quote_bits = _mm_movemask_epi8(_mm_or_si128(bs_or_quote, in_range)); - if quote_bits == 0 { - idx += 16; - } else { - let quote_dist = quote_bits.trailing_zeros() as usize; - stry!(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])), - } - - *string = &string[idx + quote_dist + 1..]; - idx = 0; - } - } - stry!(writer.write_all(&string[0..idx])); - *string = &string[idx..]; - Ok(()) + write_str_simd_avx2(self.get_writer(), string) } #[cfg(all( - not(target_arch = "aarch64"), - not(all(target_arch = "wasm32", target_feature = "simd128")), - not(any(target_feature = "avx2", target_feature = "sse2")), - feature = "allow-non-simd" + target_feature = "sse2", + not(target_feature = "avx2"), + not(feature = "runtime-detection") ))] - #[inline(always)] + #[inline] + /// Writes a string with simd-acceleration + /// # Safety + /// 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<()> { + write_str_simd_sse42(self.get_writer(), string) + } + + #[cfg(not(any( + feature = "runtime-detection", + feature = "portable", + target_feature = "avx2", + target_feature = "sse4.2", + target_feature = "simd128", + target_arch = "aarch64", + )))] + #[inline] /// Writes a string with simd-acceleration (not really, as the architecture doesn't support it) /// # Safety /// This function is unsafe because it uses simd instructions @@ -360,17 +238,8 @@ pub trait BaseGenerator { self.write_simple_string(std::str::from_utf8_unchecked(string)) } - #[cfg(all( - not(target_arch = "aarch64"), - not(all(target_arch = "wasm32", target_feature = "simd128")), - not(any(target_feature = "avx2", target_feature = "sse2")), - not(feature = "allow-non-simd") - ))] - unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> { - compile_error!("write_str_simd not supported on the current architecture") - } #[cfg(target_arch = "aarch64")] - #[inline(always)] + #[inline] /// Writes a string with simd-acceleration /// # Safety /// This function is unsafe because it uses simd instructions @@ -383,7 +252,7 @@ pub trait BaseGenerator { }; use std::mem; - #[inline(always)] + #[inline] unsafe fn bit_mask() -> uint8x16_t { mem::transmute([ 0x01_u8, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, @@ -391,7 +260,7 @@ pub trait BaseGenerator { ]) } - #[inline(always)] + #[inline] unsafe fn neon_movemask(input: uint8x16_t) -> u16 { let simd_input: uint8x16_t = vandq_u8(input, bit_mask()); let tmp: uint8x16_t = vpaddq_u8(simd_input, simd_input); @@ -444,7 +313,7 @@ pub trait BaseGenerator { } #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))] - #[inline(always)] + #[inline] /// Writes a string with simd-acceleration /// # Safety /// This function is unsafe because it uses simd instructions @@ -498,22 +367,290 @@ pub trait BaseGenerator { } } +unsafe fn write_str_simd_fastest(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +where + W: Write, +{ + // 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<()>; + // static FN: AtomicPtr<()> = AtomicPtr::new(get_fastest:: as FnRaw); + // #[inline] + // fn get_fastest_available_implementation() -> WriteStrFn + // where + // W: Write, + // { + // if std::is_x86_feature_detected!("avx2") { + // write_str_simd_avx2 + // } else if std::is_x86_feature_detected!("sse4.2") { + // write_str_simd_sse42 + // } else { + // write_str_simd_rust + // } + // } + + // #[inline] + // unsafe fn get_fastest<'invoke, 'de, W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()> + // where + // W: Write, + // 'de: 'invoke, + // { + // let fun = get_fastest_available_implementation(); + // FN.store(fun as FnRaw, Ordering::Relaxed); + // (fun)(writer, string) + // } + // let fun = FN.load(Ordering::Relaxed); + // mem::transmute::(fun)(writer, string) + + if std::is_x86_feature_detected!("avx2") { + write_str_simd_avx2(writer, string) + } else if std::is_x86_feature_detected!("sse4.2") { + write_str_simd_sse42(writer, string) + } else { + #[cfg(not(feature = "portable"))] + let r = write_string_rust(writer, string); + #[cfg(feature = "portable")] + let r = write_str_simd_portable(writer, string); + + r + } +} +#[inline] +fn write_string_complex(writer: &mut W, string: &[u8], mut start: usize) -> io::Result<()> +where + W: Write, +{ + stry!(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])); + if escape == b'u' { + stry!(u_encode(writer, *ch)); + } else { + stry!(writer.write_all(&[b'\\', escape])); + }; + start = index + 1; + } + } + writer.write_all(&string[start..]) +} + +#[inline] +fn write_string_rust(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +where + W: Write, +{ + // Legacy code to handle the remainder of the code + for (index, ch) in string.iter().enumerate() { + if ESCAPED[*ch as usize] > 0 { + return write_string_complex(writer, string, index); + } + } + writer.write_all(string) +} + +#[cfg(feature = "portable")] +#[inline] +/// Writes a string with simd-acceleration +/// # Safety +/// 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<()> +where + W: Write, +{ + use std::simd::{u8x32, SimdPartialEq, ToBitMask}; + + let mut idx = 0; + let zero = u8x32::splat(0); + let lower_quote_range = u8x32::splat(0x1F_u8); + let quote = u8x32::splat(b'"'); + let backslash = u8x32::splat(b'\\'); + while string.len() - idx >= 32 { + // Load 32 bytes of data; + let data = u8x32::from_slice(&string[idx..]); + // Test the data against being backslash and quote. + let bs_or_quote = data.simd_eq(backslash) | data.simd_eq(quote); + // Now mask the data with the quote range (0x1F). + let in_quote_range = data & lower_quote_range; + // then test of the data is unchanged. aka: xor it with the + // Any field that was inside the quote range it will be zero + // now. + let is_unchanged = data ^ in_quote_range; + let in_range = is_unchanged.simd_eq(zero); + let quote_bits = (bs_or_quote | in_range).to_bitmask(); + if quote_bits == 0 { + idx += 32; + } else { + let quote_dist = quote_bits.trailing_zeros() as usize; + stry!(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])), + }; + + *string = string.get_unchecked(idx + quote_dist + 1..); + idx = 0; + } + } + stry!(writer.write_all(&string[0..idx])); + *string = string.get_unchecked(idx..); + Ok(()) +} + +#[target_feature(enable = "avx2")] +#[inline] +/// Writes a string with simd-acceleration +/// # Safety +/// 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<()> +where + W: Write, +{ + #[cfg(target_arch = "x86")] + use std::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::{ + __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8, + _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256, + }; + + let mut idx = 0; + let zero = _mm256_set1_epi8(0); + let lower_quote_range = _mm256_set1_epi8(0x1F_i8); + #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine + let quote = _mm256_set1_epi8(b'"' as i8); + #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine + let backslash = _mm256_set1_epi8(b'\\' as i8); + while string.len() - idx >= 32 { + // Load 32 bytes of data; _mm256_loadu_si256 does not require alignment + #[allow(clippy::cast_ptr_alignment)] + let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx).cast::<__m256i>()); + // Test the data against being backslash and quote. + let bs_or_quote = _mm256_or_si256( + _mm256_cmpeq_epi8(data, backslash), + _mm256_cmpeq_epi8(data, quote), + ); + // Now mask the data with the quote range (0x1F). + let in_quote_range = _mm256_and_si256(data, lower_quote_range); + // then test of the data is unchanged. aka: xor it with the + // Any field that was inside the quote range it will be zero + // now. + let is_unchanged = _mm256_xor_si256(data, in_quote_range); + let in_range = _mm256_cmpeq_epi8(is_unchanged, zero); + let quote_bits = _mm256_movemask_epi8(_mm256_or_si256(bs_or_quote, in_range)); + if quote_bits == 0 { + idx += 32; + } else { + let quote_dist = quote_bits.trailing_zeros() as usize; + stry!(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])), + }; + + *string = string.get_unchecked(idx + quote_dist + 1..); + idx = 0; + } + } + stry!(writer.write_all(&string[0..idx])); + *string = string.get_unchecked(idx..); + Ok(()) +} + +#[target_feature(enable = "sse4.2")] +#[inline] +/// Writes a string with simd-acceleration +/// # Safety +/// This function is unsafe because it uses simd instructions +/// # Errors +/// if the write fails +unsafe fn write_str_simd_sse42(writer: &mut W, string: &mut &[u8]) -> io::Result<()> +where + W: Write, +{ + #[cfg(target_arch = "x86")] + use std::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::{ + __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128, + _mm_set1_epi8, _mm_xor_si128, + }; + + let mut idx = 0; + let zero = _mm_set1_epi8(0); + let lower_quote_range = _mm_set1_epi8(0x1F_i8); + #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine + let quote = _mm_set1_epi8(b'"' as i8); + #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine + let backslash = _mm_set1_epi8(b'\\' as i8); + while string.len() - idx > 16 { + // Load 16 bytes of data; _mm_loadu_si128 does not require alignment + #[allow(clippy::cast_ptr_alignment)] + let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx).cast::<__m128i>()); + // Test the data against being backslash and quote. + let bs_or_quote = + _mm_or_si128(_mm_cmpeq_epi8(data, backslash), _mm_cmpeq_epi8(data, quote)); + // Now mask the data with the quote range (0x1F). + let in_quote_range = _mm_and_si128(data, lower_quote_range); + // then test of the data is unchanged. aka: xor it with the + // Any field that was inside the quote range it will be zero + // now. + let is_unchanged = _mm_xor_si128(data, in_quote_range); + let in_range = _mm_cmpeq_epi8(is_unchanged, zero); + let quote_bits = _mm_movemask_epi8(_mm_or_si128(bs_or_quote, in_range)); + if quote_bits == 0 { + idx += 16; + } else { + let quote_dist = quote_bits.trailing_zeros() as usize; + stry!(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])), + } + + *string = &string[idx + quote_dist + 1..]; + idx = 0; + } + } + stry!(writer.write_all(&string[0..idx])); + *string = &string[idx..]; + Ok(()) +} + /// Simple dump Generator -pub struct DumpGenerator { - _value: PhantomData, +pub struct DumpGenerator { code: Vec, } -impl Default for DumpGenerator { +impl Default for DumpGenerator { fn default() -> Self { Self { - _value: PhantomData, code: Vec::with_capacity(1024), } } } -impl DumpGenerator { +impl DumpGenerator { /// Creates a new generator #[must_use] pub fn new() -> Self { @@ -529,26 +666,26 @@ impl DumpGenerator { } } -impl BaseGenerator for DumpGenerator { +impl BaseGenerator for DumpGenerator { type T = Vec; - #[inline(always)] + #[inline] fn write(&mut self, slice: &[u8]) -> io::Result<()> { extend_from_slice(&mut self.code, slice); Ok(()) } - #[inline(always)] + #[inline] fn write_char(&mut self, ch: u8) -> io::Result<()> { self.code.push(ch); Ok(()) } - #[inline(always)] + #[inline] fn get_writer(&mut self) -> &mut Vec { &mut self.code } - #[inline(always)] + #[inline] fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> { self.code.push(min); Ok(()) @@ -556,14 +693,13 @@ impl BaseGenerator for DumpGenerator { } /// Pretty Generator -pub struct PrettyGenerator { +pub struct PrettyGenerator { code: Vec, dent: u16, spaces_per_indent: u16, - _value: PhantomData, } -impl PrettyGenerator { +impl PrettyGenerator { /// Creates a new pretty printing generator #[must_use] pub fn new(spaces: u16) -> Self { @@ -571,7 +707,6 @@ impl PrettyGenerator { code: Vec::with_capacity(1024), dent: 0, spaces_per_indent: spaces, - _value: PhantomData, } } @@ -582,26 +717,26 @@ impl PrettyGenerator { } } -impl BaseGenerator for PrettyGenerator { +impl BaseGenerator for PrettyGenerator { type T = Vec; - #[inline(always)] + #[inline] fn write(&mut self, slice: &[u8]) -> io::Result<()> { extend_from_slice(&mut self.code, slice); Ok(()) } - #[inline(always)] + #[inline] fn write_char(&mut self, ch: u8) -> io::Result<()> { self.code.push(ch); Ok(()) } - #[inline(always)] + #[inline] fn get_writer(&mut self) -> &mut Vec { &mut self.code } - #[inline(always)] + #[inline] fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> { extend_from_slice(&mut self.code, slice); Ok(()) @@ -626,38 +761,32 @@ impl BaseGenerator for PrettyGenerator { } /// Writer Generator -pub struct WriterGenerator<'w, W: 'w + Write, V: Value> { +pub struct WriterGenerator<'w, W: 'w + Write> { writer: &'w mut W, - _value: PhantomData, } -impl<'w, W, V> WriterGenerator<'w, W, V> +impl<'w, W> WriterGenerator<'w, W> where W: 'w + Write, - V: Value, { /// Creates a new generator pub fn new(writer: &'w mut W) -> Self { - WriterGenerator { - writer, - _value: PhantomData, - } + WriterGenerator { writer } } } -impl<'w, W, V> BaseGenerator for WriterGenerator<'w, W, V> +impl<'w, W> BaseGenerator for WriterGenerator<'w, W> where W: Write, - V: Value, { type T = W; - #[inline(always)] + #[inline] fn get_writer(&mut self) -> &mut W { self.writer } - #[inline(always)] + #[inline] fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> { self.writer.write_all(&[min]) } @@ -665,21 +794,18 @@ where /// Pretty Writer Generator -pub struct PrettyWriterGenerator<'w, W, V> +pub struct PrettyWriterGenerator<'w, W> where W: 'w + Write, - V: Value, { writer: &'w mut W, dent: u16, spaces_per_indent: u16, - _value: PhantomData, } -impl<'w, W, V> PrettyWriterGenerator<'w, W, V> +impl<'w, W> PrettyWriterGenerator<'w, W> where W: 'w + Write, - V: Value, { /// Creates a new generator pub fn new(writer: &'w mut W, spaces_per_indent: u16) -> Self { @@ -687,24 +813,22 @@ where writer, dent: 0, spaces_per_indent, - _value: PhantomData, } } } -impl<'w, W, V> BaseGenerator for PrettyWriterGenerator<'w, W, V> +impl<'w, W> BaseGenerator for PrettyWriterGenerator<'w, W> where W: Write, - V: Value, { type T = W; - #[inline(always)] + #[inline] fn get_writer(&mut self) -> &mut W { self.writer } - #[inline(always)] + #[inline] fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> { self.writer.write_all(slice) } @@ -730,8 +854,7 @@ where // // LLVM is not able to lower `Vec::extend_from_slice` into a memcpy, so this // helps eke out that last bit of performance. -#[inline(always)] -#[allow(clippy::uninit_vec)] +#[inline] pub(crate) fn extend_from_slice(dst: &mut Vec, src: &[u8]) { let dst_len = dst.len(); let src_len = src.len(); @@ -739,9 +862,8 @@ pub(crate) fn extend_from_slice(dst: &mut Vec, src: &[u8]) { dst.reserve(src_len); unsafe { - // We would have failed if `reserve` overflowed - dst.set_len(dst_len + src_len); - + // We would have failed if `reserve` overflowed\ ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len); + dst.set_len(dst_len + src_len); } } diff --git a/src/lib.rs b/src/lib.rs index 1f4523d..21da144 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,16 @@ //! A crate providing generalised value traits for working with //! `JSONesque` values. #![warn(unused_extern_crates)] +#![cfg_attr(feature = "portable", feature(portable_simd))] #![deny( clippy::all, clippy::unwrap_used, clippy::unnecessary_unwrap, - clippy::pedantic + clippy::pedantic, + missing_docs )] // We might want to revisit inline_always -#![allow(clippy::module_name_repetitions, clippy::inline_always)] -#![allow(clippy::type_repetition_in_bounds)] -#![deny(missing_docs)] +#![allow(clippy::module_name_repetitions)] #[cfg(all(feature = "128bit", feature = "c-abi"))] compile_error!( @@ -577,13 +577,13 @@ pub trait ValueAccess: Sized { } /// Tries to represent the value as a f32 - #[allow(clippy::cast_possible_truncation)] #[inline] #[must_use] fn as_f32(&self) -> Option { self.as_f64().and_then(|u| { if u <= f64::from(std::f32::MAX) && u >= f64::from(std::f32::MIN) { // Since we check above + #[allow(clippy::cast_possible_truncation)] Some(u as f32) } else { None @@ -669,10 +669,10 @@ pub trait ValueAccess: Sized { /// it was asked for. #[inline] #[must_use] - fn get(&self, k: &Q) -> Option<&Self::Target> + fn get(&self, k: &Q) -> Option<&Self::Target> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.as_object().and_then(|a| a.get(k)) } @@ -682,10 +682,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the value is not an object #[inline] - fn try_get(&self, k: &Q) -> Result, TryTypeError> + fn try_get(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { Ok(self .as_object() @@ -700,10 +700,10 @@ pub trait ValueAccess: Sized { /// flase if Value isn't an object #[inline] #[must_use] - fn contains_key(&self, k: &Q) -> bool + fn contains_key(&self, k: &Q) -> bool where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.as_object().and_then(|a| a.get(k)).is_some() } @@ -735,10 +735,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a bool #[inline] #[must_use] - fn get_bool(&self, k: &Q) -> Option + fn get_bool(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_bool) } @@ -748,10 +748,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_bool(&self, k: &Q) -> Result, TryTypeError> + fn try_get_bool(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_bool).transpose() } @@ -759,10 +759,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a i128 #[inline] #[must_use] - fn get_i128(&self, k: &Q) -> Option + fn get_i128(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_i128) } @@ -772,10 +772,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_i128(&self, k: &Q) -> Result, TryTypeError> + fn try_get_i128(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_i128).transpose() } @@ -783,10 +783,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a i64 #[inline] #[must_use] - fn get_i64(&self, k: &Q) -> Option + fn get_i64(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_i64) } @@ -796,10 +796,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_i64(&self, k: &Q) -> Result, TryTypeError> + fn try_get_i64(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_i64).transpose() } @@ -807,10 +807,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a i32 #[inline] #[must_use] - fn get_i32(&self, k: &Q) -> Option + fn get_i32(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_i32) } @@ -820,10 +820,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_i32(&self, k: &Q) -> Result, TryTypeError> + fn try_get_i32(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_i32).transpose() } @@ -831,10 +831,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a i16 #[inline] #[must_use] - fn get_i16(&self, k: &Q) -> Option + fn get_i16(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_i16) } @@ -843,10 +843,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_i16(&self, k: &Q) -> Result, TryTypeError> + fn try_get_i16(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_i16).transpose() } @@ -854,10 +854,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a i8 #[inline] #[must_use] - fn get_i8(&self, k: &Q) -> Option + fn get_i8(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_i8) } @@ -867,10 +867,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_i8(&self, k: &Q) -> Result, TryTypeError> + fn try_get_i8(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_i8).transpose() } @@ -878,10 +878,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a u128 #[inline] #[must_use] - fn get_u128(&self, k: &Q) -> Option + fn get_u128(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_u128) } @@ -891,10 +891,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_u128(&self, k: &Q) -> Result, TryTypeError> + fn try_get_u128(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_u128).transpose() } @@ -902,10 +902,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a u64 #[inline] #[must_use] - fn get_u64(&self, k: &Q) -> Option + fn get_u64(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_u64) } @@ -915,10 +915,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_u64(&self, k: &Q) -> Result, TryTypeError> + fn try_get_u64(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_u64).transpose() } @@ -926,10 +926,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a usize #[inline] #[must_use] - fn get_usize(&self, k: &Q) -> Option + fn get_usize(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_usize) } @@ -939,10 +939,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_usize(&self, k: &Q) -> Result, TryTypeError> + fn try_get_usize(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_usize).transpose() } @@ -950,10 +950,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a u32 #[inline] #[must_use] - fn get_u32(&self, k: &Q) -> Option + fn get_u32(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_u32) } @@ -963,10 +963,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_u32(&self, k: &Q) -> Result, TryTypeError> + fn try_get_u32(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_u32).transpose() } @@ -974,10 +974,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a u16 #[inline] #[must_use] - fn get_u16(&self, k: &Q) -> Option + fn get_u16(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_u16) } @@ -987,10 +987,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_u16(&self, k: &Q) -> Result, TryTypeError> + fn try_get_u16(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_u16).transpose() } @@ -998,10 +998,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a u8 #[inline] #[must_use] - fn get_u8(&self, k: &Q) -> Option + fn get_u8(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_u8) } @@ -1011,10 +1011,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_u8(&self, k: &Q) -> Result, TryTypeError> + fn try_get_u8(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_u8).transpose() } @@ -1022,10 +1022,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a f64 #[inline] #[must_use] - fn get_f64(&self, k: &Q) -> Option + fn get_f64(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_f64) } @@ -1035,10 +1035,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_f64(&self, k: &Q) -> Result, TryTypeError> + fn try_get_f64(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_f64).transpose() } @@ -1046,10 +1046,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a f32 #[inline] #[must_use] - fn get_f32(&self, k: &Q) -> Option + fn get_f32(&self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_f32) } @@ -1059,10 +1059,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_f32(&self, k: &Q) -> Result, TryTypeError> + fn try_get_f32(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k)?.map(ValueAccess::try_as_f32).transpose() } @@ -1070,10 +1070,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a str #[inline] #[must_use] - fn get_str(&self, k: &Q) -> Option<&str> + fn get_str(&self, k: &Q) -> Option<&str> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_str) } @@ -1083,10 +1083,10 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_str(&self, k: &Q) -> Result, TryTypeError> + fn try_get_str(&self, k: &Q) -> Result, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k) .and_then(|s| s.map(ValueAccess::try_as_str).transpose()) @@ -1095,13 +1095,10 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a array #[inline] #[must_use] - fn get_array( - &self, - k: &Q, - ) -> Option<&<::Target as ValueAccess>::Array> + fn get_array(&self, k: &Q) -> Option<&<::Target as ValueAccess>::Array> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_array) } @@ -1111,13 +1108,13 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_array( + fn try_get_array( &self, k: &Q, ) -> Result::Target as ValueAccess>::Array>, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k) .and_then(|s| s.map(ValueAccess::try_as_array).transpose()) @@ -1126,13 +1123,13 @@ pub trait ValueAccess: Sized { /// Tries to get an element of an object as a object #[inline] #[must_use] - fn get_object( + fn get_object( &self, k: &Q, ) -> Option<&<::Target as ValueAccess>::Object> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.get(k).and_then(ValueAccess::as_object) } @@ -1143,13 +1140,13 @@ pub trait ValueAccess: Sized { /// # Errors /// if the requested type doesn't match the actual type or the value is not an object #[inline] - fn try_get_object( + fn try_get_object( &self, k: &Q, ) -> Result::Target as ValueAccess>::Object>, TryTypeError> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.try_get(k) .and_then(|s| s.map(ValueAccess::try_as_object).transpose()) @@ -1384,10 +1381,10 @@ pub trait Mutable: IndexMut + Value + Sized { /// /// Will return `Err` if `self` is not an Object. #[inline] - fn remove(&mut self, k: &Q) -> std::result::Result, AccessError> + fn remove(&mut self, k: &Q) -> std::result::Result, AccessError> where ::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.as_object_mut() .ok_or(AccessError::NotAnObject) @@ -1398,10 +1395,10 @@ pub trait Mutable: IndexMut + Value + Sized { /// If the `Value` isn't an object this opoeration will /// return `None` and have no effect. #[inline] - fn try_remove(&mut self, k: &Q) -> Option + fn try_remove(&mut self, k: &Q) -> Option where ::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.remove(k).ok().flatten() } @@ -1457,10 +1454,10 @@ pub trait Mutable: IndexMut + Value + Sized { /// Same as `get` but returns a mutable ref instead // fn get_amut(&mut self, k: &str) -> Option<&mut Self>; - fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Target> + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Target> where ::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { self.as_object_mut().and_then(|m| m.get_mut(k)) } diff --git a/src/node.rs b/src/node.rs index b6565cc..026c0f6 100644 --- a/src/node.rs +++ b/src/node.rs @@ -168,7 +168,6 @@ impl ValueAccess for StaticNode { #[cfg(not(feature = "128bit"))] #[inline] #[must_use] - #[allow(clippy::cast_sign_loss)] fn as_u64(&self) -> Option { match self { Self::I64(i) => u64::try_from(*i).ok(), @@ -180,7 +179,6 @@ impl ValueAccess for StaticNode { #[cfg(feature = "128bit")] #[inline] #[must_use] - #[allow(clippy::cast_sign_loss)] fn as_u64(&self) -> Option { match self { Self::I64(i) => u64::try_from(*i).ok(), @@ -193,7 +191,6 @@ impl ValueAccess for StaticNode { #[cfg(feature = "128bit")] #[inline] #[must_use] - #[allow(clippy::cast_sign_loss)] fn as_u128(&self) -> Option { match self { Self::U128(i) => Some(*i), @@ -309,7 +306,7 @@ impl PartialEq for StaticNode { (Self::U64(v1), Self::U128(v2)) => v2.eq(&u128::from(*v1)), (Self::I64(v1), Self::U64(v2)) if *v1 >= 0 => (*v1 as u64).eq(v2), - (Self::I64(v1), Self::I128(v2)) => (*v2 as i128).eq(&i128::from(*v1)), + (Self::I64(v1), Self::I128(v2)) => (*v2).eq(&i128::from(*v1)), (Self::I64(v1), Self::U128(v2)) if *v1 >= 0 => v2.eq(&(*v1 as u128)), (Self::U128(v1), Self::I128(v2)) if *v2 >= 0 => (*v2 as u128).eq(v1), diff --git a/src/node/from.rs b/src/node/from.rs index 62a0f89..c04b56a 100644 --- a/src/node/from.rs +++ b/src/node/from.rs @@ -87,7 +87,6 @@ impl From for StaticNode { impl From for StaticNode { #[inline] #[must_use] - #[allow(clippy::cast_possible_wrap)] fn from(i: u64) -> Self { Self::U64(i) } diff --git a/src/object.rs b/src/object.rs index 1b4bdec..81f5ef3 100644 --- a/src/object.rs +++ b/src/object.rs @@ -17,17 +17,17 @@ pub trait Object { /// current Value isn't an Object or doesn't contain the key /// it was asked for. #[must_use] - fn get(&self, k: &Q) -> Option<&Self::Element> + fn get(&self, k: &Q) -> Option<&Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord; + Q: ?Sized + Hash + Eq + Ord; /// Gets the value of a key as a mutable reference. #[must_use] - fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord; + Q: ?Sized + Hash + Eq + Ord; /// Inserts a value #[must_use] @@ -39,10 +39,10 @@ pub trait Object { /// Removes a value from the object #[must_use] - fn remove(&mut self, k: &Q) -> Option + fn remove(&mut self, k: &Q) -> Option where Self::Key: Borrow, - Q: Hash + Eq + Ord; + Q: ?Sized + Hash + Eq + Ord; /// Iterates over the key value paris #[must_use] @@ -77,19 +77,19 @@ where type Element = MapE; #[inline] - fn get(&self, k: &Q) -> Option<&Self::Element> + fn get(&self, k: &Q) -> Option<&Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { Halfbrown::get(self, k) } #[inline] - fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { Halfbrown::get_mut(self, k) } @@ -105,10 +105,10 @@ where } #[inline] - fn remove(&mut self, k: &Q) -> Option + fn remove(&mut self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { Halfbrown::remove(self, k) } @@ -142,19 +142,19 @@ where type Element = MapE; #[inline] - fn get(&self, k: &Q) -> Option<&Self::Element> + fn get(&self, k: &Q) -> Option<&Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { HashMap::get(self, k) } #[inline] - fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { HashMap::get_mut(self, k) } @@ -170,10 +170,10 @@ where } #[inline] - fn remove(&mut self, k: &Q) -> Option + fn remove(&mut self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { HashMap::remove(self, k) } @@ -208,19 +208,19 @@ where type Element = MapE; #[inline] - fn get(&self, k: &Q) -> Option<&Self::Element> + fn get(&self, k: &Q) -> Option<&Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { Hashbrown::get(self, k) } #[inline] - fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { Hashbrown::get_mut(self, k) } @@ -236,10 +236,10 @@ where } #[inline] - fn remove(&mut self, k: &Q) -> Option + fn remove(&mut self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { Hashbrown::remove(self, k) } @@ -275,19 +275,19 @@ where type Element = MapE; #[inline] - fn get(&self, k: &Q) -> Option<&Self::Element> + fn get(&self, k: &Q) -> Option<&Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { abi_stable::std_types::RHashMap::get(self, k) } #[inline] - fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Element> where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { abi_stable::std_types::RHashMap::get_mut(self, k) } @@ -303,10 +303,10 @@ where } #[inline] - fn remove(&mut self, k: &Q) -> Option + fn remove(&mut self, k: &Q) -> Option where Self::Key: Borrow + Hash + Eq, - Q: Hash + Eq + Ord, + Q: ?Sized + Hash + Eq + Ord, { abi_stable::std_types::RHashMap::remove(self, k).into() }