22
22
'use strict' ;
23
23
24
24
const {
25
+ ERR_BROTLI_INVALID_PARAM ,
25
26
ERR_BUFFER_TOO_LARGE ,
26
27
ERR_INVALID_ARG_TYPE ,
27
28
ERR_OUT_OF_RANGE ,
28
- ERR_ZLIB_INITIALIZATION_FAILED
29
+ ERR_ZLIB_INITIALIZATION_FAILED ,
29
30
} = require ( 'internal/errors' ) . codes ;
30
31
const Transform = require ( '_stream_transform' ) ;
31
32
const {
@@ -46,11 +47,18 @@ const { owner_symbol } = require('internal/async_hooks').symbols;
46
47
47
48
const constants = process . binding ( 'constants' ) . zlib ;
48
49
const {
50
+ // Zlib flush levels
49
51
Z_NO_FLUSH , Z_BLOCK , Z_PARTIAL_FLUSH , Z_SYNC_FLUSH , Z_FULL_FLUSH , Z_FINISH ,
52
+ // Zlib option values
50
53
Z_MIN_CHUNK , Z_MIN_WINDOWBITS , Z_MAX_WINDOWBITS , Z_MIN_LEVEL , Z_MAX_LEVEL ,
51
54
Z_MIN_MEMLEVEL , Z_MAX_MEMLEVEL , Z_DEFAULT_CHUNK , Z_DEFAULT_COMPRESSION ,
52
55
Z_DEFAULT_STRATEGY , Z_DEFAULT_WINDOWBITS , Z_DEFAULT_MEMLEVEL , Z_FIXED ,
53
- DEFLATE , DEFLATERAW , INFLATE , INFLATERAW , GZIP , GUNZIP , UNZIP
56
+ // Node's compression stream modes (node_zlib_mode)
57
+ DEFLATE , DEFLATERAW , INFLATE , INFLATERAW , GZIP , GUNZIP , UNZIP ,
58
+ BROTLI_DECODE , BROTLI_ENCODE ,
59
+ // Brotli operations (~flush levels)
60
+ BROTLI_OPERATION_PROCESS , BROTLI_OPERATION_FLUSH ,
61
+ BROTLI_OPERATION_FINISH
54
62
} = constants ;
55
63
56
64
// translation table for return codes.
@@ -211,7 +219,7 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) {
211
219
// The ZlibBase class is not exported to user land, the mode should only be
212
220
// passed in by us.
213
221
assert ( typeof mode === 'number' ) ;
214
- assert ( mode >= DEFLATE && mode <= UNZIP ) ;
222
+ assert ( mode >= DEFLATE && mode <= BROTLI_ENCODE ) ;
215
223
216
224
if ( opts ) {
217
225
chunkSize = opts . chunkSize ;
@@ -478,7 +486,7 @@ function processCallback() {
478
486
// important to null out the values once they are no longer needed since
479
487
// `_handle` can stay in memory long after the buffer is needed.
480
488
var handle = this ;
481
- var self = this . jsref ;
489
+ var self = this [ owner_symbol ] ;
482
490
var state = self . _writeState ;
483
491
484
492
if ( self . _hadError ) {
@@ -619,6 +627,9 @@ function Zlib(opts, mode) {
619
627
this . _writeState ,
620
628
processCallback ,
621
629
dictionary ) ) {
630
+ // TODO(addaleax): Sometimes we generate better error codes in C++ land,
631
+ // e.g. ERR_BROTLI_PARAM_SET_FAILED -- it's hard to access them with
632
+ // the current bindings setup, though.
622
633
throw new ERR_ZLIB_INITIALIZATION_FAILED ( ) ;
623
634
}
624
635
@@ -723,6 +734,70 @@ function createConvenienceMethod(ctor, sync) {
723
734
}
724
735
}
725
736
737
+ const kMaxBrotliParam = Math . max ( ...Object . keys ( constants ) . map ( ( key ) => {
738
+ return key . startsWith ( 'BROTLI_PARAM_' ) ? constants [ key ] : 0 ;
739
+ } ) ) ;
740
+
741
+ const brotliInitParamsArray = new Uint32Array ( kMaxBrotliParam + 1 ) ;
742
+
743
+ const brotliDefaultOpts = {
744
+ flush : BROTLI_OPERATION_PROCESS ,
745
+ finishFlush : BROTLI_OPERATION_FINISH ,
746
+ fullFlush : BROTLI_OPERATION_FLUSH
747
+ } ;
748
+ function Brotli ( opts , mode ) {
749
+ assert ( mode === BROTLI_DECODE || mode === BROTLI_ENCODE ) ;
750
+
751
+ brotliInitParamsArray . fill ( - 1 ) ;
752
+ if ( opts && opts . params ) {
753
+ for ( const origKey of Object . keys ( opts . params ) ) {
754
+ const key = + origKey ;
755
+ if ( Number . isNaN ( key ) || key < 0 || key > kMaxBrotliParam ||
756
+ ( brotliInitParamsArray [ key ] | 0 ) !== - 1 ) {
757
+ throw new ERR_BROTLI_INVALID_PARAM ( origKey ) ;
758
+ }
759
+
760
+ const value = opts . params [ origKey ] ;
761
+ if ( typeof value !== 'number' && typeof value !== 'boolean' ) {
762
+ throw new ERR_INVALID_ARG_TYPE ( 'options.params[key]' ,
763
+ 'number' , opts . params [ origKey ] ) ;
764
+ }
765
+ brotliInitParamsArray [ key ] = value ;
766
+ }
767
+ }
768
+
769
+ const handle = mode === BROTLI_DECODE ?
770
+ new binding . BrotliDecoder ( mode ) : new binding . BrotliEncoder ( mode ) ;
771
+
772
+ this . _writeState = new Uint32Array ( 2 ) ;
773
+ if ( ! handle . init ( brotliInitParamsArray ,
774
+ this . _writeState ,
775
+ processCallback ) ) {
776
+ throw new ERR_ZLIB_INITIALIZATION_FAILED ( ) ;
777
+ }
778
+
779
+ ZlibBase . call ( this , opts , mode , handle , brotliDefaultOpts ) ;
780
+ }
781
+ Object . setPrototypeOf ( Brotli . prototype , Zlib . prototype ) ;
782
+ Object . setPrototypeOf ( Brotli , Zlib ) ;
783
+
784
+ function BrotliCompress ( opts ) {
785
+ if ( ! ( this instanceof BrotliCompress ) )
786
+ return new BrotliCompress ( opts ) ;
787
+ Brotli . call ( this , opts , BROTLI_ENCODE ) ;
788
+ }
789
+ Object . setPrototypeOf ( BrotliCompress . prototype , Brotli . prototype ) ;
790
+ Object . setPrototypeOf ( BrotliCompress , Brotli ) ;
791
+
792
+ function BrotliDecompress ( opts ) {
793
+ if ( ! ( this instanceof BrotliDecompress ) )
794
+ return new BrotliDecompress ( opts ) ;
795
+ Brotli . call ( this , opts , BROTLI_DECODE ) ;
796
+ }
797
+ Object . setPrototypeOf ( BrotliDecompress . prototype , Brotli . prototype ) ;
798
+ Object . setPrototypeOf ( BrotliDecompress , Brotli ) ;
799
+
800
+
726
801
function createProperty ( ctor ) {
727
802
return {
728
803
configurable : true ,
@@ -748,6 +823,8 @@ module.exports = {
748
823
DeflateRaw,
749
824
InflateRaw,
750
825
Unzip,
826
+ BrotliCompress,
827
+ BrotliDecompress,
751
828
752
829
// Convenience methods.
753
830
// compress/decompress a string or buffer in one step.
@@ -764,7 +841,11 @@ module.exports = {
764
841
gunzip : createConvenienceMethod ( Gunzip , false ) ,
765
842
gunzipSync : createConvenienceMethod ( Gunzip , true ) ,
766
843
inflateRaw : createConvenienceMethod ( InflateRaw , false ) ,
767
- inflateRawSync : createConvenienceMethod ( InflateRaw , true )
844
+ inflateRawSync : createConvenienceMethod ( InflateRaw , true ) ,
845
+ brotliCompress : createConvenienceMethod ( BrotliCompress , false ) ,
846
+ brotliCompressSync : createConvenienceMethod ( BrotliCompress , true ) ,
847
+ brotliDecompress : createConvenienceMethod ( BrotliDecompress , false ) ,
848
+ brotliDecompressSync : createConvenienceMethod ( BrotliDecompress , true ) ,
768
849
} ;
769
850
770
851
Object . defineProperties ( module . exports , {
@@ -775,6 +856,8 @@ Object.defineProperties(module.exports, {
775
856
createGzip : createProperty ( Gzip ) ,
776
857
createGunzip : createProperty ( Gunzip ) ,
777
858
createUnzip : createProperty ( Unzip ) ,
859
+ createBrotliCompress : createProperty ( BrotliCompress ) ,
860
+ createBrotliDecompress : createProperty ( BrotliDecompress ) ,
778
861
constants : {
779
862
configurable : false ,
780
863
enumerable : true ,
@@ -792,6 +875,7 @@ Object.defineProperties(module.exports, {
792
875
const bkeys = Object . keys ( constants ) ;
793
876
for ( var bk = 0 ; bk < bkeys . length ; bk ++ ) {
794
877
var bkey = bkeys [ bk ] ;
878
+ if ( bkey . startsWith ( 'BROTLI' ) ) continue ;
795
879
Object . defineProperty ( module . exports , bkey , {
796
880
enumerable : true , value : constants [ bkey ] , writable : false
797
881
} ) ;
0 commit comments