use anyhow::Result;
use bao_tree::blake3;
use bytes::Bytes;
use derive_more::{Debug, Display, From, Into};
use postcard::experimental::max_size::MaxSize;
use serde::{
de::{self, SeqAccess},
ser::SerializeTuple,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{borrow::Borrow, fmt, result, str::FromStr, sync::Arc, time::SystemTime};
use thiserror::Error;
pub mod io;
pub mod progress;
pub mod runtime;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default, Debug)]
pub enum BlobFormat {
#[default]
Raw,
HashSeq,
}
impl From<BlobFormat> for u64 {
fn from(value: BlobFormat) -> Self {
match value {
BlobFormat::Raw => 0,
BlobFormat::HashSeq => 1,
}
}
}
impl BlobFormat {
pub const fn is_raw(&self) -> bool {
matches!(self, BlobFormat::Raw)
}
pub const fn is_hash_seq(&self) -> bool {
matches!(self, BlobFormat::HashSeq)
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, From, Into)]
pub struct Tag(pub Bytes);
impl Borrow<[u8]> for Tag {
fn borrow(&self) -> &[u8] {
self.0.as_ref()
}
}
impl From<String> for Tag {
fn from(value: String) -> Self {
Self(Bytes::from(value))
}
}
impl From<&str> for Tag {
fn from(value: &str) -> Self {
Self(Bytes::from(value.to_owned()))
}
}
impl Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let bytes = self.0.as_ref();
match std::str::from_utf8(bytes) {
Ok(s) => write!(f, "\"{}\"", s),
Err(_) => write!(f, "{}", hex::encode(bytes)),
}
}
}
impl Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Tag").field(&DD(self)).finish()
}
}
impl Tag {
pub fn auto(time: SystemTime, exists: impl Fn(&[u8]) -> bool) -> Self {
let now = chrono::DateTime::<chrono::Utc>::from(time);
let mut i = 0;
loop {
let mut text = format!("auto-{}", now.format("%Y-%m-%dT%H:%M:%S%.3fZ"));
if i != 0 {
text.push_str(&format!("-{}", i));
}
if !exists(text.as_bytes()) {
return Self::from(text);
}
i += 1;
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct HashAndFormat {
pub hash: Hash,
pub format: BlobFormat,
}
impl HashAndFormat {
pub fn raw(hash: Hash) -> Self {
Self {
hash,
format: BlobFormat::Raw,
}
}
pub fn hash_seq(hash: Hash) -> Self {
Self {
hash,
format: BlobFormat::HashSeq,
}
}
}
impl Display for HashAndFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut slice = [0u8; 65];
hex::encode_to_slice(self.hash.as_bytes(), &mut slice[1..]).unwrap();
match self.format {
BlobFormat::Raw => {
write!(f, "{}", std::str::from_utf8(&slice[1..]).unwrap())
}
BlobFormat::HashSeq => {
slice[0] = b's';
write!(f, "{}", std::str::from_utf8(&slice).unwrap())
}
}
}
}
impl FromStr for HashAndFormat {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.as_bytes();
let mut hash = [0u8; 32];
match s.len() {
64 => {
hex::decode_to_slice(s, &mut hash)?;
Ok(Self::raw(hash.into()))
}
65 if s[0].to_ascii_lowercase() == b's' => {
hex::decode_to_slice(&s[1..], &mut hash)?;
Ok(Self::hash_seq(hash.into()))
}
_ => anyhow::bail!("invalid hash and format"),
}
}
}
#[derive(PartialEq, Eq, Copy, Clone, Hash)]
pub struct Hash(blake3::Hash);
impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Hash").field(&DD(self.to_hex())).finish()
}
}
struct DD<T: fmt::Display>(T);
impl<T: fmt::Display> fmt::Debug for DD<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl Hash {
pub const EMPTY: Hash = Hash::from_bytes([
175, 19, 73, 185, 245, 249, 161, 166, 160, 64, 77, 234, 54, 220, 201, 73, 155, 203, 37,
201, 173, 193, 18, 183, 204, 154, 147, 202, 228, 31, 50, 98,
]);
pub fn new(buf: impl AsRef<[u8]>) -> Self {
let val = blake3::hash(buf.as_ref());
Hash(val)
}
pub fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes()
}
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self(blake3::Hash::from_bytes(bytes))
}
pub fn as_cid_bytes(&self) -> [u8; 36] {
let hash = self.0.as_bytes();
let mut res = [0u8; 36];
res[0..4].copy_from_slice(&CID_PREFIX);
res[4..36].copy_from_slice(hash);
res
}
pub fn from_cid_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
anyhow::ensure!(
bytes.len() == 36,
"invalid cid length, expected 36, got {}",
bytes.len()
);
anyhow::ensure!(bytes[0..4] == CID_PREFIX, "invalid cid prefix");
let mut hash = [0u8; 32];
hash.copy_from_slice(&bytes[4..36]);
Ok(Self::from(hash))
}
pub fn to_hex(&self) -> String {
self.0.to_hex().to_string()
}
}
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl From<Hash> for blake3::Hash {
fn from(value: Hash) -> Self {
value.0
}
}
impl From<blake3::Hash> for Hash {
fn from(value: blake3::Hash) -> Self {
Hash(value)
}
}
impl From<[u8; 32]> for Hash {
fn from(value: [u8; 32]) -> Self {
Hash(blake3::Hash::from(value))
}
}
impl From<Hash> for [u8; 32] {
fn from(value: Hash) -> Self {
*value.as_bytes()
}
}
impl From<&[u8; 32]> for Hash {
fn from(value: &[u8; 32]) -> Self {
Hash(blake3::Hash::from(*value))
}
}
impl PartialOrd for Hash {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.0.as_bytes().cmp(other.0.as_bytes()))
}
}
impl Ord for Hash {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.as_bytes().cmp(other.0.as_bytes())
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = [b'b'; 59];
data_encoding::BASE32_NOPAD.encode_mut(&self.as_cid_bytes(), &mut res[1..]);
let t = std::str::from_utf8_mut(res.as_mut()).unwrap();
t.make_ascii_lowercase();
f.write_str(t)
}
}
impl FromStr for Hash {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let sb = s.as_bytes();
if sb.len() == 64 {
let mut bytes = [0u8; 32];
if hex::decode_to_slice(sb, &mut bytes).is_ok() {
return Ok(Self::from(bytes));
}
}
if sb.len() == 59 && sb[0] == b'b' {
let mut t = [0u8; 58];
t.copy_from_slice(&sb[1..]);
std::str::from_utf8_mut(t.as_mut())
.unwrap()
.make_ascii_uppercase();
let mut res = [0u8; 36];
data_encoding::BASE32_NOPAD
.decode_mut(&t, &mut res)
.map_err(|_e| anyhow::anyhow!("invalid base32"))?;
Self::from_cid_bytes(&res)
} else {
let (_base, bytes) = multibase::decode(s)?;
Self::from_cid_bytes(bytes.as_ref())
}
}
}
impl Serialize for Hash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_tuple(32)?;
for item in self.0.as_bytes() {
s.serialize_element(item)?;
}
s.end()
}
}
impl<'de> Deserialize<'de> for Hash {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_tuple(32, HashVisitor)
}
}
struct HashVisitor;
impl<'de> de::Visitor<'de> for HashVisitor {
type Value = Hash;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "an array of 32 bytes containing hash data")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut arr = [0u8; 32];
let mut i = 0;
while let Some(val) = seq.next_element()? {
arr[i] = val;
i += 1;
if i > 32 {
return Err(de::Error::invalid_length(i, &self));
}
}
Ok(Hash::from(arr))
}
}
impl MaxSize for Hash {
const POSTCARD_MAX_SIZE: usize = 32;
}
pub trait LivenessTracker: std::fmt::Debug + Send + Sync + 'static {
fn on_clone(&self, inner: &HashAndFormat);
fn on_drop(&self, inner: &HashAndFormat);
}
#[derive(Debug)]
pub struct TempTag {
inner: HashAndFormat,
liveness: Option<Arc<dyn LivenessTracker>>,
}
impl TempTag {
pub fn new(inner: HashAndFormat, liveness: Option<Arc<dyn LivenessTracker>>) -> Self {
if let Some(liveness) = liveness.as_ref() {
liveness.on_clone(&inner);
}
Self { inner, liveness }
}
pub fn inner(&self) -> &HashAndFormat {
&self.inner
}
pub fn hash(&self) -> &Hash {
&self.inner.hash
}
pub fn format(&self) -> BlobFormat {
self.inner.format
}
pub fn leak(mut self) {
self.liveness = None;
}
}
impl Clone for TempTag {
fn clone(&self) -> Self {
Self::new(self.inner, self.liveness.clone())
}
}
impl Drop for TempTag {
fn drop(&mut self) {
if let Some(liveness) = self.liveness.as_ref() {
liveness.on_drop(&self.inner);
}
}
}
const CID_PREFIX: [u8; 4] = [
0x01, 0x55, 0x1e, 0x20, ];
#[derive(Serialize, Deserialize, Debug, Error)]
pub struct RpcError(serde_error::Error);
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl From<anyhow::Error> for RpcError {
fn from(e: anyhow::Error) -> Self {
RpcError(serde_error::Error::new(&*e))
}
}
impl From<std::io::Error> for RpcError {
fn from(e: std::io::Error) -> Self {
RpcError(serde_error::Error::new(&e))
}
}
#[allow(dead_code)]
pub type RpcResult<T> = result::Result<T, RpcError>;
#[derive(Debug)]
pub(crate) struct NonSend {
_marker: std::marker::PhantomData<std::rc::Rc<()>>,
}
impl NonSend {
#[allow(dead_code)]
pub const fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use iroh_test::{assert_eq_hex, hexdump::parse_hexdump};
use super::*;
use serde_test::{assert_tokens, Token};
#[test]
fn test_display_parse_roundtrip() {
for i in 0..100 {
let hash: Hash = blake3::hash(&[i]).into();
let text = hash.to_string();
let hash1 = text.parse::<Hash>().unwrap();
assert_eq!(hash, hash1);
let text = hash.to_hex();
let hash1 = Hash::from_str(&text).unwrap();
assert_eq!(hash, hash1);
}
}
#[test]
fn test_hash() {
let data = b"hello world";
let hash = Hash::new(data);
let encoded = hash.to_string();
assert_eq!(encoded.parse::<Hash>().unwrap(), hash);
}
#[test]
fn test_empty_hash() {
let hash = Hash::new(b"");
assert_eq!(hash, Hash::EMPTY);
}
#[test]
fn hash_wire_format() {
let hash = Hash::from([0xab; 32]);
let serialized = postcard::to_stdvec(&hash).unwrap();
let expected = parse_hexdump(r"
ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab # hash
").unwrap();
assert_eq_hex!(serialized, expected);
}
#[test]
fn test_hash_serde() {
let hash = Hash::new("hello");
let mut tokens = Vec::new();
tokens.push(Token::Tuple { len: 32 });
for byte in hash.as_bytes() {
tokens.push(Token::U8(*byte));
}
tokens.push(Token::TupleEnd);
assert_eq!(tokens.len(), 34);
assert_tokens(&hash, &tokens);
}
#[test]
fn test_hash_postcard() {
let hash = Hash::new("hello");
let ser = postcard::to_stdvec(&hash).unwrap();
let de = postcard::from_bytes(&ser).unwrap();
assert_eq!(hash, de);
assert_eq!(ser.len(), 32);
}
#[test]
fn test_hash_and_format_parse() {
let hash = Hash::new("hello");
let expected = HashAndFormat::raw(hash);
let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
assert_eq!(expected, actual);
let expected = HashAndFormat::hash_seq(hash);
let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
assert_eq!(expected, actual);
}
}