@@ -3,16 +3,22 @@ const { Transform } = require('stream');
33const DEBUG_FRAME_DECODER = process . env . FRAME_DECODER_DEBUG === '1' ;
44
55const varint = {
6- encode : ( n ) => {
6+ // Write varint-encoded `n` into `target` at `offset`. Returns number of bytes written.
7+ encodeTo : ( target , offset , n ) => {
78 if ( n < 0 ) throw new RangeError ( 'varint unsigned only' ) ;
8- const o = [ ] ;
9+ let i = 0 ;
910 do {
1011 let b = n & 0x7f ;
1112 n = Math . floor ( n / 128 ) ;
1213 if ( n > 0 ) b |= 0x80 ;
13- o . push ( b ) ;
14+ target [ offset + ( i ++ ) ] = b ;
1415 } while ( n > 0 ) ;
15- return Buffer . from ( o ) ;
16+ return i ;
17+ } ,
18+ encode : ( n ) => {
19+ const buf = Buffer . allocUnsafe ( 10 ) ;
20+ const len = varint . encodeTo ( buf , 0 , n ) ;
21+ return buf . slice ( 0 , len ) ;
1622 } ,
1723 decodeFrom : ( buf , offset = 0 ) => {
1824 let r = 0 , s = 0 , i = offset ;
@@ -30,11 +36,35 @@ const varint = {
3036class FrameEncoder extends Transform {
3137 constructor ( ) {
3238 super ( { writableObjectMode : true } ) ;
39+ let drainDeferred = null ;
40+ // per-instance varint buffer to avoid allocating a small header Buffer per frame
41+ this . _varintBuf = Buffer . allocUnsafe ( 10 ) ;
42+ this . waitForDrain = ( ) => {
43+ if ( ! drainDeferred ) {
44+ drainDeferred = { } ;
45+ drainDeferred . promise = new Promise ( resolve => {
46+ drainDeferred . resolve = resolve ;
47+ } ) ;
48+ this . once ( 'drain' , ( ) => {
49+ if ( drainDeferred ) {
50+ drainDeferred . resolve ( ) ;
51+ drainDeferred = null ;
52+ }
53+ } ) ;
54+ }
55+ return drainDeferred . promise ;
56+ } ;
3357 }
3458 _transform ( f , e , cb ) {
3559 try {
3660 if ( ! Buffer . isBuffer ( f ) ) f = Buffer . from ( f ) ;
37- this . push ( Buffer . concat ( [ varint . encode ( f . length ) , f ] ) ) ;
61+ // encode varint header into reusable buffer then copy into final frame
62+ const payloadLen = f . length ;
63+ const hdrLen = varint . encodeTo ( this . _varintBuf , 0 , payloadLen ) ;
64+ const frame = Buffer . allocUnsafe ( hdrLen + payloadLen ) ;
65+ this . _varintBuf . copy ( frame , 0 , 0 , hdrLen ) ;
66+ f . copy ( frame , hdrLen ) ;
67+ this . push ( frame ) ;
3868 cb ( ) ;
3969 } catch ( err ) {
4070 cb ( err ) ;
@@ -194,9 +224,31 @@ FrameDecoderCirc._nextId = 1;
194224function createLibp2pStream ( socket ) {
195225 const d = new FrameDecoderCirc ( ) , e = new FrameEncoder ( ) ;
196226 socket . pipe ( d ) ; e . pipe ( socket ) ;
197- const s = { source : ( async function * ( ) { for await ( const c of d ) yield c ; } ) ( ) , sink : async ( src ) => { for await ( const c of src ) { if ( ! e . write ( c ) ) await new Promise ( r => e . once ( 'drain' , r ) ) ; } e . end ( ) ; } } ;
227+ const s = {
228+ source : ( async function * ( ) { for await ( const c of d ) yield c ; } ) ( ) ,
229+ sink : async ( src ) => {
230+ for await ( const c of src ) {
231+ if ( ! e . write ( c ) ) await e . waitForDrain ( ) ;
232+ }
233+ e . end ( ) ;
234+ }
235+ } ;
198236 s [ Symbol . asyncIterator ] = ( ) => s . source [ Symbol . asyncIterator ] ( ) ;
199237 return s ;
200238}
201239
202- module . exports = { FrameEncoder, FrameDecoderCirc, createLibp2pStream, encodeFrame : ( b ) => { const buf = Buffer . isBuffer ( b ) ? b : Buffer . from ( b ) ; return Buffer . concat ( [ varint . encode ( buf . length ) , buf ] ) ; } } ;
240+ module . exports = {
241+ FrameEncoder,
242+ FrameDecoderCirc,
243+ createLibp2pStream,
244+ encodeFrame : ( b ) => {
245+ const buf = Buffer . isBuffer ( b ) ? b : Buffer . from ( b ) ;
246+ // Avoid Buffer.concat by preallocating exact size and writing header then payload
247+ const tmp = Buffer . allocUnsafe ( 10 ) ;
248+ const hdrLen = varint . encodeTo ( tmp , 0 , buf . length ) ;
249+ const out = Buffer . allocUnsafe ( hdrLen + buf . length ) ;
250+ tmp . copy ( out , 0 , 0 , hdrLen ) ;
251+ buf . copy ( out , hdrLen ) ;
252+ return out ;
253+ }
254+ } ;
0 commit comments