| ... | @@ -2190,6 +2190,156 @@ test "splitBackwards (reset)" { | ... | @@ -2190,6 +2190,156 @@ test "splitBackwards (reset)" { |
| 2190 | try testing.expect(it.next() == null); | 2190 | try testing.expect(it.next() == null); |
| 2191 | } | 2191 | } |
| 2192 | | 2192 | |
| | 2193 | /// Returns an iterator with a sliding window of slices for `buffer`. |
| | 2194 | /// The sliding window has length `size` and on every iteration moves |
| | 2195 | /// forward by `advance`. |
| | 2196 | /// |
| | 2197 | /// Extract data for moving average with: |
| | 2198 | /// `window(u8, "abcdefg", 3, 1)` will return slices |
| | 2199 | /// "abc", "bcd", "cde", "def", "efg", null, in that order. |
| | 2200 | /// |
| | 2201 | /// Chunk or split every N items with: |
| | 2202 | /// `window(u8, "abcdefg", 3, 3)` will return slices |
| | 2203 | /// "abc", "def", "g", null, in that order. |
| | 2204 | /// |
| | 2205 | /// Pick every even index with: |
| | 2206 | /// `window(u8, "abcdefg", 1, 2)` will return slices |
| | 2207 | /// "a", "c", "e", "g" null, in that order. |
| | 2208 | /// |
| | 2209 | /// The `size` and `advance` must be not be zero. |
| | 2210 | pub fn window(comptime T: type, buffer: []const T, size: usize, advance: usize) WindowIterator(T) { |
| | 2211 | assert(size != 0); |
| | 2212 | assert(advance != 0); |
| | 2213 | return .{ |
| | 2214 | .index = 0, |
| | 2215 | .buffer = buffer, |
| | 2216 | .size = size, |
| | 2217 | .advance = advance, |
| | 2218 | }; |
| | 2219 | } |
| | 2220 | |
| | 2221 | test "window" { |
| | 2222 | { |
| | 2223 | // moving average size 3 |
| | 2224 | var it = window(u8, "abcdefg", 3, 1); |
| | 2225 | try testing.expectEqualSlices(u8, it.next().?, "abc"); |
| | 2226 | try testing.expectEqualSlices(u8, it.next().?, "bcd"); |
| | 2227 | try testing.expectEqualSlices(u8, it.next().?, "cde"); |
| | 2228 | try testing.expectEqualSlices(u8, it.next().?, "def"); |
| | 2229 | try testing.expectEqualSlices(u8, it.next().?, "efg"); |
| | 2230 | try testing.expectEqual(it.next(), null); |
| | 2231 | |
| | 2232 | // multibyte |
| | 2233 | var it16 = window(u16, std.unicode.utf8ToUtf16LeStringLiteral("abcdefg"), 3, 1); |
| | 2234 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("abc")); |
| | 2235 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("bcd")); |
| | 2236 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("cde")); |
| | 2237 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("def")); |
| | 2238 | try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("efg")); |
| | 2239 | try testing.expectEqual(it16.next(), null); |
| | 2240 | } |
| | 2241 | |
| | 2242 | { |
| | 2243 | // chunk/split every 3 |
| | 2244 | var it = window(u8, "abcdefg", 3, 3); |
| | 2245 | try testing.expectEqualSlices(u8, it.next().?, "abc"); |
| | 2246 | try testing.expectEqualSlices(u8, it.next().?, "def"); |
| | 2247 | try testing.expectEqualSlices(u8, it.next().?, "g"); |
| | 2248 | try testing.expectEqual(it.next(), null); |
| | 2249 | } |
| | 2250 | |
| | 2251 | { |
| | 2252 | // pick even |
| | 2253 | var it = window(u8, "abcdefg", 1, 2); |
| | 2254 | try testing.expectEqualSlices(u8, it.next().?, "a"); |
| | 2255 | try testing.expectEqualSlices(u8, it.next().?, "c"); |
| | 2256 | try testing.expectEqualSlices(u8, it.next().?, "e"); |
| | 2257 | try testing.expectEqualSlices(u8, it.next().?, "g"); |
| | 2258 | try testing.expectEqual(it.next(), null); |
| | 2259 | } |
| | 2260 | |
| | 2261 | { |
| | 2262 | // empty |
| | 2263 | var it = window(u8, "", 1, 1); |
| | 2264 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 2265 | try testing.expectEqual(it.next(), null); |
| | 2266 | |
| | 2267 | it = window(u8, "", 10, 1); |
| | 2268 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 2269 | try testing.expectEqual(it.next(), null); |
| | 2270 | |
| | 2271 | it = window(u8, "", 1, 10); |
| | 2272 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 2273 | try testing.expectEqual(it.next(), null); |
| | 2274 | |
| | 2275 | it = window(u8, "", 10, 10); |
| | 2276 | try testing.expectEqualSlices(u8, it.next().?, ""); |
| | 2277 | try testing.expectEqual(it.next(), null); |
| | 2278 | } |
| | 2279 | |
| | 2280 | { |
| | 2281 | // first |
| | 2282 | var it = window(u8, "abcdefg", 3, 3); |
| | 2283 | try testing.expectEqualSlices(u8, it.first(), "abc"); |
| | 2284 | it.reset(); |
| | 2285 | try testing.expectEqualSlices(u8, it.next().?, "abc"); |
| | 2286 | } |
| | 2287 | |
| | 2288 | { |
| | 2289 | // reset |
| | 2290 | var it = window(u8, "abcdefg", 3, 3); |
| | 2291 | try testing.expectEqualSlices(u8, it.next().?, "abc"); |
| | 2292 | try testing.expectEqualSlices(u8, it.next().?, "def"); |
| | 2293 | try testing.expectEqualSlices(u8, it.next().?, "g"); |
| | 2294 | try testing.expectEqual(it.next(), null); |
| | 2295 | |
| | 2296 | it.reset(); |
| | 2297 | try testing.expectEqualSlices(u8, it.next().?, "abc"); |
| | 2298 | try testing.expectEqualSlices(u8, it.next().?, "def"); |
| | 2299 | try testing.expectEqualSlices(u8, it.next().?, "g"); |
| | 2300 | try testing.expectEqual(it.next(), null); |
| | 2301 | } |
| | 2302 | } |
| | 2303 | |
| | 2304 | pub fn WindowIterator(comptime T: type) type { |
| | 2305 | return struct { |
| | 2306 | buffer: []const T, |
| | 2307 | index: ?usize, |
| | 2308 | size: usize, |
| | 2309 | advance: usize, |
| | 2310 | |
| | 2311 | const Self = @This(); |
| | 2312 | |
| | 2313 | /// Returns a slice of the first window. This never fails. |
| | 2314 | /// Call this only to get the first window and then use `next` to get |
| | 2315 | /// all subsequent windows. |
| | 2316 | pub fn first(self: *Self) []const T { |
| | 2317 | assert(self.index.? == 0); |
| | 2318 | return self.next().?; |
| | 2319 | } |
| | 2320 | |
| | 2321 | /// Returns a slice of the next window, or null if window is at end. |
| | 2322 | pub fn next(self: *Self) ?[]const T { |
| | 2323 | const start = self.index orelse return null; |
| | 2324 | const next_index = start + self.advance; |
| | 2325 | const end = if (start + self.size < self.buffer.len and next_index < self.buffer.len) blk: { |
| | 2326 | self.index = next_index; |
| | 2327 | break :blk start + self.size; |
| | 2328 | } else blk: { |
| | 2329 | self.index = null; |
| | 2330 | break :blk self.buffer.len; |
| | 2331 | }; |
| | 2332 | |
| | 2333 | return self.buffer[start..end]; |
| | 2334 | } |
| | 2335 | |
| | 2336 | /// Resets the iterator to the initial window. |
| | 2337 | pub fn reset(self: *Self) void { |
| | 2338 | self.index = 0; |
| | 2339 | } |
| | 2340 | }; |
| | 2341 | } |
| | 2342 | |
| 2193 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { | 2343 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| 2194 | return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); | 2344 | return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); |
| 2195 | } | 2345 | } |