(&mut self, len: usize)
| 910 | |
| 911 | #[inline(always)] |
| 912 | pub fn read_latin1_string(&mut self, len: usize) -> Result<String, Error> { |
| 913 | self.check_bound(len)?; |
| 914 | if len < SIMD_THRESHOLD { |
| 915 | // Fast path for small buffers |
| 916 | unsafe { |
| 917 | let src = self.sub_slice(self.cursor, self.cursor + len)?; |
| 918 | |
| 919 | // Check if all bytes are ASCII (< 0x80) |
| 920 | let is_ascii = src.iter().all(|&b| b < 0x80); |
| 921 | |
| 922 | if is_ascii { |
| 923 | // ASCII fast path: Latin1 == UTF-8, direct copy |
| 924 | let mut vec = Vec::with_capacity(len); |
| 925 | let dst = vec.as_mut_ptr(); |
| 926 | std::ptr::copy_nonoverlapping(src.as_ptr(), dst, len); |
| 927 | vec.set_len(len); |
| 928 | self.move_next(len); |
| 929 | Ok(String::from_utf8_unchecked(vec)) |
| 930 | } else { |
| 931 | // Contains Latin1 bytes (0x80-0xFF): must convert to UTF-8 |
| 932 | let mut out: Vec<u8> = Vec::with_capacity(len * 2); |
| 933 | let out_ptr = out.as_mut_ptr(); |
| 934 | let mut out_len = 0; |
| 935 | |
| 936 | for &b in src { |
| 937 | if b < 0x80 { |
| 938 | *out_ptr.add(out_len) = b; |
| 939 | out_len += 1; |
| 940 | } else { |
| 941 | // Latin1 -> UTF-8 encoding |
| 942 | *out_ptr.add(out_len) = 0xC0 | (b >> 6); |
| 943 | *out_ptr.add(out_len + 1) = 0x80 | (b & 0x3F); |
| 944 | out_len += 2; |
| 945 | } |
| 946 | } |
| 947 | |
| 948 | out.set_len(out_len); |
| 949 | self.move_next(len); |
| 950 | Ok(String::from_utf8_unchecked(out)) |
| 951 | } |
| 952 | } |
| 953 | } else { |
| 954 | // Use SIMD for larger strings where the overhead is amortized |
| 955 | read_latin1_simd(self, len) |
| 956 | } |
| 957 | } |
| 958 | |
| 959 | #[inline(always)] |
| 960 | pub fn read_utf8_string(&mut self, len: usize) -> Result<String, Error> { |
no test coverage detected