1use std::ptr;
19
20const MAX_HASH32: u64 = (1 << 31) - 1;
21
22#[allow(dead_code)]
23pub enum StringFlag {
24 LATIN1 = 0,
25 UTF8 = 1,
26}
27
28fn swap_endian(value: u16) -> u16 {
30 value.rotate_right(8)
31}
32
33pub fn to_utf8(utf16: &[u16], is_little_endian: bool) -> Result<Vec<u8>, String> {
35 let mut utf8_bytes: Vec<u8> = Vec::with_capacity(utf16.len() * 3);
38 let ptr = utf8_bytes.as_mut_ptr();
39 let mut offset = 0;
40 let mut iter = utf16.iter();
41 while let Some(&wc) = iter.next() {
42 let wc = if is_little_endian {
43 swap_endian(wc)
44 } else {
45 wc
46 };
47 match wc {
48 code_point if code_point < 0x80 => {
49 unsafe {
50 ptr.add(offset).write(code_point as u8);
51 }
52 offset += 1;
53 }
54 code_point if code_point < 0x800 => {
55 let bytes = [
56 ((code_point >> 6) & 0b1_1111) as u8 | 0b1100_0000,
57 (code_point & 0b11_1111) as u8 | 0b1000_0000,
58 ];
59 unsafe {
60 ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.add(offset), 2);
61 }
62 offset += 2;
63 }
64 wc1 if (0xd800..=0xdbff).contains(&wc1) => {
65 if let Some(&wc2) = iter.next() {
66 let wc2 = if is_little_endian {
67 swap_endian(wc2)
68 } else {
69 wc2
70 };
71 if !(0xdc00..=0xdfff).contains(&wc2) {
72 return Err("Invalid UTF-16 string: wrong surrogate pair".to_string());
73 }
74 let code_point =
75 ((((wc1 as u32) - 0xd800) << 10) | ((wc2 as u32) - 0xdc00)) + 0x10000;
76 let bytes = [
77 ((code_point >> 18) & 0b111) as u8 | 0b1111_0000,
78 ((code_point >> 12) & 0b11_1111) as u8 | 0b1000_0000,
79 ((code_point >> 6) & 0b11_1111) as u8 | 0b1000_0000,
80 (code_point & 0b11_1111) as u8 | 0b1000_0000,
81 ];
82 unsafe {
83 ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.add(offset), 4);
84 }
85 offset += 4;
86 } else {
87 return Err("Invalid UTF-16 string: missing surrogate pair".to_string());
88 }
89 }
90 _ => {
91 let bytes = [
92 ((wc >> 12) | 0b1110_0000) as u8,
93 ((wc >> 6) & 0b11_1111) as u8 | 0b1000_0000,
94 (wc & 0b11_1111) as u8 | 0b1000_0000,
95 ];
96 unsafe {
97 ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.add(offset), 3);
98 }
99 offset += 3;
100 }
101 }
102 }
103 unsafe {
104 utf8_bytes.set_len(offset);
105 }
106 Ok(utf8_bytes)
107}
108
109pub fn to_snake_case(name: &str) -> String {
111 let mut result = String::with_capacity(name.len() + 4);
112 let chars: Vec<char> = name.chars().collect();
113
114 for (i, &c) in chars.iter().enumerate() {
115 if c.is_ascii_uppercase() {
116 if i > 0 {
117 let prev_upper = chars.get(i - 1).is_some_and(|c| c.is_ascii_uppercase());
118 let next_upper_or_end = chars.get(i + 1).map_or(true, |c| c.is_ascii_uppercase());
119 if !prev_upper || !next_upper_or_end {
120 result.push('_');
121 }
122 }
123 result.push(c.to_ascii_lowercase());
124 } else {
125 result.push(c);
126 }
127 }
128 result
129}
130
131pub fn to_camel_case(name: &str) -> String {
133 let mut result = String::with_capacity(name.len());
134 let mut capitalize_next = false;
135
136 for c in name.chars() {
137 if c == '_' {
138 capitalize_next = true;
139 } else if capitalize_next {
140 result.push(c.to_ascii_uppercase());
141 capitalize_next = false;
142 } else {
143 result.push(c);
144 }
145 }
146 result
147}
148
149#[allow(dead_code)]
150pub fn compute_string_hash(s: &str) -> u32 {
151 let mut hash: u64 = 17;
152 s.as_bytes().iter().for_each(|b| {
153 hash = (hash * 31) + (*b as u64);
154 while hash >= MAX_HASH32 {
155 hash /= 7;
156 }
157 });
158 hash as u32
159}
160
161#[cfg(target_feature = "neon")]
162use std::arch::aarch64::*;
163
164#[cfg(target_feature = "avx2")]
165use std::arch::x86_64::*;
166
167#[cfg(target_feature = "sse2")]
168use std::arch::x86_64::*;
169
170#[cfg(target_arch = "x86_64")]
171pub const MIN_DIM_SIZE_AVX: usize = 32;
172
173#[cfg(any(
174 target_arch = "x86",
175 target_arch = "x86_64",
176 all(target_arch = "aarch64", target_feature = "neon")
177))]
178pub const MIN_DIM_SIZE_SIMD: usize = 16;
179
180#[cfg(target_arch = "x86_64")]
181unsafe fn is_latin_avx(s: &str) -> bool {
182 let bytes = s.as_bytes();
183 let len = bytes.len();
184 let mut i = 0;
185 while i + MIN_DIM_SIZE_AVX <= len {
187 let chunk = _mm256_loadu_si256(bytes.as_ptr().add(i) as *const __m256i);
188 let hi_mask = _mm256_set1_epi8(0x80u8 as i8);
189 let masked = _mm256_and_si256(chunk, hi_mask);
190 let cmp = _mm256_cmpeq_epi8(masked, _mm256_setzero_si256());
191 if _mm256_movemask_epi8(cmp) != -1 {
192 break;
193 }
194 i += MIN_DIM_SIZE_AVX;
195 }
196 let s_tail = &s[i..];
198 for c in s_tail.chars() {
199 if c as u32 > 0xFF {
200 return false;
201 }
202 }
203 true
204}
205
206#[cfg(target_feature = "sse2")]
207unsafe fn is_latin_sse(s: &str) -> bool {
208 let bytes = s.as_bytes();
209 let len = bytes.len();
210 let mut i = 0;
211 while i + MIN_DIM_SIZE_SIMD <= len {
213 let chunk = _mm_loadu_si128(bytes.as_ptr().add(i) as *const __m128i);
214 let hi_mask = _mm_set1_epi8(0x80u8 as i8);
215 let masked = _mm_and_si128(chunk, hi_mask);
216 let cmp = _mm_cmpeq_epi8(masked, _mm_setzero_si128());
217 if _mm_movemask_epi8(cmp) != 0xFFFF {
218 break;
219 }
220 i += MIN_DIM_SIZE_SIMD;
221 }
222 let s_tail = &s[i..];
224 for c in s_tail.chars() {
225 if c as u32 > 0xFF {
226 return false;
227 }
228 }
229 true
230}
231
232#[cfg(target_feature = "neon")]
233unsafe fn is_latin_neon(s: &str) -> bool {
234 let bytes = s.as_bytes();
235 let len = bytes.len();
236 let mut i = 0;
237 while i + MIN_DIM_SIZE_SIMD <= len {
239 let chunk = vld1q_u8(bytes.as_ptr().add(i));
240 let hi_mask = vdupq_n_u8(0x80);
241 let masked = vandq_u8(chunk, hi_mask);
242 if vmaxvq_u8(masked) != 0 {
243 break;
244 }
245 i += MIN_DIM_SIZE_SIMD;
246 }
247 let s_tail = &s[i..];
249 for c in s_tail.chars() {
250 if c as u32 > 0xFF {
251 return false;
252 }
253 }
254 true
255}
256
257fn is_latin_standard(s: &str) -> bool {
258 s.chars().all(|c| c as u32 <= 0xFF)
259}
260
261pub fn is_latin(s: &str) -> bool {
262 #[cfg(target_arch = "x86_64")]
263 {
264 if is_x86_feature_detected!("avx")
265 && is_x86_feature_detected!("fma")
266 && s.len() >= MIN_DIM_SIZE_AVX
267 {
268 return unsafe { is_latin_avx(s) };
269 }
270 }
271
272 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
273 {
274 if is_x86_feature_detected!("sse") && s.len() >= MIN_DIM_SIZE_SIMD {
275 return unsafe { is_latin_sse(s) };
276 }
277 }
278
279 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
280 {
281 if std::arch::is_aarch64_feature_detected!("neon") && s.len() >= MIN_DIM_SIZE_SIMD {
282 return unsafe { is_latin_neon(s) };
283 }
284 }
285 is_latin_standard(s)
286}
287
288#[cfg(target_arch = "x86_64")]
289unsafe fn get_latin1_length_avx(s: &str) -> i32 {
290 let bytes = s.as_bytes();
291 let len = bytes.len();
292 let mut count = 0;
293 while count + MIN_DIM_SIZE_AVX <= len {
295 let chunk = _mm256_loadu_si256(bytes.as_ptr().add(count) as *const __m256i);
296 let hi_mask = _mm256_set1_epi8(0x80u8 as i8);
297 let masked = _mm256_and_si256(chunk, hi_mask);
298 let cmp = _mm256_cmpeq_epi8(masked, _mm256_setzero_si256());
299 if _mm256_movemask_epi8(cmp) != -1 {
300 break;
301 }
302 count += MIN_DIM_SIZE_AVX;
303 }
304 let s_tail = &s[count..];
306 for c in s_tail.chars() {
307 if c as u32 > 0xFF {
308 return -1;
309 }
310 count += 1;
311 }
312 count as i32
313}
314
315#[cfg(target_feature = "sse2")]
316unsafe fn get_latin1_length_sse(s: &str) -> i32 {
317 let bytes = s.as_bytes();
318 let len = bytes.len();
319 let mut count = 0;
320 while count + MIN_DIM_SIZE_SIMD <= len {
322 let chunk = _mm_loadu_si128(bytes.as_ptr().add(count) as *const __m128i);
323 let hi_mask = _mm_set1_epi8(0x80u8 as i8);
324 let masked = _mm_and_si128(chunk, hi_mask);
325 let cmp = _mm_cmpeq_epi8(masked, _mm_setzero_si128());
326 if _mm_movemask_epi8(cmp) != 0xFFFF {
327 break;
328 }
329 count += MIN_DIM_SIZE_SIMD;
330 }
331 let s_tail = &s[count..];
333 for c in s_tail.chars() {
334 if c as u32 > 0xFF {
335 return -1;
336 }
337 count += 1;
338 }
339 count as i32
340}
341
342#[cfg(target_feature = "neon")]
343unsafe fn get_latin1_length_neon(s: &str) -> i32 {
344 let bytes = s.as_bytes();
345 let len = bytes.len();
346 let mut count = 0;
347 while count + MIN_DIM_SIZE_SIMD <= len {
349 let chunk = vld1q_u8(bytes.as_ptr().add(count));
350 let hi_mask = vdupq_n_u8(0x80);
351 let masked = vandq_u8(chunk, hi_mask);
352 if vmaxvq_u8(masked) != 0 {
353 break;
354 }
355 count += MIN_DIM_SIZE_SIMD;
356 }
357 let s_tail = &s[count..];
359 for c in s_tail.chars() {
360 if c as u32 > 0xFF {
361 return -1;
362 }
363 count += 1;
364 }
365 count as i32
366}
367
368fn get_latin1_length_standard(s: &str) -> i32 {
369 let mut count = 0;
370 for c in s.chars() {
371 if c as u32 > 0xFF {
372 return -1;
373 }
374 count += 1;
375 }
376 count
377}
378
379pub fn get_latin1_length(s: &str) -> i32 {
380 #[cfg(target_arch = "x86_64")]
381 {
382 if is_x86_feature_detected!("avx")
383 && is_x86_feature_detected!("fma")
384 && s.len() >= MIN_DIM_SIZE_AVX
385 {
386 return unsafe { get_latin1_length_avx(s) };
387 }
388 }
389
390 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
391 {
392 if is_x86_feature_detected!("sse") && s.len() >= MIN_DIM_SIZE_SIMD {
393 return unsafe { get_latin1_length_sse(s) };
394 }
395 }
396
397 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
398 {
399 if std::arch::is_aarch64_feature_detected!("neon") && s.len() >= MIN_DIM_SIZE_SIMD {
400 return unsafe { get_latin1_length_neon(s) };
401 }
402 }
403 get_latin1_length_standard(s)
404}
405
406#[cfg(test)]
407mod latin_tests {
408 use super::*;
410 use rand::Rng;
411
412 fn generate_random_string(length: usize) -> String {
413 const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
414 let mut rng = rand::thread_rng();
415
416 let result: String = (0..length)
417 .map(|_| {
418 let idx = rng.gen_range(0..CHARSET.len());
419 CHARSET[idx] as char
420 })
421 .collect();
422
423 result
424 }
425
426 #[test]
427 fn test_is_latin() {
428 let s = generate_random_string(1000);
429 let not_latin_str = generate_random_string(1000) + "abc\u{1234}";
430
431 #[cfg(target_arch = "x86_64")]
432 {
433 if is_x86_feature_detected!("avx") && is_x86_feature_detected!("fma") {
434 assert!(unsafe { is_latin_avx(&s) });
435 assert!(!unsafe { is_latin_avx(¬_latin_str) });
436 }
437 }
438
439 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
440 {
441 if is_x86_feature_detected!("sse") && s.len() >= MIN_DIM_SIZE_SIMD {
442 assert!(unsafe { is_latin_sse(&s) });
443 assert!(!unsafe { is_latin_sse(¬_latin_str) });
444 }
445 }
446
447 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
448 {
449 if std::arch::is_aarch64_feature_detected!("neon") && s.len() >= MIN_DIM_SIZE_SIMD {
450 assert!(unsafe { is_latin_neon(&s) });
451 assert!(!unsafe { is_latin_neon(¬_latin_str) });
452 }
453 }
454 assert!(is_latin_standard(&s));
455 assert!(!is_latin_standard(¬_latin_str));
456 }
457}
458
459fn fmix64(mut k: u64) -> u64 {
460 k ^= k >> 33;
461 k = k.wrapping_mul(0xff51afd7ed558ccdu64);
462 k ^= k >> 33;
463 k = k.wrapping_mul(0xc4ceb9fe1a85ec53u64);
464 k ^= k >> 33;
465
466 k
467}
468
469pub fn murmurhash3_x64_128(bytes: &[u8], seed: u64) -> (u64, u64) {
470 let c1 = 0x87c37b91114253d5u64;
471 let c2 = 0x4cf5ad432745937fu64;
472 let read_size = 16;
473 let len = bytes.len() as u64;
474 let block_count = len / read_size;
475
476 let (mut h1, mut h2) = (seed, seed);
477
478 for i in 0..block_count as usize {
479 let offset = i * read_size as usize;
480 let mut k1 = u64::from_le_bytes(bytes[offset..offset + 8].try_into().unwrap());
483 let mut k2 = u64::from_le_bytes(bytes[offset + 8..offset + 16].try_into().unwrap());
484
485 k1 = k1.wrapping_mul(c1);
486 k1 = k1.rotate_left(31);
487 k1 = k1.wrapping_mul(c2);
488 h1 ^= k1;
489
490 h1 = h1.rotate_left(27);
491 h1 = h1.wrapping_add(h2);
492 h1 = h1.wrapping_mul(5);
493 h1 = h1.wrapping_add(0x52dce729);
494
495 k2 = k2.wrapping_mul(c2);
496 k2 = k2.rotate_left(33);
497 k2 = k2.wrapping_mul(c1);
498 h2 ^= k2;
499
500 h2 = h2.rotate_left(31);
501 h2 = h2.wrapping_add(h1);
502 h2 = h2.wrapping_mul(5);
503 h2 = h2.wrapping_add(0x38495ab5);
504 }
505 let (mut k1, mut k2) = (0u64, 0u64);
506
507 if len & 15 == 15 {
508 k2 ^= (bytes[(block_count * read_size) as usize + 14] as u64) << 48;
509 }
510 if len & 15 >= 14 {
511 k2 ^= (bytes[(block_count * read_size) as usize + 13] as u64) << 40;
512 }
513 if len & 15 >= 13 {
514 k2 ^= (bytes[(block_count * read_size) as usize + 12] as u64) << 32;
515 }
516 if len & 15 >= 12 {
517 k2 ^= (bytes[(block_count * read_size) as usize + 11] as u64) << 24;
518 }
519 if len & 15 >= 11 {
520 k2 ^= (bytes[(block_count * read_size) as usize + 10] as u64) << 16;
521 }
522 if len & 15 >= 10 {
523 k2 ^= (bytes[(block_count * read_size) as usize + 9] as u64) << 8;
524 }
525 if len & 15 >= 9 {
526 k2 ^= bytes[(block_count * read_size) as usize + 8] as u64;
527 k2 = k2.wrapping_mul(c2);
528 k2 = k2.rotate_left(33);
529 k2 = k2.wrapping_mul(c1);
530 h2 ^= k2;
531 }
532
533 if len & 15 >= 8 {
534 k1 ^= (bytes[(block_count * read_size) as usize + 7] as u64) << 56;
535 }
536 if len & 15 >= 7 {
537 k1 ^= (bytes[(block_count * read_size) as usize + 6] as u64) << 48;
538 }
539 if len & 15 >= 6 {
540 k1 ^= (bytes[(block_count * read_size) as usize + 5] as u64) << 40;
541 }
542 if len & 15 >= 5 {
543 k1 ^= (bytes[(block_count * read_size) as usize + 4] as u64) << 32;
544 }
545 if len & 15 >= 4 {
546 k1 ^= (bytes[(block_count * read_size) as usize + 3] as u64) << 24;
547 }
548 if len & 15 >= 3 {
549 k1 ^= (bytes[(block_count * read_size) as usize + 2] as u64) << 16;
550 }
551 if len & 15 >= 2 {
552 k1 ^= (bytes[(block_count * read_size) as usize + 1] as u64) << 8;
553 }
554 if len & 15 >= 1 {
555 k1 ^= bytes[(block_count * read_size) as usize] as u64;
556 k1 = k1.wrapping_mul(c1);
557 k1 = k1.rotate_left(31);
558 k1 = k1.wrapping_mul(c2);
559 h1 ^= k1;
560 }
561
562 h1 ^= bytes.len() as u64;
563 h2 ^= bytes.len() as u64;
564
565 h1 = h1.wrapping_add(h2);
566 h2 = h2.wrapping_add(h1);
567
568 h1 = fmix64(h1);
569 h2 = fmix64(h2);
570
571 h1 = h1.wrapping_add(h2);
572 h2 = h2.wrapping_add(h1);
573
574 (h1, h2)
575}
576
577#[cfg(test)]
578mod test_hash {
579 use super::murmurhash3_x64_128;
580
581 #[test]
582 fn test_empty_string() {
583 assert!(murmurhash3_x64_128("".as_bytes(), 0) == (0, 0));
584 }
585
586 #[test]
587 fn test_tail_lengths() {
588 assert!(
589 murmurhash3_x64_128("1".as_bytes(), 0) == (8213365047359667313, 10676604921780958775)
590 );
591 assert!(
592 murmurhash3_x64_128("12".as_bytes(), 0) == (5355690773644049813, 9855895140584599837)
593 );
594 assert!(
595 murmurhash3_x64_128("123".as_bytes(), 0) == (10978418110857903978, 4791445053355511657)
596 );
597 assert!(
598 murmurhash3_x64_128("1234".as_bytes(), 0) == (619023178690193332, 3755592904005385637)
599 );
600 assert!(
601 murmurhash3_x64_128("12345".as_bytes(), 0)
602 == (2375712675693977547, 17382870096830835188)
603 );
604 assert!(
605 murmurhash3_x64_128("123456".as_bytes(), 0)
606 == (16435832985690558678, 5882968373513761278)
607 );
608 assert!(
609 murmurhash3_x64_128("1234567".as_bytes(), 0)
610 == (3232113351312417698, 4025181827808483669)
611 );
612 assert!(
613 murmurhash3_x64_128("12345678".as_bytes(), 0)
614 == (4272337174398058908, 10464973996478965079)
615 );
616 assert!(
617 murmurhash3_x64_128("123456789".as_bytes(), 0)
618 == (4360720697772133540, 11094893415607738629)
619 );
620 assert!(
621 murmurhash3_x64_128("123456789a".as_bytes(), 0)
622 == (12594836289594257748, 2662019112679848245)
623 );
624 assert!(
625 murmurhash3_x64_128("123456789ab".as_bytes(), 0)
626 == (6978636991469537545, 12243090730442643750)
627 );
628 assert!(
629 murmurhash3_x64_128("123456789abc".as_bytes(), 0)
630 == (211890993682310078, 16480638721813329343)
631 );
632 assert!(
633 murmurhash3_x64_128("123456789abcd".as_bytes(), 0)
634 == (12459781455342427559, 3193214493011213179)
635 );
636 assert!(
637 murmurhash3_x64_128("123456789abcde".as_bytes(), 0)
638 == (12538342858731408721, 9820739847336455216)
639 );
640 assert!(
641 murmurhash3_x64_128("123456789abcdef".as_bytes(), 0)
642 == (9165946068217512774, 2451472574052603025)
643 );
644 assert!(
645 murmurhash3_x64_128("123456789abcdef1".as_bytes(), 0)
646 == (9259082041050667785, 12459473952842597282)
647 );
648 }
649
650 #[test]
651 fn test_large_data() {
652 assert!(murmurhash3_x64_128("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam at consequat massa. Cras eleifend pellentesque ex, at dignissim libero maximus ut. Sed eget nulla felis".as_bytes(), 0)
653 == (9455322759164802692, 17863277201603478371));
654 }
655
656 #[test]
657 fn test_unaligned_full_block() {
658 let data = b"x123456789abcdef1";
659 assert!(murmurhash3_x64_128(&data[1..], 0) == (9259082041050667785, 12459473952842597282));
660 }
661}
662
663#[cfg(test)]
664mod case_tests {
665 use super::*;
666
667 #[test]
668 fn test_to_snake_case() {
669 assert_eq!(to_snake_case("camelCase"), "camel_case");
670 assert_eq!(to_snake_case("PascalCase"), "pascal_case");
671 assert_eq!(to_snake_case("HTTPRequest"), "http_request");
672 assert_eq!(to_snake_case("simpleTest"), "simple_test");
673 assert_eq!(to_snake_case("already_snake"), "already_snake");
674 assert_eq!(to_snake_case("ABC"), "abc");
675 }
676
677 #[test]
678 fn test_to_camel_case() {
679 assert_eq!(to_camel_case("snake_case"), "snakeCase");
680 assert_eq!(to_camel_case("simple_test"), "simpleTest");
681 assert_eq!(to_camel_case("already"), "already");
682 assert_eq!(to_camel_case("a_b_c"), "aBC");
683 }
684}
685
686pub mod buffer_rw_string {
687 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
688 use std::arch::aarch64::*;
689 #[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
690 use std::arch::x86_64::*;
691 #[cfg(all(
692 any(target_arch = "x86", target_arch = "x86_64"),
693 target_feature = "sse2",
694 not(target_feature = "avx2")
695 ))]
696 use std::arch::x86_64::*;
697
698 use crate::buffer::{Reader, Writer};
699 use crate::error::Error;
700
701 #[inline]
702 pub fn write_latin1_standard(writer: &mut Writer, s: &str) {
703 for c in s.chars() {
704 let b = c as u32;
705 assert!(b <= 0xFF, "Non-Latin1 character found");
706 writer.write_u8(b as u8);
707 }
708 }
709
710 #[inline(always)]
711 pub fn write_latin1_string(writer: &mut Writer, s: &str) {
712 if s.len() < 128 {
713 let bytes = s.as_bytes();
715 let is_ascii = bytes.iter().all(|&b| b < 0x80);
717 if is_ascii {
718 writer.bf.reserve(s.len());
719 writer.bf.extend_from_slice(bytes);
720 } else {
721 writer.bf.reserve(s.len());
723 for c in s.chars() {
724 let v = c as u32;
725 assert!(v <= 0xFF, "Non-Latin1 character found");
726 writer.bf.push(v as u8);
727 }
728 }
729 return;
730 }
731 write_latin1_simd(writer, s);
732 }
733
734 #[inline]
735 pub fn write_utf8_standard(writer: &mut Writer, s: &str) {
736 let bytes = s.as_bytes();
737 writer.bf.extend_from_slice(bytes);
738 }
739
740 #[inline]
741 pub fn write_utf16_standard(writer: &mut Writer, utf16: &[u16]) {
742 #[cfg(target_endian = "little")]
743 {
744 let total_bytes = utf16.len() * 2;
745 let old_len = writer.bf.len();
746 writer.bf.reserve(total_bytes);
747 unsafe {
748 let dest = writer.bf.as_mut_ptr().add(old_len);
749 let src = utf16.as_ptr() as *const u8;
750 std::ptr::copy_nonoverlapping(src, dest, total_bytes);
751 writer.bf.set_len(old_len + total_bytes);
752 }
753 }
754 #[cfg(target_endian = "big")]
755 {
756 let total_bytes = utf16.len() * 2;
757 let old_len = writer.bf.len();
758 writer.bf.reserve(total_bytes);
759 unsafe {
760 let dest = writer.bf.as_mut_ptr().add(old_len);
761 for (i, &unit) in utf16.iter().enumerate() {
763 let swapped = unit.swap_bytes();
764 let ptr = dest.add(i * 2) as *mut u16;
765 std::ptr::write_unaligned(ptr, swapped);
766 }
767 writer.bf.set_len(old_len + total_bytes);
768 }
769 }
770 }
771
772 #[inline]
773 pub fn read_latin1_standard(reader: &mut Reader, len: usize) -> Result<String, Error> {
774 let slice = reader.sub_slice(reader.get_cursor(), reader.get_cursor() + len)?;
775 let result: String = slice.iter().map(|&b| b as char).collect();
776 reader.move_next(len);
777 Ok(result)
778 }
779
780 #[inline]
781 pub fn read_utf8_standard(reader: &mut Reader, len: usize) -> Result<String, Error> {
782 let slice = reader.sub_slice(reader.get_cursor(), reader.get_cursor() + len)?;
783 let value = std::str::from_utf8(slice)
786 .map_err(|_| Error::encoding_error("invalid UTF-8 string"))?
787 .to_owned();
788 reader.move_next(len);
789 Ok(value)
790 }
791
792 #[inline]
793 pub fn read_utf16_standard(reader: &mut Reader, len: usize) -> Result<String, Error> {
794 if len % 2 != 0 {
795 return Err(Error::encoding_error("UTF-16 length must be even"));
796 }
797 unsafe {
798 let slice = std::slice::from_raw_parts(reader.bf.as_ptr().add(reader.cursor), len);
799 let units: Vec<u16> = slice
800 .chunks_exact(2)
801 .map(|c| u16::from_le_bytes([c[0], c[1]]))
802 .collect();
803 reader.move_next(len);
804 Ok(String::from_utf16_lossy(&units))
805 }
806 }
807
808 #[inline]
809 fn is_ascii_bytes(bytes: &[u8]) -> bool {
810 let len = bytes.len();
811 let mut i = 0;
812
813 #[cfg(target_arch = "x86_64")]
814 unsafe {
815 if is_x86_feature_detected!("avx2") && len >= 32 {
816 while i + 32 <= len {
817 let chunk = _mm256_loadu_si256(bytes.as_ptr().add(i) as *const __m256i);
818 let mask = _mm256_movemask_epi8(chunk);
819 if mask != 0 {
820 return false;
821 }
822 i += 32;
823 }
824 }
825 }
826
827 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
828 unsafe {
829 if is_x86_feature_detected!("sse2") && len >= 16 {
830 while i + 16 <= len {
831 let chunk = _mm_loadu_si128(bytes.as_ptr().add(i) as *const __m128i);
832 let mask = _mm_movemask_epi8(chunk);
833 if mask != 0 {
834 return false;
835 }
836 i += 16;
837 }
838 }
839 }
840
841 #[cfg(target_arch = "aarch64")]
842 unsafe {
843 if std::arch::is_aarch64_feature_detected!("neon") && len >= 16 {
844 while i + 16 <= len {
845 let chunk = vld1q_u8(bytes.as_ptr().add(i));
846 if vmaxvq_u8(chunk) >= 0x80 {
847 return false;
848 }
849 i += 16;
850 }
851 }
852 }
853
854 bytes[i..].iter().all(|&b| b < 0x80)
856 }
857
858 #[inline]
859 pub fn write_latin1_simd(writer: &mut Writer, s: &str) {
860 if s.is_empty() {
861 return;
862 }
863
864 let bytes = s.as_bytes();
865
866 if is_ascii_bytes(bytes) {
869 let len = bytes.len();
871 writer.bf.reserve(len);
872 writer.bf.extend_from_slice(bytes);
873 } else {
874 let mut buf: Vec<u8> = Vec::with_capacity(s.len());
877 for c in s.chars() {
878 let v = c as u32;
879 assert!(v <= 0xFF, "Non-Latin1 character found");
880 buf.push(v as u8);
881 }
882 let len = buf.len();
883 writer.bf.reserve(len);
884 writer.bf.extend_from_slice(&buf);
885 }
886 }
887
888 #[inline]
889 pub fn read_latin1_simd(reader: &mut Reader, len: usize) -> Result<String, Error> {
890 if len == 0 {
891 return Ok(String::new());
892 }
893 let src = reader.sub_slice(reader.get_cursor(), reader.get_cursor() + len)?;
894
895 let mut out: Vec<u8> = Vec::with_capacity(len * 2);
897
898 unsafe {
899 let out_ptr = out.as_mut_ptr();
900 let mut out_len = 0usize;
901 let mut i = 0usize;
902
903 #[cfg(target_arch = "x86_64")]
905 {
906 if std::arch::is_x86_feature_detected!("avx2") {
907 use std::arch::x86_64::*;
908 while i + 32 <= len {
909 let ptr = src.as_ptr().add(i) as *const __m256i;
910 let chunk = _mm256_loadu_si256(ptr);
911 let mask = _mm256_movemask_epi8(chunk);
912 if mask == 0 {
913 _mm256_storeu_si256(out_ptr.add(out_len) as *mut __m256i, chunk);
915 out_len += 32;
916 i += 32;
917 continue;
918 } else {
919 break;
921 }
922 }
923 }
924 }
925
926 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
928 {
929 if std::arch::is_x86_feature_detected!("sse2") {
930 use std::arch::x86_64::*;
931 while i + 16 <= len {
932 let ptr = src.as_ptr().add(i) as *const __m128i;
933 let chunk = _mm_loadu_si128(ptr);
934 let mask = _mm_movemask_epi8(chunk);
935 if mask == 0 {
936 _mm_storeu_si128(out_ptr.add(out_len) as *mut __m128i, chunk);
938 out_len += 16;
939 i += 16;
940 continue;
941 } else {
942 break;
943 }
944 }
945 }
946 }
947
948 #[cfg(target_arch = "aarch64")]
950 {
951 if std::arch::is_aarch64_feature_detected!("neon") {
952 use std::arch::aarch64::*;
953 while i + 16 <= len {
954 let ptr = src.as_ptr().add(i);
955 let v = vld1q_u8(ptr);
956 if vmaxvq_u8(v) < 0x80 {
958 vst1q_u8(out_ptr.add(out_len), v);
960 out_len += 16;
961 i += 16;
962 continue;
963 } else {
964 break;
965 }
966 }
967 }
968 }
969
970 while i < len {
974 let b = *src.get_unchecked(i);
975 if b < 0x80 {
976 *out_ptr.add(out_len) = b;
977 out_len += 1;
978 } else {
979 *out_ptr.add(out_len) = 0xC0 | (b >> 6);
982 *out_ptr.add(out_len + 1) = 0x80 | (b & 0x3F);
983 out_len += 2;
984 }
985 i += 1;
986 }
987
988 out.set_len(out_len);
989 }
990 reader.move_next(len);
991 Ok(unsafe { String::from_utf8_unchecked(out) })
992 }
993
994 #[cfg(test)]
995 mod tests {
996 use super::*;
997 use crate::buffer::{Reader, Writer};
998
999 #[test]
1000 fn test_latin1() {
1001 let samples = [
1002 "Hello World!",
1003 "Rusty Café",
1004 "1234567890",
1005 "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝ",
1006 ];
1007
1008 for s in samples {
1009 let mut buffer = vec![];
1010 let mut writer = Writer::from_buffer(&mut buffer);
1011 write_latin1_simd(&mut writer, s);
1012 write_latin1_simd(&mut writer, s);
1013 let bytes = &*writer.dump();
1014 let bytes_len = bytes.len() / 2;
1015 let mut reader = Reader::new(bytes);
1016 assert_eq!(read_latin1_standard(&mut reader, bytes_len).unwrap(), s);
1017 assert_eq!(read_latin1_standard(&mut reader, bytes_len).unwrap(), s);
1018
1019 let mut buffer = vec![];
1020 let mut writer = Writer::from_buffer(&mut buffer);
1021 write_latin1_standard(&mut writer, s);
1022 write_latin1_standard(&mut writer, s);
1023 let bytes = &*writer.dump();
1024 let bytes_len = bytes.len() / 2;
1025 let mut reader = Reader::new(bytes);
1026 assert_eq!(read_latin1_simd(&mut reader, bytes_len).unwrap(), s);
1027 assert_eq!(read_latin1_simd(&mut reader, bytes_len).unwrap(), s);
1028 }
1029 }
1030
1031 #[test]
1032 fn test_utf8() {
1033 let samples = [
1034 "hello",
1035 "rust语言",
1036 "你好,世界",
1037 "emoji 😀😃😄😁",
1038 "mixed ASCII + 中文 + emoji 😁",
1039 ];
1040
1041 for s in samples {
1042 let bytes_len = s.len();
1043
1044 let mut buffer = vec![];
1045 let mut writer = Writer::from_buffer(&mut buffer);
1046 write_utf8_standard(&mut writer, s);
1047 write_utf8_standard(&mut writer, s);
1048 let bytes = &*writer.dump();
1049 let mut reader = Reader::new(bytes);
1050 assert_eq!(read_utf8_standard(&mut reader, bytes_len).unwrap(), s);
1051 assert_eq!(read_utf8_standard(&mut reader, bytes_len).unwrap(), s);
1052 }
1053 }
1054
1055 #[test]
1056 fn test_utf16() {
1057 let samples = [
1058 "hello",
1059 "rust语言",
1060 "你好,世界",
1061 "emoji 😀😃😄😁",
1062 "混合文字 + emoji 🐍💻🦀",
1063 ];
1064 for s in samples {
1065 let utf16: Vec<u16> = s.encode_utf16().collect();
1066 let bytes_len = utf16.len() * 2;
1067
1068 let mut buffer = vec![];
1069 let mut writer = Writer::from_buffer(&mut buffer);
1070 write_utf16_standard(&mut writer, &utf16);
1071 write_utf16_standard(&mut writer, &utf16);
1072
1073 let mut buffer = vec![];
1074 let mut writer = Writer::from_buffer(&mut buffer);
1075 write_utf16_standard(&mut writer, &utf16);
1076 write_utf16_standard(&mut writer, &utf16);
1077 let bytes = &*writer.dump();
1078 let mut reader = Reader::new(bytes);
1079 assert_eq!(read_utf16_standard(&mut reader, bytes_len).unwrap(), s);
1080 assert_eq!(read_utf16_standard(&mut reader, bytes_len).unwrap(), s);
1081 }
1082 }
1083 }
1084}