(bytes)
| 192 | } |
| 193 | |
| 194 | static _decodeCodePointsFromUTF8Bytes (bytes) { |
| 195 | // In the worst case byte needs to be represented by one code point |
| 196 | // Create a fixed size array that gets sliced at the end |
| 197 | const size = bytes.length |
| 198 | const codePoints = new Array(size) |
| 199 | |
| 200 | let remainingBytes = 0 |
| 201 | let i = -1 |
| 202 | let j = 0 |
| 203 | let byte, codePoint |
| 204 | |
| 205 | while (++i < size) { |
| 206 | byte = bytes[i] |
| 207 | |
| 208 | if (byte > 0b01111111 && byte <= 0b10111111) { |
| 209 | // Continuation byte identified |
| 210 | if (--remainingBytes < 0) { |
| 211 | throw new TextEncodingError( |
| 212 | 'Invalid UTF-8 encoded text: ' + |
| 213 | `Unexpected continuation byte at 0x${i.toString(16)}`, i) |
| 214 | } |
| 215 | |
| 216 | // Append bits to current code point |
| 217 | codePoint = (codePoint << 6) | (byte & 0x3F) |
| 218 | |
| 219 | if (remainingBytes === 0) { |
| 220 | // Completed a code point |
| 221 | codePoints[j++] = codePoint |
| 222 | } |
| 223 | } else if (remainingBytes > 0) { |
| 224 | // this must be a continuation byte |
| 225 | throw new TextEncodingError( |
| 226 | 'Invalid UTF-8 encoded text: ' + |
| 227 | `Continuation byte expected at 0x${i.toString(16)}`, i) |
| 228 | } else if (byte <= 0b01111111) { |
| 229 | // 1 byte code point |
| 230 | codePoints[j++] = byte |
| 231 | } else if (byte <= 0b11011111) { |
| 232 | // 2 byte code point |
| 233 | codePoint = byte & 0b00011111 |
| 234 | remainingBytes = 1 |
| 235 | } else if (byte <= 0b11101111) { |
| 236 | // 3 byte code point |
| 237 | codePoint = byte & 0b00001111 |
| 238 | remainingBytes = 2 |
| 239 | } else if (byte <= 0b11110111) { |
| 240 | // 4 byte code point |
| 241 | codePoint = byte & 0b00000111 |
| 242 | remainingBytes = 3 |
| 243 | } else { |
| 244 | throw new TextEncodingError( |
| 245 | 'Invalid UTF-8 encoded text: ' + |
| 246 | `Invalid byte ${byte} at 0x${i.toString(16)}`, i) |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | if (remainingBytes !== 0) { |
| 251 | throw new TextEncodingError( |
no test coverage detected