use std::borrow::Cow;
use super::UrnaView;
use crate::bytes::le_u64;
use crate::encoding::{decode_dedup_map, decode_payload, decode_payload_with_dict, expand_dedup};
use crate::error::UrnaError;
use crate::layout::{
CANONICAL_SECTIONS, SECTION_CHUNKS_CANONICAL, SECTION_DEDUP_MAP, SECTION_DICTIONARY,
SECTION_SEARCH_CONTRACT,
};
use crate::sections::{
SearchContract, decode_chunks_canonical, decode_search_contract, encode_chunks_canonical,
};
impl<'a> UrnaView<'a> {
pub fn decoded_section(&self, section_id: u32) -> crate::Result<Cow<'a, [u8]>> {
if section_id == SECTION_CHUNKS_CANONICAL {
return self.decoded_chunks_canonical();
}
self.decoded_section_plain(section_id)
}
fn decoded_section_plain(&self, section_id: u32) -> crate::Result<Cow<'a, [u8]>> {
let entry = self.entry(section_id)?;
let phys = self.get_section_data(section_id)?;
decode_payload(entry.encoding, phys).map_err(|e| Self::tag_err(section_id, e))
}
fn decoded_chunks_canonical(&self) -> crate::Result<Cow<'a, [u8]>> {
let entry = self.entry(SECTION_CHUNKS_CANONICAL)?;
let phys = self.get_section_data(SECTION_CHUNKS_CANONICAL)?;
let dict = self
.entry(SECTION_DICTIONARY)
.ok()
.and_then(|_| self.get_section_data(SECTION_DICTIONARY).ok());
let decoded = decode_payload_with_dict(entry.encoding, phys, dict)
.map_err(|e| Self::tag_err(SECTION_CHUNKS_CANONICAL, e))?;
if self.entry(SECTION_DEDUP_MAP).is_err() {
return Ok(decoded);
}
let map_phys = self.get_section_data(SECTION_DEDUP_MAP)?;
let back_refs =
decode_dedup_map(map_phys).map_err(|e| Self::tag_err(SECTION_DEDUP_MAP, e))?;
let unique = decode_chunks_canonical(&decoded, count_prefix(&decoded)?)?;
let full = expand_dedup(&unique, &back_refs)?;
Ok(Cow::Owned(encode_chunks_canonical(&full)?))
}
fn tag_err(section_id: u32, e: UrnaError) -> UrnaError {
match e {
UrnaError::UnsupportedSectionEncoding { encoding, .. } => {
UrnaError::UnsupportedSectionEncoding {
section_id,
encoding,
}
}
UrnaError::MalformedSectionPayload { reason, .. } => {
UrnaError::MalformedSectionPayload { section_id, reason }
}
other => other,
}
}
pub fn search_contract(&self) -> crate::Result<SearchContract> {
let bytes = self.decoded_section(SECTION_SEARCH_CONTRACT)?;
decode_search_contract(&bytes)
}
pub fn file_hash_hex(&self) -> String {
use sha2::{Digest, Sha256};
let h = Sha256::digest(self.data);
format!("sha256:{}", hex::encode(h))
}
pub fn content_hash_hex(&self) -> crate::Result<String> {
use sha2::{Digest, Sha256};
let mut h = Sha256::new();
for (id, name) in CANONICAL_SECTIONS {
let bytes = self.decoded_section(*id)?;
h.update((name.len() as u32).to_le_bytes());
h.update(name.as_bytes());
h.update((bytes.len() as u64).to_le_bytes());
h.update(bytes.as_ref());
}
Ok(format!("sha256:{}", hex::encode(h.finalize())))
}
}
fn count_prefix(payload: &[u8]) -> crate::Result<usize> {
if payload.len() < 12 {
return Err(UrnaError::MalformedSectionPayload {
section_id: SECTION_CHUNKS_CANONICAL,
reason: "chunks_canonical: truncated count prefix".into(),
});
}
Ok(le_u64(&payload[4..12])? as usize)
}