summaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorAndrew Gallant <jamslam@gmail.com>2023-11-21 12:40:45 -0500
committerAndrew Gallant <jamslam@gmail.com>2023-11-21 18:39:32 -0500
commit9c84575229131239f92149cd5790ddb553d7eea8 (patch)
tree4c145119d903e94e17a444e91cf79586cef1bffb /crates
parentcddb5f57f8fa3413a6210bdd3a8f687b7dac69b2 (diff)
printer: drop dependency on serde_derive
As suggested by @epage[1]. Ad hoc timings on my i7-12900K: before cargo build: 4.91s before cargo build release: 8.05s after cargo build: 4.69s after cargo build release: 7.83s ... pretty underwhelming if you ask me. Ah well. And on my M2 mac mini: before cargo build: 6.18s before cargo build release: 14.50s after cargo build: 5.52s after cargo build release: 13.44s Still kind of underwhelming, but definitely better. It shaves a full second off of compile times in release mode. I went back to my i7-12900K, but passed `-j1` to `cargo build` to force single threaded mode: before cargo build: 19.44s before cargo build release: 50.64s after cargo build: 16.76s after cargo build release: 48.00s Which seems pretty consistent with the modest improvements above. Looking at `cargo build --timings`, the beefiest chunk of time is spent in compiling `regex-automata`, by far. This is fine because it's core functionality. I wish a fast general purpose regex engine with its internals exposed as a separately versioned library didn't require so much code... Blech. [1]: https://old.reddit.com/r/rust/comments/17rd8ww/faster_compilation_with_the_parallel_frontend_in/k8igjlg/
Diffstat (limited to 'crates')
-rw-r--r--crates/printer/Cargo.toml3
-rw-r--r--crates/printer/src/jsont.rs184
-rw-r--r--crates/printer/src/stats.rs110
-rw-r--r--crates/printer/src/util.rs12
4 files changed, 202 insertions, 107 deletions
diff --git a/crates/printer/Cargo.toml b/crates/printer/Cargo.toml
index 4193d3d1..10537c53 100644
--- a/crates/printer/Cargo.toml
+++ b/crates/printer/Cargo.toml
@@ -16,7 +16,7 @@ edition = "2021"
[features]
default = ["serde"]
-serde = ["dep:base64", "dep:serde", "dep:serde_derive", "dep:serde_json"]
+serde = ["dep:base64", "dep:serde", "dep:serde_json"]
[dependencies]
base64 = { version = "0.21.4", optional = true }
@@ -26,7 +26,6 @@ grep-searcher = { version = "0.1.11", path = "../searcher" }
log = "0.4.5"
termcolor = "1.3.0"
serde = { version = "1.0.193", optional = true }
-serde_derive = { version = "1.0.193", optional = true }
serde_json = { version = "1.0.107", optional = true }
[dev-dependencies]
diff --git a/crates/printer/src/jsont.rs b/crates/printer/src/jsont.rs
index 5d901041..6e5e85df 100644
--- a/crates/printer/src/jsont.rs
+++ b/crates/printer/src/jsont.rs
@@ -8,13 +8,6 @@
use std::{borrow::Cow, path::Path};
-use {base64, serde::Serializer, serde_derive::Serialize};
-
-use crate::stats::Stats;
-
-#[derive(Serialize)]
-#[serde(tag = "type", content = "data")]
-#[serde(rename_all = "snake_case")]
pub(crate) enum Message<'a> {
Begin(Begin<'a>),
End(End<'a>),
@@ -22,51 +15,145 @@ pub(crate) enum Message<'a> {
Context(Context<'a>),
}
-#[derive(Serialize)]
+impl<'a> serde::Serialize for Message<'a> {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("Message", 2)?;
+ match *self {
+ Message::Begin(ref msg) => {
+ state.serialize_field("type", &"begin")?;
+ state.serialize_field("data", msg)?;
+ }
+ Message::End(ref msg) => {
+ state.serialize_field("type", &"end")?;
+ state.serialize_field("data", msg)?;
+ }
+ Message::Match(ref msg) => {
+ state.serialize_field("type", &"match")?;
+ state.serialize_field("data", msg)?;
+ }
+ Message::Context(ref msg) => {
+ state.serialize_field("type", &"context")?;
+ state.serialize_field("data", msg)?;
+ }
+ }
+ state.end()
+ }
+}
+
pub(crate) struct Begin<'a> {
- #[serde(serialize_with = "ser_path")]
pub(crate) path: Option<&'a Path>,
}
-#[derive(Serialize)]
+impl<'a> serde::Serialize for Begin<'a> {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("Begin", 1)?;
+ state.serialize_field("path", &self.path.map(Data::from_path))?;
+ state.end()
+ }
+}
+
pub(crate) struct End<'a> {
- #[serde(serialize_with = "ser_path")]
pub(crate) path: Option<&'a Path>,
pub(crate) binary_offset: Option<u64>,
- pub(crate) stats: Stats,
+ pub(crate) stats: crate::stats::Stats,
+}
+
+impl<'a> serde::Serialize for End<'a> {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("End", 3)?;
+ state.serialize_field("path", &self.path.map(Data::from_path))?;
+ state.serialize_field("binary_offset", &self.binary_offset)?;
+ state.serialize_field("stats", &self.stats)?;
+ state.end()
+ }
}
-#[derive(Serialize)]
pub(crate) struct Match<'a> {
- #[serde(serialize_with = "ser_path")]
pub(crate) path: Option<&'a Path>,
- #[serde(serialize_with = "ser_bytes")]
pub(crate) lines: &'a [u8],
pub(crate) line_number: Option<u64>,
pub(crate) absolute_offset: u64,
pub(crate) submatches: &'a [SubMatch<'a>],
}
-#[derive(Serialize)]
+impl<'a> serde::Serialize for Match<'a> {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("Match", 5)?;
+ state.serialize_field("path", &self.path.map(Data::from_path))?;
+ state.serialize_field("lines", &Data::from_bytes(self.lines))?;
+ state.serialize_field("line_number", &self.line_number)?;
+ state.serialize_field("absolute_offset", &self.absolute_offset)?;
+ state.serialize_field("submatches", &self.submatches)?;
+ state.end()
+ }
+}
+
pub(crate) struct Context<'a> {
- #[serde(serialize_with = "ser_path")]
pub(crate) path: Option<&'a Path>,
- #[serde(serialize_with = "ser_bytes")]
pub(crate) lines: &'a [u8],
pub(crate) line_number: Option<u64>,
pub(crate) absolute_offset: u64,
pub(crate) submatches: &'a [SubMatch<'a>],
}
-#[derive(Serialize)]
+impl<'a> serde::Serialize for Context<'a> {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("Context", 5)?;
+ state.serialize_field("path", &self.path.map(Data::from_path))?;
+ state.serialize_field("lines", &Data::from_bytes(self.lines))?;
+ state.serialize_field("line_number", &self.line_number)?;
+ state.serialize_field("absolute_offset", &self.absolute_offset)?;
+ state.serialize_field("submatches", &self.submatches)?;
+ state.end()
+ }
+}
+
pub(crate) struct SubMatch<'a> {
- #[serde(rename = "match")]
- #[serde(serialize_with = "ser_bytes")]
pub(crate) m: &'a [u8],
pub(crate) start: usize,
pub(crate) end: usize,
}
+impl<'a> serde::Serialize for SubMatch<'a> {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("SubMatch", 3)?;
+ state.serialize_field("match", &Data::from_bytes(self.m))?;
+ state.serialize_field("start", &self.start)?;
+ state.serialize_field("end", &self.end)?;
+ state.end()
+ }
+}
+
/// Data represents things that look like strings, but may actually not be
/// valid UTF-8. To handle this, `Data` is serialized as an object with one
/// of two keys: `text` (for valid UTF-8) or `bytes` (for invalid UTF-8).
@@ -74,16 +161,10 @@ pub(crate) struct SubMatch<'a> {
/// The happy path is valid UTF-8, which streams right through as-is, since
/// it is natively supported by JSON. When invalid UTF-8 is found, then it is
/// represented as arbitrary bytes and base64 encoded.
-#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize)]
-#[serde(untagged)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
enum Data<'a> {
- Text {
- text: Cow<'a, str>,
- },
- Bytes {
- #[serde(serialize_with = "to_base64")]
- bytes: &'a [u8],
- },
+ Text { text: Cow<'a, str> },
+ Bytes { bytes: &'a [u8] },
}
impl<'a> Data<'a> {
@@ -115,29 +196,22 @@ impl<'a> Data<'a> {
}
}
-fn to_base64<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error>
-where
- T: AsRef<[u8]>,
- S: Serializer,
-{
- use base64::engine::{general_purpose::STANDARD, Engine};
- ser.serialize_str(&STANDARD.encode(&bytes))
-}
-
-fn ser_bytes<T, S>(bytes: T, ser: S) -> Result<S::Ok, S::Error>
-where
- T: AsRef<[u8]>,
- S: Serializer,
-{
- use serde::Serialize;
- Data::from_bytes(bytes.as_ref()).serialize(ser)
-}
-
-fn ser_path<P, S>(path: &Option<P>, ser: S) -> Result<S::Ok, S::Error>
-where
- P: AsRef<Path>,
- S: Serializer,
-{
- use serde::Serialize;
- path.as_ref().map(|p| Data::from_path(p.as_ref())).serialize(ser)
+impl<'a> serde::Serialize for Data<'a> {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("Data", 1)?;
+ match *self {
+ Data::Text { ref text } => state.serialize_field("text", text)?,
+ Data::Bytes { bytes } => {
+ use base64::engine::{general_purpose::STANDARD, Engine};
+ let encoded = STANDARD.encode(bytes);
+ state.serialize_field("bytes", &encoded)?;
+ }
+ }
+ state.end()
+ }
}
diff --git a/crates/printer/src/stats.rs b/crates/printer/src/stats.rs
index f1898c0b..555401b3 100644
--- a/crates/printer/src/stats.rs
+++ b/crates/printer/src/stats.rs
@@ -10,7 +10,6 @@ use crate::util::NiceDuration;
/// When statistics are reported by a printer, they correspond to all searches
/// executed with that printer.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
-#[cfg_attr(feature = "serde", derive(serde_derive::Serialize))]
pub struct Stats {
elapsed: NiceDuration,
searches: u64,
@@ -21,49 +20,6 @@ pub struct Stats {
matches: u64,
}
-impl Add for Stats {
- type Output = Stats;
-
- fn add(self, rhs: Stats) -> Stats {
- self + &rhs
- }
-}
-
-impl<'a> Add<&'a Stats> for Stats {
- type Output = Stats;
-
- fn add(self, rhs: &'a Stats) -> Stats {
- Stats {
- elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0),
- searches: self.searches + rhs.searches,
- searches_with_match: self.searches_with_match
- + rhs.searches_with_match,
- bytes_searched: self.bytes_searched + rhs.bytes_searched,
- bytes_printed: self.bytes_printed + rhs.bytes_printed,
- matched_lines: self.matched_lines + rhs.matched_lines,
- matches: self.matches + rhs.matches,
- }
- }
-}
-
-impl AddAssign for Stats {
- fn add_assign(&mut self, rhs: Stats) {
- *self += &rhs;
- }
-}
-
-impl<'a> AddAssign<&'a Stats> for Stats {
- fn add_assign(&mut self, rhs: &'a Stats) {
- self.elapsed.0 += rhs.elapsed.0;
- self.searches += rhs.searches;
- self.searches_with_match += rhs.searches_with_match;
- self.bytes_searched += rhs.bytes_searched;
- self.bytes_printed += rhs.bytes_printed;
- self.matched_lines += rhs.matched_lines;
- self.matches += rhs.matches;
- }
-}
-
impl Stats {
/// Return a new value for tracking aggregate statistics across searches.
///
@@ -147,3 +103,69 @@ impl Stats {
self.matches += n;
}
}
+
+impl Add for Stats {
+ type Output = Stats;
+
+ fn add(self, rhs: Stats) -> Stats {
+ self + &rhs
+ }
+}
+
+impl<'a> Add<&'a Stats> for Stats {
+ type Output = Stats;
+
+ fn add(self, rhs: &'a Stats) -> Stats {
+ Stats {
+ elapsed: NiceDuration(self.elapsed.0 + rhs.elapsed.0),
+ searches: self.searches + rhs.searches,
+ searches_with_match: self.searches_with_match
+ + rhs.searches_with_match,
+ bytes_searched: self.bytes_searched + rhs.bytes_searched,
+ bytes_printed: self.bytes_printed + rhs.bytes_printed,
+ matched_lines: self.matched_lines + rhs.matched_lines,
+ matches: self.matches + rhs.matches,
+ }
+ }
+}
+
+impl AddAssign for Stats {
+ fn add_assign(&mut self, rhs: Stats) {
+ *self += &rhs;
+ }
+}
+
+impl<'a> AddAssign<&'a Stats> for Stats {
+ fn add_assign(&mut self, rhs: &'a Stats) {
+ self.elapsed.0 += rhs.elapsed.0;
+ self.searches += rhs.searches;
+ self.searches_with_match += rhs.searches_with_match;
+ self.bytes_searched += rhs.bytes_searched;
+ self.bytes_printed += rhs.bytes_printed;
+ self.matched_lines += rhs.matched_lines;
+ self.matches += rhs.matches;
+ }
+}
+
+#[cfg(feature = "serde")]
+impl serde::Serialize for Stats {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ s: S,
+ ) -> Result<S::Ok, S::Error> {
+ use serde::ser::SerializeStruct;
+
+ let mut state = s.serialize_struct("Stats", 7)?;
+ state.serialize_field("elapsed", &self.elapsed)?;
+ state.serialize_field("searches", &self.searches)?;
+ state.serialize_field(
+ "searches_with_match",
+ &self.searches_with_match,
+ )?;
+ state.serialize_field("bytes_searched", &self.bytes_searched)?;
+ state.serialize_field("bytes_printed", &self.bytes_printed)?;
+ state.serialize_field("matched_lines", &self.matched_lines)?;
+ state.serialize_field("matches", &self.matches)?;
+ state.end()
+ }
+}
diff --git a/crates/printer/src/util.rs b/crates/printer/src/util.rs
index db19504c..04f9e129 100644
--- a/crates/printer/src/util.rs
+++ b/crates/printer/src/util.rs
@@ -8,9 +8,6 @@ use {
},
};
-#[cfg(feature = "serde")]
-use serde::{Serialize, Serializer};
-
use crate::{hyperlink::HyperlinkPath, MAX_LOOK_AHEAD};
/// A type for handling replacements while amortizing allocation.
@@ -385,11 +382,14 @@ impl NiceDuration {
}
#[cfg(feature = "serde")]
-impl Serialize for NiceDuration {
- fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
+impl serde::Serialize for NiceDuration {
+ fn serialize<S: serde::Serializer>(
+ &self,
+ ser: S,
+ ) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
- let mut state = ser.serialize_struct("Duration", 2)?;
+ let mut state = ser.serialize_struct("Duration", 3)?;
state.serialize_field("secs", &self.0.as_secs())?;
state.serialize_field("nanos", &self.0.subsec_nanos())?;
state.serialize_field("human", &format!("{}", self))?;