2222'use strict' ;
2323
2424const {
25+ ERR_BROTLI_INVALID_PARAM ,
2526 ERR_BUFFER_TOO_LARGE ,
2627 ERR_INVALID_ARG_TYPE ,
2728 ERR_OUT_OF_RANGE ,
28- ERR_ZLIB_INITIALIZATION_FAILED
29+ ERR_ZLIB_INITIALIZATION_FAILED ,
2930} = require ( 'internal/errors' ) . codes ;
3031const Transform = require ( '_stream_transform' ) ;
3132const {
@@ -47,11 +48,18 @@ const { owner_symbol } = require('internal/async_hooks').symbols;
4748
4849const constants = internalBinding ( 'constants' ) . zlib ;
4950const {
51+ // Zlib flush levels
5052 Z_NO_FLUSH , Z_BLOCK , Z_PARTIAL_FLUSH , Z_SYNC_FLUSH , Z_FULL_FLUSH , Z_FINISH ,
53+ // Zlib option values
5154 Z_MIN_CHUNK , Z_MIN_WINDOWBITS , Z_MAX_WINDOWBITS , Z_MIN_LEVEL , Z_MAX_LEVEL ,
5255 Z_MIN_MEMLEVEL , Z_MAX_MEMLEVEL , Z_DEFAULT_CHUNK , Z_DEFAULT_COMPRESSION ,
5356 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
5563} = constants ;
5664
5765// translation table for return codes.
@@ -212,7 +220,7 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) {
212220 // The ZlibBase class is not exported to user land, the mode should only be
213221 // passed in by us.
214222 assert ( typeof mode === 'number' ) ;
215- assert ( mode >= DEFLATE && mode <= UNZIP ) ;
223+ assert ( mode >= DEFLATE && mode <= BROTLI_ENCODE ) ;
216224
217225 if ( opts ) {
218226 chunkSize = opts . chunkSize ;
@@ -481,7 +489,7 @@ function processCallback() {
481489 // important to null out the values once they are no longer needed since
482490 // `_handle` can stay in memory long after the buffer is needed.
483491 var handle = this ;
484- var self = this . jsref ;
492+ var self = this [ owner_symbol ] ;
485493 var state = self . _writeState ;
486494
487495 if ( self . _hadError ) {
@@ -622,6 +630,9 @@ function Zlib(opts, mode) {
622630 this . _writeState ,
623631 processCallback ,
624632 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.
625636 throw new ERR_ZLIB_INITIALIZATION_FAILED ( ) ;
626637 }
627638
@@ -726,6 +737,70 @@ function createConvenienceMethod(ctor, sync) {
726737 }
727738}
728739
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+
729804function createProperty ( ctor ) {
730805 return {
731806 configurable : true ,
@@ -751,6 +826,8 @@ module.exports = {
751826 DeflateRaw,
752827 InflateRaw,
753828 Unzip,
829+ BrotliCompress,
830+ BrotliDecompress,
754831
755832 // Convenience methods.
756833 // compress/decompress a string or buffer in one step.
@@ -767,7 +844,11 @@ module.exports = {
767844 gunzip : createConvenienceMethod ( Gunzip , false ) ,
768845 gunzipSync : createConvenienceMethod ( Gunzip , true ) ,
769846 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 ) ,
771852} ;
772853
773854Object . defineProperties ( module . exports , {
@@ -778,6 +859,8 @@ Object.defineProperties(module.exports, {
778859 createGzip : createProperty ( Gzip ) ,
779860 createGunzip : createProperty ( Gunzip ) ,
780861 createUnzip : createProperty ( Unzip ) ,
862+ createBrotliCompress : createProperty ( BrotliCompress ) ,
863+ createBrotliDecompress : createProperty ( BrotliDecompress ) ,
781864 constants : {
782865 configurable : false ,
783866 enumerable : true ,
@@ -795,6 +878,7 @@ Object.defineProperties(module.exports, {
795878const bkeys = Object . keys ( constants ) ;
796879for ( var bk = 0 ; bk < bkeys . length ; bk ++ ) {
797880 var bkey = bkeys [ bk ] ;
881+ if ( bkey . startsWith ( 'BROTLI' ) ) continue ;
798882 Object . defineProperty ( module . exports , bkey , {
799883 enumerable : true , value : constants [ bkey ] , writable : false
800884 } ) ;
0 commit comments