Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: don't prettify json when not necessary #24

Merged
merged 2 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))? };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs safety

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SAFETY: not safe, but whatever

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough :D

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