Skip to content

Commit 9dc329a

Browse files
committed
fix: Release reader lock on exceptions
1 parent cfe5120 commit 9dc329a

File tree

1 file changed

+38
-36
lines changed

1 file changed

+38
-36
lines changed

src/files/tsv.ts

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,45 +14,47 @@ async function _loadTSV(file: BIDSFile, maxRows: number = -1): Promise<ColumnsMa
1414
.pipeThrough(new TextLineStream())
1515
.getReader()
1616

17-
const headerRow = await reader.read()
18-
const headers = (headerRow.done || !headerRow.value) ? [] : headerRow.value.split('\t')
19-
20-
// Initialize columns in array for construction efficiency
21-
const initialCapacity = maxRows >= 0 ? maxRows : 1000
22-
const columns: string[][] = headers.map(() => new Array<string>(initialCapacity))
23-
24-
maxRows = maxRows >= 0 ? maxRows : Infinity
25-
let rowIndex = 0 // Keep in scope after loop
26-
for (; rowIndex < maxRows; rowIndex++) {
27-
const { done, value } = await reader.read()
28-
if (done) break
29-
30-
// Expect a newline at the end of the file, but otherwise error on empty lines
31-
if (!value) {
32-
const nextRow = await reader.read()
33-
if (nextRow.done) break
34-
throw { key: 'TSV_EMPTY_LINE', line: rowIndex + 2 }
35-
}
36-
37-
const values = value.split('\t')
38-
if (values.length !== headers.length) {
39-
throw { key: 'TSV_EQUAL_ROWS', line: rowIndex + 2 }
40-
}
41-
columns.forEach((column, columnIndex) => {
42-
// Double array size if we exceed the current capacity
43-
if (rowIndex >= column.length) {
44-
column.length = column.length * 2
17+
try {
18+
const headerRow = await reader.read()
19+
const headers = (headerRow.done || !headerRow.value) ? [] : headerRow.value.split('\t')
20+
21+
// Initialize columns in array for construction efficiency
22+
const initialCapacity = maxRows >= 0 ? maxRows : 1000
23+
const columns: string[][] = headers.map(() => new Array<string>(initialCapacity))
24+
25+
maxRows = maxRows >= 0 ? maxRows : Infinity
26+
let rowIndex = 0 // Keep in scope after loop
27+
for (; rowIndex < maxRows; rowIndex++) {
28+
const { done, value } = await reader.read()
29+
if (done) break
30+
31+
// Expect a newline at the end of the file, but otherwise error on empty lines
32+
if (!value) {
33+
const nextRow = await reader.read()
34+
if (nextRow.done) break
35+
throw { key: 'TSV_EMPTY_LINE', line: rowIndex + 2 }
4536
}
46-
column[rowIndex] = values[columnIndex]
47-
})
48-
}
4937

50-
reader.releaseLock()
38+
const values = value.split('\t')
39+
if (values.length !== headers.length) {
40+
throw { key: 'TSV_EQUAL_ROWS', line: rowIndex + 2 }
41+
}
42+
columns.forEach((column, columnIndex) => {
43+
// Double array size if we exceed the current capacity
44+
if (rowIndex >= column.length) {
45+
column.length = column.length * 2
46+
}
47+
column[rowIndex] = values[columnIndex]
48+
})
49+
}
5150

52-
// Construct map, truncating columns to number of rows read
53-
return new ColumnsMap(
54-
headers.map((header, index) => [header, columns[index].slice(0, rowIndex)]),
55-
)
51+
// Construct map, truncating columns to number of rows read
52+
return new ColumnsMap(
53+
headers.map((header, index) => [header, columns[index].slice(0, rowIndex)]),
54+
)
55+
} finally {
56+
reader.releaseLock()
57+
}
5658
}
5759

5860
export const loadTSV = filememoizeAsync(_loadTSV)

0 commit comments

Comments
 (0)