Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(fs): improve FileHandle.read performance #1950

Merged
merged 5 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/fs-perf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"fs": patch
"fs-js": patch
---

Improve performance of the `FileHandle.read` and `writeTextFile` APIs.
2 changes: 1 addition & 1 deletion plugins/fs/api-iife.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 36 additions & 6 deletions plugins/fs/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,25 @@
}
}

// https://mstn.github.io/2018/06/08/fixed-size-arrays-in-typescript/
type FixedSizeArray<T, N extends number> = ReadonlyArray<T> & {
length: N
}

// https://gist.github.com/zapthedingbat/38ebfbedd98396624e5b5f2ff462611d
/** Converts a big-endian eight byte array to number */
function fromBytes(buffer: FixedSizeArray<number, 8>): number {
const bytes = new Uint8ClampedArray(buffer)
const size = bytes.byteLength
let x = 0
for (let i = 0; i < size; i++) {
const byte = bytes[i]

Check warning on line 258 in plugins/fs/guest-js/index.ts

View workflow job for this annotation

GitHub Actions / eslint

Variable Assigned to Object Injection Sink
x *= 0x100
x += byte
}
return x
}

/**
* The Tauri abstraction for reading and writing files.
*
Expand Down Expand Up @@ -285,12 +304,20 @@
return 0
}

const [data, nread] = await invoke<[number[], number]>('plugin:fs|read', {
const data = await invoke<ArrayBuffer | number[]>('plugin:fs|read', {
rid: this.rid,
len: buffer.byteLength
})

buffer.set(data)
// Rust side will never return an empty array for this command and
// ensure there is at least 8 elements there.
//
// This is an optimization to include the number of read bytes (as bigendian bytes)
// at the end of returned array to avoid serialization overhead of separate values.
const nread = fromBytes(data.slice(-8) as FixedSizeArray<number, 8>)

const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data
buffer.set(bytes.slice(0, bytes.length - 8))

return nread === 0 ? null : nread
}
Expand Down Expand Up @@ -1041,10 +1068,13 @@
throw new TypeError('Must be a file URL.')
}

await invoke('plugin:fs|write_text_file', {
path: path instanceof URL ? path.toString() : path,
data,
options
const encoder = new TextEncoder()

await invoke('plugin:fs|write_text_file', encoder.encode(data), {
headers: {
path: path instanceof URL ? path.toString() : path,
options: JSON.stringify(options)
}
})
}

Expand Down
Loading
Loading