diff --git a/src/tool/subcommands/benchmark_cmd.rs b/src/tool/subcommands/benchmark_cmd.rs index 956d5781b477..27b9dd802b51 100644 --- a/src/tool/subcommands/benchmark_cmd.rs +++ b/src/tool/subcommands/benchmark_cmd.rs @@ -13,7 +13,6 @@ use crate::utils::db::car_stream::{CarBlock, CarStream}; use crate::utils::encoding::extract_cids; use crate::utils::stream::par_buffer; use anyhow::Context as _; -use cid::Cid; use clap::Subcommand; use futures::{StreamExt, TryStreamExt}; use fvm_ipld_encoding::DAG_CBOR; @@ -139,7 +138,7 @@ async fn benchmark_car_streaming_inspect(input: Vec) -> anyhow::Result< while let Some(block) = s.try_next().await? { let block: CarBlock = block; if block.cid.codec() == DAG_CBOR { - let cid_vec: Vec = extract_cids(&block.data)?; + let cid_vec = extract_cids(&block.data)?; let _ = cid_vec.iter().unique().count(); } sink.write_all(&block.data).await? diff --git a/src/utils/encoding/cid_de_cbor.rs b/src/utils/encoding/cid_de_cbor.rs index 88cae4a27ef2..accb21adc274 100644 --- a/src/utils/encoding/cid_de_cbor.rs +++ b/src/utils/encoding/cid_de_cbor.rs @@ -6,22 +6,25 @@ use cid::Cid; use cid::serde::BytesToCidVisitor; use serde::Deserializer; use serde::de::{self, DeserializeSeed, SeqAccess, Visitor}; +use smallvec::SmallVec; use std::fmt; +pub type SmallCidVec = SmallVec<[Cid; 8]>; + /// Find and extract all the [`Cid`] from a `DAG_CBOR`-encoded blob without employing any /// intermediate recursive structures, eliminating unnecessary allocations. -pub fn extract_cids(cbor_blob: &[u8]) -> anyhow::Result> { +pub fn extract_cids(cbor_blob: &[u8]) -> anyhow::Result { let CidVec(v) = from_slice_with_fallback(cbor_blob)?; Ok(v) } /// [`CidVec`] allows for efficient zero-copy de-serialization of `DAG_CBOR`-encoded nodes into a /// vector of [`Cid`]. -struct CidVec(Vec); +struct CidVec(SmallCidVec); /// [`FilterCids`] traverses an [`ipld_core::ipld::Ipld`] tree, appending [`Cid`]s (and only CIDs) to a single vector. /// This is much faster than constructing an [`ipld_core::ipld::Ipld`] tree and then performing the filtering. -struct FilterCids<'a>(&'a mut Vec); +struct FilterCids<'a>(&'a mut SmallCidVec); impl<'de> DeserializeSeed<'de> for FilterCids<'_> { type Value = (); @@ -30,7 +33,21 @@ impl<'de> DeserializeSeed<'de> for FilterCids<'_> { where D: Deserializer<'de>, { - struct FilterCidsVisitor<'a>(&'a mut Vec); + struct IgnoredSeed; + + impl<'de> DeserializeSeed<'de> for IgnoredSeed { + type Value = (); + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_ignored_any(de::IgnoredAny)?; + Ok(()) + } + } + + struct FilterCidsVisitor<'a>(&'a mut SmallCidVec); impl<'de> Visitor<'de> for FilterCidsVisitor<'_> { type Value = (); @@ -50,7 +67,7 @@ impl<'de> DeserializeSeed<'de> for FilterCids<'_> { // This is where recursion happens, we unravel each [`Ipld`] till we reach all // the nodes. while visitor - .next_entry_seed(FilterCids(&mut Vec::new()), FilterCids(self.0))? + .next_entry_seed(IgnoredSeed, FilterCids(self.0))? .is_some() { // Nothing to do; inner map values have been into `vec`. @@ -171,7 +188,7 @@ impl<'de> de::Deserialize<'de> for CidVec { where D: de::Deserializer<'de>, { - let mut vec = CidVec(Vec::new()); + let mut vec = CidVec(SmallCidVec::new()); FilterCids(&mut vec.0).deserialize(deserializer)?; Ok(vec) }