Skip to content

Commit

Permalink
use from_utf8_unchecked in the fmt::Display implementation of Value
Browse files Browse the repository at this point in the history
`WriterFormatter` has been moved into it's own little module to enforce using it's `new` method to convey the unsafe requirements.
the safety requirements that the serializer only emits valid utf8 is already assumed by the existing `to_string` methods.

this speeds up formatting large `Value`s by about 33% in my testing
  • Loading branch information
icewind1991 committed Feb 5, 2021
1 parent 7ff8ec6 commit e0747b9
Showing 1 changed file with 34 additions and 16 deletions.
50 changes: 34 additions & 16 deletions src/value/mod.rs
Expand Up @@ -91,7 +91,6 @@
//! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html

use crate::error::Error;
use crate::io;
use crate::lib::*;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
Expand Down Expand Up @@ -191,27 +190,43 @@ impl Debug for Value {
}
}

struct WriterFormatter<'a, 'b: 'a> {
inner: &'a mut fmt::Formatter<'b>,
}
// WriterFormatter is in it's own little module to enforce using the unsafe constructor
mod writer_formatter {
use crate::io;
use crate::lib::*;

pub struct WriterFormatter<'a, 'b: 'a> {
inner: &'a mut fmt::Formatter<'b>,
}

impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
fn io_error<E>(_: E) -> io::Error {
// Error value does not matter because fmt::Display impl below just
// maps it to fmt::Error
io::Error::new(io::ErrorKind::Other, "fmt error")
impl<'a, 'b: 'a> WriterFormatter<'a, 'b> {
/// Safety: the caller needs to ensure that only valid utf8 bytes are written into this writer
pub unsafe fn new(inner: &'a mut fmt::Formatter<'b>) -> Self {
Self { inner }
}
let s = tri!(str::from_utf8(buf).map_err(io_error));
tri!(self.inner.write_str(s).map_err(io_error));
Ok(buf.len())
}

fn flush(&mut self) -> io::Result<()> {
Ok(())
impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
fn io_error<E>(_: E) -> io::Error {
// Error value does not matter because fmt::Display impl below just
// maps it to fmt::Error
io::Error::new(io::ErrorKind::Other, "fmt error")
}
// The safety requirements of passing in valid utf8 is passed trough to the caller trough the unsafe `new` method
let s = unsafe { str::from_utf8_unchecked(buf) };
tri!(self.inner.write_str(s).map_err(io_error));
Ok(buf.len())
}

fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
}

use writer_formatter::WriterFormatter;

impl fmt::Display for Value {
/// Display a JSON value as a string.
///
Expand Down Expand Up @@ -239,7 +254,10 @@ impl fmt::Display for Value {
/// ```
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let alternate = f.alternate();
let mut wr = WriterFormatter { inner: f };
let mut wr = unsafe {
// The serializer only emits valid json with the default formatters
WriterFormatter::new(f)
};
if alternate {
// {:#}
super::ser::to_writer_pretty(&mut wr, self).map_err(|_| fmt::Error)
Expand Down

0 comments on commit e0747b9

Please sign in to comment.