Skip to content

Commit

Permalink
perf(fs): improve FileHandle.read performance (#1950)
Browse files Browse the repository at this point in the history
* perf(fs): improve `FileHandle.read` performance

* handle different target pointer width

* improve `writeTextFile` performance

* revert packageManager field

* change file

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
  • Loading branch information
amrbashir and lucasfernog authored Oct 20, 2024
1 parent 2302c2d commit ae80245
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 57 deletions.
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 @@ function parseFileInfo(r: UnparsedFileInfo): FileInfo {
}
}

// 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 @@ class FileHandle extends Resource {
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 @@ async function writeTextFile(
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

0 comments on commit ae80245

Please sign in to comment.