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 scanner; | 5 part of scanner; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Scanner that reads from a UTF-8 encoded list of bytes and creates tokens | 8 * Scanner that reads from a UTF-8 encoded list of bytes and creates tokens |
| 9 * that points to substrings. | 9 * that points to substrings. |
| 10 */ | 10 */ |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 * string text of the source file is decoded. | 62 * string text of the source file is decoded. |
| 63 * | 63 * |
| 64 * The list of UTF-8 bytes [file.slowUtf8Bytes()] is expected to return an | 64 * The list of UTF-8 bytes [file.slowUtf8Bytes()] is expected to return an |
| 65 * array whose last element is '0' to signal the end of the file. If this | 65 * array whose last element is '0' to signal the end of the file. If this |
| 66 * is not the case, the entire array is copied before scanning. | 66 * is not the case, the entire array is copied before scanning. |
| 67 */ | 67 */ |
| 68 Utf8BytesScanner(SourceFile file, {bool includeComments: false}) | 68 Utf8BytesScanner(SourceFile file, {bool includeComments: false}) |
| 69 : bytes = file.slowUtf8Bytes(), | 69 : bytes = file.slowUtf8Bytes(), |
| 70 super(file, includeComments) { | 70 super(file, includeComments) { |
| 71 ensureZeroTermination(); | 71 ensureZeroTermination(); |
| 72 // Skip a leading BOM. | |
| 73 if (_containsBomAt(0)) byteOffset += 3; | |
| 72 } | 74 } |
| 73 | 75 |
| 74 /** | 76 /** |
| 75 * Creates a new Utf8BytesScanner from a list of UTF-8 bytes. | 77 * Creates a new Utf8BytesScanner from a list of UTF-8 bytes. |
| 76 * | 78 * |
| 77 * The last element of the list is expected to be '0' to signal the end of | 79 * The last element of the list is expected to be '0' to signal the end of |
| 78 * the file. If this is not the case, the entire array is copied before | 80 * the file. If this is not the case, the entire array is copied before |
| 79 * scanning. | 81 * scanning. |
| 80 */ | 82 */ |
| 81 Utf8BytesScanner.fromBytes(this.bytes, {bool includeComments: false}) | 83 Utf8BytesScanner.fromBytes(this.bytes, {bool includeComments: false}) |
| 82 : super(null, includeComments) { | 84 : super(null, includeComments) { |
| 83 ensureZeroTermination(); | 85 ensureZeroTermination(); |
| 84 } | 86 } |
| 85 | 87 |
| 86 void ensureZeroTermination() { | 88 void ensureZeroTermination() { |
| 87 if (bytes.isEmpty || bytes[bytes.length - 1] != 0) { | 89 if (bytes.isEmpty || bytes[bytes.length - 1] != 0) { |
| 88 // TODO(lry): abort instead of copying the array, or warn? | 90 // TODO(lry): abort instead of copying the array, or warn? |
| 89 var newBytes = new Uint8List(bytes.length + 1); | 91 var newBytes = new Uint8List(bytes.length + 1); |
| 90 for (int i = 0; i < bytes.length; i++) { | 92 for (int i = 0; i < bytes.length; i++) { |
| 91 newBytes[i] = bytes[i]; | 93 newBytes[i] = bytes[i]; |
| 92 } | 94 } |
| 93 newBytes[bytes.length] = 0; | 95 newBytes[bytes.length] = 0; |
| 94 bytes = newBytes; | 96 bytes = newBytes; |
| 95 } | 97 } |
| 96 } | 98 } |
| 97 | 99 |
| 100 bool _containsBomAt(int offset) { | |
| 101 const BOM_UTF8 = const [0xEF, 0xBB, 0xBF]; | |
| 102 | |
| 103 assert(() { | |
|
ahe
2014/01/15 13:15:38
I would probably add a unit test for this instead
floitsch
2014/01/15 13:44:46
There is already a test.
Removing the assert.
| |
| 104 // Verify that BOM_UTF8 corresponds to the UTF-8 BOM character. | |
| 105 // The UTF-8 decoder strips leading BOMs. Add another character in front. | |
| 106 List<int> prefixedBom = [0x20]..addAll(BOM_UTF8); | |
| 107 String decoded = UTF8.decode(prefixedBom); | |
| 108 return decoded.codeUnitAt(1) == UNICODE_BOM_CHARACTER_RUNE; | |
| 109 }); | |
| 110 | |
| 111 return offset + 3 < bytes.length && | |
| 112 bytes[offset] == BOM_UTF8[0] && | |
| 113 bytes[offset + 1] == BOM_UTF8[1] && | |
| 114 bytes[offset + 2] == BOM_UTF8[2]; | |
| 115 } | |
| 116 | |
| 98 int advance() => bytes[++byteOffset]; | 117 int advance() => bytes[++byteOffset]; |
| 99 | 118 |
| 100 int peek() => bytes[byteOffset + 1]; | 119 int peek() => bytes[byteOffset + 1]; |
| 101 | 120 |
| 102 /** | 121 /** |
| 103 * Returns the unicode code point starting at the byte offset [startOffset] | 122 * Returns the unicode code point starting at the byte offset [startOffset] |
| 104 * with the byte [nextByte]. If [advance] is true the current [byteOffset] | 123 * with the byte [nextByte]. If [advance] is true the current [byteOffset] |
| 105 * is advanced to the last byte of the code point. | 124 * is advanced to the last byte of the code point. |
| 106 */ | 125 */ |
| 107 int nextCodePoint(int startOffset, int nextByte, bool advance) { | 126 int nextCodePoint(int startOffset, int nextByte, bool advance) { |
| 108 // The number of 1s in the first byte indicate the number of bytes, at | 127 // The number of 1s in the first byte indicate the number of bytes, at |
| 109 // least 2. | 128 // least 2. |
| 110 int numBytes = 2; | 129 int numBytes = 2; |
| 111 int bit = 0x20; | 130 int bit = 0x20; |
| 112 while ((nextByte & bit) != 0) { | 131 while ((nextByte & bit) != 0) { |
| 113 numBytes++; | 132 numBytes++; |
| 114 bit >>= 1; | 133 bit >>= 1; |
| 115 } | 134 } |
| 116 int end = startOffset + numBytes; | 135 int end = startOffset + numBytes; |
| 117 if (advance) { | 136 if (advance) { |
| 118 byteOffset = end - 1; | 137 byteOffset = end - 1; |
| 119 } | 138 } |
| 120 // TODO(lry): measurably slow, decode creates first a Utf8Decoder and a | 139 // TODO(lry): measurably slow, decode creates first a Utf8Decoder and a |
| 121 // _Utf8Decoder instance. Also the sublist is eagerly allocated. | 140 // _Utf8Decoder instance. Also the sublist is eagerly allocated. |
| 122 String codePoint = UTF8.decode(bytes.sublist(startOffset, end)); | 141 String codePoint = UTF8.decode(bytes.sublist(startOffset, end)); |
| 142 if (codePoint.length == 0) { | |
| 143 // The UTF-8 decoder discards leading BOM characters. | |
| 144 // TODO(floitsch): don't just assume that removed characters were the | |
| 145 // BOM. | |
| 146 assert(_containsBomAt(startOffset)); | |
| 147 codePoint = "\u{FEFF}"; | |
|
ahe
2014/01/14 14:23:59
Can you use UNICODE_BOM_CHARACTER_RUNE somehow?
floitsch
2014/01/14 18:37:58
I don't think so.
I don't want to recreate a strin
ahe
2014/01/15 13:15:38
I would expect that it is extremely rare if a file
floitsch
2014/01/15 13:44:46
I will allocate a new string.
I prefer keeping the
ahe
2014/01/15 13:54:23
Yes. Keep the assert on line 146. That assert test
floitsch
2014/01/15 14:51:39
I see.
As you say: there shouldn't be many BOMs in
| |
| 148 } | |
| 123 if (codePoint.length == 1) { | 149 if (codePoint.length == 1) { |
| 124 if (advance) { | 150 if (advance) { |
| 125 utf8Slack += (numBytes - 1); | 151 utf8Slack += (numBytes - 1); |
| 126 scanSlack = numBytes - 1; | 152 scanSlack = numBytes - 1; |
| 127 scanSlackOffset = byteOffset; | 153 scanSlackOffset = byteOffset; |
| 128 } | 154 } |
| 129 return codePoint.codeUnitAt(0); | 155 return codePoint.codeUnitAt(0); |
| 130 } else if (codePoint.length == 2) { | 156 } else if (codePoint.length == 2) { |
| 131 if (advance) { | 157 if (advance) { |
| 132 utf8Slack += (numBytes - 2); | 158 utf8Slack += (numBytes - 2); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 | 212 |
| 187 void appendSubstringToken(PrecedenceInfo info, int start, bool asciiOnly, | 213 void appendSubstringToken(PrecedenceInfo info, int start, bool asciiOnly, |
| 188 [int extraOffset = 0]) { | 214 [int extraOffset = 0]) { |
| 189 tail.next = new StringToken.fromUtf8Bytes( | 215 tail.next = new StringToken.fromUtf8Bytes( |
| 190 info, bytes, start, byteOffset + extraOffset, asciiOnly, tokenStart); | 216 info, bytes, start, byteOffset + extraOffset, asciiOnly, tokenStart); |
| 191 tail = tail.next; | 217 tail = tail.next; |
| 192 } | 218 } |
| 193 | 219 |
| 194 bool atEndOfFile() => byteOffset >= bytes.length - 1; | 220 bool atEndOfFile() => byteOffset >= bytes.length - 1; |
| 195 } | 221 } |
| OLD | NEW |