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

Commit16f452f

Browse files
authored
Merge pull request#1213 from streamich/overallocate-node-buffer
Over-allocate `Node` buffer for fast append writes
2 parentse26954f +d1edb3f commit16f452f

File tree

2 files changed

+200
-35
lines changed

2 files changed

+200
-35
lines changed

‎src/core/Node.ts‎

Lines changed: 63 additions & 35 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,8 +31,14 @@ export class Node {
3031
private_mtime=newDate();
3132
private_ctime=newDate();
3233

33-
// data: string = '';
34-
buf:Buffer;
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+
3542
rdev:number=0;
3643

3744
mode:number;// S_IFDIR, S_IFREG, etc..
@@ -114,24 +121,30 @@ export class Node {
114121
}
115122

116123
setString(str:string){
117-
// this.setBuffer(bufferFrom(str, 'utf8'));
118-
this.buf=bufferFrom(str,'utf8');
119-
this.touch();
124+
this._setBuf(bufferFrom(str,'utf8'));
120125
}
121126

122127
getBuffer():Buffer{
123128
this.atime=newDate();
124129
if(!this.buf)this.buf=bufferAllocUnsafe(0);
125-
returnbufferFrom(this.buf);// Return a copy.
130+
returnbufferFrom(this.buf.subarray(0,this.size));// Return a copy of used portion.
126131
}
127132

128133
setBuffer(buf:Buffer){
129-
this.buf=bufferFrom(buf);// Creates a copy of data.
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;
130143
this.touch();
131144
}
132145

133146
getSize():number{
134-
returnthis.buf ?this.buf.length :0;
147+
returnthis.size;
135148
}
136149

137150
setModeProperty(property:number){
@@ -161,57 +174,72 @@ export class Node {
161174
}
162175

163176
write(buf:Buffer,off:number=0,len:number=buf.length,pos:number=0):number{
164-
if(!this.buf)this.buf=bufferAllocUnsafe(0);
165-
166-
if(pos+len>this.buf.length){
167-
constnewBuf=bufferAllocUnsafe(pos+len);
168-
this.buf.copy(newBuf,0,0,this.buf.length);
177+
constbufLength=buf.length;
178+
if(off+len>bufLength)len=bufLength-off;
179+
if(len<=0)return0;
180+
constrequiredSize=pos+len;
181+
if(requiredSize>this.capacity){
182+
letnewCapacity=Math.max(this.capacity*2,64);
183+
while(newCapacity<requiredSize)newCapacity*=2;
184+
constnewBuf=bufferAllocUnsafe(newCapacity);
185+
if(this.size>0)this.buf.copy(newBuf,0,0,this.size);
169186
this.buf=newBuf;
187+
this.capacity=newCapacity;
170188
}
171-
189+
if(pos>this.size)this.buf.fill(0,this.size,pos);
172190
buf.copy(this.buf,pos,off,off+len);
173-
191+
if(requiredSize>this.size)this.size=requiredSize;
174192
this.touch();
175-
176193
returnlen;
177194
}
178195

179-
// 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+
*/
180205
read(
181206
buf:Buffer|ArrayBufferView|DataView,
182207
off:number=0,
183208
len:number=buf.byteLength,
184209
pos:number=0,
185210
):number{
186211
this.atime=newDate();
187-
if(!this.buf)this.buf=bufferAllocUnsafe(0);
188-
if(pos>=this.buf.length)return0;
212+
if(pos>=this.size)return0;
189213
letactualLen=len;
190-
if(actualLen>buf.byteLength){
191-
actualLen=buf.byteLength;
192-
}
193-
if(actualLen+pos>this.buf.length){
194-
actualLen=this.buf.length-pos;
195-
}
214+
if(actualLen>buf.byteLength)actualLen=buf.byteLength;
215+
if(actualLen+pos>this.size)actualLen=this.size-pos;
216+
if(actualLen<=0)return0;
196217
constbuf2=bufinstanceofBuffer ?buf :Buffer.from(buf.buffer,buf.byteOffset,buf.byteLength);
197218
this.buf.copy(buf2,off,pos,pos+actualLen);
198219
returnactualLen;
199220
}
200221

201222
truncate(len:number=0){
202-
if(!len)this.buf=bufferAllocUnsafe(0);
223+
if(!len){
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;
203231
else{
204-
if(!this.buf)this.buf=bufferAllocUnsafe(0);
205-
if(len<=this.buf.length){
206-
this.buf=this.buf.slice(0,len);
207-
}else{
208-
constbuf=bufferAllocUnsafe(len);
209-
this.buf.copy(buf);
210-
buf.fill(0,this.buf.length);
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);
211238
this.buf=buf;
212-
}
239+
this.capacity=newCapacity;
240+
}elsethis.buf.fill(0,this.size,len);
241+
this.size=len;
213242
}
214-
215243
this.touch();
216244
}
217245

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

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import{Node}from'../Node';
2+
import{bufferFrom}from'../../vendor/node/internal/buffer';
3+
4+
describe('Node',()=>{
5+
describe('buffer management with capacity tracking',()=>{
6+
it('should handle sequential small writes efficiently',()=>{
7+
constnode=newNode(1,0o666);
8+
constsmallBuf=bufferFrom('x');
9+
for(leti=0;i<100;i++)node.write(smallBuf,0,1,i);
10+
expect(node.getSize()).toBe(100);
11+
constresult=node.getBuffer();
12+
expect(result.length).toBe(100);
13+
expect(result.toString()).toBe('x'.repeat(100));
14+
});
15+
16+
it('should handle writes at various positions',()=>{
17+
constnode=newNode(1,0o666);
18+
node.write(bufferFrom('hello'),0,5,0);
19+
expect(node.getSize()).toBe(5);
20+
expect(node.getBuffer().toString()).toBe('hello');
21+
node.write(bufferFrom('world'),0,5,5);
22+
expect(node.getSize()).toBe(10);
23+
expect(node.getBuffer().toString()).toBe('helloworld');
24+
});
25+
26+
it('overwrites and extends file size',()=>{
27+
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+
});
35+
36+
it('should handle overwriting existing data',()=>{
37+
constnode=newNode(1,0o666);
38+
node.write(bufferFrom('hello world'),0,11,0);
39+
expect(node.getSize()).toBe(11);
40+
node.write(bufferFrom('HELLO'),0,5,0);
41+
expect(node.getSize()).toBe(11);
42+
expect(node.getBuffer().toString()).toBe('HELLO world');
43+
});
44+
45+
it('should handle truncate to smaller size',()=>{
46+
constnode=newNode(1,0o666);
47+
node.write(bufferFrom('hello world'),0,11,0);
48+
expect(node.getSize()).toBe(11);
49+
node.truncate(5);
50+
expect(node.getSize()).toBe(5);
51+
expect(node.getBuffer().toString()).toBe('hello');
52+
});
53+
54+
it('should handle truncate to larger size with zero-fill',()=>{
55+
constnode=newNode(1,0o666);
56+
node.write(bufferFrom('hello'),0,5,0);
57+
expect(node.getSize()).toBe(5);
58+
node.truncate(10);
59+
expect(node.getSize()).toBe(10);
60+
constresult=node.getBuffer();
61+
expect(result.toString('utf8',0,5)).toBe('hello');
62+
expect(result[5]).toBe(0);
63+
expect(result[9]).toBe(0);
64+
});
65+
66+
it('should handle truncate to zero',()=>{
67+
constnode=newNode(1,0o666);
68+
node.write(bufferFrom('hello'),0,5,0);
69+
expect(node.getSize()).toBe(5);
70+
node.truncate(0);
71+
expect(node.getSize()).toBe(0);
72+
expect(node.getBuffer().length).toBe(0);
73+
});
74+
75+
it('should handle read from various positions',()=>{
76+
constnode=newNode(1,0o666);
77+
node.write(bufferFrom('hello world'),0,11,0);
78+
constbuf1=Buffer.alloc(5);
79+
constread1=node.read(buf1,0,5,0);
80+
expect(read1).toBe(5);
81+
expect(buf1.toString()).toBe('hello');
82+
constbuf2=Buffer.alloc(5);
83+
constread2=node.read(buf2,0,5,6);
84+
expect(read2).toBe(5);
85+
expect(buf2.toString()).toBe('world');
86+
});
87+
88+
it('should handle read past end of file',()=>{
89+
constnode=newNode(1,0o666);
90+
node.write(bufferFrom('hello'),0,5,0);
91+
constbuf=Buffer.alloc(10);
92+
constread=node.read(buf,0,10,0);
93+
expect(read).toBe(5);
94+
expect(buf.toString('utf8',0,5)).toBe('hello');
95+
});
96+
97+
it('should handle setBuffer and getBuffer',()=>{
98+
constnode=newNode(1,0o666);
99+
consttestBuf=bufferFrom('test data');
100+
node.setBuffer(testBuf);
101+
expect(node.getSize()).toBe(9);
102+
expect(node.getBuffer().toString()).toBe('test data');
103+
});
104+
105+
it('should handle setString and getString',()=>{
106+
constnode=newNode(1,0o666);
107+
node.setString('test string');
108+
expect(node.getSize()).toBe(11);
109+
expect(node.getString()).toBe('test string');
110+
});
111+
112+
it('should return zero size for empty node',()=>{
113+
constnode=newNode(1,0o666);
114+
expect(node.getSize()).toBe(0);
115+
});
116+
117+
it('should handle multiple appends efficiently',()=>{
118+
constnode=newNode(1,0o666);
119+
constchunk=bufferFrom('chunk');
120+
for(leti=0;i<20;i++)node.write(chunk,0,5,i*5);
121+
expect(node.getSize()).toBe(100);
122+
expect(node.getBuffer().toString()).toBe('chunk'.repeat(20));
123+
});
124+
125+
it('should handle write at position beyond current size with zero-fill',()=>{
126+
constnode=newNode(1,0o666);
127+
node.write(bufferFrom('hello'),0,5,10);
128+
expect(node.getSize()).toBe(15);
129+
constresult=node.getBuffer();
130+
// First 10 bytes should be zero-filled
131+
for(leti=0;i<10;i++){
132+
expect(result[i]).toBe(0);
133+
}
134+
expect(result.toString('utf8',10,15)).toBe('hello');
135+
});
136+
});
137+
});

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp