-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
153 lines (133 loc) · 4.1 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import fs from 'fs';
const LF = 10;
const CR = 13;
/**
* Combines two buffers
*
* @param {Object} [buffOne] First buffer object
* @param {Object} [buffTwo] Second buffer object
* @return {Object} Combined buffer object
*/
function _concat(buffOne?: Buffer, buffTwo?: Buffer): Buffer {
if (!buffOne && !buffTwo) {
throw new Error(
'when concatenating two buffers, at least one buffer but exist',
);
}
if (!buffOne) return buffTwo as Buffer;
if (!buffTwo) return buffOne as Buffer;
const newLength = buffOne.length + buffTwo.length;
return Buffer.concat([buffOne, buffTwo], newLength);
}
/**
* Generator based line reader
*
* @param {Number} [fd] The file descriptor
* @param {Number} [filesize] The size of the file in bytes
* @param {Number} [bufferSize] The size of the buffer in bytes
* @param {Number} [position] The position where to start reading the file in bytes
* @param {Number} [maxLineLength] The length to stop reading at if no line break has been reached
* @return {Object} The generator object
*/
function* readlines(
fd: number,
filesize: number,
{
bufferSize = 64 * 1024,
position = 0,
maxLineLength = Infinity,
}: {
bufferSize?: number;
position?: number;
maxLineLength?: number;
} = {},
): Generator<Buffer> {
if (maxLineLength <= 0) {
throw new Error('maxLineLength must be a positive number');
}
const originalMaxLineLength = maxLineLength;
let lineBuffer;
let lastWasCR;
while (position < filesize) {
let remaining = filesize - position;
if (remaining < bufferSize) bufferSize = remaining;
let readChunk = Buffer.alloc(bufferSize);
let bytesRead = fs.readSync(fd, readChunk, 0, bufferSize, position);
let curpos = 0;
let startpos = 0;
let curbyte;
while (curpos < bytesRead) {
curbyte = readChunk[curpos++];
// break after CR or LF, or after the maximum length, otherwise go on
if (
curbyte !== LF &&
curbyte !== CR &&
curpos - startpos <= maxLineLength
) {
lastWasCR = false;
continue;
}
// skip this LF if the last chunk ended with a CR
if (curbyte === LF && lastWasCR) {
startpos = curpos;
lastWasCR = false;
continue;
}
// create buffer from the last line break to the current position
const buffer = _concat(lineBuffer, readChunk.slice(startpos, curpos - 1));
// yield the buffer
const wantedLength: any = yield buffer;
// change the maximum length to the latest parameter of next()
maxLineLength = wantedLength > 0 ? wantedLength : originalMaxLineLength;
// skip one more character if a LF follows a CR, otherwise remember
// that the CR was standing alone
if (curbyte === CR) {
if (readChunk[curpos] === LF) {
++curpos;
lastWasCR = false;
} else {
lastWasCR = true;
}
} else {
lastWasCR = false;
}
// invalidate the yielded buffer and move after the line break
lineBuffer = undefined;
startpos = curpos;
}
position += bytesRead;
if (startpos < bytesRead) {
lineBuffer = _concat(lineBuffer, readChunk.slice(startpos, bytesRead));
}
}
// dump what ever is left in the buffer
if (Buffer.isBuffer(lineBuffer)) {
yield lineBuffer;
}
}
/**
* Generator based line reader with simplified API
*
* @param {string} [filename] Name of input file
* @param {Number} [bufferSize] The size of the buffer in bytes
* @param {Number} [maxLineLength] The length to stop reading at if no line break has been reached
* @return {Object} The generator object
*/
function* fromFile(
filename: string,
{
bufferSize,
maxLineLength,
}: {
bufferSize?: number;
maxLineLength?: number;
} = {},
) {
const fd = fs.openSync(filename, 'r');
const fileSize = fs.statSync(filename).size;
yield* readlines(fd, fileSize, { bufferSize, maxLineLength });
fs.closeSync(fd);
}
readlines.fromFile = fromFile;
readlines.default = readlines; // compatibility with 1.0.0
export = readlines; // does not wrap the default export in { default }