@@ -72,6 +72,26 @@ pub fn constructor(
7272 return .{ .slice = "" , .mime = mime };
7373}
7474
75+ const largest_vector = @max (std .simd .suggestVectorLength (u8 ) orelse 1 , 8 );
76+ /// Array of possible vector sizes for the current arch in decrementing order.
77+ /// We may move this to some file for SIMD helpers in the future.
78+ const vector_sizes = blk : {
79+ // Required for length calculation.
80+ var n : usize = largest_vector ;
81+ var total : usize = 0 ;
82+ while (n != 2 ) : (n /= 2 ) total += 1 ;
83+ // Populate an array with vector sizes.
84+ n = largest_vector ;
85+ var i : usize = 0 ;
86+ var items : [total ]usize = undefined ;
87+ while (n != 2 ) : (n /= 2 ) {
88+ defer i += 1 ;
89+ items [i ] = n ;
90+ }
91+
92+ break :blk items ;
93+ };
94+
7595/// Writes blob parts to given `Writer` with desired endings.
7696fn writeBlobParts (
7797 writer : * Writer ,
@@ -88,7 +108,6 @@ fn writeBlobParts(
88108 }
89109
90110 // TODO: Windows support.
91- // TODO: Vector search.
92111
93112 // Linux & Unix.
94113 // Both Firefox and Chrome implement it as such:
@@ -108,10 +127,44 @@ fn writeBlobParts(
108127 // ```
109128 scan_parts : for (blob_parts ) | part | {
110129 var end : usize = 0 ;
111- var start = end ;
130+
131+ inline for (vector_sizes ) | vector_len | {
132+ const Vec = @Vector (vector_len , u8 );
133+
134+ while (end + vector_len <= part .len ) : (end += vector_len ) {
135+ const cr : Vec = @splat ('\r ' );
136+ // Load chunk as vectors.
137+ const slice = part [end .. ][0.. vector_len ];
138+ const chunk : Vec = slice .* ;
139+ // Look for CR.
140+ const match = chunk == cr ;
141+
142+ // Create a bitset out of match vector.
143+ const bitset = std .bit_set .IntegerBitSet (vector_len ){
144+ .mask = @bitCast (@intFromBool (match )),
145+ };
146+
147+ var iter = bitset .iterator (.{});
148+ var relative_start : usize = 0 ;
149+ while (iter .next ()) | index | {
150+ _ = try writer .writeVec (&.{ slice [relative_start .. index ], "\n " });
151+
152+ if (index + 1 != slice .len and slice [index + 1 ] == '\n ' ) {
153+ relative_start = index + 2 ;
154+ } else {
155+ relative_start = index + 1 ;
156+ }
157+ }
158+
159+ _ = try writer .writeVec (&.{slice [relative_start .. ]});
160+ }
161+ }
162+
163+ // Scalar scan fallback.
164+ var relative_start : usize = end ;
112165 while (end < part .len ) {
113166 if (part [end ] == '\r ' ) {
114- _ = try writer .writeVec (&.{ part [start .. end ], "\n " });
167+ _ = try writer .writeVec (&.{ part [relative_start .. end ], "\n " });
115168
116169 // Part ends with CR. We can continue to next part.
117170 if (end + 1 == part .len ) {
@@ -120,9 +173,9 @@ fn writeBlobParts(
120173
121174 // If next char is LF, skip it too.
122175 if (part [end + 1 ] == '\n ' ) {
123- start = end + 2 ;
176+ relative_start = end + 2 ;
124177 } else {
125- start = end + 1 ;
178+ relative_start = end + 1 ;
126179 }
127180 }
128181
@@ -132,7 +185,7 @@ fn writeBlobParts(
132185 // Write the remaining. We get this in such situations:
133186 // `the quick brown\rfox`
134187 // `the quick brown\r\nfox`
135- try writer .writeAll (part [start .. end ]);
188+ try writer .writeAll (part [relative_start .. end ]);
136189 }
137190}
138191
0 commit comments