diff --git a/.github/workflows/debug.yaml b/.github/workflows/debug.yaml index debee92a..f411cb91 100644 --- a/.github/workflows/debug.yaml +++ b/.github/workflows/debug.yaml @@ -13,9 +13,12 @@ jobs: ] python: [ { version: '3.12', abi: 'cp312-cp312' }, - { version: '3.11', abi: 'cp311-cp311' }, { version: '3.8', abi: 'cp38-cp38' }, ] + features: [ + "", + "--features=yyjson,simd-write", + ] env: CC: "gcc" CFLAGS: "-O2 -fno-plt" @@ -36,9 +39,8 @@ jobs: PATH="$HOME/.cargo/bin:$PATH" maturin build --release \ --out=dist \ --profile=dev \ - --features=yyjson \ --interpreter python${{ matrix.python.version }} \ - --target=x86_64-unknown-linux-gnu + --target=x86_64-unknown-linux-gnu ${{ matrix.features }} - run: python -m pip install --user dist/orjson*.whl - run: python -m pip install --user -r test/requirements.txt -r integration/requirements.txt diff --git a/.github/workflows/linux.yaml b/.github/workflows/linux.yaml index 0632d6cc..3e837749 100644 --- a/.github/workflows/linux.yaml +++ b/.github/workflows/linux.yaml @@ -84,7 +84,7 @@ jobs: - run: | maturin build --release --strip \ --out=dist \ - --features=no-panic,yyjson \ + --features=no-panic,simd-write,yyjson \ --compatibility manylinux_2_17 \ --interpreter python${{ matrix.python.version }} \ --target=x86_64-unknown-linux-gnu @@ -147,7 +147,7 @@ jobs: rustup-components: rust-src target: ${{ matrix.platform.target }} manylinux: musllinux_1_1 - args: --release --strip --out=dist --features=no-panic,yyjson -i python${{ matrix.python.version }} + args: --release --strip --out=dist --features=no-panic,simd-write,yyjson -i python${{ matrix.python.version }} - name: Set up QEMU if: matrix.platform.arch != 'x86_64' @@ -194,27 +194,31 @@ jobs: target: [ { arch: 'aarch64', - target: 'aarch64-unknown-linux-gnu', cflags: '-O2 -flto', + features: 'no-panic,simd-write,yyjson', # NEON rustflags: '-Z mir-opt-level=4 -D warnings', + target: 'aarch64-unknown-linux-gnu', }, { arch: 'armv7', - target: 'armv7-unknown-linux-gnueabihf', cflags: '-Os -flto -fstrict-aliasing', + features: 'no-panic,yyjson', # no SIMD rustflags: '-C opt-level=s -Z mir-opt-level=4 -D warnings', + target: 'armv7-unknown-linux-gnueabihf', }, { arch: 'ppc64le', - target: 'powerpc64le-unknown-linux-gnu', cflags: '-O2 -flto', + features: 'no-panic,yyjson', # unknown SIMD baseline rustflags: '-Z mir-opt-level=4 -D warnings', + target: 'powerpc64le-unknown-linux-gnu', }, { arch: 's390x', - target: 's390x-unknown-linux-gnu', cflags: '-O2 -flto -march=z10', + features: 'no-panic,yyjson', # unknown SIMD baseline rustflags: '-Z mir-opt-level=4 -C target-cpu=z10 -D warnings', + target: 's390x-unknown-linux-gnu', }, ] steps: @@ -237,7 +241,7 @@ jobs: rust-toolchain: nightly-2023-12-10 rustup-components: rust-src manylinux: auto - args: --release --strip --out=dist --features=no-panic,yyjson -i python${{ matrix.python.version }} + args: --release --strip --out=dist --features=${{ matrix.target.features }} -i python${{ matrix.python.version }} - uses: uraimo/run-on-arch-action@v2 name: Test diff --git a/Cargo.lock b/Cargo.lock index 63dee98e..278d8957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,7 +179,9 @@ dependencies = [ "encoding_rs", "itoa", "itoap", + "libc", "once_cell", + "packed_simd", "pyo3-build-config", "pyo3-ffi", "ryu", diff --git a/Cargo.toml b/Cargo.toml index 5a0dab31..281e1896 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,11 @@ no-panic = [ "ryu/no-panic", ] +simd-write = [ + "packed_simd", + "libc", +] + # Build yyjson as a backend and panic if it fails. The default is to attempt # to build and on failure fall back to another backend. yyjson = [] @@ -58,7 +63,9 @@ compact_str = { version = "0.7", default_features = false, features = ["serde"] encoding_rs = { version = "0.8", default_features = false } itoa = { version = "1", default_features = false } itoap = { version = "1", features = ["std", "simd"] } +libc = { version = "0.2", default_features = false, optional = true } once_cell = { version = "1", default_features = false, features = ["race"] } +packed_simd = { version = "0.3", default_features = false, optional = true } pyo3-ffi = { version = "^0.20", default_features = false, features = ["extension-module"]} ryu = { version = "1", default_features = false } serde = { version = "1", default_features = false } diff --git a/ci/azure-macos.yml b/ci/azure-macos.yml index e8ea91f0..9ea13b79 100644 --- a/ci/azure-macos.yml +++ b/ci/azure-macos.yml @@ -23,7 +23,7 @@ steps: PATH=$HOME/.cargo/bin:$PATH \ MACOSX_DEPLOYMENT_TARGET=$(macosx_deployment_target) \ PYO3_CROSS_LIB_DIR=$(python -c "import sysconfig;print(sysconfig.get_config_var('LIBDIR'))") \ - maturin build --release --strip --features=no-panic,yyjson --interpreter $(interpreter) --target=universal2-apple-darwin + maturin build --release --strip --features=no-panic,simd-write,yyjson --interpreter $(interpreter) --target=universal2-apple-darwin env: CC: "clang" CFLAGS: "-O2 -fno-plt -flto=thin -fstrict-aliasing" diff --git a/src/serialize/backend/format_str/json.rs b/src/serialize/backend/format_str/json.rs new file mode 100644 index 00000000..7df561a1 --- /dev/null +++ b/src/serialize/backend/format_str/json.rs @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +// This is an adaptation of `src/value/ser.rs` from serde-json. + +pub fn format_escaped_str(writer: &mut W, formatter: &mut F, value: &str) -> io::Result<()> +where + W: ?Sized + io::Write + WriteExt, + F: ?Sized + Formatter, +{ + let len = value.len(); + + if len == 0 { + reserve_minimum!(writer); + return unsafe { writer.write_reserved_fragment(b"\"\"") }; + } + unsafe { + let mut escapes: u8 = __; + let mut idx = 0; + let as_bytes = value.as_bytes(); + while idx < len.saturating_sub(8) { + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx) as usize); + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx + 1) as usize); + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx + 2) as usize); + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx + 3) as usize); + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx + 4) as usize); + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx + 5) as usize); + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx + 6) as usize); + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx + 7) as usize); + if unlikely!(escapes != __) { + return format_escaped_str_with_escapes(writer, formatter, as_bytes, idx); + } + idx += 8; + } + while idx < len { + escapes |= *ESCAPE.get_unchecked(*as_bytes.get_unchecked(idx) as usize); + if unlikely!(escapes != __) { + return format_escaped_str_with_escapes(writer, formatter, as_bytes, idx); + } + idx += 1; + } + } + + writer.write_str(value) +} + +fn format_escaped_str_with_escapes( + writer: &mut W, + formatter: &mut F, + value: &[u8], + initial: usize, +) -> io::Result<()> +where + W: ?Sized + io::Write + WriteExt, + F: ?Sized + Formatter, +{ + writer.reserve((value.len() * 8) + 2); + unsafe { + writer.write_reserved_punctuation(b'"').unwrap(); + if initial > 0 { + writer + .write_reserved_fragment(value.get_unchecked(0..initial)) + .unwrap(); + } + format_escaped_str_contents(writer, formatter, value.get_unchecked(initial..)).unwrap(); + writer.write_reserved_punctuation(b'"').unwrap(); + }; + Ok(()) +} + +fn format_escaped_str_contents( + writer: &mut W, + formatter: &mut F, + bytes: &[u8], +) -> io::Result<()> +where + W: ?Sized + io::Write + WriteExt, + F: ?Sized + Formatter, +{ + let len = bytes.len(); + let mut start = 0; + let mut idx = 0; + + let mut escape: u8; + loop { + if idx < len.saturating_sub(4) { + escape = 0; + unsafe { + escape |= *ESCAPE.get_unchecked(*bytes.get_unchecked(idx) as usize); + escape |= *ESCAPE.get_unchecked(*bytes.get_unchecked(idx + 1) as usize); + escape |= *ESCAPE.get_unchecked(*bytes.get_unchecked(idx + 2) as usize); + escape |= *ESCAPE.get_unchecked(*bytes.get_unchecked(idx + 3) as usize); + } + if escape == 0 { + idx += 4; + continue; + } + } + + let byte = unsafe { *bytes.get_unchecked(idx) }; + escape = unsafe { *ESCAPE.get_unchecked(byte as usize) }; + if escape == 0 { + idx += 1; + if idx == len { + break; + } else { + continue; + } + } + + if start < idx { + unsafe { + writer + .write_reserved_fragment(bytes.get_unchecked(start..idx)) + .unwrap() + }; + } + + let char_escape = CharEscape::from_escape_table(escape, byte); + formatter.write_char_escape(writer, char_escape)?; + + idx += 1; + start = idx; + if idx == len { + break; + } + } + + if start != len { + unsafe { + writer + .write_reserved_fragment(bytes.get_unchecked(start..len)) + .unwrap() + }; + } + Ok(()) +} + + +pub enum CharEscape { + /// An escaped quote `"` + Quote, + /// An escaped reverse solidus `\` + ReverseSolidus, + /// An escaped backspace character (usually escaped as `\b`) + Backspace, + /// An escaped form feed character (usually escaped as `\f`) + FormFeed, + /// An escaped line feed character (usually escaped as `\n`) + LineFeed, + /// An escaped carriage return character (usually escaped as `\r`) + CarriageReturn, + /// An escaped tab character (usually escaped as `\t`) + Tab, + /// An escaped ASCII plane control character (usually escaped as + /// `\u00XX` where `XX` are two hex characters) + AsciiControl(u8), +} + +impl CharEscape { + #[inline] + fn from_escape_table(escape: u8, byte: u8) -> CharEscape { + match escape { + self::BB => CharEscape::Backspace, + self::TT => CharEscape::Tab, + self::NN => CharEscape::LineFeed, + self::FF => CharEscape::FormFeed, + self::RR => CharEscape::CarriageReturn, + self::QU => CharEscape::Quote, + self::BS => CharEscape::ReverseSolidus, + self::UU => CharEscape::AsciiControl(byte), + _ => unreachable!(), + } + } +} + +const BB: u8 = b'b'; // \x08 +const TT: u8 = b't'; // \x09 +const NN: u8 = b'n'; // \x0A +const FF: u8 = b'f'; // \x0C +const RR: u8 = b'r'; // \x0D +const QU: u8 = b'"'; // \x22 +const BS: u8 = b'\\'; // \x5C +const UU: u8 = b'u'; // \x00...\x1F except the ones above +const __: u8 = 0; + +// Lookup table of escape sequences. A value of b'x' at index i means that byte +// i is escaped as "\x" in JSON. A value of 0 means that byte i is not escaped. +const ESCAPE: [u8; 256] = [ + // 1 2 3 4 5 6 7 8 9 A B C D E F + UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0 + UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1 + __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4 + __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F +]; diff --git a/src/serialize/backend/format_str/mod.rs b/src/serialize/backend/format_str/mod.rs new file mode 100644 index 00000000..bca9855c --- /dev/null +++ b/src/serialize/backend/format_str/mod.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +mod json; + +#[cfg(feature = "simd-write")] +mod sonic; + +#[cfg(not(feature = "simd-write"))] +pub use json::format_escaped_str; + +#[cfg(feature = "simd-write")] +fn format_escaped_str(writer: &mut W, formatter: &mut F, value: &str) -> io::Result<()> +where + W: ?Sized + io::Write + WriteExt, + F: ?Sized + Formatter, +{ + if std::is_x86_feature_detected!("avx2") { + sonic::format_escaped_str(writer, formatter, value) + } else { + json::format_escaped_str(writer, formatter, value) + } +} diff --git a/src/serialize/backend/format_str/sonic.rs b/src/serialize/backend/format_str/sonic.rs new file mode 100644 index 00000000..9cf8eb6e --- /dev/null +++ b/src/serialize/backend/format_str/sonic.rs @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 liuq19, chris-ha458 +// adapted from sonic-rs' src/util/string.rs +// this lifts PAGE_SIZE for a large efficiency and omits `need_quote: bool` + +use std::{ + mem::MaybeUninit, + slice::{from_raw_parts, from_raw_parts_mut}, +}; + +use packed_simd::u8x32; + +pub const QUOTE_TAB: [(u8, [u8; 8]); 256] = [ + // 0x00 ~ 0x1f + (6, *b"\\u0000\0\0"), + (6, *b"\\u0001\0\0"), + (6, *b"\\u0002\0\0"), + (6, *b"\\u0003\0\0"), + (6, *b"\\u0004\0\0"), + (6, *b"\\u0005\0\0"), + (6, *b"\\u0006\0\0"), + (6, *b"\\u0007\0\0"), + (2, *b"\\b\0\0\0\0\0\0"), + (2, *b"\\t\0\0\0\0\0\0"), + (2, *b"\\n\0\0\0\0\0\0"), + (6, *b"\\u000b\0\0"), + (2, *b"\\f\0\0\0\0\0\0"), + (2, *b"\\r\0\0\0\0\0\0"), + (6, *b"\\u000e\0\0"), + (6, *b"\\u000f\0\0"), + (6, *b"\\u0010\0\0"), + (6, *b"\\u0011\0\0"), + (6, *b"\\u0012\0\0"), + (6, *b"\\u0013\0\0"), + (6, *b"\\u0014\0\0"), + (6, *b"\\u0015\0\0"), + (6, *b"\\u0016\0\0"), + (6, *b"\\u0017\0\0"), + (6, *b"\\u0018\0\0"), + (6, *b"\\u0019\0\0"), + (6, *b"\\u001a\0\0"), + (6, *b"\\u001b\0\0"), + (6, *b"\\u001c\0\0"), + (6, *b"\\u001d\0\0"), + (6, *b"\\u001e\0\0"), + (6, *b"\\u001f\0\0"), + // 0x20 ~ 0x2f + (0, [0; 8]), + (0, [0; 8]), + (2, *b"\\\"\0\0\0\0\0\0"), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x30 ~ 0x3f + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x40 ~ 0x4f + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x50 ~ 0x5f + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (2, *b"\\\\\0\0\0\0\0\0"), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x60 ~ 0xff + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), +]; + +const NEED_ESCAPED: [u8; 256] = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +#[inline] +fn escaped_mask(v: u8x32) -> u32 { + let _x20 = u8x32::splat(32); // 0x00 ~ 0x20 + let blash = u8x32::splat(b'\\'); + let quote = u8x32::splat(b'"'); + (v.lt(_x20) | v.eq(blash) | v.eq(quote)).bitmask() +} + +// only check the src length. +#[inline(always)] +unsafe fn escape_unchecked(src: &mut *const u8, nb: &mut usize, dst: &mut *mut u8) { + assert!(*nb >= 1); + loop { + let ch = *(*src); + let cnt = QUOTE_TAB[ch as usize].0 as usize; + assert!( + cnt != 0, + "char is {}, cnt is {}, NEED_ESCAPED is {}", + ch as char, + cnt, + NEED_ESCAPED[ch as usize] + ); + std::ptr::copy_nonoverlapping(QUOTE_TAB[ch as usize].1.as_ptr(), *dst, 8); + (*dst) = (*dst).add(cnt); + (*src) = (*src).add(1); + (*nb) -= 1; + if (*nb) == 0 || NEED_ESCAPED[*(*src) as usize] == 0 { + return; + } + } +} + +#[inline(always)] +fn cross_page(ptr: *const u8, step: usize) -> bool { + unsafe { ((ptr as usize & (crate::typeref::PAGE_SIZE - 1)) + step) > crate::typeref::PAGE_SIZE } +} + +#[inline(always)] +pub fn format_string(value: &str, dst: &mut [u8]) -> usize { + assert!(dst.len() >= value.len() * 6 + 32 + 3); + + const LANS: usize = 32; + unsafe { + let slice = value.as_bytes(); + let mut sptr = slice.as_ptr(); + let mut dptr = dst.as_mut_ptr() as *mut u8; + let dstart = dptr; + let mut nb: usize = slice.len(); + + *dptr = b'"'; + dptr = dptr.add(1); + while nb >= LANS { + let v = { + let raw = std::slice::from_raw_parts(sptr, LANS); + { + u8x32::from_slice_unaligned_unchecked(raw) + } + }; + v.write_to_slice_unaligned_unchecked(std::slice::from_raw_parts_mut(dptr, LANS)); + let mask = escaped_mask(v); + if mask == 0 { + nb -= LANS; + dptr = dptr.add(LANS); + sptr = sptr.add(LANS); + } else { + let cn = mask.trailing_zeros() as usize; + nb -= cn; + dptr = dptr.add(cn); + sptr = sptr.add(cn); + escape_unchecked(&mut sptr, &mut nb, &mut dptr); + } + } + + let mut temp: [u8; LANS] = [0u8; LANS]; + while nb > 0 { + let v = if cross_page(sptr, LANS) { + std::ptr::copy_nonoverlapping(sptr, temp[..].as_mut_ptr(), nb); + u8x32::from_slice_unaligned_unchecked(&temp[..]) + } else { + #[cfg(not(debug_assertions))] + { + // disable memory sanitizer here + let raw = std::slice::from_raw_parts(sptr, LANS); + u8x32::from_slice_unaligned_unchecked(raw) + } + #[cfg(debug_assertions)] + { + std::ptr::copy_nonoverlapping(sptr, temp[..].as_mut_ptr(), nb); + u8x32::from_slice_unaligned_unchecked(&temp[..]) + } + }; + v.write_to_slice_unaligned_unchecked(std::slice::from_raw_parts_mut(dptr, LANS)); + + let mask = escaped_mask(v) & (0xFFFFFFFFu32 >> (LANS - nb)); + if mask == 0 { + dptr = dptr.add(nb); + break; + } else { + let cn = mask.trailing_zeros() as usize; + nb -= cn; + dptr = dptr.add(cn); + sptr = sptr.add(cn); + escape_unchecked(&mut sptr, &mut nb, &mut dptr); + } + } + *dptr = b'"'; + dptr = dptr.add(1); + dptr as usize - dstart as usize + } +} + +pub fn format_escaped_str(writer: &mut W, formatter: &mut F, value: &str) -> io::Result<()> +where + W: ?Sized + io::Write + WriteExt, + F: ?Sized + Formatter, +{ + let num_reserved_bytes = value.len() * 6 + 32 + 3; + writer.reserve(num_reserved_bytes); + let mut dst_slice = + unsafe { std::slice::from_raw_parts_mut(writer.as_mut_buffer_ptr(), num_reserved_bytes) }; + let written = format_string(value, &mut dst_slice); + writer.set_written(written); + Ok(()) +} diff --git a/src/serialize/json.rs b/src/serialize/backend/json.rs similarity index 97% rename from src/serialize/json.rs rename to src/serialize/backend/json.rs index 746845e6..583a8c60 100644 --- a/src/serialize/json.rs +++ b/src/serialize/backend/json.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) // This is an adaptation of `src/value/ser.rs` from serde-json. -use crate::serialize::writer::WriteExt; +use crate::serialize::backend::WriteExt; use serde::ser::{self, Impossible, Serialize}; use serde_json::error::{Error, Result}; use std::io; @@ -622,6 +622,7 @@ where } } +#[cfg(not(feature = "simd-write"))] pub enum CharEscape { /// An escaped quote `"` Quote, @@ -642,6 +643,7 @@ pub enum CharEscape { AsciiControl(u8), } +#[cfg(not(feature = "simd-write"))] impl CharEscape { #[inline] fn from_escape_table(escape: u8, byte: u8) -> CharEscape { @@ -863,6 +865,7 @@ pub trait Formatter { } #[inline] + #[cfg(not(feature = "simd-write"))] fn write_char_escape(&mut self, writer: &mut W, char_escape: CharEscape) -> io::Result<()> where W: ?Sized + io::Write + WriteExt, @@ -1137,6 +1140,16 @@ impl Formatter for PrettyFormatter { } } +#[cfg(feature = "simd-write")] +fn format_escaped_str(writer: &mut W, _formatter: &mut F, value: &str) -> io::Result<()> +where + W: ?Sized + io::Write + WriteExt, + F: ?Sized + Formatter, +{ + crate::serialize::backend::sonic::format_escaped_str(writer, value) +} + +#[cfg(not(feature = "simd-write"))] fn format_escaped_str(writer: &mut W, formatter: &mut F, value: &str) -> io::Result<()> where W: ?Sized + io::Write + WriteExt, @@ -1148,7 +1161,6 @@ where reserve_minimum!(writer); return unsafe { writer.write_reserved_fragment(b"\"\"") }; } - unsafe { let mut escapes: u8 = __; let mut idx = 0; @@ -1179,6 +1191,7 @@ where writer.write_str(value) } +#[cfg(not(feature = "simd-write"))] fn format_escaped_str_with_escapes( writer: &mut W, formatter: &mut F, @@ -1203,6 +1216,7 @@ where Ok(()) } +#[cfg(not(feature = "simd-write"))] fn format_escaped_str_contents( writer: &mut W, formatter: &mut F, @@ -1271,18 +1285,28 @@ where Ok(()) } +#[cfg(not(feature = "simd-write"))] const BB: u8 = b'b'; // \x08 +#[cfg(not(feature = "simd-write"))] const TT: u8 = b't'; // \x09 +#[cfg(not(feature = "simd-write"))] const NN: u8 = b'n'; // \x0A +#[cfg(not(feature = "simd-write"))] const FF: u8 = b'f'; // \x0C +#[cfg(not(feature = "simd-write"))] const RR: u8 = b'r'; // \x0D +#[cfg(not(feature = "simd-write"))] const QU: u8 = b'"'; // \x22 +#[cfg(not(feature = "simd-write"))] const BS: u8 = b'\\'; // \x5C +#[cfg(not(feature = "simd-write"))] const UU: u8 = b'u'; // \x00...\x1F except the ones above +#[cfg(not(feature = "simd-write"))] const __: u8 = 0; // Lookup table of escape sequences. A value of b'x' at index i means that byte // i is escaped as "\x" in JSON. A value of 0 means that byte i is not escaped. +#[cfg(not(feature = "simd-write"))] const ESCAPE: [u8; 256] = [ // 1 2 3 4 5 6 7 8 9 A B C D E F UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0 diff --git a/src/serialize/backend/mod.rs b/src/serialize/backend/mod.rs new file mode 100644 index 00000000..965970ee --- /dev/null +++ b/src/serialize/backend/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 + +mod json; +#[cfg(feature = "simd-write")] +mod sonic; +mod writer; + +pub use json::{to_writer, to_writer_pretty}; +pub use writer::{BytesWriter, WriteExt}; diff --git a/src/serialize/backend/sonic.rs b/src/serialize/backend/sonic.rs new file mode 100644 index 00000000..045fddfd --- /dev/null +++ b/src/serialize/backend/sonic.rs @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 liuq19, chris-ha458 +// adapted from sonic-rs' src/util/string.rs +// this lifts PAGE_SIZE, omits `need_quote: bool`, and reduces to 128-bit + +use crate::serialize::backend::WriteExt; +use core::mem::MaybeUninit; +use std::io; + +use packed_simd::u8x16; + +const LANS: usize = 16; + +const QUOTE_TAB: [(u8, [u8; 8]); 256] = [ + // 0x00 ~ 0x1f + (6, *b"\\u0000\0\0"), + (6, *b"\\u0001\0\0"), + (6, *b"\\u0002\0\0"), + (6, *b"\\u0003\0\0"), + (6, *b"\\u0004\0\0"), + (6, *b"\\u0005\0\0"), + (6, *b"\\u0006\0\0"), + (6, *b"\\u0007\0\0"), + (2, *b"\\b\0\0\0\0\0\0"), + (2, *b"\\t\0\0\0\0\0\0"), + (2, *b"\\n\0\0\0\0\0\0"), + (6, *b"\\u000b\0\0"), + (2, *b"\\f\0\0\0\0\0\0"), + (2, *b"\\r\0\0\0\0\0\0"), + (6, *b"\\u000e\0\0"), + (6, *b"\\u000f\0\0"), + (6, *b"\\u0010\0\0"), + (6, *b"\\u0011\0\0"), + (6, *b"\\u0012\0\0"), + (6, *b"\\u0013\0\0"), + (6, *b"\\u0014\0\0"), + (6, *b"\\u0015\0\0"), + (6, *b"\\u0016\0\0"), + (6, *b"\\u0017\0\0"), + (6, *b"\\u0018\0\0"), + (6, *b"\\u0019\0\0"), + (6, *b"\\u001a\0\0"), + (6, *b"\\u001b\0\0"), + (6, *b"\\u001c\0\0"), + (6, *b"\\u001d\0\0"), + (6, *b"\\u001e\0\0"), + (6, *b"\\u001f\0\0"), + // 0x20 ~ 0x2f + (0, [0; 8]), + (0, [0; 8]), + (2, *b"\\\"\0\0\0\0\0\0"), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x30 ~ 0x3f + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x40 ~ 0x4f + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x50 ~ 0x5f + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (2, *b"\\\\\0\0\0\0\0\0"), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + // 0x60 ~ 0xff + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), + (0, [0; 8]), +]; + +const NEED_ESCAPED: [u8; 256] = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +#[inline] +fn escaped_mask(v: u8x16) -> u16 { + let _x20 = u8x16::splat(32); // 0x00 ~ 0x20 + let blash = u8x16::splat(b'\\'); + let quote = u8x16::splat(b'"'); + (v.lt(_x20) | v.eq(blash) | v.eq(quote)).bitmask() +} + +// only check the src length. +#[inline(always)] +unsafe fn escape_unchecked(src: &mut *const u8, nb: &mut usize, dst: &mut *mut u8) { + debug_assert!(*nb >= 1); + loop { + let ch = *(*src); + let cnt = QUOTE_TAB[ch as usize].0 as usize; + debug_assert!( + cnt != 0, + "char is {}, cnt is {}, NEED_ESCAPED is {}", + ch as char, + cnt, + NEED_ESCAPED[ch as usize] + ); + std::ptr::copy_nonoverlapping(QUOTE_TAB[ch as usize].1.as_ptr(), *dst, 8); + (*dst) = (*dst).add(cnt); + (*src) = (*src).add(1); + (*nb) -= 1; + if (*nb) == 0 || NEED_ESCAPED[*(*src) as usize] == 0 { + return; + } + } +} + +#[inline(always)] +fn cross_page(ptr: *const u8, step: usize) -> bool { + unsafe { ((ptr as usize & (crate::typeref::PAGE_SIZE - 1)) + step) > crate::typeref::PAGE_SIZE } +} + +#[inline(always)] +pub fn format_string(value: &str, dst: &mut [MaybeUninit]) -> usize { + debug_assert!(dst.len() >= value.len() * 6 + LANS + 3); + + unsafe { + let slice = value.as_bytes(); + let mut sptr = slice.as_ptr(); + let mut dptr = dst.as_mut_ptr() as *mut u8; + let dstart = dptr; + let mut nb: usize = slice.len(); + + *dptr = b'"'; + dptr = dptr.add(1); + while nb >= LANS { + let v = { + let raw = std::slice::from_raw_parts(sptr, LANS); + u8x16::from_slice_unaligned_unchecked(raw) + }; + v.write_to_slice_unaligned_unchecked(std::slice::from_raw_parts_mut(dptr, LANS)); + let mask = escaped_mask(v); + if mask == 0 { + nb -= LANS; + dptr = dptr.add(LANS); + sptr = sptr.add(LANS); + } else { + let cn = mask.trailing_zeros() as usize; + nb -= cn; + dptr = dptr.add(cn); + sptr = sptr.add(cn); + escape_unchecked(&mut sptr, &mut nb, &mut dptr); + } + } + + let mut temp: [u8; LANS] = [0u8; LANS]; + while nb > 0 { + let v = if cross_page(sptr, LANS) { + std::ptr::copy_nonoverlapping(sptr, temp[..].as_mut_ptr(), nb); + u8x16::from_slice_unaligned_unchecked(&temp[..]) + } else { + let raw = std::slice::from_raw_parts(sptr, LANS); + u8x16::from_slice_unaligned_unchecked(raw) + }; + v.write_to_slice_unaligned_unchecked(std::slice::from_raw_parts_mut(dptr, LANS)); + + let mask = escaped_mask(v) & (0xFFFFu16 >> (LANS - nb)); + if mask == 0 { + dptr = dptr.add(nb); + break; + } else { + let cn = mask.trailing_zeros() as usize; + nb -= cn; + dptr = dptr.add(cn); + sptr = sptr.add(cn); + escape_unchecked(&mut sptr, &mut nb, &mut dptr); + } + } + *dptr = b'"'; + dptr = dptr.add(1); + dptr as usize - dstart as usize + } +} + +pub fn format_escaped_str(writer: &mut W, value: &str) -> io::Result<()> +where + W: ?Sized + io::Write + WriteExt, +{ + let num_reserved_bytes = value.len() * 6 + LANS + 3; + writer.reserve(num_reserved_bytes); + let mut dst_slice = + unsafe { std::slice::from_raw_parts_mut(writer.as_mut_buffer_ptr(), num_reserved_bytes) }; + let dst_ref = + unsafe { std::mem::transmute::<&mut [u8], &mut [MaybeUninit]>(&mut dst_slice) }; + let written = format_string(value, dst_ref); + writer.set_written(written); + Ok(()) +} diff --git a/src/serialize/writer.rs b/src/serialize/backend/writer.rs similarity index 100% rename from src/serialize/writer.rs rename to src/serialize/backend/writer.rs diff --git a/src/serialize/mod.rs b/src/serialize/mod.rs index 9a0da4de..aa2db62d 100644 --- a/src/serialize/mod.rs +++ b/src/serialize/mod.rs @@ -1,11 +1,10 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) +mod backend; mod error; -mod json; mod obtype; mod per_type; mod serializer; mod state; -mod writer; pub use serializer::serialize; diff --git a/src/serialize/serializer.rs b/src/serialize/serializer.rs index c3edb9ff..6c5bb1a3 100644 --- a/src/serialize/serializer.rs +++ b/src/serialize/serializer.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) use crate::opt::{Opt, APPEND_NEWLINE, INDENT_2, STRICT_INTEGER}; +use crate::serialize::backend::{to_writer, to_writer_pretty, BytesWriter}; use crate::serialize::obtype::{pyobject_to_obtype, ObType}; use crate::serialize::per_type::{ BoolSerializer, DataclassGenericSerializer, Date, DateTime, DefaultSerializer, @@ -9,13 +10,10 @@ use crate::serialize::per_type::{ StrSerializer, StrSubclassSerializer, Time, UUID, }; use crate::serialize::state::SerializerState; -use crate::serialize::writer::*; use serde::ser::{Serialize, Serializer}; use std::io::Write; use std::ptr::NonNull; -use crate::serialize::json::{to_writer, to_writer_pretty}; - pub fn serialize( ptr: *mut pyo3_ffi::PyObject, default: Option>, diff --git a/src/typeref.rs b/src/typeref.rs index dd1c2dbb..0fc2dada 100644 --- a/src/typeref.rs +++ b/src/typeref.rs @@ -93,6 +93,9 @@ pub fn ahash_init() -> Box { } } +#[cfg(feature = "simd-write")] +pub static mut PAGE_SIZE: usize = 4096; + #[cfg(feature = "yyjson")] pub const YYJSON_BUFFER_SIZE: usize = 1024 * 1024 * 8; @@ -156,6 +159,12 @@ fn _init_typerefs_impl() -> bool { assert!(crate::deserialize::KEY_MAP .set(crate::deserialize::KeyMap::default()) .is_ok()); + + #[cfg(feature = "simd-write")] + { + PAGE_SIZE = libc::sysconf(libc::_SC_PAGESIZE) as usize; + } + FRAGMENT_TYPE = orjson_fragmenttype_new(); PyDateTime_IMPORT(); NONE = Py_None();