Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd1edb3f

Browse files
committed
feat: 🎸 improve Node buffer allocation strategy
1 parent95d6f09 commitd1edb3f

File tree

2 files changed

+79
-118
lines changed

2 files changed

+79
-118
lines changed

‎src/core/Node.ts‎

Lines changed: 62 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type NodeEvent = NodeEventModify | NodeEventDelete;
1212
const{S_IFMT,S_IFDIR,S_IFREG,S_IFLNK,S_IFCHR}=constants;
1313
constgetuid=():number=>process.getuid?.()??0;
1414
constgetgid=():number=>process.getgid?.()??0;
15+
constEMPTY_BUFFER=bufferAllocUnsafe(0);
1516

1617
/**
1718
* Node in a file system (like i-node, v-node).
@@ -30,10 +31,14 @@ export class Node {
3031
private_mtime=newDate();
3132
private_ctime=newDate();
3233

33-
// data: string = '';
34-
buf:Buffer;
35-
privatebufCapacity:number=0;// Total allocated capacity
36-
privatebufUsed:number=0;// Actually used bytes
34+
buf:Buffer=EMPTY_BUFFER;
35+
36+
/** Total allocated memory capacity for this node. */
37+
privatecapacity:number=0;
38+
39+
/** Actually used bytes to store content. */
40+
privatesize:number=0;
41+
3742
rdev:number=0;
3843

3944
mode:number;// S_IFDIR, S_IFREG, etc..
@@ -116,28 +121,30 @@ export class Node {
116121
}
117122

118123
setString(str:string){
119-
// this.setBuffer(bufferFrom(str, 'utf8'));
120-
this.buf=bufferFrom(str,'utf8');
121-
this.bufCapacity=this.buf.length;
122-
this.bufUsed=this.buf.length;
123-
this.touch();
124+
this._setBuf(bufferFrom(str,'utf8'));
124125
}
125126

126127
getBuffer():Buffer{
127128
this.atime=newDate();
128129
if(!this.buf)this.buf=bufferAllocUnsafe(0);
129-
returnbufferFrom(this.buf.subarray(0,this.bufUsed));// Return a copy of used portion.
130+
returnbufferFrom(this.buf.subarray(0,this.size));// Return a copy of used portion.
130131
}
131132

132133
setBuffer(buf:Buffer){
133-
this.buf=bufferFrom(buf);// Creates a copy of data.
134-
this.bufCapacity=this.buf.length;
135-
this.bufUsed=this.buf.length;
134+
constcopy=bufferFrom(buf);// Creates a copy of data.
135+
this._setBuf(copy);
136+
}
137+
138+
private_setBuf(buf:Buffer):void{
139+
constsize=buf.length;
140+
this.buf=buf;
141+
this.capacity=size;
142+
this.size=size;
136143
this.touch();
137144
}
138145

139146
getSize():number{
140-
returnthis.bufUsed;
147+
returnthis.size;
141148
}
142149

