Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012, 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 final int _UTF8_ONE_BYTE_MAX = 0x7f; | |
| 6 final int _UTF8_TWO_BYTE_MAX = 0x7ff; | |
| 7 final int _UTF8_THREE_BYTE_MAX = 0xffff; | |
| 8 | |
| 9 final int _UTF8_LO_SIX_BIT_MASK = 0x3f; | |
| 10 | |
| 11 final int _UTF8_FIRST_BYTE_OF_TWO_BASE = 0xc0; | |
| 12 final int _UTF8_FIRST_BYTE_OF_THREE_BASE = 0xe0; | |
| 13 final int _UTF8_FIRST_BYTE_OF_FOUR_BASE = 0xf0; | |
| 14 final int _UTF8_FIRST_BYTE_OF_FIVE_BASE = 0xf8; | |
| 15 final int _UTF8_FIRST_BYTE_OF_SIX_BASE = 0xfc; | |
| 16 | |
| 17 final int _UTF8_FIRST_BYTE_OF_TWO_MASK = 0x1f; | |
| 18 final int _UTF8_FIRST_BYTE_OF_THREE_MASK = 0xf; | |
| 19 final int _UTF8_FIRST_BYTE_OF_FOUR_MASK = 0x7; | |
| 20 | |
| 21 final int _UTF8_FIRST_BYTE_BOUND_EXCL = 0xfe; | |
| 22 final int _UTF8_SUBSEQUENT_BYTE_BASE = 0x80; | |
| 23 | |
| 24 final List<int> _UTF8_REPLACEMENT_CHARACTER = const <int>[0xef, 0xbf, 0xbd]; | |
| 25 | |
| 26 /** | |
| 27 * Produce a sequence of UTF8 encoded bytes from the provided string. | |
| 28 */ | |
| 29 List<int> encodeAsUtf8(String str) => | |
| 30 _codepointsToUtf8(stringToCodepoints(str)); | |
| 31 | |
| 32 /** | |
| 33 * Produce a String from a sequence of UTF8 encoded bytes. | |
| 34 */ | |
| 35 String decodeFromUtf8(List<int> bytes) => | |
|
Søren Gjesse
2012/02/01 11:25:25
Should we perhaps have an optional offset and leng
Søren Gjesse
2012/02/01 11:25:25
Should there be optional arguments for:
1. Specif
dcarlson
2012/02/01 22:18:46
Done.
dcarlson
2012/02/01 22:18:46
Done.
| |
| 36 codepointsToString(_utf8ToCodepoints(bytes, 0, bytes.length)); | |
|
Søren Gjesse
2012/02/01 11:25:25
Consider having both _utf8ToCodepoints and _utf8To
dcarlson
2012/02/01 22:18:46
I don't think this buys us much. The StringBuffer
| |
| 37 | |
| 38 int _addToEncoding(int start, int bytes, int value, List<int> buffer) { | |
| 39 while(bytes > 0) { | |
| 40 buffer[start + bytes] = _UTF8_SUBSEQUENT_BYTE_BASE | | |
| 41 (value & _UTF8_LO_SIX_BIT_MASK); | |
| 42 value = value >> 6; | |
| 43 bytes--; | |
| 44 } | |
| 45 return value; | |
| 46 } | |
| 47 | |
| 48 /** | |
| 49 * Encode code points as UTF8 code units. | |
| 50 */ | |
| 51 List<int> _codepointsToUtf8(List<int> codepoints, | |
| 52 [int start = 0, int length = null]) { | |
| 53 if (!(start >= 0)) { | |
| 54 throw new IllegalArgumentException("start"); | |
| 55 } | |
| 56 | |
| 57 if (!(length == null || length >= 0)) { | |
| 58 throw new IllegalArgumentException("length"); | |
| 59 } | |
| 60 | |
| 61 int end = length != null ? | |
| 62 Math.min(codepoints.length, start + length) : | |
| 63 codepoints.length; | |
| 64 | |
| 65 int encodedLength = 0; | |
| 66 for (int i = start; i < end; i++) { | |
| 67 int value = codepoints[i]; | |
| 68 if (value < 0 || value > UNICODE_VALID_RANGE_MAX) { | |
| 69 encodedLength += 3; | |
| 70 } else if (value <= _UTF8_ONE_BYTE_MAX) { | |
| 71 encodedLength++; | |
| 72 } else if (value <= _UTF8_TWO_BYTE_MAX) { | |
| 73 encodedLength += 2; | |
| 74 } else if (value <= _UTF8_THREE_BYTE_MAX) { | |
| 75 encodedLength += 3; | |
| 76 } else if (value <= UNICODE_VALID_RANGE_MAX) { | |
| 77 encodedLength += 4; | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 List<int> encoded = new List<int>(encodedLength); | |
| 82 int insertAt = 0; | |
| 83 for (int i = start; i < end; i++) { | |
| 84 int value = codepoints[i]; | |
| 85 if (value < 0 || value > UNICODE_VALID_RANGE_MAX) { | |
| 86 encoded.setRange(insertAt, 3, [0xef, 0xbf, 0xbd]); | |
| 87 insertAt += 3; | |
| 88 } else if (value <= _UTF8_ONE_BYTE_MAX) { | |
| 89 encoded[insertAt] = value; | |
| 90 insertAt++; | |
| 91 } else if (value <= _UTF8_TWO_BYTE_MAX) { | |
| 92 encoded[insertAt] = _UTF8_FIRST_BYTE_OF_TWO_BASE | ( | |
| 93 _UTF8_FIRST_BYTE_OF_TWO_MASK & | |
| 94 _addToEncoding(insertAt, 1, value, encoded)); | |
| 95 insertAt += 2; | |
| 96 } else if (value <= _UTF8_THREE_BYTE_MAX) { | |
| 97 encoded[insertAt] = _UTF8_FIRST_BYTE_OF_THREE_BASE | ( | |
| 98 _UTF8_FIRST_BYTE_OF_THREE_MASK & | |
| 99 _addToEncoding(insertAt, 2, value, encoded)); | |
| 100 insertAt += 3; | |
| 101 } else if (value <= UNICODE_VALID_RANGE_MAX) { | |
| 102 encoded[insertAt] = _UTF8_FIRST_BYTE_OF_FOUR_BASE | ( | |
| 103 _UTF8_FIRST_BYTE_OF_FOUR_MASK & | |
| 104 _addToEncoding(insertAt, 3, value, encoded)); | |
| 105 insertAt += 4; | |
| 106 } | |
| 107 } | |
| 108 return encoded; | |
| 109 } | |
| 110 | |
| 111 // Because UTF-8 specifies byte order, we do not have to follow the pattern | |
| 112 // used by UTF-16 & -32 regarding byte order. | |
| 113 List<int> _utf8ToCodepoints(List<int> utf8EncodedBytes, | |
| 114 [int start = 0, int length = null]) { | |
|
Søren Gjesse
2012/02/01 11:25:25
I think corelib normally uses the name offset inst
dcarlson
2012/02/01 22:18:46
Done.
| |
| 115 if (!(start >= 0)) { | |
| 116 throw new IllegalArgumentException("start"); | |
| 117 } | |
| 118 | |
| 119 if (!(length == null || length >= 0)) { | |
| 120 throw new IllegalArgumentException("length"); | |
| 121 } | |
| 122 | |
| 123 int end = length != null ? | |
| 124 Math.min(utf8EncodedBytes.length, start + length) : | |
| 125 utf8EncodedBytes.length; | |
| 126 | |
| 127 List<int> codepointBuffer = <int>[]; | |
|
Søren Gjesse
2012/02/01 11:25:25
Have you thought about having an initial scan for
dcarlson
2012/02/01 22:18:46
Done.
| |
| 128 int i = start; | |
| 129 while (i < end) { | |
| 130 int value = utf8EncodedBytes[i++]; | |
| 131 if (value >= 0) { | |
| 132 if (value <= _UTF8_ONE_BYTE_MAX) { | |
| 133 codepointBuffer.add(value); | |
| 134 } else if (value < _UTF8_FIRST_BYTE_OF_TWO_BASE) { | |
| 135 codepointBuffer.add(UNICODE_REPLACEMENT_CHARACTER_CODEPOINT); | |
| 136 continue; | |
| 137 } else { | |
| 138 int additionalBytes = 0; | |
| 139 if (value < _UTF8_FIRST_BYTE_OF_THREE_BASE) { | |
| 140 value -= _UTF8_FIRST_BYTE_OF_TWO_BASE; | |
| 141 additionalBytes = 1; | |
| 142 } else if (value < _UTF8_FIRST_BYTE_OF_FOUR_BASE) { | |
| 143 value -= _UTF8_FIRST_BYTE_OF_THREE_BASE; | |
| 144 additionalBytes = 2; | |
| 145 } else if (value < _UTF8_FIRST_BYTE_OF_FIVE_BASE) { | |
| 146 value -= _UTF8_FIRST_BYTE_OF_FOUR_BASE; | |
| 147 additionalBytes = 3; | |
|
Søren Gjesse
2012/02/01 11:25:25
Do we ever want to decode more than 4 bytes. There
dcarlson
2012/02/01 22:18:46
No valid codepoints, but the spec defines decoding
| |
| 148 } else if (value < _UTF8_FIRST_BYTE_OF_SIX_BASE) { | |
| 149 value -= _UTF8_FIRST_BYTE_OF_FIVE_BASE; | |
| 150 additionalBytes = 4; | |
| 151 } else if (value < _UTF8_FIRST_BYTE_BOUND_EXCL) { | |
| 152 value -= _UTF8_FIRST_BYTE_OF_SIX_BASE; | |
| 153 additionalBytes = 5; | |
| 154 } else { | |
| 155 codepointBuffer.add(UNICODE_REPLACEMENT_CHARACTER_CODEPOINT); | |
| 156 continue; | |
| 157 } | |
| 158 int j = 0; | |
| 159 while (j < additionalBytes && i < end) { | |
| 160 int nextValue = utf8EncodedBytes[i++]; | |
| 161 if (nextValue > _UTF8_ONE_BYTE_MAX && | |
| 162 nextValue < _UTF8_FIRST_BYTE_OF_TWO_BASE) { | |
| 163 value = (value << 6) | (nextValue & _UTF8_LO_SIX_BIT_MASK); | |
| 164 } else { | |
| 165 // if sequence-starting code unit, reposition cursor to start here | |
| 166 if (nextValue >= _UTF8_FIRST_BYTE_OF_TWO_BASE) { | |
| 167 i--; | |
| 168 } | |
| 169 break; | |
| 170 } | |
| 171 j++; | |
| 172 } | |
| 173 if (j == additionalBytes && ( | |
| 174 value < UNICODE_UTF16_RESERVED_LO || | |
| 175 value > UNICODE_UTF16_RESERVED_HI)) { | |
| 176 if (additionalBytes == 1 && value > _UTF8_ONE_BYTE_MAX) { | |
| 177 codepointBuffer.add(value); | |
| 178 } else if (additionalBytes == 2 && value > _UTF8_TWO_BYTE_MAX) { | |
| 179 codepointBuffer.add(value); | |
| 180 } else if (additionalBytes == 3 && value > _UTF8_THREE_BYTE_MAX && | |
| 181 value <= UNICODE_VALID_RANGE_MAX) { | |
| 182 codepointBuffer.add(value); | |
| 183 } else { | |
| 184 codepointBuffer.add(UNICODE_REPLACEMENT_CHARACTER_CODEPOINT); | |
| 185 } | |
| 186 } else { | |
| 187 codepointBuffer.add(UNICODE_REPLACEMENT_CHARACTER_CODEPOINT); | |
| 188 continue; | |
| 189 } | |
| 190 } | |
| 191 } else { | |
| 192 codepointBuffer.add(UNICODE_REPLACEMENT_CHARACTER_CODEPOINT); | |
| 193 continue; | |
| 194 } | |
| 195 } | |
| 196 return codepointBuffer; | |
| 197 } | |
| OLD | NEW |