| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of dart.io; |
| |
| |
| /** |
| * An instance of the default implementation of the [ZLibCodec]. |
| */ |
| const ZLIB = const ZLibCodec(); |
| |
| |
| /** |
| * The [ZLibCodec] encodes raw bytes to ZLib compressed bytes and decodes ZLib |
| * compressed bytes to raw bytes. |
| */ |
| class ZLibCodec extends Codec<List<int>, List<int>> { |
| /** |
| * The compression level of the [ZLibCodec]. |
| */ |
| final int level; |
| |
| /** |
| * Get a [Converter] for encoding to `ZLib` compressed data. |
| */ |
| Converter<List<int>, List<int>> get encoder => |
| new ZLibEncoder(gzip: false, level: level); |
| |
| /** |
| * Get a [Converter] for decoding `ZLib` compressed data. |
| */ |
| Converter<List<int>, List<int>> get decoder => const ZLibDecoder(); |
| |
| /** |
| * The compression-[level] can be set in the range of `1..10`, with `6` being |
| * the default compression level. Levels above 6 will have higher compression |
| * rates at the cost of more CPU and memory usage. Levels below 6 will use |
| * less CPU and memory, but at the cost of lower compression rates. |
| */ |
| const ZLibCodec({this.level: 6}); |
| } |
| |
| |
| /** |
| * An instance of the default implementation of the [GZipCodec]. |
| */ |
| const GZIP = const GZipCodec(); |
| |
| |
| /** |
| * The [GZipCodec] encodes raw bytes to GZip compressed bytes and decodes GZip |
| * compressed bytes to raw bytes. |
| * |
| * The difference between [ZLibCodec] and [GZipCodec] is that the [GZipCodec] |
| * wraps the `ZLib` compressed bytes in `GZip` frames. |
| */ |
| class GZipCodec extends Codec<List<int>, List<int>> { |
| /** |
| * The compression level of the [ZLibCodec]. |
| */ |
| final int level; |
| |
| /** |
| * Get a [Converter] for encoding to `GZip` compressed data. |
| */ |
| Converter<List<int>, List<int>> get encoder => |
| new ZLibEncoder(gzip: true, level: level); |
| |
| /** |
| * Get a [Converter] for decoding `GZip` compressed data. |
| */ |
| Converter<List<int>, List<int>> get decoder => const ZLibDecoder(); |
| |
| /** |
| * The compression-[level] can be set in the range of `1..10`, with `6` being |
| * the default compression level. Levels above 6 will have higher compression |
| * rates at the cost of more CPU and memory usage. Levels below 6 will use |
| * less CPU and memory, but at the cost of lower compression rates. |
| */ |
| const GZipCodec({this.level: 6}); |
| } |
| |
| |
| /** |
| * The [ZLibEncoder] is the encoder used by [ZLibCodec] and [GZipCodec] to |
| * compress data. |
| */ |
| class ZLibEncoder extends Converter<List<int>, List<int>> { |
| /** |
| * If [gzip] is true, `GZip` frames will be added to the compressed data. |
| */ |
| final bool gzip; |
| |
| /** |
| * The compression level used by the encoder. |
| */ |
| final int level; |
| |
| /** |
| * Create a new [ZLibEncoder] converter. If the [gzip] flag is set, the |
| * encoder will wrap the encoded ZLib data in GZip frames. |
| */ |
| const ZLibEncoder({this.gzip: false, this.level: 6}); |
| |
| |
| /** |
| * Convert a list of bytes using the options given to the [ZLibEncoder] |
| * constructor. |
| */ |
| List<int> convert(List<int> bytes) { |
| _BufferSink sink = new _BufferSink(); |
| startChunkedConversion(sink) |
| ..add(bytes) |
| ..close(); |
| return sink.builder.takeBytes(); |
| } |
| |
| /** |
| * Start a chunked conversion using the options given to the [ZLibEncoder] |
| * constructor. While it accepts any [ChunkedConversionSink] taking |
| * [List<int>]'s, the optimal sink to be passed as [sink] is a |
| * [ByteConversionSink]. |
| */ |
| ByteConversionSink startChunkedConversion( |
| ChunkedConversionSink<List<int>> sink) { |
| if (sink is! ByteConversionSink) { |
| sink = new ByteConversionSink.from(sink); |
| } |
| return new _ZLibEncoderSink(sink, gzip, level); |
| } |
| } |
| |
| |
| /** |
| * The [ZLibDecoder] is the decoder used by [ZLibCodec] and [GZipCodec] to |
| * decompress data. |
| */ |
| class ZLibDecoder extends Converter<List<int>, List<int>> { |
| |
| /** |
| * Create a new [ZLibEncoder] converter. |
| */ |
| const ZLibDecoder(); |
| |
| /** |
| * Convert a list of bytes using the options given to the [ZLibDecoder] |
| * constructor. |
| */ |
| List<int> convert(List<int> bytes) { |
| _BufferSink sink = new _BufferSink(); |
| startChunkedConversion(sink) |
| ..add(bytes) |
| ..close(); |
| return sink.builder.takeBytes(); |
| } |
| |
| /** |
| * Start a chunked conversion. While it accepts any [ChunkedConversionSink] |
| * taking [List<int>]'s, the optimal sink to be passed as [sink] is a |
| * [ByteConversionSink]. |
| */ |
| ByteConversionSink startChunkedConversion( |
| ChunkedConversionSink<List<int>> sink) { |
| if (sink is! ByteConversionSink) { |
| sink = new ByteConversionSink.from(sink); |
| } |
| return new _ZLibDecoderSink(sink); |
| } |
| } |
| |
| |
| class _BufferSink extends ByteConversionSink { |
| final BytesBuilder builder = new BytesBuilder(); |
| |
| void add(List<int> chunk) { |
| builder.add(chunk); |
| } |
| |
| void addSlice(List<int> chunk, int start, int end, bool isLast) { |
| if (chunk is Uint8List) { |
| Uint8List list = chunk; |
| builder.add(new Uint8List.view(list.buffer, start, end - start)); |
| } else { |
| builder.add(chunk.sublist(start, end)); |
| } |
| } |
| |
| void close() {} |
| } |
| |
| |
| class _ZLibEncoderSink extends _FilterSink { |
| _ZLibEncoderSink(ByteConversionSink sink, bool gzip, int level) |
| : super(sink, _Filter.newZLibDeflateFilter(gzip, level)); |
| } |
| |
| |
| class _ZLibDecoderSink extends _FilterSink { |
| _ZLibDecoderSink(ByteConversionSink sink) |
| : super(sink, _Filter.newZLibInflateFilter()); |
| } |
| |
| |
| class _FilterSink extends ByteConversionSink { |
| final _Filter _filter; |
| final ByteConversionSink _sink; |
| bool _closed = false; |
| bool _empty = true; |
| |
| _FilterSink(ByteConversionSink this._sink, _Filter this._filter); |
| |
| void add(List<int> data) { |
| addSlice(data, 0, data.length, false); |
| } |
| |
| void addSlice(List<int> data, int start, int end, bool isLast) { |
| if (_closed) return; |
| if (start < 0 || start > data.length) { |
| throw new ArgumentError("Invalid start position"); |
| } |
| if (end < 0 || end > data.length || end < start) { |
| throw new ArgumentError("Invalid end position"); |
| } |
| try { |
| _empty = false; |
| _filter.process(data, start, end); |
| var out; |
| while ((out = _filter.processed(flush: false)) != null) { |
| _sink.add(out); |
| } |
| } catch (e) { |
| _closed = true; |
| throw e; |
| } |
| |
| if (isLast) close(); |
| } |
| |
| void close() { |
| if (_closed) return; |
| // Be sure to send process an empty chunk of data. Without this, the empty |
| // message would not have a GZip frame (if compressed with GZip). |
| if (_empty) _filter.process(const [], 0, 0); |
| try { |
| var out; |
| while ((out = _filter.processed(end: true)) != null) { |
| _sink.add(out); |
| } |
| } catch (e) { |
| _closed = true; |
| throw e; |
| } |
| if (!_closed) _filter.end(); |
| _closed = true; |
| _sink.close(); |
| } |
| } |
| |
| |
| |
| /** |
| * Private helper-class to handle native filters. |
| */ |
| abstract class _Filter { |
| /** |
| * Call to process a chunk of data. A call to [process] should only be made |
| * when [processed] returns [null]. |
| */ |
| void process(List<int> data, int start, int end); |
| |
| /** |
| * Get a chunk of processed data. When there are no more data available, |
| * [processed] will return [null]. Set [flush] to [false] for non-final |
| * calls to improve performance of some filters. |
| * |
| * The last call to [processed] should have [end] set to [true]. This will make |
| * sure a 'end' packet is written on the stream. |
| */ |
| List<int> processed({bool flush: true, bool end: false}); |
| |
| /** |
| * Mark the filter as closed. Always call this method for any filter created |
| * to avoid leaking resources. [end] can be called at any time, but any |
| * successive calls to [process] or [processed] will fail. |
| */ |
| void end(); |
| |
| external static _Filter newZLibDeflateFilter(bool gzip, int level); |
| external static _Filter newZLibInflateFilter(); |
| } |