Skip to content

Commit

Permalink
feat: Add a function to get the exact size of a PublicFile and Privat…
Browse files Browse the repository at this point in the history
…eFile
  • Loading branch information
icidasset committed Feb 28, 2024
1 parent 8ac4021 commit d562cbb
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 41 deletions.
17 changes: 17 additions & 0 deletions wnfs-wasm/src/fs/private/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,23 @@ impl PrivateFile {
self.read_at(value!(0).into(), None, forest, store)
}

/// Gets the exact content size without fetching all content blocks.
#[wasm_bindgen(js_name = "getSize")]
pub fn get_size(&self, forest: &PrivateForest, store: BlockStore) -> JsResult<Promise> {
let file = Rc::clone(&self.0);
let store = ForeignBlockStore(store);
let forest = Rc::clone(&forest.0);

Ok(future_to_promise(async move {
let size = file
.get_size(&forest, &store)
.await
.map_err(error("Cannot determine file size"))?;

Ok(value!(size))
}))
}

/// Gets the metadata of this file.
pub fn metadata(&self) -> JsResult<JsValue> {
JsMetadata(self.0.get_metadata()).try_into()
Expand Down
16 changes: 16 additions & 0 deletions wnfs-wasm/src/fs/public/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ impl PublicFile {
self.read_at(value!(0).into(), None, store)
}

/// Gets the exact content size without fetching all content blocks.
#[wasm_bindgen(js_name = "getSize")]
pub fn get_size(&self, store: BlockStore) -> JsResult<Promise> {
let file = Rc::clone(&self.0);
let store = ForeignBlockStore(store);

Ok(future_to_promise(async move {
let size = file
.get_size(&store)
.await
.map_err(error("Cannot determine file size"))?;

Ok(value!(size))
}))
}

/// Gets the metadata of this file.
pub fn metadata(&self) -> JsResult<JsValue> {
JsMetadata(self.0.get_metadata()).try_into()
Expand Down
25 changes: 25 additions & 0 deletions wnfs-wasm/tests/private.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,31 @@ test.describe("PrivateFile", () => {
expect(new Uint8Array(Object.values(content))).toEqual(new Uint8Array([2, 3, 4]));
});

test("getSize returns the exact content size", async ({ page }) => {
const size = await page.evaluate(async () => {
const {
wnfs: { PrivateFile, PrivateForest },
mock: { MemoryBlockStore, Rng },
} = await window.setup();

const rng = new Rng();
const initialForest = new PrivateForest(rng);
const store = new MemoryBlockStore();
var [file, forest] = await PrivateFile.withContent(
initialForest.emptyName(),
new Date(),
new Uint8Array(2 * 1024 * 1024),
initialForest,
store,
rng,
);

return await file.getSize(forest, store);
});

expect(size).toEqual(2 * 1024 * 1024);
});

test("A PrivateDirectory has the correct metadata", async ({ page }) => {
const result = await page.evaluate(async () => {
const {
Expand Down
77 changes: 36 additions & 41 deletions wnfs-wasm/tests/public.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ test.describe("PublicDirectory", () => {
expect(result).toBeDefined();
});

test("lookupNode cannot fetch file not added to directory", async ({
page,
}) => {
test("lookupNode cannot fetch file not added to directory", async ({ page }) => {
const result = await page.evaluate(async () => {
const {
wnfs: { PublicDirectory },
Expand Down Expand Up @@ -65,18 +63,12 @@ test.describe("PublicDirectory", () => {
["pictures", "cats", "tabby.png"],
sampleCID,
time,
store
store,
);

let result0 = await rootDir.getNode(
["pictures", "cats", "tabby.png"],
store
);
let result0 = await rootDir.getNode(["pictures", "cats", "tabby.png"], store);

let result1 = await rootDir.getNode(
["pictures", "dogs", "bingo.png"],
store
);
let result1 = await rootDir.getNode(["pictures", "dogs", "bingo.png"], store);

return [result0, result1];
});
Expand All @@ -102,7 +94,7 @@ test.describe("PublicDirectory", () => {
["pictures", "cats", "tabby.png"],
sampleCID,
time,
store
store,
);

await rootDir.getNode(["pictures", "cats", "tabby.png"], store);
Expand Down Expand Up @@ -130,7 +122,7 @@ test.describe("PublicDirectory", () => {
["pictures", "cats", "tabby.png"],
sampleCID,
time,
store
store,
);

const result = await rootDir.ls(["pictures"], store);
Expand Down Expand Up @@ -158,14 +150,14 @@ test.describe("PublicDirectory", () => {
["pictures", "dogs", "billie.jpeg"],
sampleCID,
time,
store
store,
);

var { rootDir } = await rootDir.write(
["pictures", "cats", "tabby.png"],
sampleCID,
time,
store
store,
);

var { rootDir } = await rootDir.rm(["pictures", "cats"], store);
Expand All @@ -190,18 +182,13 @@ test.describe("PublicDirectory", () => {
const store = new MemoryBlockStore();
const root = new PublicDirectory(time);

var { rootDir } = await root.write(
["pictures", "cats", "luna.jpeg"],
sampleCID,
time,
store
);
var { rootDir } = await root.write(["pictures", "cats", "luna.jpeg"], sampleCID, time, store);

var { rootDir } = await rootDir.write(
["pictures", "cats", "tabby.png"],
sampleCID,
time,
store
store,
);

var { rootDir } = await rootDir.mkdir(["images"], time, store);
Expand All @@ -210,7 +197,7 @@ test.describe("PublicDirectory", () => {
["pictures", "cats"],
["images", "cats"],
time,
store
store,
);

const imagesContent = await rootDir.ls(["images"], store);
Expand All @@ -236,28 +223,18 @@ test.describe("PublicDirectory", () => {
const store = new MemoryBlockStore();
const root = new PublicDirectory(time);

var { rootDir } = await root.write(
["pictures", "cats", "luna.jpeg"],
sampleCID,
time,
store
);
var { rootDir } = await root.write(["pictures", "cats", "luna.jpeg"], sampleCID, time, store);

var { rootDir } = await rootDir.write(
["pictures", "cats", "tabby.png"],
sampleCID,
time,
store
store,
);

var { rootDir } = await rootDir.mkdir(["images"], time, store);

var { rootDir } = await rootDir.cp(
["pictures", "cats"],
["images", "cats"],
time,
store
);
var { rootDir } = await rootDir.cp(["pictures", "cats"], ["images", "cats"], time, store);

const imagesContent = await rootDir.ls(["images"], store);

Expand Down Expand Up @@ -314,10 +291,7 @@ test.describe("PublicDirectory", () => {
const readBack = await file2.getContent(store);
const partialRead = await file2.readAt(7, 5, store);

return [
new TextDecoder().decode(readBack),
new TextDecoder().decode(partialRead)
];
return [new TextDecoder().decode(readBack), new TextDecoder().decode(partialRead)];
});

expect(result[0]).toEqual("Hello, World!");
Expand Down Expand Up @@ -370,4 +344,25 @@ test.describe("PublicDirectory", () => {
expect(result).not.toBeUndefined();
expect(result).toEqual("bafkr4ihkr4ld3m4gqkjf4reryxsy2s5tkbxprqkow6fin2iiyvreuzzab4");
});

test("A PublicFile has a content size", async ({ page }) => {
const result = await page.evaluate(async () => {
const {
wnfs: { PublicFile },
mock: { MemoryBlockStore },
} = await window.setup();

const store = new MemoryBlockStore();
const time = new Date();
const file = new PublicFile(time);

const longString = "x".repeat(5 * 1024 * 1024);
const content = new TextEncoder().encode(longString);
const file2 = await file.setContent(time, content, store);

return await file2.getSize(store);
});

expect(result).toEqual(5 * 1024 * 1024);
});
});
66 changes: 66 additions & 0 deletions wnfs/src/private/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,55 @@ impl PrivateFile {
Ok(self.prepare_next_revision()?.get_metadata_mut())
}

/// Gets the exact content size without fetching all content blocks.
///
/// # Examples
///
/// ```
/// use anyhow::Result;
/// use chrono::Utc;
/// use rand_chacha::ChaCha12Rng;
/// use rand_core::SeedableRng;
/// use wnfs::{
/// private::{PrivateFile, forest::{hamt::HamtForest, traits::PrivateForest}},
/// common::{MemoryBlockStore, utils::get_random_bytes},
/// };
///
/// #[async_std::main]
/// async fn main() -> Result<()> {
/// let store = &MemoryBlockStore::new();
/// let rng = &mut ChaCha12Rng::from_entropy();
/// let forest = &mut HamtForest::new_rsa_2048_rc(rng);
///
/// let content = get_random_bytes::<324_568>(rng).to_vec();
/// let file = PrivateFile::with_content(
/// &forest.empty_name(),
/// Utc::now(),
/// content.clone(),
/// forest,
/// store,
/// rng,
/// )
/// .await?;
///
/// let mut size = file.get_size(forest, store).await?;
///
/// assert_eq!(content.len(), size);
///
/// Ok(())
/// }
/// ```
pub async fn get_size(
&self,
forest: &impl PrivateForest,
store: &impl BlockStore,
) -> Result<usize> {
match &self.content.content {
FileContent::Inline { data } => Ok(data.len()),
FileContent::External(forest_content) => forest_content.get_size(forest, store).await,
}
}

/// Gets the entire content of a file.
///
/// # Examples
Expand Down Expand Up @@ -1039,6 +1088,23 @@ impl PrivateForestContent {
self.block_count * self.block_content_size
}

/// Gets the exact size of the content.
pub async fn get_size(
&self,
forest: &impl PrivateForest,
store: &impl BlockStore,
) -> Result<usize> {
let size_without_last_block =
std::cmp::max(0, self.block_count - 1) * self.block_content_size;

let size_last_block = self
.read_at(size_without_last_block as u64, None, forest, store)
.await?
.len();

Ok(size_without_last_block + size_last_block)
}

/// Generates the labels for all of the content shard blocks.
pub(crate) fn generate_shard_labels<'a>(
key: &'a SnapshotKey,
Expand Down
38 changes: 38 additions & 0 deletions wnfs/src/public/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,44 @@ impl PublicFile {
}
}

/// Gets the exact content size without fetching all content blocks.
///
/// # Examples
///
/// ```
/// use anyhow::Result;
/// use rand_chacha::ChaCha12Rng;
/// use rand_core::SeedableRng;
/// use chrono::Utc;
/// use wnfs::{
/// public::PublicFile,
/// common::{MemoryBlockStore, utils::get_random_bytes},
/// };
///
/// #[async_std::main]
/// async fn main() -> Result<()> {
/// let store = &MemoryBlockStore::new();
/// let rng = &mut ChaCha12Rng::from_entropy();
/// let content = get_random_bytes::<324_568>(rng).to_vec();
/// let file = PublicFile::with_content(
/// Utc::now(),
/// content.clone(),
/// store,
/// )
/// .await?;
///
/// let mut size = file.get_size(store).await?;
///
/// assert_eq!(content.len(), size);
///
/// Ok(())
/// }
/// ```
pub async fn get_size(&self, store: &impl BlockStore) -> Result<usize> {
let value = self.userland.resolve_value(store).await?;
Ok(value.filesize().unwrap_or(0) as usize)
}

/// Gets the entire content of a file.
///
/// # Examples
Expand Down

0 comments on commit d562cbb

Please sign in to comment.