Skip to main content

blob_metadata/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod zerocopy_serialization;
6
7use fprint::TypeFingerprint;
8use fuchsia_merkle::{Hash, MerkleVerifier};
9use serde::{Deserialize, Serialize};
10
11#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, TypeFingerprint)]
12pub struct BlobMetadataUnversioned {
13    pub hashes: Vec<[u8; 32]>,
14    pub chunk_size: u64,
15    pub compressed_offsets: Vec<u64>,
16    pub uncompressed_size: u64,
17}
18
19pub type BlobMetadata = BlobMetadataV53;
20pub type BlobFormat = BlobFormatV53;
21pub type MerkleLeaves = Vec<[u8; 32]>;
22
23#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, TypeFingerprint)]
24pub struct BlobMetadataV53 {
25    #[serde(with = "zerocopy_serialization")]
26    pub merkle_leaves: MerkleLeaves,
27    pub format: BlobFormatV53,
28}
29
30impl BlobMetadataV53 {
31    pub fn empty() -> Self {
32        Self { merkle_leaves: Vec::new(), format: BlobFormatV53::Uncompressed }
33    }
34
35    pub fn is_empty(&self) -> bool {
36        *self == Self::empty()
37    }
38
39    pub fn into_merkle_verifier(self, root: Hash) -> Result<MerkleVerifier, anyhow::Error> {
40        let hashes = if self.merkle_leaves.is_empty() {
41            Box::new([root])
42        } else {
43            self.merkle_leaves.into_iter().map(Into::into).collect::<Box<[Hash]>>()
44        };
45        Ok(MerkleVerifier::new(root, hashes)?)
46    }
47}
48
49impl From<BlobMetadataUnversioned> for BlobMetadataV53 {
50    fn from(old: BlobMetadataUnversioned) -> Self {
51        if old.compressed_offsets.is_empty() {
52            Self { merkle_leaves: old.hashes, format: BlobFormat::Uncompressed }
53        } else {
54            Self {
55                merkle_leaves: old.hashes,
56                format: BlobFormat::ChunkedZstd {
57                    uncompressed_size: old.uncompressed_size,
58                    chunk_size: old.chunk_size,
59                    compressed_offsets: old.compressed_offsets,
60                },
61            }
62        }
63    }
64}
65
66#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, TypeFingerprint)]
67pub enum BlobFormatV53 {
68    Uncompressed,
69    ChunkedZstd { uncompressed_size: u64, chunk_size: u64, compressed_offsets: Vec<u64> },
70    ChunkedLz4 { uncompressed_size: u64, chunk_size: u64, compressed_offsets: Vec<u64> },
71}