1
1
'use strict'
2
2
3
- const errCode = require ( 'err-code' )
4
- const Long = require ( 'long' )
5
3
const BufferList = require ( 'bl' )
6
- let rabin
4
+ const { create } = require ( ' rabin-wasm' )
7
5
8
6
module . exports = async function * rabinChunker ( source , options ) {
9
- if ( ! rabin ) {
10
- try {
11
- rabin = nativeRabin ( )
12
- } catch ( _ ) {
13
- // fallback to js implementation
14
- rabin = jsRabin ( )
15
- }
16
- }
7
+ const rabin = jsRabin ( )
17
8
18
9
let min , max , avg
19
10
@@ -40,191 +31,17 @@ module.exports = async function * rabinChunker (source, options) {
40
31
}
41
32
}
42
33
43
- const nativeRabin = ( ) => {
44
- const createRabin = require ( 'rabin' )
45
-
46
- if ( typeof rabin !== 'function' ) {
47
- throw errCode ( new Error ( `rabin was not a function` ) , 'ERR_UNSUPPORTED' )
48
- }
49
-
50
- return async function * ( source , options ) {
51
- const rabin = createRabin ( options )
52
-
53
- // TODO: rewrite rabin using node streams v3
54
- for await ( const chunk of source ) {
55
- rabin . buffers . append ( chunk )
56
- rabin . pending . push ( chunk )
57
-
58
- const sizes = [ ]
59
-
60
- rabin . rabin . fingerprint ( rabin . pending , sizes )
61
- rabin . pending = [ ]
62
-
63
- for ( let i = 0 ; i < sizes . length ; i ++ ) {
64
- const size = sizes [ i ]
65
- const buf = rabin . buffers . slice ( 0 , size )
66
- rabin . buffers . consume ( size )
67
-
68
- yield buf
69
- }
70
- }
71
-
72
- if ( rabin . buffers . length ) {
73
- yield rabin . buffers . slice ( 0 )
74
- }
75
- }
76
- }
77
-
78
34
const jsRabin = ( ) => {
79
- // see https://github.com./datproject/rabin/blob/c0378395dc0a125ab21ac176ec504f9995b34e62/src/rabin.cc
80
- class Rabin {
81
- constructor ( options ) {
82
- this . window = new Array ( options . window || 64 ) . fill ( Long . fromInt ( 0 ) )
83
- this . wpos = 0
84
- this . count = 0
85
- this . digest = Long . fromInt ( 0 )
86
- this . chunkLength = 0
87
- this . polynomial = options . polynomial
88
- this . polynomialDegree = 53
89
- this . polynomialShift = this . polynomialDegree - 8
90
- this . averageBits = options . bits || 12
91
- this . minSize = options . min || 8 * 1024
92
- this . maxSize = options . max || 32 * 1024
93
- this . mask = Long . fromInt ( 1 ) . shiftLeft ( this . averageBits ) . subtract ( 1 )
94
- this . modTable = [ ]
95
- this . outTable = [ ]
96
-
97
- this . calculateTables ( )
98
- }
99
-
100
- calculateTables ( ) {
101
- for ( let i = 0 ; i < 256 ; i ++ ) {
102
- let hash = Long . fromInt ( 0 , true )
103
-
104
- hash = this . appendByte ( hash , i )
105
-
106
- for ( let j = 0 ; j < this . window . length - 1 ; j ++ ) {
107
- hash = this . appendByte ( hash , 0 )
108
- }
109
-
110
- this . outTable [ i ] = hash
111
- }
112
-
113
- const k = this . deg ( this . polynomial )
114
-
115
- for ( let i = 0 ; i < 256 ; i ++ ) {
116
- const b = Long . fromInt ( i , true )
117
-
118
- this . modTable [ i ] = b . shiftLeft ( k )
119
- . modulo ( this . polynomial )
120
- . or ( b . shiftLeft ( k ) )
121
- }
122
- }
123
-
124
- deg ( p ) {
125
- let mask = Long . fromString ( '0x8000000000000000' , true , 16 )
126
-
127
- for ( let i = 0 ; i < 64 ; i ++ ) {
128
- if ( mask . and ( p ) . greaterThan ( 0 ) ) {
129
- return Long . fromInt ( 63 - i )
130
- }
131
-
132
- mask = mask . shiftRight ( 1 )
133
- }
134
-
135
- return Long . fromInt ( - 1 )
136
- }
137
-
138
- appendByte ( hash , b ) {
139
- hash = hash . shiftLeft ( 8 )
140
- hash = hash . or ( b )
141
-
142
- return hash . modulo ( this . polynomial )
143
- }
144
-
145
- getFingerprints ( bufs ) {
146
- const lengths = [ ]
147
-
148
- for ( let i = 0 ; i < bufs . length ; i ++ ) {
149
- let buf = bufs [ i ]
150
-
151
- while ( true ) {
152
- const remaining = this . nextChunk ( buf )
153
-
154
- if ( remaining < 0 ) {
155
- break
156
- }
157
-
158
- buf = buf . slice ( remaining )
159
-
160
- lengths . push ( this . chunkLength )
161
- }
162
- }
163
-
164
- return lengths
165
- }
166
-
167
- nextChunk ( buf ) {
168
- for ( let i = 0 ; i < buf . length ; i ++ ) {
169
- const val = Long . fromInt ( buf [ i ] )
170
-
171
- this . slide ( val )
172
-
173
- this . count ++
174
-
175
- if ( ( this . count >= this . minSize && this . digest . and ( this . mask ) . equals ( 0 ) ) || this . count >= this . maxSize ) {
176
- this . chunkLength = this . count
177
-
178
- this . reset ( )
179
-
180
- return i + 1
181
- }
182
- }
183
-
184
- return - 1
185
- }
186
-
187
- slide ( value ) {
188
- const out = this . window [ this . wpos ] . toInt ( ) & 255
189
- this . window [ this . wpos ] = value
190
- this . digest = this . digest . xor ( this . outTable [ out ] )
191
- this . wpos = ( this . wpos + 1 ) % this . window . length
192
-
193
- this . append ( value )
194
- }
195
-
196
- reset ( ) {
197
- this . window = this . window . map ( ( ) => Long . fromInt ( 0 ) )
198
- this . wpos = 0
199
- this . count = 0
200
- this . digest = Long . fromInt ( 0 )
201
-
202
- this . slide ( Long . fromInt ( 1 ) )
203
- }
204
-
205
- append ( value ) {
206
- const index = this . digest . shiftRight ( this . polynomialShift ) . toInt ( ) & 255
207
- this . digest = this . digest . shiftLeft ( 8 )
208
- this . digest = this . digest . or ( value )
209
-
210
- const entry = this . modTable [ index ]
211
-
212
- if ( entry ) {
213
- this . digest = this . digest . xor ( entry )
214
- }
215
- }
216
- }
217
-
218
35
return async function * ( source , options ) {
219
- const r = new Rabin ( options )
36
+ const r = await create ( options . bits , options . min , options . max , options . window )
220
37
const buffers = new BufferList ( )
221
38
let pending = [ ]
222
39
223
40
for await ( const chunk of source ) {
224
41
buffers . append ( chunk )
225
42
pending . push ( chunk )
226
43
227
- const sizes = r . getFingerprints ( pending )
44
+ const sizes = r . fingerprint ( Buffer . concat ( pending ) )
228
45
pending = [ ]
229
46
230
47
for ( let i = 0 ; i < sizes . length ; i ++ ) {
0 commit comments