Skip to content
Merged
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 @@ -266,7 +266,7 @@
const size = bytes.byteLength
let x = 0
for (let i = 0; i < size; i++) {
const byte = bytes[i]

Check warning on line 269 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 @@ -769,10 +769,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 @@ -803,6 +807,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 @@ -811,19 +816,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