use crate::error::Result;
use crate::field::FieldStore;
use crate::field::node::{NodeKind, SeedNode};
use crate::store::NodeId;
pub const MAX_SHARED_RESOURCE_BYTES: usize = 1 << 20;
pub const RESOURCE_BLOB_PROVENANCE: &str = "res:blob";
pub fn resource_blob_node(bytes: &[u8]) -> SeedNode {
let mut node = SeedNode::new(
NodeKind::ResourceBlob,
bytes.len() as u64,
bytes.to_vec(),
Vec::new(),
RESOURCE_BLOB_PROVENANCE,
);
node.limits.max_output_bytes = node.limits.max_output_bytes.max(bytes.len() as u64);
node
}
pub fn is_shareable_resource(bytes: &[u8]) -> bool {
bytes.len() <= MAX_SHARED_RESOURCE_BYTES && starts_with_signature(bytes)
}
fn starts_with_signature(b: &[u8]) -> bool {
const SIGNATURES: &[&[u8]] = &[
b"\x89PNG\r\n\x1a\n", b"\xFF\xD8\xFF", b"GIF87a", b"GIF89a", b"wOFF", b"wOF2", b"\x00\x01\x00\x00", b"OTTO", b"ttcf", ];
SIGNATURES.iter().any(|sig| b.starts_with(sig))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResourceRegistration {
pub id: NodeId,
pub len: u64,
pub preexisting: bool,
}
pub fn register_resource_blob(
store: &mut FieldStore,
bytes: &[u8],
) -> Result<ResourceRegistration> {
let node = resource_blob_node(bytes);
let id = node.content_id();
let preexisting = store.seeds().contains_node(&id)?;
store.seeds_mut().put_node(&node.encode_canonical())?;
Ok(ResourceRegistration {
id,
len: bytes.len() as u64,
preexisting,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn signatures_are_recognized_and_bounded() {
assert!(is_shareable_resource(b"\x89PNG\r\n\x1a\nrest"));
assert!(is_shareable_resource(b"\xFF\xD8\xFF\xE0jpeg"));
assert!(is_shareable_resource(b"GIF89a...."));
assert!(is_shareable_resource(b"wOFF...."));
assert!(!is_shareable_resource(b"<?xml version=\"1.0\"?>"));
assert!(!is_shareable_resource(b"plain text"));
assert!(!is_shareable_resource(b""));
}
#[test]
fn identical_bytes_produce_one_content_id() {
let bytes = b"\x89PNG\r\n\x1a\nshared-image-bytes";
let a = resource_blob_node(bytes);
let b = resource_blob_node(bytes);
assert_eq!(a.content_id(), b.content_id());
let different = resource_blob_node(b"\x89PNG\r\n\x1a\nother-image-bytes");
assert_ne!(a.content_id(), different.content_id());
}
#[test]
fn oversized_units_do_not_share() {
let mut big = vec![0u8; MAX_SHARED_RESOURCE_BYTES + 1];
big[0] = 0x89;
big[1] = b'P';
assert!(!is_shareable_resource(&big));
}
}