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 |