Chromium Code Reviews| Index: sdk/lib/io/data_transformer.dart |
| diff --git a/sdk/lib/io/data_transformer.dart b/sdk/lib/io/data_transformer.dart |
| index c7c53adc7938ef6a2558bd734441bd05f5679fa8..2cfa3e9e7ed78edd774a44f8a2a6b292925177c0 100644 |
| --- a/sdk/lib/io/data_transformer.dart |
| +++ b/sdk/lib/io/data_transformer.dart |
| @@ -4,95 +4,287 @@ |
| part of dart.io; |
| + |
| /** |
| - * Private helper-class to handle native filters. |
| + * An instance of the default implementation of the [ZLibCodec]. |
| */ |
| -abstract class _Filter { |
| +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>> { |
| /** |
| - * Call to process a chunk of data. A call to [process] should only be made |
| - * when [processed] returns [null]. |
| + * The compression level of the [ZLibCodec]. |
| */ |
| - void process(List<int> data); |
| + final int level; |
| /** |
| - * 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. |
| + * Get a [Converter] for encoding to `ZLib` compressed data. |
| */ |
| - List<int> processed({bool flush: true, bool end: false}); |
| + Converter<List<int>, List<int>> get encoder => |
| + const ZLibEncoder(gzip: false, level: level); |
| /** |
| - * 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. |
| + * Get a [Converter] for decoding `ZLib` compressed data. |
| */ |
| - void end(); |
| + Converter<List<int>, List<int>> get decoder => const ZLibDecoder(); |
| - external static _Filter newZLibDeflateFilter(bool gzip, int level); |
| - external static _Filter newZLibInflateFilter(); |
| + /** |
| + * 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. |
| + */ |
| + final Converter<List<int>, List<int>> encoder = |
| + const ZLibEncoder(gzip: true, level: level); |
| + |
| + /** |
| + * Get a [Converter] for decoding `GZip` compressed data. |
| + */ |
| + final Converter<List<int>, List<int>> 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>> { |
|
Søren Gjesse
2013/08/22 16:07:26
We could consider making this and ZLibDecoder priv
Anders Johnsen
2013/08/23 07:11:53
Exposing both codecs and converters is in sync wit
|
| + /** |
| + * 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); |
| + } |
| } |
| -class _FilterTransformer extends StreamEventTransformer<List<int>, List<int>> { |
| +/** |
| + * 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) { |
| + builder.add(Uint8List.view(chunk, start, end - start)); |
| + } else { |
| + buidler.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; |
| - _FilterTransformer(_Filter this._filter); |
| + _FilterSink(ByteConversionSink this._sink, _Filter this._filter); |
| + |
| + void add(List<int> data) { |
| + addSlice(data, 0, data.length, false); |
| + } |
| - void handleData(List<int> data, EventSink<List<int>> sink) { |
| + void addSlice(List<int> data, int start, int end, bool isLast) { |
| if (_closed) return; |
| + if (start < 0 || start > data.length) { |
| + throw ArgumentError("Invalid start position"); |
| + } |
| + if (end < 0 || end > data.length || end < start) { |
| + throw ArgumentError("Invalid end position"); |
| + } |
| try { |
| _empty = false; |
| - _filter.process(data); |
| + _filter.process(data, start, end); |
| var out; |
| while ((out = _filter.processed(flush: false)) != null) { |
| - sink.add(out); |
| + _sink.add(out); |
| } |
| - } catch (e, s) { |
| + } catch (e) { |
| _closed = true; |
| - // TODO(floitsch): we are losing the stack trace. |
| - sink.addError(e); |
| - sink.close(); |
| + throw e; |
| } |
| + |
| + if (isLast) close(); |
| } |
| - void handleDone(EventSink<List<int>> sink) { |
| + void close() { |
| if (_closed) return; |
| - if (_empty) _filter.process(const []); |
| + // 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); |
| + _sink.add(out); |
| } |
| - } catch (e, s) { |
| - // TODO(floitsch): we are losing the stack trace. |
| - sink.addError(e); |
| + } catch (e) { |
| _closed = true; |
| + throw e; |
| } |
| if (!_closed) _filter.end(); |
| _closed = true; |
| - sink.close(); |
| + _sink.close(); |
| } |
| } |
| + |
| /** |
| - * ZLibDeflater class used to deflate a stream of bytes, using zlib. |
| + * Private helper-class to handle native filters. |
| */ |
| -class ZLibDeflater extends _FilterTransformer { |
| - ZLibDeflater({bool gzip: true, int level: 6}) |
| - : super(_Filter.newZLibDeflateFilter(gzip, level)); |
| -} |
| +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}); |
| -/** |
| - * ZLibInflater class used to inflate a stream of bytes, using zlib. |
| - */ |
| -class ZLibInflater extends _FilterTransformer { |
| - ZLibInflater() : super(_Filter.newZLibInflateFilter()); |
| -} |
| + /** |
| + * 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(); |
| +} |