@@ -2,6 +2,7 @@ const std = @import("./std.zig");
2
2
const assert = std .debug .assert ;
3
3
const testing = std .testing ;
4
4
const mem = std .mem ;
5
+ const builtin = @import ("builtin" );
5
6
6
7
/// Use this to replace an unknown, unrecognized, or unrepresentable character.
7
8
///
@@ -756,8 +757,34 @@ pub fn utf16leToUtf8Alloc(allocator: mem.Allocator, utf16le: []const u16) ![]u8
756
757
// optimistically guess that it will all be ascii.
757
758
var result = try std .ArrayList (u8 ).initCapacity (allocator , utf16le .len );
758
759
errdefer result .deinit ();
759
- var out_index : usize = 0 ;
760
- var it = Utf16LeIterator .init (utf16le );
760
+
761
+ var remaining = utf16le ;
762
+ if (builtin .zig_backend != .stage2_x86_64 ) {
763
+ const chunk_len = std .simd .suggestVectorSize (u16 ) orelse 1 ;
764
+ const Chunk = @Vector (chunk_len , u16 );
765
+
766
+ // Fast path. Check for and encode ASCII characters at the start of the input.
767
+ while (remaining .len >= chunk_len ) {
768
+ const chunk : Chunk = remaining [0.. chunk_len ].* ;
769
+ const mask : Chunk = @splat (std .mem .nativeToLittle (u16 , 0x7F ));
770
+ if (@reduce (.Or , chunk | mask != mask )) {
771
+ // found a non ASCII code unit
772
+ break ;
773
+ }
774
+ const chunk_byte_len = chunk_len * 2 ;
775
+ const chunk_bytes : @Vector (chunk_byte_len , u8 ) = (std .mem .sliceAsBytes (remaining )[0.. chunk_byte_len ]).* ;
776
+ const deinterlaced_bytes = std .simd .deinterlace (2 , chunk_bytes );
777
+ const ascii_bytes : [chunk_len ]u8 = deinterlaced_bytes [0 ];
778
+ // We allocated enough space to encode every UTF-16 code unit
779
+ // as ASCII, so if the entire string is ASCII then we are
780
+ // guaranteed to have enough space allocated
781
+ result .appendSliceAssumeCapacity (& ascii_bytes );
782
+ remaining = remaining [chunk_len .. ];
783
+ }
784
+ }
785
+
786
+ var out_index : usize = result .items .len ;
787
+ var it = Utf16LeIterator .init (remaining );
761
788
while (try it .nextCodepoint ()) | codepoint | {
762
789
const utf8_len = utf8CodepointSequenceLength (codepoint ) catch unreachable ;
763
790
try result .resize (result .items .len + utf8_len );
@@ -773,8 +800,34 @@ pub fn utf16leToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) ![:0]
773
800
// optimistically guess that it will all be ascii (and allocate space for the null terminator)
774
801
var result = try std .ArrayList (u8 ).initCapacity (allocator , utf16le .len + 1 );
775
802
errdefer result .deinit ();
776
- var out_index : usize = 0 ;
777
- var it = Utf16LeIterator .init (utf16le );
803
+
804
+ var remaining = utf16le ;
805
+ if (builtin .zig_backend != .stage2_x86_64 ) {
806
+ const chunk_len = std .simd .suggestVectorSize (u16 ) orelse 1 ;
807
+ const Chunk = @Vector (chunk_len , u16 );
808
+
809
+ // Fast path. Check for and encode ASCII characters at the start of the input.
810
+ while (remaining .len >= chunk_len ) {
811
+ const chunk : Chunk = remaining [0.. chunk_len ].* ;
812
+ const mask : Chunk = @splat (std .mem .nativeToLittle (u16 , 0x7F ));
813
+ if (@reduce (.Or , chunk | mask != mask )) {
814
+ // found a non ASCII code unit
815
+ break ;
816
+ }
817
+ const chunk_byte_len = chunk_len * 2 ;
818
+ const chunk_bytes : @Vector (chunk_byte_len , u8 ) = (std .mem .sliceAsBytes (remaining )[0.. chunk_byte_len ]).* ;
819
+ const deinterlaced_bytes = std .simd .deinterlace (2 , chunk_bytes );
820
+ const ascii_bytes : [chunk_len ]u8 = deinterlaced_bytes [0 ];
821
+ // We allocated enough space to encode every UTF-16 code unit
822
+ // as ASCII, so if the entire string is ASCII then we are
823
+ // guaranteed to have enough space allocated
824
+ result .appendSliceAssumeCapacity (& ascii_bytes );
825
+ remaining = remaining [chunk_len .. ];
826
+ }
827
+ }
828
+
829
+ var out_index = result .items .len ;
830
+ var it = Utf16LeIterator .init (remaining );
778
831
while (try it .nextCodepoint ()) | codepoint | {
779
832
const utf8_len = utf8CodepointSequenceLength (codepoint ) catch unreachable ;
780
833
try result .resize (result .items .len + utf8_len );
@@ -788,7 +841,31 @@ pub fn utf16leToUtf8AllocZ(allocator: mem.Allocator, utf16le: []const u16) ![:0]
788
841
/// Returns end byte index into utf8.
789
842
pub fn utf16leToUtf8 (utf8 : []u8 , utf16le : []const u16 ) ! usize {
790
843
var end_index : usize = 0 ;
791
- var it = Utf16LeIterator .init (utf16le );
844
+
845
+ var remaining = utf16le ;
846
+ if (builtin .zig_backend != .stage2_x86_64 ) {
847
+ const chunk_len = std .simd .suggestVectorSize (u16 ) orelse 1 ;
848
+ const Chunk = @Vector (chunk_len , u16 );
849
+
850
+ // Fast path. Check for and encode ASCII characters at the start of the input.
851
+ while (remaining .len >= chunk_len ) {
852
+ const chunk : Chunk = remaining [0.. chunk_len ].* ;
853
+ const mask : Chunk = @splat (std .mem .nativeToLittle (u16 , 0x7F ));
854
+ if (@reduce (.Or , chunk | mask != mask )) {
855
+ // found a non ASCII code unit
856
+ break ;
857
+ }
858
+ const chunk_byte_len = chunk_len * 2 ;
859
+ const chunk_bytes : @Vector (chunk_byte_len , u8 ) = (std .mem .sliceAsBytes (remaining )[0.. chunk_byte_len ]).* ;
860
+ const deinterlaced_bytes = std .simd .deinterlace (2 , chunk_bytes );
861
+ const ascii_bytes : [chunk_len ]u8 = deinterlaced_bytes [0 ];
862
+ @memcpy (utf8 [end_index .. end_index + chunk_len ], & ascii_bytes );
863
+ end_index += chunk_len ;
864
+ remaining = remaining [chunk_len .. ];
865
+ }
866
+ }
867
+
868
+ var it = Utf16LeIterator .init (remaining );
792
869
while (try it .nextCodepoint ()) | codepoint | {
793
870
end_index += try utf8Encode (codepoint , utf8 [end_index .. ]);
794
871
}
@@ -863,7 +940,27 @@ pub fn utf8ToUtf16LeWithNull(allocator: mem.Allocator, utf8: []const u8) ![:0]u1
863
940
var result = try std .ArrayList (u16 ).initCapacity (allocator , utf8 .len + 1 );
864
941
errdefer result .deinit ();
865
942
866
- const view = try Utf8View .init (utf8 );
943
+ var remaining = utf8 ;
944
+ if (builtin .zig_backend != .stage2_x86_64 ) {
945
+ const chunk_len = std .simd .suggestVectorSize (u8 ) orelse 1 ;
946
+ const Chunk = @Vector (chunk_len , u8 );
947
+
948
+ // Fast path. Check for and encode ASCII characters at the start of the input.
949
+ while (remaining .len >= chunk_len ) {
950
+ const chunk : Chunk = remaining [0.. chunk_len ].* ;
951
+ const mask : Chunk = @splat (0x80 );
952
+ if (@reduce (.Or , chunk & mask == mask )) {
953
+ // found a non ASCII code unit
954
+ break ;
955
+ }
956
+ const zeroes : Chunk = @splat (0 );
957
+ const utf16_chunk : [chunk_len * 2 ]u8 align (@alignOf (u16 )) = std .simd .interlace (.{ chunk , zeroes });
958
+ result .appendSliceAssumeCapacity (std .mem .bytesAsSlice (u16 , & utf16_chunk ));
959
+ remaining = remaining [chunk_len .. ];
960
+ }
961
+ }
962
+
963
+ const view = try Utf8View .init (remaining );
867
964
var it = view .iterator ();
868
965
while (it .nextCodepoint ()) | codepoint | {
869
966
if (codepoint < 0x10000 ) {
@@ -886,11 +983,33 @@ pub fn utf8ToUtf16LeWithNull(allocator: mem.Allocator, utf8: []const u8) ![:0]u1
886
983
/// Assumes there is enough space for the output.
887
984
pub fn utf8ToUtf16Le (utf16le : []u16 , utf8 : []const u8 ) ! usize {
888
985
var dest_i : usize = 0 ;
986
+
987
+ var remaining = utf8 ;
988
+ if (builtin .zig_backend != .stage2_x86_64 ) {
989
+ const chunk_len = std .simd .suggestVectorSize (u8 ) orelse 1 ;
990
+ const Chunk = @Vector (chunk_len , u8 );
991
+
992
+ // Fast path. Check for and encode ASCII characters at the start of the input.
993
+ while (remaining .len >= chunk_len ) {
994
+ const chunk : Chunk = remaining [0.. chunk_len ].* ;
995
+ const mask : Chunk = @splat (0x80 );
996
+ if (@reduce (.Or , chunk & mask == mask )) {
997
+ // found a non ASCII code unit
998
+ break ;
999
+ }
1000
+ const zeroes : Chunk = @splat (0 );
1001
+ const utf16_bytes : [chunk_len * 2 ]u8 align (@alignOf (u16 )) = std .simd .interlace (.{ chunk , zeroes });
1002
+ @memcpy (utf16le [dest_i .. ][0.. chunk_len ], std .mem .bytesAsSlice (u16 , & utf16_bytes ));
1003
+ dest_i += chunk_len ;
1004
+ remaining = remaining [chunk_len .. ];
1005
+ }
1006
+ }
1007
+
889
1008
var src_i : usize = 0 ;
890
- while (src_i < utf8 .len ) {
891
- const n = utf8ByteSequenceLength (utf8 [src_i ]) catch return error .InvalidUtf8 ;
1009
+ while (src_i < remaining .len ) {
1010
+ const n = utf8ByteSequenceLength (remaining [src_i ]) catch return error .InvalidUtf8 ;
892
1011
const next_src_i = src_i + n ;
893
- const codepoint = utf8Decode (utf8 [src_i .. next_src_i ]) catch return error .InvalidUtf8 ;
1012
+ const codepoint = utf8Decode (remaining [src_i .. next_src_i ]) catch return error .InvalidUtf8 ;
894
1013
if (codepoint < 0x10000 ) {
895
1014
const short = @as (u16 , @intCast (codepoint ));
896
1015
utf16le [dest_i ] = mem .nativeToLittle (u16 , short );
0 commit comments