Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.io; | 5 part of dart.io; |
| 6 | 6 |
| 7 | |
| 8 /** | |
| 9 * An instance of the default implementation of the [ZLibCodec]. | |
| 10 */ | |
| 11 const ZLIB = const ZLibCodec(); | |
| 12 | |
| 13 | |
| 14 /** | |
| 15 * The [ZLibCodec] encodes raw bytes to ZLib compressed bytes and decodes ZLib | |
| 16 * compressed bytes to raw bytes. | |
| 17 */ | |
| 18 class ZLibCodec extends Codec<List<int>, List<int>> { | |
| 19 /** | |
| 20 * The compression level of the [ZLibCodec]. | |
| 21 */ | |
| 22 final int level; | |
| 23 | |
| 24 /** | |
| 25 * Get a [Converter] for encoding to `ZLib` compressed data. | |
| 26 */ | |
| 27 Converter<List<int>, List<int>> get encoder => | |
| 28 const ZLibEncoder(gzip: false, level: level); | |
| 29 | |
| 30 /** | |
| 31 * Get a [Converter] for decoding `ZLib` compressed data. | |
| 32 */ | |
| 33 Converter<List<int>, List<int>> get decoder => const ZLibDecoder(); | |
| 34 | |
| 35 /** | |
| 36 * The compression-[level] can be set in the range of `1..10`, with `6` being | |
| 37 * the default compression level. Levels above 6 will have higher compression | |
| 38 * rates at the cost of more CPU and memory usage. Levels below 6 will use | |
| 39 * less CPU and memory, but at the cost of lower compression rates. | |
| 40 */ | |
| 41 const ZLibCodec({this.level: 6}); | |
| 42 } | |
| 43 | |
| 44 | |
| 45 /** | |
| 46 * An instance of the default implementation of the [GZipCodec]. | |
| 47 */ | |
| 48 const GZIP = const GZipCodec(); | |
| 49 | |
| 50 | |
| 51 /** | |
| 52 * The [GZipCodec] encodes raw bytes to GZip compressed bytes and decodes GZip | |
| 53 * compressed bytes to raw bytes. | |
| 54 * | |
| 55 * The difference between [ZLibCodec] and [GZipCodec] is that the [GZipCodec] | |
| 56 * wraps the `ZLib` compressed bytes in `GZip` frames. | |
| 57 */ | |
| 58 class GZipCodec extends Codec<List<int>, List<int>> { | |
| 59 /** | |
| 60 * The compression level of the [ZLibCodec]. | |
| 61 */ | |
| 62 final int level; | |
| 63 | |
| 64 /** | |
| 65 * Get a [Converter] for encoding to `GZip` compressed data. | |
| 66 */ | |
| 67 final Converter<List<int>, List<int>> encoder = | |
| 68 const ZLibEncoder(gzip: true, level: level); | |
| 69 | |
| 70 /** | |
| 71 * Get a [Converter] for decoding `GZip` compressed data. | |
| 72 */ | |
| 73 final Converter<List<int>, List<int>> decoder = const ZLibDecoder(); | |
| 74 | |
| 75 /** | |
| 76 * The compression-[level] can be set in the range of `1..10`, with `6` being | |
| 77 * the default compression level. Levels above 6 will have higher compression | |
| 78 * rates at the cost of more CPU and memory usage. Levels below 6 will use | |
| 79 * less CPU and memory, but at the cost of lower compression rates. | |
| 80 */ | |
| 81 const GZipCodec({this.level: 6}); | |
| 82 } | |
| 83 | |
| 84 | |
| 85 /** | |
| 86 * The [ZLibEncoder] is the encoder used by [ZLibCodec] and [GZipCodec] to | |
| 87 * compress data. | |
| 88 */ | |
| 89 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
| |
| 90 /** | |
| 91 * If [gzip] is true, `GZip` frames will be added to the compressed data. | |
| 92 */ | |
| 93 final bool gzip; | |
| 94 | |
| 95 /** | |
| 96 * The compression level used by the encoder. | |
| 97 */ | |
| 98 final int level; | |
| 99 | |
| 100 /** | |
| 101 * Create a new [ZLibEncoder] converter. If the [gzip] flag is set, the | |
| 102 * encoder will wrap the encoded ZLib data in GZip frames. | |
| 103 */ | |
| 104 const ZLibEncoder({this.gzip: false, this.level: 6}); | |
| 105 | |
| 106 | |
| 107 /** | |
| 108 * Convert a list of bytes using the options given to the [ZLibEncoder] | |
| 109 * constructor. | |
| 110 */ | |
| 111 List<int> convert(List<int> bytes) { | |
| 112 _BufferSink sink = new _BufferSink(); | |
| 113 startChunkedConversion(sink) | |
| 114 ..add(bytes) | |
| 115 ..close(); | |
| 116 return sink.builder.takeBytes(); | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * Start a chunked conversion using the options given to the [ZLibEncoder] | |
| 121 * constructor. While it accepts any [ChunkedConversionSink] taking | |
| 122 * [List<int>]'s, the optimal sink to be passed as [sink] is a | |
| 123 * [ByteConversionSink]. | |
| 124 */ | |
| 125 ByteConversionSink startChunkedConversion( | |
| 126 ChunkedConversionSink<List<int>> sink) { | |
| 127 if (sink is! ByteConversionSink) { | |
| 128 sink = new ByteConversionSink.from(sink); | |
| 129 } | |
| 130 return new _ZLibEncoderSink(sink, gzip, level); | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 | |
| 135 /** | |
| 136 * The [ZLibDecoder] is the decoder used by [ZLibCodec] and [GZipCodec] to | |
| 137 * decompress data. | |
| 138 */ | |
| 139 class ZLibDecoder extends Converter<List<int>, List<int>> { | |
| 140 | |
| 141 /** | |
| 142 * Create a new [ZLibEncoder] converter. | |
| 143 */ | |
| 144 const ZLibDecoder(); | |
| 145 | |
| 146 /** | |
| 147 * Convert a list of bytes using the options given to the [ZLibDecoder] | |
| 148 * constructor. | |
| 149 */ | |
| 150 List<int> convert(List<int> bytes) { | |
| 151 _BufferSink sink = new _BufferSink(); | |
| 152 startChunkedConversion(sink) | |
| 153 ..add(bytes) | |
| 154 ..close(); | |
| 155 return sink.builder.takeBytes(); | |
| 156 } | |
| 157 | |
| 158 /** | |
| 159 * Start a chunked conversion. While it accepts any [ChunkedConversionSink] | |
| 160 * taking [List<int>]'s, the optimal sink to be passed as [sink] is a | |
| 161 * [ByteConversionSink]. | |
| 162 */ | |
| 163 ByteConversionSink startChunkedConversion( | |
| 164 ChunkedConversionSink<List<int>> sink) { | |
| 165 if (sink is! ByteConversionSink) { | |
| 166 sink = new ByteConversionSink.from(sink); | |
| 167 } | |
| 168 return new _ZLibDecoderSink(sink); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 | |
| 173 class _BufferSink extends ByteConversionSink { | |
| 174 final BytesBuilder builder = new BytesBuilder(); | |
| 175 | |
| 176 void add(List<int> chunk) { | |
| 177 builder.add(chunk); | |
| 178 } | |
| 179 | |
| 180 void addSlice(List<int> chunk, int start, int end, bool isLast) { | |
| 181 if (chunk is Uint8List) { | |
| 182 builder.add(Uint8List.view(chunk, start, end - start)); | |
| 183 } else { | |
| 184 buidler.add(chunk.sublist(start, end)); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 void close() {} | |
| 189 } | |
| 190 | |
| 191 | |
| 192 class _ZLibEncoderSink extends _FilterSink { | |
| 193 _ZLibEncoderSink(ByteConversionSink sink, bool gzip, int level) | |
| 194 : super(sink, _Filter.newZLibDeflateFilter(gzip, level)); | |
| 195 } | |
| 196 | |
| 197 | |
| 198 class _ZLibDecoderSink extends _FilterSink { | |
| 199 _ZLibDecoderSink(ByteConversionSink sink) | |
| 200 : super(sink, _Filter.newZLibInflateFilter()); | |
| 201 } | |
| 202 | |
| 203 | |
| 204 class _FilterSink extends ByteConversionSink { | |
| 205 final _Filter _filter; | |
| 206 final ByteConversionSink _sink; | |
| 207 bool _closed = false; | |
| 208 bool _empty = true; | |
| 209 | |
| 210 _FilterSink(ByteConversionSink this._sink, _Filter this._filter); | |
| 211 | |
| 212 void add(List<int> data) { | |
| 213 addSlice(data, 0, data.length, false); | |
| 214 } | |
| 215 | |
| 216 void addSlice(List<int> data, int start, int end, bool isLast) { | |
| 217 if (_closed) return; | |
| 218 if (start < 0 || start > data.length) { | |
| 219 throw ArgumentError("Invalid start position"); | |
| 220 } | |
| 221 if (end < 0 || end > data.length || end < start) { | |
| 222 throw ArgumentError("Invalid end position"); | |
| 223 } | |
| 224 try { | |
| 225 _empty = false; | |
| 226 _filter.process(data, start, end); | |
| 227 var out; | |
| 228 while ((out = _filter.processed(flush: false)) != null) { | |
| 229 _sink.add(out); | |
| 230 } | |
| 231 } catch (e) { | |
| 232 _closed = true; | |
| 233 throw e; | |
| 234 } | |
| 235 | |
| 236 if (isLast) close(); | |
| 237 } | |
| 238 | |
| 239 void close() { | |
| 240 if (_closed) return; | |
| 241 // Be sure to send process an empty chunk of data. Without this, the empty | |
| 242 // message would not have a GZip frame (if compressed with GZip). | |
| 243 if (_empty) _filter.process(const [], 0, 0); | |
| 244 try { | |
| 245 var out; | |
| 246 while ((out = _filter.processed(end: true)) != null) { | |
| 247 _sink.add(out); | |
| 248 } | |
| 249 } catch (e) { | |
| 250 _closed = true; | |
| 251 throw e; | |
| 252 } | |
| 253 if (!_closed) _filter.end(); | |
| 254 _closed = true; | |
| 255 _sink.close(); | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 | |
| 260 | |
| 7 /** | 261 /** |
| 8 * Private helper-class to handle native filters. | 262 * Private helper-class to handle native filters. |
| 9 */ | 263 */ |
| 10 abstract class _Filter { | 264 abstract class _Filter { |
| 11 /** | 265 /** |
| 12 * Call to process a chunk of data. A call to [process] should only be made | 266 * Call to process a chunk of data. A call to [process] should only be made |
| 13 * when [processed] returns [null]. | 267 * when [processed] returns [null]. |
| 14 */ | 268 */ |
| 15 void process(List<int> data); | 269 void process(List<int> data, int start, int end); |
| 16 | 270 |
| 17 /** | 271 /** |
| 18 * Get a chunk of processed data. When there are no more data available, | 272 * Get a chunk of processed data. When there are no more data available, |
| 19 * [processed] will return [null]. Set [flush] to [false] for non-final | 273 * [processed] will return [null]. Set [flush] to [false] for non-final |
| 20 * calls to improve performance of some filters. | 274 * calls to improve performance of some filters. |
| 21 * | 275 * |
| 22 * The last call to [processed] should have [end] set to [true]. This will mak e | 276 * The last call to [processed] should have [end] set to [true]. This will mak e |
| 23 * sure a 'end' packet is written on the stream. | 277 * sure a 'end' packet is written on the stream. |
| 24 */ | 278 */ |
| 25 List<int> processed({bool flush: true, bool end: false}); | 279 List<int> processed({bool flush: true, bool end: false}); |
| 26 | 280 |
| 27 /** | 281 /** |
| 28 * Mark the filter as closed. Always call this method for any filter created | 282 * Mark the filter as closed. Always call this method for any filter created |
| 29 * to avoid leaking resources. [end] can be called at any time, but any | 283 * to avoid leaking resources. [end] can be called at any time, but any |
| 30 * successive calls to [process] or [processed] will fail. | 284 * successive calls to [process] or [processed] will fail. |
| 31 */ | 285 */ |
| 32 void end(); | 286 void end(); |
| 33 | 287 |
| 34 external static _Filter newZLibDeflateFilter(bool gzip, int level); | 288 external static _Filter newZLibDeflateFilter(bool gzip, int level); |
| 35 external static _Filter newZLibInflateFilter(); | 289 external static _Filter newZLibInflateFilter(); |
| 36 } | 290 } |
| 37 | |
| 38 | |
| 39 class _FilterTransformer extends StreamEventTransformer<List<int>, List<int>> { | |
| 40 final _Filter _filter; | |
| 41 bool _closed = false; | |
| 42 bool _empty = true; | |
| 43 | |
| 44 _FilterTransformer(_Filter this._filter); | |
| 45 | |
| 46 void handleData(List<int> data, EventSink<List<int>> sink) { | |
| 47 if (_closed) return; | |
| 48 try { | |
| 49 _empty = false; | |
| 50 _filter.process(data); | |
| 51 var out; | |
| 52 while ((out = _filter.processed(flush: false)) != null) { | |
| 53 sink.add(out); | |
| 54 } | |
| 55 } catch (e, s) { | |
| 56 _closed = true; | |
| 57 // TODO(floitsch): we are losing the stack trace. | |
| 58 sink.addError(e); | |
| 59 sink.close(); | |
| 60 } | |
| 61 } | |
| 62 | |
| 63 void handleDone(EventSink<List<int>> sink) { | |
| 64 if (_closed) return; | |
| 65 if (_empty) _filter.process(const []); | |
| 66 try { | |
| 67 var out; | |
| 68 while ((out = _filter.processed(end: true)) != null) { | |
| 69 sink.add(out); | |
| 70 } | |
| 71 } catch (e, s) { | |
| 72 // TODO(floitsch): we are losing the stack trace. | |
| 73 sink.addError(e); | |
| 74 _closed = true; | |
| 75 } | |
| 76 if (!_closed) _filter.end(); | |
| 77 _closed = true; | |
| 78 sink.close(); | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 | |
| 83 /** | |
| 84 * ZLibDeflater class used to deflate a stream of bytes, using zlib. | |
| 85 */ | |
| 86 class ZLibDeflater extends _FilterTransformer { | |
| 87 ZLibDeflater({bool gzip: true, int level: 6}) | |
| 88 : super(_Filter.newZLibDeflateFilter(gzip, level)); | |
| 89 } | |
| 90 | |
| 91 | |
| 92 /** | |
| 93 * ZLibInflater class used to inflate a stream of bytes, using zlib. | |
| 94 */ | |
| 95 class ZLibInflater extends _FilterTransformer { | |
| 96 ZLibInflater() : super(_Filter.newZLibInflateFilter()); | |
| 97 } | |
| 98 | |
| OLD | NEW |