For UTF-8: returns the number of trailing bytes that form an incomplete multi-byte sequence at the end of `bytes`. Returns 0 if the sequence is complete or invalid.
(bytes: &[u8])
| 22 | /// multi-byte sequence at the end of `bytes`. Returns 0 if the sequence is |
| 23 | /// complete or invalid. |
| 24 | fn utf8_incomplete_tail(bytes: &[u8]) -> usize { |
| 25 | let len = bytes.len(); |
| 26 | for i in 1..=4.min(len) { |
| 27 | let b = bytes[len - i]; |
| 28 | if b < 0x80 { |
| 29 | return 0; |
| 30 | } |
| 31 | if b >= 0xC0 { |
| 32 | let expected = match b { |
| 33 | 0xC2..=0xDF => 2, |
| 34 | 0xE0..=0xEF => 3, |
| 35 | 0xF0..=0xF4 => 4, |
| 36 | _ => return 0, |
| 37 | }; |
| 38 | if i >= expected { |
| 39 | return 0; |
| 40 | } |
| 41 | // Validate continuation bytes have correct ranges |
| 42 | let tail = &bytes[len - i + 1..]; |
| 43 | for (j, &c) in tail.iter().enumerate() { |
| 44 | if j == 0 { |
| 45 | // First continuation byte has restricted ranges for some leads |
| 46 | let valid = match b { |
| 47 | 0xE0 => (0xA0..=0xBF).contains(&c), |
| 48 | 0xED => (0x80..=0x9F).contains(&c), |
| 49 | 0xF0 => (0x90..=0xBF).contains(&c), |
| 50 | 0xF4 => (0x80..=0x8F).contains(&c), |
| 51 | _ => (0x80..=0xBF).contains(&c), |
| 52 | }; |
| 53 | if !valid { |
| 54 | return 0; |
| 55 | } |
| 56 | } else if c & 0xC0 != 0x80 { |
| 57 | return 0; |
| 58 | } |
| 59 | } |
| 60 | return i; |
| 61 | } |
| 62 | } |
| 63 | 0 |
| 64 | } |
| 65 | |
| 66 | #[rquickjs::methods] |
| 67 | impl<'js> TextDecoder { |