Skip to content

Commit

Permalink
perf: don't prettify json when not necessary (#24)
Browse files Browse the repository at this point in the history
* perf: don't prettify json when not necessary

* update async
  • Loading branch information
DaniPopes authored Nov 14, 2023
1 parent 0fc9af7 commit b4a21cd
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 40 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ tempfile = { version = "3.8.0", optional = true }
fs_extra = { version = "1.3.0", optional = true }
rand = { version = "0.8", optional = true }
futures-util = { version = "0.3.28", optional = true }
memmap2 = "0.9.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
home = "0.5.5"
Expand Down
4 changes: 1 addition & 3 deletions src/artifact_output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ impl<T: Serialize> ArtifactFile<T> {
pub fn write(&self) -> Result<()> {
trace!("writing artifact file {:?} {}", self.file, self.version);
utils::create_parent_dir_all(&self.file)?;
fs::write(&self.file, serde_json::to_vec_pretty(&self.artifact)?)
.map_err(|err| SolcError::io(err, &self.file))?;
Ok(())
utils::write_json_file(&self.artifact, &self.file, 64 * 1024)
}
}

Expand Down
32 changes: 20 additions & 12 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ use std::{
btree_map::{BTreeMap, Entry},
hash_map, BTreeSet, HashMap, HashSet,
},
fs::{self},
io::Write,
fs,
path::{Path, PathBuf},
time::{Duration, UNIX_EPOCH},
};
Expand Down Expand Up @@ -133,16 +132,13 @@ impl SolFilesCache {
/// Write the cache as json file to the given path
pub fn write(&self, path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
utils::create_parent_dir_all(path)?;
let file = fs::File::create(path).map_err(|err| SolcError::io(err, path))?;
tracing::trace!(
"writing cache with {} entries to json file: \"{}\"",
self.len(),
path.display()
);
let mut writer = std::io::BufWriter::with_capacity(1024 * 256, file);
serde_json::to_writer_pretty(&mut writer, self)?;
writer.flush().map_err(|e| SolcError::io(e, path))?;
utils::create_parent_dir_all(path)?;
utils::write_json_file(self, path, 128 * 1024)?;
tracing::trace!("cache file located: \"{}\"", path.display());
Ok(())
}
Expand Down Expand Up @@ -370,17 +366,29 @@ impl SolFilesCache {
#[cfg(feature = "async")]
impl SolFilesCache {
pub async fn async_read(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
let content =
tokio::fs::read_to_string(path).await.map_err(|err| SolcError::io(err, path))?;
Ok(serde_json::from_str(&content)?)
let path = path.as_ref().to_owned();
Self::asyncify(move || Self::read(path)).await
}

pub async fn async_write(&self, path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
let content = serde_json::to_vec_pretty(self)?;
let content = serde_json::to_vec(self)?;
tokio::fs::write(path, content).await.map_err(|err| SolcError::io(err, path))
}

async fn asyncify<F, T>(f: F) -> Result<T>
where
F: FnOnce() -> Result<T> + Send + 'static,
T: Send + 'static,
{
match tokio::task::spawn_blocking(f).await {
Ok(res) => res,
Err(_) => Err(SolcError::io(
std::io::Error::new(std::io::ErrorKind::Other, "background task failed"),
"",
)),
}
}
}

impl Default for SolFilesCache {
Expand Down
20 changes: 10 additions & 10 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ pub type Result<T> = std::result::Result<T, SolcError>;
#[derive(Debug, Error)]
pub enum SolcError {
/// Errors related to the Solc executable itself.
#[error("Solc exited with {0}\n{1}")]
#[error("solc exited with {0}\n{1}")]
SolcError(std::process::ExitStatus, String),
#[error("Missing pragma from solidity file")]
#[error("missing pragma from Solidity file")]
PragmaNotFound,
#[error("Could not find solc version locally or upstream")]
#[error("could not find Solc version locally or upstream")]
VersionNotFound,
#[error("Checksum mismatch for {file}: expected {expected} found {detected} for {version}")]
#[error("checksum mismatch for {file}: expected {expected} found {detected} for {version}")]
ChecksumMismatch { version: Version, expected: String, detected: String, file: PathBuf },
#[error("Checksum not found for {version}")]
#[error("checksum not found for {version}")]
ChecksumNotFound { version: Version },
#[error(transparent)]
SemverError(#[from] semver::Error),
Expand All @@ -29,12 +29,12 @@ pub enum SolcError {
/// Filesystem IO error
#[error(transparent)]
Io(#[from] SolcIoError),
#[error("File could not be resolved due to broken symlink: {0}.")]
#[error("file could not be resolved due to broken symlink: {0}")]
ResolveBadSymlink(SolcIoError),
/// Failed to resolve a file
#[error("Failed to resolve file: {0}.\n Check configured remappings.")]
#[error("failed to resolve file: {0}; check configured remappings")]
Resolve(SolcIoError),
#[error("File cannot be resolved due to mismatch of file name case: {error}.\n Found existing file: {existing_file:?}\n Please check the case of the import.")]
#[error("file cannot be resolved due to mismatch of file name case: {error}.\nFound existing file: {existing_file:?}\nPlease check the case of the import.")]
ResolveCaseSensitiveFileName { error: SolcIoError, existing_file: PathBuf },
#[error(
r#"{0}.
Expand All @@ -45,15 +45,15 @@ pub enum SolcError {
#[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
#[error(transparent)]
SvmError(#[from] svm::SolcVmError),
#[error("No contracts found at \"{0}\"")]
#[error("no contracts found at \"{0}\"")]
NoContracts(String),
#[error(transparent)]
PatternError(#[from] glob::PatternError),
/// General purpose message.
#[error("{0}")]
Message(String),

#[error("No artifact found for `{}:{}`", .0.display(), .1)]
#[error("no artifact found for `{}:{}`", .0.display(), .1)]
ArtifactNotFound(PathBuf, String),

#[cfg(feature = "project-util")]
Expand Down
47 changes: 32 additions & 15 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
//! Utility functions
use crate::{error::SolcError, SolcIoError};
use cfg_if::cfg_if;
use once_cell::sync::Lazy;
use regex::{Match, Regex};
use semver::Version;
use serde::{de::DeserializeOwned, Serialize};
use std::{
collections::HashSet,
fs,
io::Write,
ops::Range,
path::{Component, Path, PathBuf},
};

use crate::{error::SolcError, SolcIoError};
use once_cell::sync::Lazy;
use regex::{Match, Regex};
use semver::Version;
use serde::de::DeserializeOwned;
use tiny_keccak::{Hasher, Keccak};
use walkdir::WalkDir;

Expand Down Expand Up @@ -441,25 +442,41 @@ impl RuntimeOrHandle {
}
}

/// Creates a new named tempdir
/// Creates a new named tempdir.
#[cfg(any(test, feature = "project-util"))]
pub(crate) fn tempdir(name: &str) -> Result<tempfile::TempDir, SolcIoError> {
tempfile::Builder::new().prefix(name).tempdir().map_err(|err| SolcIoError::new(err, name))
}

/// Reads the json file and deserialize it into the provided type
/// Reads the json file and deserialize it into the provided type.
pub fn read_json_file<T: DeserializeOwned>(path: impl AsRef<Path>) -> Result<T, SolcError> {
let path = path.as_ref();
let contents = std::fs::read_to_string(path).map_err(|err| SolcError::io(err, path))?;
serde_json::from_str(&contents).map_err(Into::into)
// See: https://github.com/serde-rs/json/issues/160
let file = fs::File::open(path).map_err(|err| SolcError::io(err, path))?;
let bytes = unsafe { memmap2::Mmap::map(&file).map_err(|err| SolcError::io(err, path))? };
serde_json::from_slice(&bytes).map_err(Into::into)
}

/// Writes serializes the provided value to JSON and writes it to a file.
pub fn write_json_file<T: Serialize>(
value: &T,
path: impl AsRef<Path>,
capacity: usize,
) -> Result<(), SolcError> {
let path = path.as_ref();
let file = fs::File::create(path).map_err(|err| SolcError::io(err, path))?;
let mut writer = std::io::BufWriter::with_capacity(capacity, file);
serde_json::to_writer(&mut writer, value)?;
writer.flush().map_err(|e| SolcError::io(e, path))
}

/// Creates the parent directory of the `file` and all its ancestors if it does not exist
/// See [`std::fs::create_dir_all()`]
/// Creates the parent directory of the `file` and all its ancestors if it does not exist.
///
/// See [`fs::create_dir_all()`].
pub fn create_parent_dir_all(file: impl AsRef<Path>) -> Result<(), SolcError> {
let file = file.as_ref();
if let Some(parent) = file.parent() {
std::fs::create_dir_all(parent).map_err(|err| {
fs::create_dir_all(parent).map_err(|err| {
SolcError::msg(format!(
"Failed to create artifact parent folder \"{}\": {}",
parent.display(),
Expand Down Expand Up @@ -488,7 +505,7 @@ mod tests {
create_dir_all(&path).unwrap();
let existing = path.join("Test.sol");
let non_existing = path.join("test.sol");
std::fs::write(&existing, b"").unwrap();
fs::write(&existing, b"").unwrap();

#[cfg(target_os = "linux")]
assert!(!non_existing.exists());
Expand All @@ -505,7 +522,7 @@ mod tests {
create_dir_all(&path).unwrap();
let existing = path.join("Test.sol");
let non_existing = path.join("test.sol");
std::fs::write(
fs::write(
existing,
"
pragma solidity ^0.8.10;
Expand Down

0 comments on commit b4a21cd

Please sign in to comment.