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 {
@@ -45,11 +46,18 @@ const { owner_symbol } = require('internal/async_hooks').symbols;
4546
4647const constants = internalBinding ( 'constants' ) . zlib ;
4748const {
49+ // Zlib flush levels
4850 Z_NO_FLUSH , Z_BLOCK , Z_PARTIAL_FLUSH , Z_SYNC_FLUSH , Z_FULL_FLUSH , Z_FINISH ,
51+ // Zlib option values
4952 Z_MIN_CHUNK , Z_MIN_WINDOWBITS , Z_MAX_WINDOWBITS , Z_MIN_LEVEL , Z_MAX_LEVEL ,
5053 Z_MIN_MEMLEVEL , Z_MAX_MEMLEVEL , Z_DEFAULT_CHUNK , Z_DEFAULT_COMPRESSION ,
5154 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
5361} = constants ;
5462
5563// 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
@@ -734,6 +745,70 @@ function createConvenienceMethod(ctor, sync) {
734745 }
735746}
736747
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+
737812function createProperty ( ctor ) {
738813 return {
739814 configurable : true ,
@@ -759,6 +834,8 @@ module.exports = {
759834 DeflateRaw,
760835 InflateRaw,
761836 Unzip,
837+ BrotliCompress,
838+ BrotliDecompress,
762839
763840 // Convenience methods.
764841 // compress/decompress a string or buffer in one step.
@@ -775,7 +852,11 @@ module.exports = {
775852 gunzip : createConvenienceMethod ( Gunzip , false ) ,
776853 gunzipSync : createConvenienceMethod ( Gunzip , true ) ,
777854 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 ) ,
779860} ;
780861
781862Object . defineProperties ( module . exports , {
@@ -786,6 +867,8 @@ Object.defineProperties(module.exports, {
786867 createGzip : createProperty ( Gzip ) ,
787868 createGunzip : createProperty ( Gunzip ) ,
788869 createUnzip : createProperty ( Unzip ) ,
870+ createBrotliCompress : createProperty ( BrotliCompress ) ,
871+ createBrotliDecompress : createProperty ( BrotliDecompress ) ,
789872 constants : {
790873 configurable : false ,
791874 enumerable : true ,
@@ -803,6 +886,7 @@ Object.defineProperties(module.exports, {
803886const bkeys = Object . keys ( constants ) ;
804887for ( var bk = 0 ; bk < bkeys . length ; bk ++ ) {
805888 var bkey = bkeys [ bk ] ;
889+ if ( bkey . startsWith ( 'BROTLI' ) ) continue ;
806890 Object . defineProperty ( module . exports , bkey , {
807891 enumerable : false , value : constants [ bkey ] , writable : false
808892 } ) ;
0 commit comments