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 {
@@ -46,11 +47,18 @@ const { owner_symbol } = require('internal/async_hooks').symbols;
4647
4748const constants = process . binding ( 'constants' ) . zlib ;
4849const {
50+ // Zlib flush levels
4951 Z_NO_FLUSH , Z_BLOCK , Z_PARTIAL_FLUSH , Z_SYNC_FLUSH , Z_FULL_FLUSH , Z_FINISH ,
52+ // Zlib option values
5053 Z_MIN_CHUNK , Z_MIN_WINDOWBITS , Z_MAX_WINDOWBITS , Z_MIN_LEVEL , Z_MAX_LEVEL ,
5154 Z_MIN_MEMLEVEL , Z_MAX_MEMLEVEL , Z_DEFAULT_CHUNK , Z_DEFAULT_COMPRESSION ,
5255 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
5462} = constants ;
5563
5664// translation table for return codes.
@@ -211,7 +219,7 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) {
211219 // The ZlibBase class is not exported to user land, the mode should only be
212220 // passed in by us.
213221 assert ( typeof mode === 'number' ) ;
214- assert ( mode >= DEFLATE && mode <= UNZIP ) ;
222+ assert ( mode >= DEFLATE && mode <= BROTLI_ENCODE ) ;
215223
216224 if ( opts ) {
217225 chunkSize = opts . chunkSize ;
@@ -478,7 +486,7 @@ function processCallback() {
478486 // important to null out the values once they are no longer needed since
479487 // `_handle` can stay in memory long after the buffer is needed.
480488 var handle = this ;
481- var self = this . jsref ;
489+ var self = this [ owner_symbol ] ;
482490 var state = self . _writeState ;
483491
484492 if ( self . _hadError ) {
@@ -619,6 +627,9 @@ function Zlib(opts, mode) {
619627 this . _writeState ,
620628 processCallback ,
621629 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.
622633 throw new ERR_ZLIB_INITIALIZATION_FAILED ( ) ;
623634 }
624635
@@ -723,6 +734,70 @@ function createConvenienceMethod(ctor, sync) {
723734 }
724735}
725736
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+
726801function createProperty ( ctor ) {
727802 return {
728803 configurable : true ,
@@ -748,6 +823,8 @@ module.exports = {
748823 DeflateRaw,
749824 InflateRaw,
750825 Unzip,
826+ BrotliCompress,
827+ BrotliDecompress,
751828
752829 // Convenience methods.
753830 // compress/decompress a string or buffer in one step.
@@ -764,7 +841,11 @@ module.exports = {
764841 gunzip : createConvenienceMethod ( Gunzip , false ) ,
765842 gunzipSync : createConvenienceMethod ( Gunzip , true ) ,
766843 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 ) ,
768849} ;
769850
770851Object . defineProperties ( module . exports , {
@@ -775,6 +856,8 @@ Object.defineProperties(module.exports, {
775856 createGzip : createProperty ( Gzip ) ,
776857 createGunzip : createProperty ( Gunzip ) ,
777858 createUnzip : createProperty ( Unzip ) ,
859+ createBrotliCompress : createProperty ( BrotliCompress ) ,
860+ createBrotliDecompress : createProperty ( BrotliDecompress ) ,
778861 constants : {
779862 configurable : false ,
780863 enumerable : true ,
@@ -792,6 +875,7 @@ Object.defineProperties(module.exports, {
792875const bkeys = Object . keys ( constants ) ;
793876for ( var bk = 0 ; bk < bkeys . length ; bk ++ ) {
794877 var bkey = bkeys [ bk ] ;
878+ if ( bkey . startsWith ( 'BROTLI' ) ) continue ;
795879 Object . defineProperty ( module . exports , bkey , {
796880 enumerable : true , value : constants [ bkey ] , writable : false
797881 } ) ;
0 commit comments