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