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 {
@@ -45,11 +46,18 @@ const { owner_symbol } = require('internal/async_hooks').symbols;
45
46
46
47
const constants = internalBinding ( 'constants' ) . zlib ;
47
48
const {
49
+ // Zlib flush levels
48
50
Z_NO_FLUSH , Z_BLOCK , Z_PARTIAL_FLUSH , Z_SYNC_FLUSH , Z_FULL_FLUSH , Z_FINISH ,
51
+ // Zlib option values
49
52
Z_MIN_CHUNK , Z_MIN_WINDOWBITS , Z_MAX_WINDOWBITS , Z_MIN_LEVEL , Z_MAX_LEVEL ,
50
53
Z_MIN_MEMLEVEL , Z_MAX_MEMLEVEL , Z_DEFAULT_CHUNK , Z_DEFAULT_COMPRESSION ,
51
54
Z_DEFAULT_STRATEGY , Z_DEFAULT_WINDOWBITS , Z_DEFAULT_MEMLEVEL , Z_FIXED ,
52
- DEFLATE , DEFLATERAW , INFLATE , INFLATERAW , GZIP , GUNZIP , UNZIP
55
+ // Node's compression stream modes (node_zlib_mode)
56
+ DEFLATE , DEFLATERAW , INFLATE , INFLATERAW , GZIP , GUNZIP , UNZIP ,
57
+ BROTLI_DECODE , BROTLI_ENCODE ,
58
+ // Brotli operations (~flush levels)
59
+ BROTLI_OPERATION_PROCESS , BROTLI_OPERATION_FLUSH ,
60
+ BROTLI_OPERATION_FINISH
53
61
} = constants ;
54
62
55
63
// 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
@@ -734,6 +745,70 @@ function createConvenienceMethod(ctor, sync) {
734
745
}
735
746
}
736
747
748
+ const kMaxBrotliParam = Math . max ( ...Object . keys ( constants ) . map ( ( key ) => {
749
+ return key . startsWith ( 'BROTLI_PARAM_' ) ? constants [ key ] : 0 ;
750
+ } ) ) ;
751
+
752
+ const brotliInitParamsArray = new Uint32Array ( kMaxBrotliParam + 1 ) ;
753
+
754
+ const brotliDefaultOpts = {
755
+ flush : BROTLI_OPERATION_PROCESS ,
756
+ finishFlush : BROTLI_OPERATION_FINISH ,
757
+ fullFlush : BROTLI_OPERATION_FLUSH
758
+ } ;
759
+ function Brotli ( opts , mode ) {
760
+ assert ( mode === BROTLI_DECODE || mode === BROTLI_ENCODE ) ;
761
+
762
+ brotliInitParamsArray . fill ( - 1 ) ;
763
+ if ( opts && opts . params ) {
764
+ for ( const origKey of Object . keys ( opts . params ) ) {
765
+ const key = + origKey ;
766
+ if ( Number . isNaN ( key ) || key < 0 || key > kMaxBrotliParam ||
767
+ ( brotliInitParamsArray [ key ] | 0 ) !== - 1 ) {
768
+ throw new ERR_BROTLI_INVALID_PARAM ( origKey ) ;
769
+ }
770
+
771
+ const value = opts . params [ origKey ] ;
772
+ if ( typeof value !== 'number' && typeof value !== 'boolean' ) {
773
+ throw new ERR_INVALID_ARG_TYPE ( 'options.params[key]' ,
774
+ 'number' , opts . params [ origKey ] ) ;
775
+ }
776
+ brotliInitParamsArray [ key ] = value ;
777
+ }
778
+ }
779
+
780
+ const handle = mode === BROTLI_DECODE ?
781
+ new binding . BrotliDecoder ( mode ) : new binding . BrotliEncoder ( mode ) ;
782
+
783
+ this . _writeState = new Uint32Array ( 2 ) ;
784
+ if ( ! handle . init ( brotliInitParamsArray ,
785
+ this . _writeState ,
786
+ processCallback ) ) {
787
+ throw new ERR_ZLIB_INITIALIZATION_FAILED ( ) ;
788
+ }
789
+
790
+ ZlibBase . call ( this , opts , mode , handle , brotliDefaultOpts ) ;
791
+ }
792
+ Object . setPrototypeOf ( Brotli . prototype , Zlib . prototype ) ;
793
+ Object . setPrototypeOf ( Brotli , Zlib ) ;
794
+
795
+ function BrotliCompress ( opts ) {
796
+ if ( ! ( this instanceof BrotliCompress ) )
797
+ return new BrotliCompress ( opts ) ;
798
+ Brotli . call ( this , opts , BROTLI_ENCODE ) ;
799
+ }
800
+ Object . setPrototypeOf ( BrotliCompress . prototype , Brotli . prototype ) ;
801
+ Object . setPrototypeOf ( BrotliCompress , Brotli ) ;
802
+
803
+ function BrotliDecompress ( opts ) {
804
+ if ( ! ( this instanceof BrotliDecompress ) )
805
+ return new BrotliDecompress ( opts ) ;
806
+ Brotli . call ( this , opts , BROTLI_DECODE ) ;
807
+ }
808
+ Object . setPrototypeOf ( BrotliDecompress . prototype , Brotli . prototype ) ;
809
+ Object . setPrototypeOf ( BrotliDecompress , Brotli ) ;
810
+
811
+
737
812
function createProperty ( ctor ) {
738
813
return {
739
814
configurable : true ,
@@ -759,6 +834,8 @@ module.exports = {
759
834
DeflateRaw,
760
835
InflateRaw,
761
836
Unzip,
837
+ BrotliCompress,
838
+ BrotliDecompress,
762
839
763
840
// Convenience methods.
764
841
// compress/decompress a string or buffer in one step.
@@ -775,7 +852,11 @@ module.exports = {
775
852
gunzip : createConvenienceMethod ( Gunzip , false ) ,
776
853
gunzipSync : createConvenienceMethod ( Gunzip , true ) ,
777
854
inflateRaw : createConvenienceMethod ( InflateRaw , false ) ,
778
- inflateRawSync : createConvenienceMethod ( InflateRaw , true )
855
+ inflateRawSync : createConvenienceMethod ( InflateRaw , true ) ,
856
+ brotliCompress : createConvenienceMethod ( BrotliCompress , false ) ,
857
+ brotliCompressSync : createConvenienceMethod ( BrotliCompress , true ) ,
858
+ brotliDecompress : createConvenienceMethod ( BrotliDecompress , false ) ,
859
+ brotliDecompressSync : createConvenienceMethod ( BrotliDecompress , true ) ,
779
860
} ;
780
861
781
862
Object . defineProperties ( module . exports , {
@@ -786,6 +867,8 @@ Object.defineProperties(module.exports, {
786
867
createGzip : createProperty ( Gzip ) ,
787
868
createGunzip : createProperty ( Gunzip ) ,
788
869
createUnzip : createProperty ( Unzip ) ,
870
+ createBrotliCompress : createProperty ( BrotliCompress ) ,
871
+ createBrotliDecompress : createProperty ( BrotliDecompress ) ,
789
872
constants : {
790
873
configurable : false ,
791
874
enumerable : true ,
@@ -803,6 +886,7 @@ Object.defineProperties(module.exports, {
803
886
const bkeys = Object . keys ( constants ) ;
804
887
for ( var bk = 0 ; bk < bkeys . length ; bk ++ ) {
805
888
var bkey = bkeys [ bk ] ;
889
+ if ( bkey . startsWith ( 'BROTLI' ) ) continue ;
806
890
Object . defineProperty ( module . exports , bkey , {
807
891
enumerable : false , value : constants [ bkey ] , writable : false
808
892
} ) ;
0 commit comments