143150
setModeProperty(property:number){
@@ -167,99 +174,72 @@ export class Node {
167174
}
168175

169176
write(buf:Buffer,off:number=0,len:number=buf.length,pos:number=0):number{
170-
if(!this.buf){
171-
this.buf=bufferAllocUnsafe(0);
172-
this.bufCapacity=0;
173-
this.bufUsed=0;
174-
}
175-
177+
constbufLength=buf.length;
178+
if(off+len>bufLength)len=bufLength-off;
179+
if(len<=0)return0;
176180
constrequiredSize=pos+len;
177-
if(requiredSize>this.bufCapacity){
178-
// Calculate new capacity: max of 2x current capacity or required size, with minimum of 64 bytes
179-
letnewCapacity=Math.max(this.bufCapacity*2,requiredSize);
180-
if(newCapacity<64)newCapacity=64;
181-
181+
if(requiredSize>this.capacity){
182+
letnewCapacity=Math.max(this.capacity*2,64);
183+
while(newCapacity<requiredSize)newCapacity*=2;
182184
constnewBuf=bufferAllocUnsafe(newCapacity);
183-
if(this.bufUsed>0){
184-
this.buf.copy(newBuf,0,0,this.bufUsed);
185-
}
185+
if(this.size>0)this.buf.copy(newBuf,0,0,this.size);
186186
this.buf=newBuf;
187-
this.bufCapacity=newCapacity;
187+
this.capacity=newCapacity;
188188
}
189-
189+
if(pos>this.size)this.buf.fill(0,this.size,pos);
190190
buf.copy(this.buf,pos,off,off+len);
191-
192-
// Update used size if we wrote beyond current used size
193-
if(requiredSize>this.bufUsed){
194-
this.bufUsed=requiredSize;
195-
}
196-
191+
if(requiredSize>this.size)this.size=requiredSize;
197192
this.touch();
198-
199193
returnlen;
200194
}
201195

202-
// Returns the number of bytes read.
196+
/**
197+
* Read data from the file.
198+
*
199+
*@param buf Buffer to read data into.
200+
*@param off Offset int the `buf` where to start writing data.
201+
*@param len How many bytes to read. Equals to `buf.byteLength` by default.
202+
*@param pos Position offset in file where to start reading. Defaults to `0`.
203+
*@returns Returns the number of bytes read.
204+
*/
203205
read(
204206
buf:Buffer|ArrayBufferView|DataView,
205207
off:number=0,
206208
len:number=buf.byteLength,
207209
pos:number=0,
208210
):number{
209211
this.atime=newDate();
210-
if(!this.buf){
211-
this.buf=bufferAllocUnsafe(0);
212-
this.bufCapacity=0;
213-
this.bufUsed=0;
214-
}
215-
if(pos>=this.bufUsed)return0;
212+
if(pos>=this.size)return0;
216213
letactualLen=len;
217-
if(actualLen>buf.byteLength){
218-
actualLen=buf.byteLength;
219-
}
220-
if(actualLen+pos>this.bufUsed){
221-
actualLen=this.bufUsed-pos;
222-
}
214+
if(actualLen>buf.byteLength)actualLen=buf.byteLength;
215+
if(actualLen+pos>this.size)actualLen=this.size-pos;
216+
if(actualLen<=0)return0;
223217
constbuf2=bufinstanceofBuffer ?buf :Buffer.from(buf.buffer,buf.byteOffset,buf.byteLength);
224218
this.buf.copy(buf2,off,pos,pos+actualLen);
225219
returnactualLen;
226220
}
227221

228222
truncate(len:number=0){
229223
if(!len){
230-
this.buf=bufferAllocUnsafe(0);
231-
this.bufCapacity=0;
232-
this.bufUsed=0;
233-
}else{
234-
if(!this.buf){
235-
this.buf=bufferAllocUnsafe(0);
236-
this.bufCapacity=0;
237-
this.bufUsed=0;
238-
}
239-
if(len<=this.bufUsed){
240-
// Shrinking: keep capacity but reduce used size
241-
this.bufUsed=len;
242-
}else{
243-
// Growing: may need to allocate more capacity
244-
if(len>this.bufCapacity){
245-
// Need more capacity - allocate with some overallocation
246-
letnewCapacity=len;
247-
if(newCapacity<64)newCapacity=64;
248-
constbuf=bufferAllocUnsafe(newCapacity);
249-
if(this.bufUsed>0){
250-
this.buf.copy(buf,0,0,this.bufUsed);
251-
}
252-
buf.fill(0,this.bufUsed,len);
253-
this.buf=buf;
254-
this.bufCapacity=newCapacity;
255-
}else{
256-
// Have enough capacity, just zero-fill the new space
257-
this.buf.fill(0,this.bufUsed,len);
258-
}
259-
this.bufUsed=len;
260-
}
224+
this.buf=EMPTY_BUFFER;
225+
this.capacity=0;
226+
this.size=0;
227+
this.touch();
228+
return;
229+
}
230+
if(len<=this.size)this.size=len;
231+
else{
232+
if(len>this.capacity){
233+
letnewCapacity=Math.max(this.capacity*2,64);
234+
while(newCapacity<len)newCapacity*=2;
235+
constbuf=bufferAllocUnsafe(newCapacity);
236+
if(this.size>0)this.buf.copy(buf,0,0,this.size);
237+
buf.fill(0,this.size,len);
238+
this.buf=buf;
239+
this.capacity=newCapacity;
240+
}elsethis.buf.fill(0,this.size,len);
241+
this.size=len;
261242
}
262-
263243
this.touch();
264244
}
265245

‎src/core/__tests__/Node.test.ts‎

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ describe('Node', () => {
66
it('should handle sequential small writes efficiently',()=>{
77
constnode=newNode(1,0o666);
88
constsmallBuf=bufferFrom('x');
9-
10-
// Write 100 small chunks
11-
for(leti=0;i<100;i++){
12-
node.write(smallBuf,0,1,i);
13-
}
14-
9+
for(leti=0;i<100;i++)node.write(smallBuf,0,1,i);
1510
expect(node.getSize()).toBe(100);
1611
constresult=node.getBuffer();
1712
expect(result.length).toBe(100);
@@ -20,45 +15,46 @@ describe('Node', () => {
2015

2116
it('should handle writes at various positions',()=>{
2217
constnode=newNode(1,0o666);
23-
2418
node.write(bufferFrom('hello'),0,5,0);
2519
expect(node.getSize()).toBe(5);
2620
expect(node.getBuffer().toString()).toBe('hello');
27-
28-
// Writing at position 5 (immediately after) should work
2921
node.write(bufferFrom('world'),0,5,5);
3022
expect(node.getSize()).toBe(10);
3123
expect(node.getBuffer().toString()).toBe('helloworld');
3224
});
3325

34-
it('should handle overwriting existing data',()=>{
26+
it('overwrites and extends file size',()=>{
3527
constnode=newNode(1,0o666);
28+
node.write(bufferFrom('hello'),0,5,0);
29+
expect(node.getSize()).toBe(5);
30+
expect(node.getBuffer().toString()).toBe('hello');
31+
node.write(bufferFrom('0 world'),0,7,4);
32+
expect(node.getSize()).toBe('hell0 world'.length);
33+
expect(node.getBuffer().toString()).toBe('hell0 world');
34+
});
3635

36+
it('should handle overwriting existing data',()=>{
37+
constnode=newNode(1,0o666);
3738
node.write(bufferFrom('hello world'),0,11,0);
3839
expect(node.getSize()).toBe(11);
39-
4040
node.write(bufferFrom('HELLO'),0,5,0);
4141
expect(node.getSize()).toBe(11);
4242
expect(node.getBuffer().toString()).toBe('HELLO world');
4343
});
4444

4545
it('should handle truncate to smaller size',()=>{
4646
constnode=newNode(1,0o666);
47-
4847
node.write(bufferFrom('hello world'),0,11,0);
4948
expect(node.getSize()).toBe(11);
50-
5149
node.truncate(5);
5250
expect(node.getSize()).toBe(5);
5351
expect(node.getBuffer().toString()).toBe('hello');
5452
});
5553

5654
it('should handle truncate to larger size with zero-fill',()=>{
5755
constnode=newNode(1,0o666);
58-
5956
node.write(bufferFrom('hello'),0,5,0);
6057
expect(node.getSize()).toBe(5);
61-
6258
node.truncate(10);
6359
expect(node.getSize()).toBe(10);
6460
constresult=node.getBuffer();
@@ -69,25 +65,20 @@ describe('Node', () => {
6965

7066
it('should handle truncate to zero',()=>{
7167
constnode=newNode(1,0o666);
72-
7368
node.write(bufferFrom('hello'),0,5,0);
7469
expect(node.getSize()).toBe(5);
75-
7670
node.truncate(0);
7771
expect(node.getSize()).toBe(0);
7872
expect(node.getBuffer().length).toBe(0);
7973
});
8074

8175
it('should handle read from various positions',()=>{
8276
constnode=newNode(1,0o666);
83-
8477
node.write(bufferFrom('hello world'),0,11,0);
85-
8678
constbuf1=Buffer.alloc(5);
8779
constread1=node.read(buf1,0,5,0);
8880
expect(read1).toBe(5);
8981
expect(buf1.toString()).toBe('hello');
90-
9182
constbuf2=Buffer.alloc(5);
9283
constread2=node.read(buf2,0,5,6);
9384
expect(read2).toBe(5);
@@ -96,9 +87,7 @@ describe('Node', () => {
9687

9788
it('should handle read past end of file',()=>{
9889
constnode=newNode(1,0o666);
99-
10090
node.write(bufferFrom('hello'),0,5,0);
101-
10291
constbuf=Buffer.alloc(10);
10392
constread=node.read(buf,0,10,0);
10493
expect(read).toBe(5);
@@ -107,19 +96,15 @@ describe('Node', () => {
10796

10897
it('should handle setBuffer and getBuffer',()=>{
10998
constnode=newNode(1,0o666);
110-
11199
consttestBuf=bufferFrom('test data');
112100
node.setBuffer(testBuf);
113-
114101
expect(node.getSize()).toBe(9);
115102
expect(node.getBuffer().toString()).toBe('test data');
116103
});
117104

118105
it('should handle setString and getString',()=>{
119106
constnode=newNode(1,0o666);
120-
121107
node.setString('test string');
122-
123108
expect(node.getSize()).toBe(11);
124109
expect(node.getString()).toBe('test string');
125110
});
@@ -132,24 +117,20 @@ describe('Node', () => {
132117
it('should handle multiple appends efficiently',()=>{
133118
constnode=newNode(1,0o666);
134119
constchunk=bufferFrom('chunk');
135-
136-
// Append 20 times
137-
for(leti=0;i<20;i++){
138-
node.write(chunk,0,5,i*5);
139-
}
140-
120+
for(leti=0;i<20;i++)node.write(chunk,0,5,i*5);
141121
expect(node.getSize()).toBe(100);
142122
expect(node.getBuffer().toString()).toBe('chunk'.repeat(20));
143123
});
144124

145-
it('should handle write at position beyond current size',()=>{
125+
it('should handle write at position beyond current size with zero-fill',()=>{
146126
constnode=newNode(1,0o666);
147-
148127
node.write(bufferFrom('hello'),0,5,10);
149128
expect(node.getSize()).toBe(15);
150-
151129
constresult=node.getBuffer();
152-
// First 10 bytes should be uninitialized/garbage
130+
// First 10 bytes should be zero-filled
131+
for(leti=0;i<10;i++){
132+
expect(result[i]).toBe(0);
133+
}
153134
expect(result.toString('utf8',10,15)).toBe('hello');
154135
});
155136
});

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp