11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors.
21cb0ef41Sopenharmony_ci//
31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a
41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the
51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including
61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish,
71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit
81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the
91cb0ef41Sopenharmony_ci// following conditions:
101cb0ef41Sopenharmony_ci//
111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included
121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software.
131cb0ef41Sopenharmony_ci//
141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE.
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ci'use strict';
231cb0ef41Sopenharmony_ciconst common = require('../common');
241cb0ef41Sopenharmony_ciconst assert = require('assert');
251cb0ef41Sopenharmony_ciconst zlib = require('zlib');
261cb0ef41Sopenharmony_ciconst stream = require('stream');
271cb0ef41Sopenharmony_ciconst fs = require('fs');
281cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures');
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ci// Should not segfault.
311cb0ef41Sopenharmony_ciassert.throws(() => zlib.gzipSync(Buffer.alloc(0), { windowBits: 8 }), {
321cb0ef41Sopenharmony_ci  code: 'ERR_OUT_OF_RANGE',
331cb0ef41Sopenharmony_ci  name: 'RangeError',
341cb0ef41Sopenharmony_ci  message: 'The value of "options.windowBits" is out of range. ' +
351cb0ef41Sopenharmony_ci           'It must be >= 9 and <= 15. Received 8',
361cb0ef41Sopenharmony_ci});
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_cilet zlibPairs = [
391cb0ef41Sopenharmony_ci  [zlib.Deflate, zlib.Inflate],
401cb0ef41Sopenharmony_ci  [zlib.Gzip, zlib.Gunzip],
411cb0ef41Sopenharmony_ci  [zlib.Deflate, zlib.Unzip],
421cb0ef41Sopenharmony_ci  [zlib.Gzip, zlib.Unzip],
431cb0ef41Sopenharmony_ci  [zlib.DeflateRaw, zlib.InflateRaw],
441cb0ef41Sopenharmony_ci  [zlib.BrotliCompress, zlib.BrotliDecompress],
451cb0ef41Sopenharmony_ci];
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci// How fast to trickle through the slowstream
481cb0ef41Sopenharmony_cilet trickle = [128, 1024, 1024 * 1024];
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci// Tunable options for zlib classes.
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci// several different chunk sizes
531cb0ef41Sopenharmony_cilet chunkSize = [128, 1024, 1024 * 16, 1024 * 1024];
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci// This is every possible value.
561cb0ef41Sopenharmony_cilet level = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
571cb0ef41Sopenharmony_cilet windowBits = [8, 9, 10, 11, 12, 13, 14, 15];
581cb0ef41Sopenharmony_cilet memLevel = [1, 2, 3, 4, 5, 6, 7, 8, 9];
591cb0ef41Sopenharmony_cilet strategy = [0, 1, 2, 3, 4];
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci// It's nice in theory to test every combination, but it
621cb0ef41Sopenharmony_ci// takes WAY too long.  Maybe a pummel test could do this?
631cb0ef41Sopenharmony_ciif (!process.env.PUMMEL) {
641cb0ef41Sopenharmony_ci  trickle = [1024];
651cb0ef41Sopenharmony_ci  chunkSize = [1024 * 16];
661cb0ef41Sopenharmony_ci  level = [6];
671cb0ef41Sopenharmony_ci  memLevel = [8];
681cb0ef41Sopenharmony_ci  windowBits = [15];
691cb0ef41Sopenharmony_ci  strategy = [0];
701cb0ef41Sopenharmony_ci}
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_cilet testFiles = ['person.jpg', 'elipses.txt', 'empty.txt'];
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ciif (process.env.FAST) {
751cb0ef41Sopenharmony_ci  zlibPairs = [[zlib.Gzip, zlib.Unzip]];
761cb0ef41Sopenharmony_ci  testFiles = ['person.jpg'];
771cb0ef41Sopenharmony_ci}
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ciconst tests = {};
801cb0ef41Sopenharmony_citestFiles.forEach(common.mustCall((file) => {
811cb0ef41Sopenharmony_ci  tests[file] = fixtures.readSync(file);
821cb0ef41Sopenharmony_ci}, testFiles.length));
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci// Stream that saves everything
861cb0ef41Sopenharmony_ciclass BufferStream extends stream.Stream {
871cb0ef41Sopenharmony_ci  constructor() {
881cb0ef41Sopenharmony_ci    super();
891cb0ef41Sopenharmony_ci    this.chunks = [];
901cb0ef41Sopenharmony_ci    this.length = 0;
911cb0ef41Sopenharmony_ci    this.writable = true;
921cb0ef41Sopenharmony_ci    this.readable = true;
931cb0ef41Sopenharmony_ci  }
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci  write(c) {
961cb0ef41Sopenharmony_ci    this.chunks.push(c);
971cb0ef41Sopenharmony_ci    this.length += c.length;
981cb0ef41Sopenharmony_ci    return true;
991cb0ef41Sopenharmony_ci  }
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci  end(c) {
1021cb0ef41Sopenharmony_ci    if (c) this.write(c);
1031cb0ef41Sopenharmony_ci    // flatten
1041cb0ef41Sopenharmony_ci    const buf = Buffer.allocUnsafe(this.length);
1051cb0ef41Sopenharmony_ci    let i = 0;
1061cb0ef41Sopenharmony_ci    this.chunks.forEach((c) => {
1071cb0ef41Sopenharmony_ci      c.copy(buf, i);
1081cb0ef41Sopenharmony_ci      i += c.length;
1091cb0ef41Sopenharmony_ci    });
1101cb0ef41Sopenharmony_ci    this.emit('data', buf);
1111cb0ef41Sopenharmony_ci    this.emit('end');
1121cb0ef41Sopenharmony_ci    return true;
1131cb0ef41Sopenharmony_ci  }
1141cb0ef41Sopenharmony_ci}
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ciclass SlowStream extends stream.Stream {
1171cb0ef41Sopenharmony_ci  constructor(trickle) {
1181cb0ef41Sopenharmony_ci    super();
1191cb0ef41Sopenharmony_ci    this.trickle = trickle;
1201cb0ef41Sopenharmony_ci    this.offset = 0;
1211cb0ef41Sopenharmony_ci    this.readable = this.writable = true;
1221cb0ef41Sopenharmony_ci  }
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_ci  write() {
1251cb0ef41Sopenharmony_ci    throw new Error('not implemented, just call ss.end(chunk)');
1261cb0ef41Sopenharmony_ci  }
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  pause() {
1291cb0ef41Sopenharmony_ci    this.paused = true;
1301cb0ef41Sopenharmony_ci    this.emit('pause');
1311cb0ef41Sopenharmony_ci  }
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci  resume() {
1341cb0ef41Sopenharmony_ci    const emit = () => {
1351cb0ef41Sopenharmony_ci      if (this.paused) return;
1361cb0ef41Sopenharmony_ci      if (this.offset >= this.length) {
1371cb0ef41Sopenharmony_ci        this.ended = true;
1381cb0ef41Sopenharmony_ci        return this.emit('end');
1391cb0ef41Sopenharmony_ci      }
1401cb0ef41Sopenharmony_ci      const end = Math.min(this.offset + this.trickle, this.length);
1411cb0ef41Sopenharmony_ci      const c = this.chunk.slice(this.offset, end);
1421cb0ef41Sopenharmony_ci      this.offset += c.length;
1431cb0ef41Sopenharmony_ci      this.emit('data', c);
1441cb0ef41Sopenharmony_ci      process.nextTick(emit);
1451cb0ef41Sopenharmony_ci    };
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci    if (this.ended) return;
1481cb0ef41Sopenharmony_ci    this.emit('resume');
1491cb0ef41Sopenharmony_ci    if (!this.chunk) return;
1501cb0ef41Sopenharmony_ci    this.paused = false;
1511cb0ef41Sopenharmony_ci    emit();
1521cb0ef41Sopenharmony_ci  }
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ci  end(chunk) {
1551cb0ef41Sopenharmony_ci    // Walk over the chunk in blocks.
1561cb0ef41Sopenharmony_ci    this.chunk = chunk;
1571cb0ef41Sopenharmony_ci    this.length = chunk.length;
1581cb0ef41Sopenharmony_ci    this.resume();
1591cb0ef41Sopenharmony_ci    return this.ended;
1601cb0ef41Sopenharmony_ci  }
1611cb0ef41Sopenharmony_ci}
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci// windowBits: 8 shouldn't throw
1641cb0ef41Sopenharmony_cizlib.createDeflateRaw({ windowBits: 8 });
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci{
1671cb0ef41Sopenharmony_ci  const node = fs.createReadStream(fixtures.path('person.jpg'));
1681cb0ef41Sopenharmony_ci  const raw = [];
1691cb0ef41Sopenharmony_ci  const reinflated = [];
1701cb0ef41Sopenharmony_ci  node.on('data', (chunk) => raw.push(chunk));
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci  // Usually, the inflate windowBits parameter needs to be at least the
1731cb0ef41Sopenharmony_ci  // value of the matching deflate’s windowBits. However, inflate raw with
1741cb0ef41Sopenharmony_ci  // windowBits = 8 should be able to handle compressed data from a source
1751cb0ef41Sopenharmony_ci  // that does not know about the silent 8-to-9 upgrade of windowBits
1761cb0ef41Sopenharmony_ci  // that most versions of zlib/Node perform, and which *still* results in
1771cb0ef41Sopenharmony_ci  // a valid 8-bit-window zlib stream.
1781cb0ef41Sopenharmony_ci  node.pipe(zlib.createDeflateRaw({ windowBits: 9 }))
1791cb0ef41Sopenharmony_ci      .pipe(zlib.createInflateRaw({ windowBits: 8 }))
1801cb0ef41Sopenharmony_ci      .on('data', (chunk) => reinflated.push(chunk))
1811cb0ef41Sopenharmony_ci      .on('end', common.mustCall(
1821cb0ef41Sopenharmony_ci        () => assert(Buffer.concat(raw).equals(Buffer.concat(reinflated)))))
1831cb0ef41Sopenharmony_ci      .on('close', common.mustCall(1));
1841cb0ef41Sopenharmony_ci}
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci// For each of the files, make sure that compressing and
1871cb0ef41Sopenharmony_ci// decompressing results in the same data, for every combination
1881cb0ef41Sopenharmony_ci// of the options set above.
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ciconst testKeys = Object.keys(tests);
1911cb0ef41Sopenharmony_citestKeys.forEach(common.mustCall((file) => {
1921cb0ef41Sopenharmony_ci  const test = tests[file];
1931cb0ef41Sopenharmony_ci  chunkSize.forEach(common.mustCall((chunkSize) => {
1941cb0ef41Sopenharmony_ci    trickle.forEach(common.mustCall((trickle) => {
1951cb0ef41Sopenharmony_ci      windowBits.forEach(common.mustCall((windowBits) => {
1961cb0ef41Sopenharmony_ci        level.forEach(common.mustCall((level) => {
1971cb0ef41Sopenharmony_ci          memLevel.forEach(common.mustCall((memLevel) => {
1981cb0ef41Sopenharmony_ci            strategy.forEach(common.mustCall((strategy) => {
1991cb0ef41Sopenharmony_ci              zlibPairs.forEach(common.mustCall((pair) => {
2001cb0ef41Sopenharmony_ci                const Def = pair[0];
2011cb0ef41Sopenharmony_ci                const Inf = pair[1];
2021cb0ef41Sopenharmony_ci                const opts = { level, windowBits, memLevel, strategy };
2031cb0ef41Sopenharmony_ci
2041cb0ef41Sopenharmony_ci                const def = new Def(opts);
2051cb0ef41Sopenharmony_ci                const inf = new Inf(opts);
2061cb0ef41Sopenharmony_ci                const ss = new SlowStream(trickle);
2071cb0ef41Sopenharmony_ci                const buf = new BufferStream();
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_ci                // Verify that the same exact buffer comes out the other end.
2101cb0ef41Sopenharmony_ci                buf.on('data', common.mustCall((c) => {
2111cb0ef41Sopenharmony_ci                  const msg = `${file} ${chunkSize} ${
2121cb0ef41Sopenharmony_ci                    JSON.stringify(opts)} ${Def.name} -> ${Inf.name}`;
2131cb0ef41Sopenharmony_ci                  let i;
2141cb0ef41Sopenharmony_ci                  for (i = 0; i < Math.max(c.length, test.length); i++) {
2151cb0ef41Sopenharmony_ci                    if (c[i] !== test[i]) {
2161cb0ef41Sopenharmony_ci                      assert.fail(msg);
2171cb0ef41Sopenharmony_ci                      break;
2181cb0ef41Sopenharmony_ci                    }
2191cb0ef41Sopenharmony_ci                  }
2201cb0ef41Sopenharmony_ci                }));
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci                // The magic happens here.
2231cb0ef41Sopenharmony_ci                ss.pipe(def).pipe(inf).pipe(buf);
2241cb0ef41Sopenharmony_ci                ss.end(test);
2251cb0ef41Sopenharmony_ci              }, zlibPairs.length));
2261cb0ef41Sopenharmony_ci            }, strategy.length));
2271cb0ef41Sopenharmony_ci          }, memLevel.length));
2281cb0ef41Sopenharmony_ci        }, level.length));
2291cb0ef41Sopenharmony_ci      }, windowBits.length));
2301cb0ef41Sopenharmony_ci    }, trickle.length));
2311cb0ef41Sopenharmony_ci  }, chunkSize.length));
2321cb0ef41Sopenharmony_ci}, testKeys.length));
233