Skip to content

Commit a115575

Browse files
authored
Merge pull request #262 from effigies/feat/cache-json
feat: Cache JSON file loads
2 parents 5a12654 + 8275c63 commit a115575

File tree

4 files changed

+63
-6
lines changed

4 files changed

+63
-6
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!--
2+
A new scriv changelog fragment.
3+
4+
Uncomment the section that is right (remove the HTML comment wrapper).
5+
For top level release notes, leave all the headers commented out.
6+
-->
7+
8+
<!--
9+
### Added
10+
11+
- A bullet item for the Added category.
12+
13+
-->
14+
### Changed
15+
16+
- Parsed JSON files are now cached to reduce I/O and parsing costs.
17+
18+
<!--
19+
### Fixed
20+
21+
- A bullet item for the Fixed category.
22+
23+
-->
24+
<!--
25+
### Deprecated
26+
27+
- A bullet item for the Deprecated category.
28+
29+
-->
30+
<!--
31+
### Removed
32+
33+
- A bullet item for the Removed category.
34+
35+
-->
36+
<!--
37+
### Security
38+
39+
- A bullet item for the Security category.
40+
41+
-->
42+
<!--
43+
### Infrastructure
44+
45+
- A bullet item for the Infrastructure category.
46+
47+
-->

src/files/json.test.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { BIDSFile } from '../types/filetree.ts'
33
import type { FileIgnoreRules } from './ignore.ts'
44
import { testAsyncFileAccess } from './access.test.ts'
55

6+
import { pathsToTree } from '../files/filetree.ts'
67
import { loadJSON } from './json.ts'
78

89
function encodeUTF16(text: string) {
@@ -18,9 +19,12 @@ function encodeUTF16(text: string) {
1819
return buffer
1920
}
2021

21-
function makeFile(text: string, encoding: string): BIDSFile {
22+
function makeFile(path: string, text: string, encoding: string): BIDSFile {
2223
const bytes = encoding === 'utf-8' ? new TextEncoder().encode(text) : encodeUTF16(text)
24+
const file = pathsToTree([path]).get(path) as BIDSFile
2325
return {
26+
path: file.path,
27+
parent: file.parent,
2428
readBytes: async (size: number) => {
2529
return new Uint8Array(bytes)
2630
},
@@ -30,13 +34,13 @@ function makeFile(text: string, encoding: string): BIDSFile {
3034

3135
Deno.test('Test JSON error conditions', async (t) => {
3236
await t.step('Load valid JSON', async () => {
33-
const JSONfile = makeFile('{"a": 1}', 'utf-8')
37+
const JSONfile = makeFile('/valid-contents.json', '{"a": 1}', 'utf-8')
3438
const result = await loadJSON(JSONfile)
3539
assertObjectMatch(result, { a: 1 })
3640
})
3741

3842
await t.step('Error on BOM', async () => {
39-
const BOMfile = makeFile('\uFEFF{"a": 1}', 'utf-8')
43+
const BOMfile = makeFile('/BOM.json', '\uFEFF{"a": 1}', 'utf-8')
4044
let error: any = undefined
4145
await loadJSON(BOMfile).catch((e) => {
4246
error = e
@@ -45,7 +49,7 @@ Deno.test('Test JSON error conditions', async (t) => {
4549
})
4650

4751
await t.step('Error on UTF-16', async () => {
48-
const UTF16file = makeFile('{"a": 1}', 'utf-16')
52+
const UTF16file = makeFile('/utf16.json', '{"a": 1}', 'utf-16')
4953
let error: any = undefined
5054
await loadJSON(UTF16file).catch((e) => {
5155
error = e
@@ -54,13 +58,14 @@ Deno.test('Test JSON error conditions', async (t) => {
5458
})
5559

5660
await t.step('Error on invalid JSON syntax', async () => {
57-
const badJSON = makeFile('{"a": 1]', 'utf-8')
61+
const badJSON = makeFile('/bad-syntax.json', '{"a": 1]', 'utf-8')
5862
let error: any = undefined
5963
await loadJSON(badJSON).catch((e) => {
6064
error = e
6165
})
6266
assertObjectMatch(error, { code: 'JSON_INVALID' })
6367
})
68+
loadJSON.cache.clear()
6469
})
6570

6671
testAsyncFileAccess('Test file access errors for loadJSON', loadJSON)

src/files/json.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { filememoizeAsync } from '../utils/memoize.ts'
12
import type { BIDSFile } from '../types/filetree.ts'
23
import { readBytes } from './access.ts'
34

@@ -21,7 +22,7 @@ async function readJSONText(file: BIDSFile): Promise<string> {
2122
}
2223
}
2324

24-
export async function loadJSON(file: BIDSFile): Promise<Record<string, unknown>> {
25+
async function _loadJSON(file: BIDSFile): Promise<Record<string, unknown>> {
2526
const text = await readJSONText(file) // Raise encoding errors
2627
let parsedText
2728
try {
@@ -37,3 +38,5 @@ export async function loadJSON(file: BIDSFile): Promise<Record<string, unknown>>
3738
}
3839
return parsedText
3940
}
41+
42+
export const loadJSON = filememoizeAsync(_loadJSON)

src/schema/walk.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { BIDSContext, type BIDSContextDataset } from './context.ts'
22
import type { BIDSFile, FileTree } from '../types/filetree.ts'
33
import type { DatasetIssues } from '../issues/datasetIssues.ts'
44
import { loadTSV } from '../files/tsv.ts'
5+
import { loadJSON } from '../files/json.ts'
56

67
function* quickWalk(dir: FileTree): Generator<BIDSFile> {
78
for (const file of dir.files) {
@@ -49,6 +50,7 @@ async function* _walkFileTree(
4950
}
5051
}
5152
loadTSV.cache.delete(fileTree.path)
53+
loadJSON.cache.delete(fileTree.path)
5254
}
5355

5456
/** Walk all files in the dataset and construct a context for each one */

0 commit comments

Comments
 (0)