use crate::trie::error::{Result, TrieError};
use sha3::{Digest, Keccak256};
use std::{io::Read, path::Path};
pub(crate) const CHECKSUM_LEN: usize = 8;
pub(crate) const MAX_CACHE_FILE_BYTES: u64 = 512 * 1024 * 1024;
pub(crate) fn write_varint(buf: &mut Vec<u8>, mut n: usize) {
while n >= 0x80 {
buf.push(((n as u8) & 0x7F) | 0x80);
n >>= 7;
}
buf.push(n as u8);
}
pub(crate) fn read_varint(data: &[u8], cursor: &mut usize) -> Result<usize> {
let mut n: usize = 0;
let mut shift: u32 = 0;
loop {
if *cursor >= data.len() {
return Err(TrieError::InvalidState("varint unexpected EOF".into()));
}
let b = data[*cursor];
*cursor += 1;
if shift >= usize::BITS {
return Err(TrieError::InvalidState("varint overflow".into()));
}
let low = (b & 0x7F) as usize;
if low > (usize::MAX >> shift) {
return Err(TrieError::InvalidState("varint overflow".into()));
}
let val = low << shift;
n = n
.checked_add(val)
.ok_or_else(|| TrieError::InvalidState("varint overflow".into()))?;
if b & 0x80 == 0 {
if shift > 0 && low == 0 {
return Err(TrieError::InvalidState(
"non-canonical varint encoding".into(),
));
}
break;
}
shift += 7;
}
Ok(n)
}
pub(crate) fn write_bytes(buf: &mut Vec<u8>, bytes: &[u8]) {
write_varint(buf, bytes.len());
buf.extend_from_slice(bytes);
}
pub(crate) fn read_bytes(data: &[u8], cursor: &mut usize) -> Result<Vec<u8>> {
let len = read_varint(data, cursor)?;
let end = checked_end(*cursor, len, data.len(), "bytes")?;
let bytes = data[*cursor..end].to_vec();
*cursor = end;
Ok(bytes)
}
pub(crate) fn checked_end(
cursor: usize,
len: usize,
total: usize,
what: &str,
) -> Result<usize> {
cursor
.checked_add(len)
.filter(|end| *end <= total)
.ok_or_else(|| TrieError::InvalidState(format!("{what} unexpected EOF")))
}
pub(crate) fn validate_cache_file_size(path: &Path) -> Result<()> {
let len = std::fs::metadata(path).map_err(io_err)?.len();
if len > MAX_CACHE_FILE_BYTES {
return Err(TrieError::InvalidState(format!(
"cache file is {len} bytes, above the {MAX_CACHE_FILE_BYTES}-byte limit"
)));
}
Ok(())
}
pub(crate) fn read_cache_bytes(r: &mut impl Read) -> Result<Vec<u8>> {
let mut data = Vec::new();
r.take(MAX_CACHE_FILE_BYTES + 1)
.read_to_end(&mut data)
.map_err(io_err)?;
if data.len() as u64 > MAX_CACHE_FILE_BYTES {
return Err(TrieError::InvalidState(format!(
"cache stream exceeds the {MAX_CACHE_FILE_BYTES}-byte limit"
)));
}
Ok(data)
}
pub(crate) fn read_u8(data: &[u8], cursor: &mut usize) -> Result<u8> {
if *cursor >= data.len() {
return Err(TrieError::InvalidState("unexpected EOF".into()));
}
let v = data[*cursor];
*cursor += 1;
Ok(v)
}
pub(crate) fn io_err(e: std::io::Error) -> TrieError {
TrieError::InvalidState(format!("I/O error: {e}"))
}
pub(crate) fn compute_checksum(data: &[u8]) -> [u8; CHECKSUM_LEN] {
let hash = Keccak256::digest(data);
let mut out = [0u8; CHECKSUM_LEN];
out.copy_from_slice(&hash[..CHECKSUM_LEN]);
out
}