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

feat(fs): improve readTextFile and readTextFileLines performance #1962

Open
wants to merge 6 commits into
base: v2
Choose a base branch
from
Open
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 `readTextFile` and `readTextFileLines` 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.

31 changes: 26 additions & 5 deletions plugins/fs/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@
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
}
Expand Down Expand Up @@ -758,10 +758,14 @@
throw new TypeError('Must be a file URL.')
}

return await invoke<string>('plugin:fs|read_text_file', {
const arr = await invoke<ArrayBuffer | number[]>('plugin:fs|read_text_file', {
path: path instanceof URL ? path.toString() : path,
options
})

const bytes = arr instanceof ArrayBuffer ? arr : Uint8Array.from(arr)

return new TextDecoder().decode(bytes)
}

/**
Expand Down Expand Up @@ -792,6 +796,7 @@
return await Promise.resolve({
path: pathStr,
rid: null as number | null,

async next(): Promise<IteratorResult<string>> {
if (this.rid === null) {
this.rid = await invoke<number>('plugin:fs|read_text_file_lines', {
Expand All @@ -800,19 +805,35 @@
})
}

const [line, done] = await invoke<[string | null, boolean]>(
const arr = await invoke<ArrayBuffer | number[]>(
'plugin:fs|read_text_file_lines_next',
{ rid: this.rid }
)

// an iteration is over, reset rid for next iteration
if (done) this.rid = null
const bytes =
arr instanceof ArrayBuffer ? new Uint8Array(arr) : Uint8Array.from(arr)

// Rust side will never return an empty array for this command and
// ensure there is at least one elements there.
//
// This is an optimization to include wether we finished iteration or not (1 or 0)
// at the end of returned array to avoid serialization overhead of separate values.
const done = bytes[bytes.byteLength - 1] === 1

if (done) {
// a full iteration is over, reset rid for next iteration
this.rid = null
return { value: null, done }
}

const line = new TextDecoder().decode(bytes.slice(0, bytes.byteLength))

return {
value: done ? '' : line!,
value: line,
done
}
},

[Symbol.asyncIterator](): AsyncIterableIterator<string> {
return this
}
Expand Down
Loading
Loading