Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 library convert.hex.encoder; | |
| 6 | |
| 7 import 'dart:convert'; | |
| 8 import 'dart:typed_data'; | |
| 9 | |
| 10 import 'package:charcode/ascii.dart'; | |
| 11 | |
| 12 /// The canonical instance of [HexEncoder]. | |
| 13 const hexEncoder = const HexEncoder._(); | |
| 14 | |
| 15 /// A converter that encodes byte arrays into hexadecimal strings. | |
| 16 /// | |
| 17 /// This will throw a [RangeError] if the byte array has any digits that don't | |
| 18 /// fit in the gamut of a byte. | |
| 19 class HexEncoder extends Converter<List<int>, String> { | |
| 20 const HexEncoder._(); | |
| 21 | |
| 22 String convert(List<int> bytes) => _convert(bytes, 0, bytes.length); | |
| 23 | |
| 24 ByteConversionSink startChunkedConversion(Sink<String> sink) => | |
| 25 new _HexEncoderSink(sink); | |
| 26 } | |
| 27 | |
| 28 /// A conversion sink for chunked hexadecimal encoding. | |
| 29 class _HexEncoderSink extends ByteConversionSinkBase { | |
| 30 /// The underlying sink to which decoded byte arrays will be passed. | |
| 31 final Sink<String> _sink; | |
| 32 | |
| 33 _HexEncoderSink(this._sink); | |
| 34 | |
| 35 void add(List<int> chunk) { | |
| 36 _sink.add(_convert(chunk, 0, chunk.length)); | |
| 37 } | |
| 38 | |
| 39 void addSlice(List<int> chunk, int start, int end, bool isLast) { | |
| 40 RangeError.checkValidRange(start, end, chunk.length); | |
| 41 _sink.add(_convert(chunk, start, end)); | |
| 42 if (isLast) _sink.close(); | |
| 43 } | |
| 44 | |
| 45 void close() { | |
| 46 _sink.close(); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 String _convert(List<int> bytes, int start, int end) { | |
| 51 // A Uint8List is more efficient than a StringBuffer given that we know that | |
| 52 // we're only emitting ASCII-compatible characters. | |
|
Lasse Reichstein Nielsen
2015/09/24 08:07:02
, and that we know the length ahead of time.
nweiz
2015/09/24 23:23:42
Done.
| |
| 53 var buffer = new Uint8List((end - start) * 2); | |
| 54 var bufferIndex = 0; | |
| 55 | |
| 56 // A bitwise OR of all bytes in [bytes]. This allows us to check for | |
| 57 // out-of-range bytes without adding more branches than necessary to the | |
| 58 // core loop. | |
| 59 var byteOr = 0; | |
| 60 for (var i = start; i < end; i++) { | |
| 61 var byte = bytes[i]; | |
| 62 byteOr |= byte; | |
| 63 buffer[bufferIndex++] = _codeUnitForDigit(byte ~/ 16); | |
| 64 buffer[bufferIndex++] = _codeUnitForDigit(byte % 16); | |
|
sra1
2015/09/24 18:39:35
consider:
buffer[bufferIndex++] = _codeUnitFo
| |
| 65 } | |
| 66 | |
| 67 if (byteOr >= 0 && byteOr <= 255) return new String.fromCharCodes(buffer); | |
| 68 | |
| 69 // If there was an invalid byte, find it and throw an exception. | |
| 70 for (var i = start; i < end; i++) { | |
| 71 var byte = bytes[i]; | |
| 72 if (byte >= 0 && byte <= 0xff) continue; | |
| 73 throw new FormatException("Invalid byte 0x${byte.toRadixString(16)}.", | |
| 74 bytes, i); | |
| 75 } | |
| 76 | |
| 77 throw 'unreachable'; | |
|
Lasse Reichstein Nielsen
2015/09/24 08:07:02
Hmm! I can see why the "throw 'unreachable' is nee
nweiz
2015/09/24 23:23:42
I think I prefer a standard-looking for loop with
| |
| 78 } | |
| 79 | |
| 80 /// Returns the ASCII/Unicode code unit corresponding to the hexadecimal digit | |
| 81 /// [digit]. | |
| 82 int _codeUnitForDigit(int digit) => digit < 10 ? digit + $0 : digit + $a - 10; | |
| OLD | NEW |