blob bbdcfef3 (196218B) - Raw
1 const std = @import("std.zig"); 2 const builtin = @import("builtin"); 3 const debug = std.debug; 4 const assert = debug.assert; 5 const math = std.math; 6 const mem = @This(); 7 const testing = std.testing; 8 const Endian = std.builtin.Endian; 9 const native_endian = builtin.cpu.arch.endian(); 10 11 /// The standard library currently thoroughly depends on byte size 12 /// being 8 bits. (see the use of u8 throughout allocation code as 13 /// the "byte" type.) Code which depends on this can reference this 14 /// declaration. If we ever try to port the standard library to a 15 /// non-8-bit-byte platform, this will allow us to search for things 16 /// which need to be updated. 17 pub const byte_size_in_bits = 8; 18 19 pub const Allocator = @import("mem/Allocator.zig"); 20 21 /// Stored as a power-of-two. 22 pub const Alignment = enum(math.Log2Int(usize)) { 23 @"1" = 0, 24 @"2" = 1, 25 @"4" = 2, 26 @"8" = 3, 27 @"16" = 4, 28 @"32" = 5, 29 @"64" = 6, 30 _, 31 32 pub fn toByteUnits(a: Alignment) usize { 33 return @as(usize, 1) << @intFromEnum(a); 34 } 35 36 pub fn fromByteUnits(n: usize) Alignment { 37 assert(std.math.isPowerOfTwo(n)); 38 return @enumFromInt(@ctz(n)); 39 } 40 41 pub fn fromByteUnitsOptional(maybe_n: ?usize) ?Alignment { 42 return if (maybe_n) |n| .fromByteUnits(n) else null; 43 } 44 45 pub inline fn of(comptime T: type) Alignment { 46 return comptime fromByteUnits(@alignOf(T)); 47 } 48 49 pub fn order(lhs: Alignment, rhs: Alignment) std.math.Order { 50 return std.math.order(@intFromEnum(lhs), @intFromEnum(rhs)); 51 } 52 53 pub fn compare(lhs: Alignment, op: std.math.CompareOperator, rhs: Alignment) bool { 54 return std.math.compare(@intFromEnum(lhs), op, @intFromEnum(rhs)); 55 } 56 57 pub fn max(lhs: Alignment, rhs: Alignment) Alignment { 58 return @enumFromInt(@max(@intFromEnum(lhs), @intFromEnum(rhs))); 59 } 60 61 pub fn min(lhs: Alignment, rhs: Alignment) Alignment { 62 return @enumFromInt(@min(@intFromEnum(lhs), @intFromEnum(rhs))); 63 } 64 65 /// Return next address with this alignment. 66 pub fn forward(a: Alignment, address: usize) usize { 67 const x = (@as(usize, 1) << @intFromEnum(a)) - 1; 68 return (address + x) & ~x; 69 } 70 71 /// Return previous address with this alignment. 72 pub fn backward(a: Alignment, address: usize) usize { 73 const x = (@as(usize, 1) << @intFromEnum(a)) - 1; 74 return address & ~x; 75 } 76 77 /// Return whether address is aligned to this amount. 78 pub fn check(a: Alignment, address: usize) bool { 79 return @ctz(address) >= @intFromEnum(a); 80 } 81 }; 82 83 /// Detects and asserts if the std.mem.Allocator interface is violated by the caller 84 /// or the allocator. 85 pub fn ValidationAllocator(comptime T: type) type { 86 return struct { 87 const Self = @This(); 88 89 underlying_allocator: T, 90 91 pub fn init(underlying_allocator: T) @This() { 92 return .{ 93 .underlying_allocator = underlying_allocator, 94 }; 95 } 96 97 pub fn allocator(self: *Self) Allocator { 98 return .{ 99 .ptr = self, 100 .vtable = &.{ 101 .alloc = alloc, 102 .resize = resize, 103 .remap = remap, 104 .free = free, 105 }, 106 }; 107 } 108 109 fn getUnderlyingAllocatorPtr(self: *Self) Allocator { 110 if (T == Allocator) return self.underlying_allocator; 111 return self.underlying_allocator.allocator(); 112 } 113 114 pub fn alloc( 115 ctx: *anyopaque, 116 n: usize, 117 alignment: mem.Alignment, 118 ret_addr: usize, 119 ) ?[*]u8 { 120 assert(n > 0); 121 const self: *Self = @ptrCast(@alignCast(ctx)); 122 const underlying = self.getUnderlyingAllocatorPtr(); 123 const result = underlying.rawAlloc(n, alignment, ret_addr) orelse 124 return null; 125 assert(alignment.check(@intFromPtr(result))); 126 return result; 127 } 128 129 pub fn resize( 130 ctx: *anyopaque, 131 buf: []u8, 132 alignment: Alignment, 133 new_len: usize, 134 ret_addr: usize, 135 ) bool { 136 const self: *Self = @ptrCast(@alignCast(ctx)); 137 assert(buf.len > 0); 138 const underlying = self.getUnderlyingAllocatorPtr(); 139 return underlying.rawResize(buf, alignment, new_len, ret_addr); 140 } 141 142 pub fn remap( 143 ctx: *anyopaque, 144 buf: []u8, 145 alignment: Alignment, 146 new_len: usize, 147 ret_addr: usize, 148 ) ?[*]u8 { 149 const self: *Self = @ptrCast(@alignCast(ctx)); 150 assert(buf.len > 0); 151 const underlying = self.getUnderlyingAllocatorPtr(); 152 return underlying.rawRemap(buf, alignment, new_len, ret_addr); 153 } 154 155 pub fn free( 156 ctx: *anyopaque, 157 buf: []u8, 158 alignment: Alignment, 159 ret_addr: usize, 160 ) void { 161 const self: *Self = @ptrCast(@alignCast(ctx)); 162 assert(buf.len > 0); 163 const underlying = self.getUnderlyingAllocatorPtr(); 164 underlying.rawFree(buf, alignment, ret_addr); 165 } 166 167 pub fn reset(self: *Self) void { 168 self.underlying_allocator.reset(); 169 } 170 }; 171 } 172 173 /// Wraps an allocator with basic validation checks. 174 /// Asserts that allocation sizes are greater than zero and returned pointers have correct alignment. 175 pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) { 176 return ValidationAllocator(@TypeOf(allocator)).init(allocator); 177 } 178 179 test "Allocator basics" { 180 try testing.expectError(error.OutOfMemory, testing.failing_allocator.alloc(u8, 1)); 181 try testing.expectError(error.OutOfMemory, testing.failing_allocator.allocSentinel(u8, 1, 0)); 182 } 183 184 test "Allocator.resize" { 185 const primitiveIntTypes = .{ 186 i8, 187 u8, 188 i16, 189 u16, 190 i32, 191 u32, 192 i64, 193 u64, 194 i128, 195 u128, 196 isize, 197 usize, 198 }; 199 inline for (primitiveIntTypes) |T| { 200 var values = try testing.allocator.alloc(T, 100); 201 defer testing.allocator.free(values); 202 203 for (values, 0..) |*v, i| v.* = @as(T, @intCast(i)); 204 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory; 205 values = values.ptr[0 .. values.len + 10]; 206 try testing.expect(values.len == 110); 207 } 208 209 const primitiveFloatTypes = .{ 210 f16, 211 f32, 212 f64, 213 f128, 214 }; 215 inline for (primitiveFloatTypes) |T| { 216 var values = try testing.allocator.alloc(T, 100); 217 defer testing.allocator.free(values); 218 219 for (values, 0..) |*v, i| v.* = @as(T, @floatFromInt(i)); 220 if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory; 221 values = values.ptr[0 .. values.len + 10]; 222 try testing.expect(values.len == 110); 223 } 224 } 225 226 test "Allocator alloc and remap with zero-bit type" { 227 var values = try testing.allocator.alloc(void, 10); 228 defer testing.allocator.free(values); 229 230 try testing.expectEqual(10, values.len); 231 const remaped = testing.allocator.remap(values, 200); 232 try testing.expect(remaped != null); 233 234 values = remaped.?; 235 try testing.expectEqual(200, values.len); 236 } 237 238 /// Copy all of source into dest at position 0. 239 /// dest.len must be >= source.len. 240 /// If the slices overlap, dest.ptr must be <= src.ptr. 241 /// This function is deprecated; use @memmove instead. 242 pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void { 243 for (dest[0..source.len], source) |*d, s| d.* = s; 244 } 245 246 /// Copy all of source into dest at position 0. 247 /// dest.len must be >= source.len. 248 /// If the slices overlap, dest.ptr must be >= src.ptr. 249 /// This function is deprecated; use @memmove instead. 250 pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { 251 // TODO instead of manually doing this check for the whole array 252 // and turning off runtime safety, the compiler should detect loops like 253 // this and automatically omit safety checks for loops 254 @setRuntimeSafety(false); 255 assert(dest.len >= source.len); 256 var i = source.len; 257 while (i > 0) { 258 i -= 1; 259 dest[i] = source[i]; 260 } 261 } 262 263 /// Generally, Zig users are encouraged to explicitly initialize all fields of a struct explicitly rather than using this function. 264 /// However, it is recognized that there are sometimes use cases for initializing all fields to a "zero" value. For example, when 265 /// interfacing with a C API where this practice is more common and relied upon. If you are performing code review and see this 266 /// function used, examine closely - it may be a code smell. 267 /// Zero initializes the type. 268 /// This can be used to zero-initialize any type for which it makes sense. Structs will be initialized recursively. 269 pub fn zeroes(comptime T: type) T { 270 switch (@typeInfo(T)) { 271 .comptime_int, .int, .comptime_float, .float => { 272 return @as(T, 0); 273 }, 274 .@"enum" => { 275 return @as(T, @enumFromInt(0)); 276 }, 277 .void => { 278 return {}; 279 }, 280 .bool => { 281 return false; 282 }, 283 .optional, .null => { 284 return null; 285 }, 286 .@"struct" => |struct_info| { 287 if (@sizeOf(T) == 0) return undefined; 288 if (struct_info.layout == .@"extern") { 289 var item: T = undefined; 290 @memset(asBytes(&item), 0); 291 return item; 292 } else { 293 var structure: T = undefined; 294 inline for (struct_info.fields) |field| { 295 if (!field.is_comptime) { 296 @field(structure, field.name) = zeroes(field.type); 297 } 298 } 299 return structure; 300 } 301 }, 302 .pointer => |ptr_info| { 303 switch (ptr_info.size) { 304 .slice => { 305 if (ptr_info.sentinel()) |sentinel| { 306 if (ptr_info.child == u8 and sentinel == 0) { 307 return ""; // A special case for the most common use-case: null-terminated strings. 308 } 309 @compileError("Can't set a sentinel slice to zero. This would require allocating memory."); 310 } else { 311 return &[_]ptr_info.child{}; 312 } 313 }, 314 .c => { 315 return null; 316 }, 317 .one, .many => { 318 if (ptr_info.is_allowzero) return @ptrFromInt(0); 319 @compileError("Only nullable and allowzero pointers can be set to zero."); 320 }, 321 } 322 }, 323 .array => |info| { 324 return @splat(zeroes(info.child)); 325 }, 326 .vector => |info| { 327 return @splat(zeroes(info.child)); 328 }, 329 .@"union" => |info| { 330 if (info.layout == .@"extern") { 331 var item: T = undefined; 332 @memset(asBytes(&item), 0); 333 return item; 334 } 335 @compileError("Can't set a " ++ @typeName(T) ++ " to zero."); 336 }, 337 .enum_literal, 338 .error_union, 339 .error_set, 340 .@"fn", 341 .type, 342 .noreturn, 343 .undefined, 344 .@"opaque", 345 .frame, 346 .@"anyframe", 347 => { 348 @compileError("Can't set a " ++ @typeName(T) ++ " to zero."); 349 }, 350 } 351 } 352 353 test zeroes { 354 const C_struct = extern struct { 355 x: u32, 356 y: u32 align(128), 357 }; 358 359 var a = zeroes(C_struct); 360 361 // Extern structs should have padding zeroed out. 362 try testing.expectEqualSlices(u8, &[_]u8{0} ** @sizeOf(@TypeOf(a)), asBytes(&a)); 363 364 a.y += 10; 365 366 try testing.expect(a.x == 0); 367 try testing.expect(a.y == 10); 368 369 const ZigStruct = struct { 370 comptime comptime_field: u8 = 5, 371 372 integral_types: struct { 373 integer_0: i0, 374 integer_8: i8, 375 integer_16: i16, 376 integer_32: i32, 377 integer_64: i64, 378 integer_128: i128, 379 unsigned_0: u0, 380 unsigned_8: u8, 381 unsigned_16: u16, 382 unsigned_32: u32, 383 unsigned_64: u64, 384 unsigned_128: u128, 385 386 float_32: f32, 387 float_64: f64, 388 }, 389 390 pointers: struct { 391 optional: ?*u8, 392 c_pointer: [*c]u8, 393 slice: []u8, 394 nullTerminatedString: [:0]const u8, 395 }, 396 397 array: [2]u32, 398 vector_u32: @Vector(2, u32), 399 vector_f32: @Vector(2, f32), 400 vector_bool: @Vector(2, bool), 401 optional_int: ?u8, 402 empty: void, 403 sentinel: [3:0]u8, 404 }; 405 406 const b = zeroes(ZigStruct); 407 try testing.expectEqual(@as(u8, 5), b.comptime_field); 408 try testing.expectEqual(@as(i8, 0), b.integral_types.integer_0); 409 try testing.expectEqual(@as(i8, 0), b.integral_types.integer_8); 410 try testing.expectEqual(@as(i16, 0), b.integral_types.integer_16); 411 try testing.expectEqual(@as(i32, 0), b.integral_types.integer_32); 412 try testing.expectEqual(@as(i64, 0), b.integral_types.integer_64); 413 try testing.expectEqual(@as(i128, 0), b.integral_types.integer_128); 414 try testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_0); 415 try testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_8); 416 try testing.expectEqual(@as(u16, 0), b.integral_types.unsigned_16); 417 try testing.expectEqual(@as(u32, 0), b.integral_types.unsigned_32); 418 try testing.expectEqual(@as(u64, 0), b.integral_types.unsigned_64); 419 try testing.expectEqual(@as(u128, 0), b.integral_types.unsigned_128); 420 try testing.expectEqual(@as(f32, 0), b.integral_types.float_32); 421 try testing.expectEqual(@as(f64, 0), b.integral_types.float_64); 422 try testing.expectEqual(@as(?*u8, null), b.pointers.optional); 423 try testing.expectEqual(@as([*c]u8, null), b.pointers.c_pointer); 424 try testing.expectEqual(@as([]u8, &[_]u8{}), b.pointers.slice); 425 try testing.expectEqual(@as([:0]const u8, ""), b.pointers.nullTerminatedString); 426 for (b.array) |e| { 427 try testing.expectEqual(@as(u32, 0), e); 428 } 429 try testing.expectEqual(@as(@TypeOf(b.vector_u32), @splat(0)), b.vector_u32); 430 try testing.expectEqual(@as(@TypeOf(b.vector_f32), @splat(0.0)), b.vector_f32); 431 if (!(builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon)) { 432 try testing.expectEqual(@as(@TypeOf(b.vector_bool), @splat(false)), b.vector_bool); 433 } 434 try testing.expectEqual(@as(?u8, null), b.optional_int); 435 for (b.sentinel) |e| { 436 try testing.expectEqual(@as(u8, 0), e); 437 } 438 439 const C_union = extern union { 440 a: u8, 441 b: u32, 442 }; 443 444 const c = zeroes(C_union); 445 try testing.expectEqual(@as(u8, 0), c.a); 446 try testing.expectEqual(@as(u32, 0), c.b); 447 448 const comptime_union = comptime zeroes(C_union); 449 try testing.expectEqual(@as(u8, 0), comptime_union.a); 450 try testing.expectEqual(@as(u32, 0), comptime_union.b); 451 452 // Ensure zero sized struct with fields is initialized correctly. 453 _ = zeroes(struct { handle: void }); 454 } 455 456 /// Initializes all fields of the struct with their default value, or zero values if no default value is present. 457 /// If the field is present in the provided initial values, it will have that value instead. 458 /// Structs are initialized recursively. 459 pub fn zeroInit(comptime T: type, init: anytype) T { 460 const Init = @TypeOf(init); 461 462 switch (@typeInfo(T)) { 463 .@"struct" => |struct_info| { 464 switch (@typeInfo(Init)) { 465 .@"struct" => |init_info| { 466 if (init_info.is_tuple) { 467 if (init_info.fields.len > struct_info.fields.len) { 468 @compileError("Tuple initializer has more elements than there are fields in `" ++ @typeName(T) ++ "`"); 469 } 470 } else { 471 inline for (init_info.fields) |field| { 472 if (!@hasField(T, field.name)) { 473 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T)); 474 } 475 } 476 } 477 478 var value: T = if (struct_info.layout == .@"extern") zeroes(T) else undefined; 479 480 inline for (struct_info.fields, 0..) |field, i| { 481 if (field.is_comptime) { 482 continue; 483 } 484 485 if (init_info.is_tuple and init_info.fields.len > i) { 486 @field(value, field.name) = @field(init, init_info.fields[i].name); 487 } else if (@hasField(@TypeOf(init), field.name)) { 488 switch (@typeInfo(field.type)) { 489 .@"struct" => { 490 @field(value, field.name) = zeroInit(field.type, @field(init, field.name)); 491 }, 492 else => { 493 @field(value, field.name) = @field(init, field.name); 494 }, 495 } 496 } else if (field.defaultValue()) |val| { 497 @field(value, field.name) = val; 498 } else { 499 switch (@typeInfo(field.type)) { 500 .@"struct" => { 501 @field(value, field.name) = std.mem.zeroInit(field.type, .{}); 502 }, 503 else => { 504 @field(value, field.name) = std.mem.zeroes(@TypeOf(@field(value, field.name))); 505 }, 506 } 507 } 508 } 509 510 return value; 511 }, 512 else => { 513 @compileError("The initializer must be a struct"); 514 }, 515 } 516 }, 517 else => { 518 @compileError("Can't default init a " ++ @typeName(T)); 519 }, 520 } 521 } 522 523 test zeroInit { 524 const I = struct { 525 d: f64, 526 }; 527 528 const S = struct { 529 a: u32, 530 b: ?bool, 531 c: I, 532 e: [3]u8, 533 f: i64 = -1, 534 }; 535 536 const s = zeroInit(S, .{ 537 .a = 42, 538 }); 539 540 try testing.expectEqual(S{ 541 .a = 42, 542 .b = null, 543 .c = .{ 544 .d = 0, 545 }, 546 .e = [3]u8{ 0, 0, 0 }, 547 .f = -1, 548 }, s); 549 550 const Color = struct { 551 r: u8, 552 g: u8, 553 b: u8, 554 a: u8, 555 }; 556 557 const c = zeroInit(Color, .{ 255, 255 }); 558 try testing.expectEqual(Color{ 559 .r = 255, 560 .g = 255, 561 .b = 0, 562 .a = 0, 563 }, c); 564 565 const Foo = struct { 566 foo: u8 = 69, 567 bar: u8, 568 }; 569 570 const f = zeroInit(Foo, .{}); 571 try testing.expectEqual(Foo{ 572 .foo = 69, 573 .bar = 0, 574 }, f); 575 576 const Bar = struct { 577 foo: u32 = 666, 578 bar: u32 = 420, 579 }; 580 581 const b = zeroInit(Bar, .{69}); 582 try testing.expectEqual(Bar{ 583 .foo = 69, 584 .bar = 420, 585 }, b); 586 587 const Baz = struct { 588 foo: [:0]const u8 = "bar", 589 }; 590 591 const baz1 = zeroInit(Baz, .{}); 592 try testing.expectEqual(Baz{}, baz1); 593 594 const baz2 = zeroInit(Baz, .{ .foo = "zab" }); 595 try testing.expectEqualSlices(u8, "zab", baz2.foo); 596 597 const NestedBaz = struct { 598 bbb: Baz, 599 }; 600 const nested_baz = zeroInit(NestedBaz, .{}); 601 try testing.expectEqual(NestedBaz{ 602 .bbb = Baz{}, 603 }, nested_baz); 604 } 605 606 /// Sorts a slice in-place using a stable algorithm (maintains relative order of equal elements). 607 /// Average time complexity: O(n log n), worst case: O(n log n) 608 /// Space complexity: O(log n) for recursive calls 609 /// 610 /// For slice of primitives with default ordering, consider using `std.sort.block` directly. 611 /// For unstable but potentially faster sorting, see `sortUnstable`. 612 pub fn sort( 613 comptime T: type, 614 items: []T, 615 context: anytype, 616 comptime lessThanFn: fn (@TypeOf(context), lhs: T, rhs: T) bool, 617 ) void { 618 std.sort.block(T, items, context, lessThanFn); 619 } 620 621 /// Sorts a slice in-place using an unstable algorithm (does not preserve relative order of equal elements). 622 /// Time complexity: O(n) best case, O(n log n) worst case and average case. 623 /// Generally faster than stable sort but order of equal elements is undefined. 624 /// 625 /// Uses pattern-defeating quicksort (PDQ) algorithm which performs well on many data patterns. 626 /// For stable sorting that preserves equal element order, use `sort`. 627 pub fn sortUnstable( 628 comptime T: type, 629 items: []T, 630 context: anytype, 631 comptime lessThanFn: fn (@TypeOf(context), lhs: T, rhs: T) bool, 632 ) void { 633 std.sort.pdq(T, items, context, lessThanFn); 634 } 635 636 /// TODO: currently this just calls `insertionSortContext`. The block sort implementation 637 /// in this file needs to be adapted to use the sort context. 638 pub fn sortContext(a: usize, b: usize, context: anytype) void { 639 std.sort.insertionContext(a, b, context); 640 } 641 642 /// Sorts a range [a, b) using an unstable algorithm with custom context. 643 /// This is a lower-level interface for sorting that works with indices instead of slices. 644 /// Does not preserve relative order of equal elements. 645 /// 646 /// The context must provide lessThan(a_idx, b_idx) and swap(a_idx, b_idx) methods. 647 /// Uses pattern-defeating quicksort (PDQ) algorithm. 648 pub fn sortUnstableContext(a: usize, b: usize, context: anytype) void { 649 std.sort.pdqContext(a, b, context); 650 } 651 652 /// Compares two slices of numbers lexicographically. O(n). 653 pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order { 654 if (lhs.ptr != rhs.ptr) { 655 const n = @min(lhs.len, rhs.len); 656 for (lhs[0..n], rhs[0..n]) |lhs_elem, rhs_elem| { 657 switch (math.order(lhs_elem, rhs_elem)) { 658 .eq => continue, 659 .lt => return .lt, 660 .gt => return .gt, 661 } 662 } 663 } 664 return math.order(lhs.len, rhs.len); 665 } 666 667 /// Compares two many-item pointers with NUL-termination lexicographically. 668 pub fn orderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T) math.Order { 669 return boundedOrderZ(T, lhs, rhs, std.math.maxInt(usize)); 670 } 671 672 /// Compares two many-item pointers with NUL-termination lexicographically until some specified bound. 673 pub fn boundedOrderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T, bound: usize) math.Order { 674 if (lhs == rhs) return .eq; 675 var i: usize = 0; 676 while (lhs[i] == rhs[i] and lhs[i] != 0 and i < bound) : (i += 1) {} 677 return if (i < bound) math.order(lhs[i], rhs[i]) else .eq; 678 } 679 680 test order { 681 try testing.expect(order(u8, "abcd", "bee") == .lt); 682 try testing.expect(order(u8, "abc", "abc") == .eq); 683 try testing.expect(order(u8, "abc", "abc0") == .lt); 684 try testing.expect(order(u8, "", "") == .eq); 685 try testing.expect(order(u8, "", "a") == .lt); 686 687 const s: []const u8 = "abc"; 688 try testing.expect(order(u8, s, s) == .eq); 689 try testing.expect(order(u8, s[0..2], s) == .lt); 690 } 691 692 test orderZ { 693 try testing.expect(orderZ(u8, "abcd", "bee") == .lt); 694 try testing.expect(orderZ(u8, "abc", "abc") == .eq); 695 try testing.expect(orderZ(u8, "abc", "abc0") == .lt); 696 try testing.expect(orderZ(u8, "", "") == .eq); 697 try testing.expect(orderZ(u8, "", "a") == .lt); 698 699 const s: [*:0]const u8 = "abc"; 700 try testing.expect(orderZ(u8, s, s) == .eq); 701 } 702 703 /// Returns true if lhs < rhs, false otherwise 704 pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool { 705 return order(T, lhs, rhs) == .lt; 706 } 707 708 test lessThan { 709 try testing.expect(lessThan(u8, "abcd", "bee")); 710 try testing.expect(!lessThan(u8, "abc", "abc")); 711 try testing.expect(lessThan(u8, "abc", "abc0")); 712 try testing.expect(!lessThan(u8, "", "")); 713 try testing.expect(lessThan(u8, "", "a")); 714 } 715 716 const use_vectors = switch (builtin.zig_backend) { 717 // These backends don't support vectors yet. 718 .stage2_aarch64, 719 .stage2_powerpc, 720 .stage2_riscv64, 721 => false, 722 // The SPIR-V backend does not support the optimized path yet. 723 .stage2_spirv => false, 724 else => true, 725 }; 726 727 // The naive memory comparison implementation is more useful for fuzzers to find interesting inputs. 728 const use_vectors_for_comparison = use_vectors and !builtin.fuzz; 729 730 /// Returns true if and only if the slices have the same length and all elements 731 /// compare true using equality operator. 732 pub fn eql(comptime T: type, a: []const T, b: []const T) bool { 733 if (!@inComptime() and @sizeOf(T) != 0 and std.meta.hasUniqueRepresentation(T) and 734 use_vectors_for_comparison) 735 { 736 return eqlBytes(sliceAsBytes(a), sliceAsBytes(b)); 737 } 738 739 if (a.len != b.len) return false; 740 if (a.len == 0 or a.ptr == b.ptr) return true; 741 742 for (a, b) |a_elem, b_elem| { 743 if (a_elem != b_elem) return false; 744 } 745 return true; 746 } 747 748 test eql { 749 try testing.expect(eql(u8, "abcd", "abcd")); 750 try testing.expect(!eql(u8, "abcdef", "abZdef")); 751 try testing.expect(!eql(u8, "abcdefg", "abcdef")); 752 753 comptime { 754 try testing.expect(eql(type, &.{ bool, f32 }, &.{ bool, f32 })); 755 try testing.expect(!eql(type, &.{ bool, f32 }, &.{ f32, bool })); 756 try testing.expect(!eql(type, &.{ bool, f32 }, &.{bool})); 757 758 try testing.expect(eql(comptime_int, &.{ 1, 2, 3 }, &.{ 1, 2, 3 })); 759 try testing.expect(!eql(comptime_int, &.{ 1, 2, 3 }, &.{ 3, 2, 1 })); 760 try testing.expect(!eql(comptime_int, &.{1}, &.{ 1, 2 })); 761 } 762 763 try testing.expect(eql(void, &.{ {}, {} }, &.{ {}, {} })); 764 try testing.expect(!eql(void, &.{{}}, &.{ {}, {} })); 765 } 766 767 /// std.mem.eql heavily optimized for slices of bytes. 768 fn eqlBytes(a: []const u8, b: []const u8) bool { 769 comptime assert(use_vectors_for_comparison); 770 771 if (a.len != b.len) return false; 772 if (a.len == 0 or a.ptr == b.ptr) return true; 773 774 if (a.len <= 16) { 775 if (a.len < 4) { 776 const x = (a[0] ^ b[0]) | (a[a.len - 1] ^ b[a.len - 1]) | (a[a.len / 2] ^ b[a.len / 2]); 777 return x == 0; 778 } 779 var x: u32 = 0; 780 for ([_]usize{ 0, a.len - 4, (a.len / 8) * 4, a.len - 4 - ((a.len / 8) * 4) }) |n| { 781 x |= @as(u32, @bitCast(a[n..][0..4].*)) ^ @as(u32, @bitCast(b[n..][0..4].*)); 782 } 783 return x == 0; 784 } 785 786 // Figure out the fastest way to scan through the input in chunks. 787 // Uses vectors when supported and falls back to usize/words when not. 788 const Scan = if (std.simd.suggestVectorLength(u8)) |vec_size| 789 struct { 790 pub const size = vec_size; 791 pub const Chunk = @Vector(size, u8); 792 pub inline fn isNotEqual(chunk_a: Chunk, chunk_b: Chunk) bool { 793 return @reduce(.Or, chunk_a != chunk_b); 794 } 795 } 796 else 797 struct { 798 pub const size = @sizeOf(usize); 799 pub const Chunk = usize; 800 pub inline fn isNotEqual(chunk_a: Chunk, chunk_b: Chunk) bool { 801 return chunk_a != chunk_b; 802 } 803 }; 804 805 inline for (1..6) |s| { 806 const n = 16 << s; 807 if (n <= Scan.size and a.len <= n) { 808 const V = @Vector(n / 2, u8); 809 var x = @as(V, a[0 .. n / 2].*) ^ @as(V, b[0 .. n / 2].*); 810 x |= @as(V, a[a.len - n / 2 ..][0 .. n / 2].*) ^ @as(V, b[a.len - n / 2 ..][0 .. n / 2].*); 811 const zero: V = @splat(0); 812 return !@reduce(.Or, x != zero); 813 } 814 } 815 // Compare inputs in chunks at a time (excluding the last chunk). 816 for (0..(a.len - 1) / Scan.size) |i| { 817 const a_chunk: Scan.Chunk = @bitCast(a[i * Scan.size ..][0..Scan.size].*); 818 const b_chunk: Scan.Chunk = @bitCast(b[i * Scan.size ..][0..Scan.size].*); 819 if (Scan.isNotEqual(a_chunk, b_chunk)) return false; 820 } 821 822 // Compare the last chunk using an overlapping read (similar to the previous size strategies). 823 const last_a_chunk: Scan.Chunk = @bitCast(a[a.len - Scan.size ..][0..Scan.size].*); 824 const last_b_chunk: Scan.Chunk = @bitCast(b[a.len - Scan.size ..][0..Scan.size].*); 825 return !Scan.isNotEqual(last_a_chunk, last_b_chunk); 826 } 827 828 /// Deprecated in favor of `findDiff`. 829 pub const indexOfDiff = findDiff; 830 831 /// Compares two slices and returns the index of the first inequality. 832 /// Returns null if the slices are equal. 833 pub fn findDiff(comptime T: type, a: []const T, b: []const T) ?usize { 834 const shortest = @min(a.len, b.len); 835 if (a.ptr == b.ptr) 836 return if (a.len == b.len) null else shortest; 837 var index: usize = 0; 838 while (index < shortest) : (index += 1) if (a[index] != b[index]) return index; 839 return if (a.len == b.len) null else shortest; 840 } 841 842 test findDiff { 843 try testing.expectEqual(findDiff(u8, "one", "one"), null); 844 try testing.expectEqual(findDiff(u8, "one two", "one"), 3); 845 try testing.expectEqual(findDiff(u8, "one", "one two"), 3); 846 try testing.expectEqual(findDiff(u8, "one twx", "one two"), 6); 847 try testing.expectEqual(findDiff(u8, "xne", "one"), 0); 848 } 849 850 /// Takes a sentinel-terminated pointer and returns a slice preserving pointer attributes. 851 /// `[*c]` pointers are assumed to be 0-terminated and assumed to not be allowzero. 852 fn Span(comptime T: type) type { 853 switch (@typeInfo(T)) { 854 .optional => |optional_info| { 855 return ?Span(optional_info.child); 856 }, 857 .pointer => |ptr_info| { 858 const new_sentinel: ?ptr_info.child = switch (ptr_info.size) { 859 .one, .slice => @compileError("invalid type given to std.mem.span: " ++ @typeName(T)), 860 .many => ptr_info.sentinel() orelse @compileError("invalid type given to std.mem.span: " ++ @typeName(T)), 861 .c => 0, 862 }; 863 return @Pointer(.slice, .{ 864 .@"const" = ptr_info.is_const, 865 .@"volatile" = ptr_info.is_volatile, 866 .@"allowzero" = ptr_info.is_allowzero and ptr_info.size != .c, 867 .@"align" = ptr_info.alignment, 868 .@"addrspace" = ptr_info.address_space, 869 }, ptr_info.child, new_sentinel); 870 }, 871 else => {}, 872 } 873 @compileError("invalid type given to std.mem.span: " ++ @typeName(T)); 874 } 875 876 test Span { 877 try testing.expect(Span([*:1]u16) == [:1]u16); 878 try testing.expect(Span(?[*:1]u16) == ?[:1]u16); 879 try testing.expect(Span([*:1]const u8) == [:1]const u8); 880 try testing.expect(Span(?[*:1]const u8) == ?[:1]const u8); 881 try testing.expect(Span([*c]u16) == [:0]u16); 882 try testing.expect(Span(?[*c]u16) == ?[:0]u16); 883 try testing.expect(Span([*c]const u8) == [:0]const u8); 884 try testing.expect(Span(?[*c]const u8) == ?[:0]const u8); 885 } 886 887 /// Takes a sentinel-terminated pointer and returns a slice, iterating over the 888 /// memory to find the sentinel and determine the length. 889 /// Pointer attributes such as const are preserved. 890 /// `[*c]` pointers are assumed to be non-null and 0-terminated. 891 pub fn span(ptr: anytype) Span(@TypeOf(ptr)) { 892 if (@typeInfo(@TypeOf(ptr)) == .optional) { 893 if (ptr) |non_null| { 894 return span(non_null); 895 } else { 896 return null; 897 } 898 } 899 const Result = Span(@TypeOf(ptr)); 900 const l = len(ptr); 901 const ptr_info = @typeInfo(Result).pointer; 902 if (ptr_info.sentinel()) |s| { 903 return ptr[0..l :s]; 904 } else { 905 return ptr[0..l]; 906 } 907 } 908 909 test span { 910 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; 911 const ptr = @as([*:3]u16, array[0..2 :3]); 912 try testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 })); 913 try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null))); 914 } 915 916 /// Helper for the return type of sliceTo() 917 fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type { 918 switch (@typeInfo(T)) { 919 .optional => |optional_info| { 920 return ?SliceTo(optional_info.child, end); 921 }, 922 .pointer => |ptr_info| { 923 const Elem = std.meta.Elem(T); 924 const have_sentinel: bool = switch (ptr_info.size) { 925 .one, .slice => if (std.meta.sentinel(T)) |s| s == end else false, 926 .many => if (std.meta.sentinel(T)) |s| s == end else true, 927 .c => true, 928 }; 929 return @Pointer(.slice, .{ 930 .@"const" = ptr_info.is_const, 931 .@"volatile" = ptr_info.is_volatile, 932 .@"allowzero" = ptr_info.is_allowzero and ptr_info.size != .c, 933 .@"align" = ptr_info.alignment, 934 .@"addrspace" = ptr_info.address_space, 935 }, Elem, if (have_sentinel) end else null); 936 }, 937 else => {}, 938 } 939 @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T)); 940 } 941 942 /// Takes a pointer to an array, a many-item pointer, or a slice, and returns a 943 /// slice of the items up to the first occurrence of `end`. 944 /// If `end` is not found, the resulting slice will include all items up to the 945 /// input's length or sentinel. 946 /// If the pointer type is unbounded (no length or sentinel), `end` will be the 947 /// sentinel for the resulting slice. 948 /// If the pointer type is sentinel-terminated by `end`, the resulting slice 949 /// will also be sentinel-terminated by `end`. 950 /// Pointer properties such as mutability and alignment are preserved. 951 /// C pointers are assumed to be non-null. 952 pub fn sliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) { 953 if (@typeInfo(@TypeOf(ptr)) == .optional) { 954 const non_null = ptr orelse return null; 955 return sliceTo(non_null, end); 956 } 957 const Result = SliceTo(@TypeOf(ptr), end); 958 const length = lenSliceTo(ptr, end); 959 const ptr_info = @typeInfo(Result).pointer; 960 if (ptr_info.sentinel()) |s| { 961 return ptr[0..length :s]; 962 } else { 963 return ptr[0..length]; 964 } 965 } 966 967 test sliceTo { 968 try testing.expectEqualSlices(u8, "aoeu", sliceTo("aoeu", 0)); 969 970 { 971 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; 972 try testing.expectEqualSlices(u16, &array, sliceTo(&array, 0)); 973 try testing.expectEqualSlices(u16, array[0..3], sliceTo(array[0..3], 0)); 974 try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3)); 975 try testing.expectEqualSlices(u16, array[0..2], sliceTo(array[0..3], 3)); 976 977 const many_ptr: [*]u16 = &array; 978 try testing.expectEqualSlices(u16, array[0..2], sliceTo(many_ptr, 3)); 979 try testing.expectEqual([:3]u16, @TypeOf(sliceTo(many_ptr, 3))); 980 981 const sentinel_ptr = @as([*:5]u16, @ptrCast(&array)); 982 try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_ptr, 3)); 983 try testing.expectEqual([]u16, @TypeOf(sliceTo(sentinel_ptr, 3))); 984 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 5)); 985 try testing.expectEqual([:5]u16, @TypeOf(sliceTo(sentinel_ptr, 5))); 986 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99)); 987 988 const optional_sentinel_ptr = @as(?[*:5]u16, @ptrCast(&array)); 989 try testing.expectEqualSlices(u16, array[0..2], sliceTo(optional_sentinel_ptr, 3).?); 990 try testing.expectEqualSlices(u16, array[0..4], sliceTo(optional_sentinel_ptr, 99).?); 991 992 const c_ptr = @as([*c]u16, &array); 993 try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3)); 994 try testing.expectEqual([:3]u16, @TypeOf(sliceTo(c_ptr, 3))); 995 996 const slice: []u16 = &array; 997 try testing.expectEqualSlices(u16, array[0..2], sliceTo(slice, 3)); 998 try testing.expectEqualSlices(u16, &array, sliceTo(slice, 99)); 999 1000 const sentinel_slice: [:5]u16 = array[0..4 :5]; 1001 try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_slice, 3)); 1002 try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_slice, 99)); 1003 } 1004 { 1005 var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; 1006 try testing.expectEqualSlices(u16, sentinel_array[0..2], sliceTo(&sentinel_array, 3)); 1007 try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 0)); 1008 try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 99)); 1009 } 1010 1011 try testing.expectEqual(@as(?[]u8, null), sliceTo(@as(?[]u8, null), 0)); 1012 } 1013 1014 /// Private helper for sliceTo(). If you want the length, use sliceTo(foo, x).len 1015 fn lenSliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) usize { 1016 switch (@typeInfo(@TypeOf(ptr))) { 1017 .pointer => |ptr_info| switch (ptr_info.size) { 1018 .one => switch (@typeInfo(ptr_info.child)) { 1019 .array => |array_info| { 1020 if (array_info.sentinel()) |s| { 1021 if (s == end) { 1022 return findSentinel(array_info.child, end, ptr); 1023 } 1024 } 1025 return findScalar(array_info.child, ptr, end) orelse array_info.len; 1026 }, 1027 else => {}, 1028 }, 1029 .many => if (ptr_info.sentinel()) |s| { 1030 if (s == end) { 1031 return findSentinel(ptr_info.child, end, ptr); 1032 } 1033 // We're looking for something other than the sentinel, 1034 // but iterating past the sentinel would be a bug so we need 1035 // to check for both. 1036 var i: usize = 0; 1037 while (ptr[i] != end and ptr[i] != s) i += 1; 1038 return i; 1039 } else { 1040 return findSentinel(ptr_info.child, end, @ptrCast(ptr)); 1041 }, 1042 .c => { 1043 assert(ptr != null); 1044 return findSentinel(ptr_info.child, end, ptr); 1045 }, 1046 .slice => { 1047 if (ptr_info.sentinel()) |s| { 1048 if (s == end) { 1049 return findSentinel(ptr_info.child, s, ptr); 1050 } 1051 } 1052 return findScalar(ptr_info.child, ptr, end) orelse ptr.len; 1053 }, 1054 }, 1055 else => {}, 1056 } 1057 @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(@TypeOf(ptr))); 1058 } 1059 1060 test lenSliceTo { 1061 try testing.expect(lenSliceTo("aoeu", 0) == 4); 1062 1063 { 1064 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; 1065 try testing.expectEqual(@as(usize, 5), lenSliceTo(&array, 0)); 1066 try testing.expectEqual(@as(usize, 3), lenSliceTo(array[0..3], 0)); 1067 try testing.expectEqual(@as(usize, 2), lenSliceTo(&array, 3)); 1068 try testing.expectEqual(@as(usize, 2), lenSliceTo(array[0..3], 3)); 1069 1070 const sentinel_ptr = @as([*:5]u16, @ptrCast(&array)); 1071 try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_ptr, 3)); 1072 try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_ptr, 99)); 1073 1074 const c_ptr = @as([*c]u16, &array); 1075 try testing.expectEqual(@as(usize, 2), lenSliceTo(c_ptr, 3)); 1076 1077 const slice: []u16 = &array; 1078 try testing.expectEqual(@as(usize, 2), lenSliceTo(slice, 3)); 1079 try testing.expectEqual(@as(usize, 5), lenSliceTo(slice, 99)); 1080 1081 const sentinel_slice: [:5]u16 = array[0..4 :5]; 1082 try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_slice, 3)); 1083 try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_slice, 99)); 1084 } 1085 { 1086 var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; 1087 try testing.expectEqual(@as(usize, 2), lenSliceTo(&sentinel_array, 3)); 1088 try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 0)); 1089 try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 99)); 1090 } 1091 } 1092 1093 /// Takes a sentinel-terminated pointer and iterates over the memory to find the 1094 /// sentinel and determine the length. 1095 /// `[*c]` pointers are assumed to be non-null and 0-terminated. 1096 pub fn len(value: anytype) usize { 1097 switch (@typeInfo(@TypeOf(value))) { 1098 .pointer => |info| switch (info.size) { 1099 .many => { 1100 const sentinel = info.sentinel() orelse 1101 @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))); 1102 return findSentinel(info.child, sentinel, value); 1103 }, 1104 .c => { 1105 assert(value != null); 1106 return findSentinel(info.child, 0, value); 1107 }, 1108 else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))), 1109 }, 1110 else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))), 1111 } 1112 } 1113 1114 test len { 1115 var array: [5]u16 = [_]u16{ 1, 2, 0, 4, 5 }; 1116 const ptr = @as([*:4]u16, array[0..3 :4]); 1117 try testing.expect(len(ptr) == 3); 1118 const c_ptr = @as([*c]u16, ptr); 1119 try testing.expect(len(c_ptr) == 2); 1120 } 1121 1122 /// Deprecated in favor of `findSentinel`. 1123 pub const indexOfSentinel = findSentinel; 1124 1125 /// Returns the index of the sentinel value in a sentinel-terminated pointer. 1126 /// Linear search through memory until the sentinel is found. 1127 pub fn findSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]const T) usize { 1128 var i: usize = 0; 1129 while (p[i] != sentinel) { 1130 i += 1; 1131 } 1132 return i; 1133 } 1134 1135 test "findSentinel vector paths" { 1136 const Types = [_]type{ u8, u16, u32, u64 }; 1137 const allocator = std.testing.allocator; 1138 const page_size = std.heap.page_size_min; 1139 1140 inline for (Types) |T| { 1141 const block_len = std.simd.suggestVectorLength(T) orelse continue; 1142 1143 // Allocate three pages so we guarantee a page-crossing address with a full page after 1144 const memory = try allocator.alloc(T, 3 * page_size / @sizeOf(T)); 1145 defer allocator.free(memory); 1146 @memset(memory, 0xaa); 1147 1148 // Find starting page-alignment = 0 1149 var start: usize = 0; 1150 const start_addr = @intFromPtr(&memory); 1151 start += (std.mem.alignForward(usize, start_addr, page_size) - start_addr) / @sizeOf(T); 1152 try testing.expect(start < page_size / @sizeOf(T)); 1153 1154 // Validate all sub-block alignments 1155 const search_len = page_size / @sizeOf(T); 1156 memory[start + search_len] = 0; 1157 for (0..block_len) |offset| { 1158 try testing.expectEqual(search_len - offset, findSentinel(T, 0, @ptrCast(&memory[start + offset]))); 1159 } 1160 memory[start + search_len] = 0xaa; 1161 1162 // Validate page boundary crossing 1163 const start_page_boundary = start + (page_size / @sizeOf(T)); 1164 memory[start_page_boundary + block_len] = 0; 1165 for (0..block_len) |offset| { 1166 try testing.expectEqual(2 * block_len - offset, findSentinel(T, 0, @ptrCast(&memory[start_page_boundary - block_len + offset]))); 1167 } 1168 } 1169 } 1170 1171 /// Returns true if all elements in a slice are equal to the scalar value provided 1172 pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { 1173 for (slice) |item| { 1174 if (item != scalar) return false; 1175 } 1176 return true; 1177 } 1178 1179 /// Remove a set of values from the beginning of a slice. 1180 pub fn trimStart(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { 1181 var begin: usize = 0; 1182 while (begin < slice.len and findScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {} 1183 return slice[begin..]; 1184 } 1185 1186 test trimStart { 1187 try testing.expectEqualSlices(u8, "foo\n ", trimStart(u8, " foo\n ", " \n")); 1188 } 1189 1190 /// Remove a set of values from the end of a slice. 1191 pub fn trimEnd(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { 1192 var end: usize = slice.len; 1193 while (end > 0 and findScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {} 1194 return slice[0..end]; 1195 } 1196 1197 test trimEnd { 1198 try testing.expectEqualSlices(u8, " foo", trimEnd(u8, " foo\n ", " \n")); 1199 } 1200 1201 /// Remove a set of values from the beginning and end of a slice. 1202 pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { 1203 var begin: usize = 0; 1204 var end: usize = slice.len; 1205 while (begin < end and findScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {} 1206 while (end > begin and findScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {} 1207 return slice[begin..end]; 1208 } 1209 1210 test trim { 1211 try testing.expectEqualSlices(u8, "foo", trim(u8, " foo\n ", " \n")); 1212 try testing.expectEqualSlices(u8, "foo", trim(u8, "foo", " \n")); 1213 } 1214 1215 /// Deprecated in favor of `findScalar`. 1216 pub const indexOfScalar = findScalar; 1217 1218 /// Linear search for the index of a scalar value inside a slice. 1219 pub fn findScalar(comptime T: type, slice: []const T, value: T) ?usize { 1220 return findScalarPos(T, slice, 0, value); 1221 } 1222 1223 /// Deprecated in favor of `findScalarLast`. 1224 pub const lastIndexOfScalar = findScalarLast; 1225 1226 /// Linear search for the last index of a scalar value inside a slice. 1227 pub fn findScalarLast(comptime T: type, slice: []const T, value: T) ?usize { 1228 var i: usize = slice.len; 1229 while (i != 0) { 1230 i -= 1; 1231 if (slice[i] == value) return i; 1232 } 1233 return null; 1234 } 1235 1236 /// Deprecated in favor of `findScalarPos`. 1237 pub const indexOfScalarPos = findScalarPos; 1238 1239 /// Linear search for the index of a scalar value inside a slice, starting from a given position. 1240 /// Returns null if the value is not found. 1241 pub fn findScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { 1242 if (start_index >= slice.len) return null; 1243 1244 var i: usize = start_index; 1245 if (use_vectors_for_comparison and 1246 !std.debug.inValgrind() and // https://github.com/ziglang/zig/issues/17717 1247 !@inComptime() and 1248 (@typeInfo(T) == .int or @typeInfo(T) == .float) and std.math.isPowerOfTwo(@bitSizeOf(T))) 1249 { 1250 if (std.simd.suggestVectorLength(T)) |block_len| { 1251 // For Intel Nehalem (2009) and AMD Bulldozer (2012) or later, unaligned loads on aligned data result 1252 // in the same execution as aligned loads. We ignore older arch's here and don't bother pre-aligning. 1253 // 1254 // Use `std.simd.suggestVectorLength(T)` to get the same alignment as used in this function 1255 // however this usually isn't necessary unless your arch has a performance penalty due to this. 1256 // 1257 // This may differ for other arch's. Arm for example costs a cycle when loading across a cache 1258 // line so explicit alignment prologues may be worth exploration. 1259 1260 // Unrolling here is ~10% improvement. We can then do one bounds check every 2 blocks 1261 // instead of one which adds up. 1262 const Block = @Vector(block_len, T); 1263 if (i + 2 * block_len < slice.len) { 1264 const mask: Block = @splat(value); 1265 while (true) { 1266 inline for (0..2) |_| { 1267 const block: Block = slice[i..][0..block_len].*; 1268 const matches = block == mask; 1269 if (@reduce(.Or, matches)) { 1270 return i + std.simd.firstTrue(matches).?; 1271 } 1272 i += block_len; 1273 } 1274 if (i + 2 * block_len >= slice.len) break; 1275 } 1276 } 1277 1278 // {block_len, block_len / 2} check 1279 inline for (0..2) |j| { 1280 const block_x_len = block_len / (1 << j); 1281 comptime if (block_x_len < 4) break; 1282 1283 const BlockX = @Vector(block_x_len, T); 1284 if (i + block_x_len < slice.len) { 1285 const mask: BlockX = @splat(value); 1286 const block: BlockX = slice[i..][0..block_x_len].*; 1287 const matches = block == mask; 1288 if (@reduce(.Or, matches)) { 1289 return i + std.simd.firstTrue(matches).?; 1290 } 1291 i += block_x_len; 1292 } 1293 } 1294 } 1295 } 1296 1297 for (slice[i..], i..) |c, j| { 1298 if (c == value) return j; 1299 } 1300 return null; 1301 } 1302 1303 test findScalarPos { 1304 const Types = [_]type{ u8, u16, u32, u64 }; 1305 1306 inline for (Types) |T| { 1307 var memory: [64 / @sizeOf(T)]T = undefined; 1308 @memset(&memory, 0xaa); 1309 memory[memory.len - 1] = 0; 1310 1311 for (0..memory.len) |i| { 1312 try testing.expectEqual(memory.len - i - 1, findScalarPos(T, memory[i..], 0, 0).?); 1313 } 1314 } 1315 } 1316 1317 /// Deprecated in favor of `findAny`. 1318 pub const indexOfAny = findAny; 1319 1320 /// Linear search for the index of any value in the provided list inside a slice. 1321 /// Returns null if no values are found. 1322 pub fn findAny(comptime T: type, slice: []const T, values: []const T) ?usize { 1323 return findAnyPos(T, slice, 0, values); 1324 } 1325 1326 /// Deprecated in favor of `findLastAny`. 1327 pub const lastIndexOfAny = findLastAny; 1328 1329 /// Linear search for the last index of any value in the provided list inside a slice. 1330 /// Returns null if no values are found. 1331 pub fn findLastAny(comptime T: type, slice: []const T, values: []const T) ?usize { 1332 var i: usize = slice.len; 1333 while (i != 0) { 1334 i -= 1; 1335 for (values) |value| { 1336 if (slice[i] == value) return i; 1337 } 1338 } 1339 return null; 1340 } 1341 1342 /// Deprecated in favor of `findAnyPos`. 1343 pub const indexOfAnyPos = findAnyPos; 1344 1345 /// Linear search for the index of any value in the provided list inside a slice, starting from a given position. 1346 /// Returns null if no values are found. 1347 pub fn findAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { 1348 if (start_index >= slice.len) return null; 1349 for (slice[start_index..], start_index..) |c, i| { 1350 for (values) |value| { 1351 if (c == value) return i; 1352 } 1353 } 1354 return null; 1355 } 1356 1357 /// Deprecated in favor of `findNone`. 1358 pub const indexOfNone = findNone; 1359 1360 /// Find the first item in `slice` which is not contained in `values`. 1361 /// 1362 /// Comparable to `strspn` in the C standard library. 1363 pub fn findNone(comptime T: type, slice: []const T, values: []const T) ?usize { 1364 return findNonePos(T, slice, 0, values); 1365 } 1366 1367 test findNone { 1368 try testing.expect(findNone(u8, "abc123", "123").? == 0); 1369 try testing.expect(findLastNone(u8, "abc123", "123").? == 2); 1370 try testing.expect(findNone(u8, "123abc", "123").? == 3); 1371 try testing.expect(findLastNone(u8, "123abc", "123").? == 5); 1372 try testing.expect(findNone(u8, "123123", "123") == null); 1373 try testing.expect(findNone(u8, "333333", "123") == null); 1374 1375 try testing.expect(findNonePos(u8, "abc123", 3, "321") == null); 1376 } 1377 1378 /// Deprecated in favor of `findLastNone`. 1379 pub const lastIndexOfNone = findLastNone; 1380 1381 /// Find the last item in `slice` which is not contained in `values`. 1382 /// 1383 /// Like `strspn` in the C standard library, but searches from the end. 1384 pub fn findLastNone(comptime T: type, slice: []const T, values: []const T) ?usize { 1385 var i: usize = slice.len; 1386 outer: while (i != 0) { 1387 i -= 1; 1388 for (values) |value| { 1389 if (slice[i] == value) continue :outer; 1390 } 1391 return i; 1392 } 1393 return null; 1394 } 1395 1396 pub const indexOfNonePos = findNonePos; 1397 1398 /// Find the first item in `slice[start_index..]` which is not contained in `values`. 1399 /// The returned index will be relative to the start of `slice`, and never less than `start_index`. 1400 /// 1401 /// Comparable to `strspn` in the C standard library. 1402 pub fn findNonePos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { 1403 if (start_index >= slice.len) return null; 1404 outer: for (slice[start_index..], start_index..) |c, i| { 1405 for (values) |value| { 1406 if (c == value) continue :outer; 1407 } 1408 return i; 1409 } 1410 return null; 1411 } 1412 1413 /// Deprecated in favor of `find`. 1414 pub const indexOf = find; 1415 1416 /// Search for needle in haystack and return the index of the first occurrence. 1417 /// Uses Boyer-Moore-Horspool algorithm on large inputs; linear search on small inputs. 1418 /// Returns null if needle is not found. 1419 pub fn find(comptime T: type, haystack: []const T, needle: []const T) ?usize { 1420 return findPos(T, haystack, 0, needle); 1421 } 1422 1423 /// Deprecated in favor of `findLastLinear`. 1424 pub const lastIndexOfLinear = findLastLinear; 1425 1426 /// Find the index in a slice of a sub-slice, searching from the end backwards. 1427 /// To start looking at a different index, slice the haystack first. 1428 /// Consider using `lastIndexOf` instead of this, which will automatically use a 1429 /// more sophisticated algorithm on larger inputs. 1430 pub fn findLastLinear(comptime T: type, haystack: []const T, needle: []const T) ?usize { 1431 if (needle.len > haystack.len) return null; 1432 var i: usize = haystack.len - needle.len; 1433 while (true) : (i -= 1) { 1434 if (mem.eql(T, haystack[i..][0..needle.len], needle)) return i; 1435 if (i == 0) return null; 1436 } 1437 } 1438 1439 pub const indexOfPosLinear = findPosLinear; 1440 1441 /// Consider using `findPos` instead of this, which will automatically use a 1442 /// more sophisticated algorithm on larger inputs. 1443 pub fn findPosLinear(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize { 1444 if (needle.len > haystack.len) return null; 1445 var i: usize = start_index; 1446 const end = haystack.len - needle.len; 1447 while (i <= end) : (i += 1) { 1448 if (eql(T, haystack[i..][0..needle.len], needle)) return i; 1449 } 1450 return null; 1451 } 1452 1453 test findPosLinear { 1454 try testing.expectEqual(0, findPosLinear(u8, "", 0, "")); 1455 try testing.expectEqual(0, findPosLinear(u8, "123", 0, "")); 1456 1457 try testing.expectEqual(null, findPosLinear(u8, "", 0, "1")); 1458 try testing.expectEqual(0, findPosLinear(u8, "1", 0, "1")); 1459 try testing.expectEqual(null, findPosLinear(u8, "2", 0, "1")); 1460 try testing.expectEqual(1, findPosLinear(u8, "21", 0, "1")); 1461 try testing.expectEqual(null, findPosLinear(u8, "222", 0, "1")); 1462 1463 try testing.expectEqual(null, findPosLinear(u8, "", 0, "12")); 1464 try testing.expectEqual(null, findPosLinear(u8, "1", 0, "12")); 1465 try testing.expectEqual(null, findPosLinear(u8, "2", 0, "12")); 1466 try testing.expectEqual(0, findPosLinear(u8, "12", 0, "12")); 1467 try testing.expectEqual(null, findPosLinear(u8, "21", 0, "12")); 1468 try testing.expectEqual(1, findPosLinear(u8, "212", 0, "12")); 1469 try testing.expectEqual(0, findPosLinear(u8, "122", 0, "12")); 1470 try testing.expectEqual(1, findPosLinear(u8, "212112", 0, "12")); 1471 } 1472 1473 fn boyerMooreHorspoolPreprocessReverse(pattern: []const u8, table: *[256]usize) void { 1474 for (table) |*c| { 1475 c.* = pattern.len; 1476 } 1477 1478 var i: usize = pattern.len - 1; 1479 // The first item is intentionally ignored and the skip size will be pattern.len. 1480 // This is the standard way Boyer-Moore-Horspool is implemented. 1481 while (i > 0) : (i -= 1) { 1482 table[pattern[i]] = i; 1483 } 1484 } 1485 1486 fn boyerMooreHorspoolPreprocess(pattern: []const u8, table: *[256]usize) void { 1487 for (table) |*c| { 1488 c.* = pattern.len; 1489 } 1490 1491 var i: usize = 0; 1492 // The last item is intentionally ignored and the skip size will be pattern.len. 1493 // This is the standard way Boyer-Moore-Horspool is implemented. 1494 while (i < pattern.len - 1) : (i += 1) { 1495 table[pattern[i]] = pattern.len - 1 - i; 1496 } 1497 } 1498 1499 /// Deprecated in favor of `find`. 1500 pub const lastIndexOf = findLast; 1501 1502 /// Find the index in a slice of a sub-slice, searching from the end backwards. 1503 /// To start looking at a different index, slice the haystack first. 1504 /// Uses the Reverse Boyer-Moore-Horspool algorithm on large inputs; 1505 /// `lastIndexOfLinear` on small inputs. 1506 pub fn findLast(comptime T: type, haystack: []const T, needle: []const T) ?usize { 1507 if (needle.len > haystack.len) return null; 1508 if (needle.len == 0) return haystack.len; 1509 1510 if (!std.meta.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4) 1511 return lastIndexOfLinear(T, haystack, needle); 1512 1513 const haystack_bytes = sliceAsBytes(haystack); 1514 const needle_bytes = sliceAsBytes(needle); 1515 1516 var skip_table: [256]usize = undefined; 1517 boyerMooreHorspoolPreprocessReverse(needle_bytes, skip_table[0..]); 1518 1519 var i: usize = haystack_bytes.len - needle_bytes.len; 1520 while (true) { 1521 if (i % @sizeOf(T) == 0 and mem.eql(u8, haystack_bytes[i .. i + needle_bytes.len], needle_bytes)) { 1522 return @divExact(i, @sizeOf(T)); 1523 } 1524 const skip = skip_table[haystack_bytes[i]]; 1525 if (skip > i) break; 1526 i -= skip; 1527 } 1528 1529 return null; 1530 } 1531 1532 /// Deprecated in favor of `findPos`. 1533 pub const indexOfPos = findPos; 1534 1535 /// Uses Boyer-Moore-Horspool algorithm on large inputs; `findPosLinear` on small inputs. 1536 pub fn findPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize { 1537 if (needle.len > haystack.len) return null; 1538 if (needle.len < 2) { 1539 if (needle.len == 0) return start_index; 1540 // findScalarPos is significantly faster than findPosLinear 1541 return findScalarPos(T, haystack, start_index, needle[0]); 1542 } 1543 1544 if (!std.meta.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4) 1545 return findPosLinear(T, haystack, start_index, needle); 1546 1547 const haystack_bytes = sliceAsBytes(haystack); 1548 const needle_bytes = sliceAsBytes(needle); 1549 1550 var skip_table: [256]usize = undefined; 1551 boyerMooreHorspoolPreprocess(needle_bytes, skip_table[0..]); 1552 1553 var i: usize = start_index * @sizeOf(T); 1554 while (i <= haystack_bytes.len - needle_bytes.len) { 1555 if (i % @sizeOf(T) == 0 and mem.eql(u8, haystack_bytes[i .. i + needle_bytes.len], needle_bytes)) { 1556 return @divExact(i, @sizeOf(T)); 1557 } 1558 i += skip_table[haystack_bytes[i + needle_bytes.len - 1]]; 1559 } 1560 1561 return null; 1562 } 1563 1564 test find { 1565 try testing.expect(find(u8, "one two three four five six seven eight nine ten eleven", "three four").? == 8); 1566 try testing.expect(lastIndexOf(u8, "one two three four five six seven eight nine ten eleven", "three four").? == 8); 1567 try testing.expect(find(u8, "one two three four five six seven eight nine ten eleven", "two two") == null); 1568 try testing.expect(lastIndexOf(u8, "one two three four five six seven eight nine ten eleven", "two two") == null); 1569 1570 try testing.expect(find(u8, "one two three four five six seven eight nine ten", "").? == 0); 1571 try testing.expect(lastIndexOf(u8, "one two three four five six seven eight nine ten", "").? == 48); 1572 1573 try testing.expect(find(u8, "one two three four", "four").? == 14); 1574 try testing.expect(lastIndexOf(u8, "one two three two four", "two").? == 14); 1575 try testing.expect(find(u8, "one two three four", "gour") == null); 1576 try testing.expect(lastIndexOf(u8, "one two three four", "gour") == null); 1577 try testing.expect(find(u8, "foo", "foo").? == 0); 1578 try testing.expect(lastIndexOf(u8, "foo", "foo").? == 0); 1579 try testing.expect(find(u8, "foo", "fool") == null); 1580 try testing.expect(lastIndexOf(u8, "foo", "lfoo") == null); 1581 try testing.expect(lastIndexOf(u8, "foo", "fool") == null); 1582 1583 try testing.expect(find(u8, "foo foo", "foo").? == 0); 1584 try testing.expect(lastIndexOf(u8, "foo foo", "foo").? == 4); 1585 try testing.expect(lastIndexOfAny(u8, "boo, cat", "abo").? == 6); 1586 try testing.expect(findScalarLast(u8, "boo", 'o').? == 2); 1587 } 1588 1589 test "find multibyte" { 1590 { 1591 // make haystack and needle long enough to trigger Boyer-Moore-Horspool algorithm 1592 const haystack = [1]u16{0} ** 100 ++ [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff }; 1593 const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee }; 1594 try testing.expectEqual(findPos(u16, &haystack, 0, &needle), 100); 1595 1596 // check for misaligned false positives (little and big endian) 1597 const needleLE = [_]u16{ 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff }; 1598 try testing.expectEqual(findPos(u16, &haystack, 0, &needleLE), null); 1599 const needleBE = [_]u16{ 0xaacc, 0xbbdd, 0xccee, 0xddff, 0xee00 }; 1600 try testing.expectEqual(findPos(u16, &haystack, 0, &needleBE), null); 1601 } 1602 1603 { 1604 // make haystack and needle long enough to trigger Boyer-Moore-Horspool algorithm 1605 const haystack = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff } ++ [1]u16{0} ** 100; 1606 const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee }; 1607 try testing.expectEqual(lastIndexOf(u16, &haystack, &needle), 0); 1608 1609 // check for misaligned false positives (little and big endian) 1610 const needleLE = [_]u16{ 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff }; 1611 try testing.expectEqual(lastIndexOf(u16, &haystack, &needleLE), null); 1612 const needleBE = [_]u16{ 0xaacc, 0xbbdd, 0xccee, 0xddff, 0xee00 }; 1613 try testing.expectEqual(lastIndexOf(u16, &haystack, &needleBE), null); 1614 } 1615 } 1616 1617 test "findPos empty needle" { 1618 try testing.expectEqual(findPos(u8, "abracadabra", 5, ""), 5); 1619 } 1620 1621 /// Returns the number of needles inside the haystack 1622 /// needle.len must be > 0 1623 /// does not count overlapping needles 1624 pub fn count(comptime T: type, haystack: []const T, needle: []const T) usize { 1625 if (needle.len == 1) return countScalar(T, haystack, needle[0]); 1626 assert(needle.len > 0); 1627 var i: usize = 0; 1628 var found: usize = 0; 1629 1630 while (findPos(T, haystack, i, needle)) |idx| { 1631 i = idx + needle.len; 1632 found += 1; 1633 } 1634 1635 return found; 1636 } 1637 1638 test count { 1639 try testing.expect(count(u8, "", "h") == 0); 1640 try testing.expect(count(u8, "h", "h") == 1); 1641 try testing.expect(count(u8, "hh", "h") == 2); 1642 try testing.expect(count(u8, "world!", "hello") == 0); 1643 try testing.expect(count(u8, "hello world!", "hello") == 1); 1644 try testing.expect(count(u8, " abcabc abc", "abc") == 3); 1645 try testing.expect(count(u8, "udexdcbvbruhasdrw", "bruh") == 1); 1646 try testing.expect(count(u8, "foo bar", "o bar") == 1); 1647 try testing.expect(count(u8, "foofoofoo", "foo") == 3); 1648 try testing.expect(count(u8, "fffffff", "ff") == 3); 1649 try testing.expect(count(u8, "owowowu", "owowu") == 1); 1650 } 1651 1652 /// Returns the number of times `element` appears in a slice of memory. 1653 pub fn countScalar(comptime T: type, list: []const T, element: T) usize { 1654 const n = list.len; 1655 var i: usize = 0; 1656 var found: usize = 0; 1657 1658 if (use_vectors_for_comparison and 1659 (@typeInfo(T) == .int or @typeInfo(T) == .float) and std.math.isPowerOfTwo(@bitSizeOf(T))) 1660 { 1661 if (std.simd.suggestVectorLength(T)) |block_size| { 1662 const Block = @Vector(block_size, T); 1663 1664 const letter_mask: Block = @splat(element); 1665 while (n - i >= block_size) : (i += block_size) { 1666 const haystack_block: Block = list[i..][0..block_size].*; 1667 found += std.simd.countTrues(letter_mask == haystack_block); 1668 } 1669 } 1670 } 1671 1672 for (list[i..n]) |item| { 1673 found += @intFromBool(item == element); 1674 } 1675 1676 return found; 1677 } 1678 1679 test countScalar { 1680 try testing.expectEqual(0, countScalar(u8, "", 'h')); 1681 try testing.expectEqual(1, countScalar(u8, "h", 'h')); 1682 try testing.expectEqual(2, countScalar(u8, "hh", 'h')); 1683 try testing.expectEqual(2, countScalar(u8, "ahhb", 'h')); 1684 try testing.expectEqual(3, countScalar(u8, " abcabc abc", 'b')); 1685 } 1686 1687 /// Returns true if the haystack contains expected_count or more needles 1688 /// needle.len must be > 0 1689 /// does not count overlapping needles 1690 // 1691 /// See also: `containsAtLeastScalar` 1692 pub fn containsAtLeast(comptime T: type, haystack: []const T, expected_count: usize, needle: []const T) bool { 1693 if (needle.len == 1) return containsAtLeastScalar(T, haystack, expected_count, needle[0]); 1694 assert(needle.len > 0); 1695 if (expected_count == 0) return true; 1696 1697 var i: usize = 0; 1698 var found: usize = 0; 1699 1700 while (findPos(T, haystack, i, needle)) |idx| { 1701 i = idx + needle.len; 1702 found += 1; 1703 if (found == expected_count) return true; 1704 } 1705 return false; 1706 } 1707 1708 test containsAtLeast { 1709 try testing.expect(containsAtLeast(u8, "aa", 0, "a")); 1710 try testing.expect(containsAtLeast(u8, "aa", 1, "a")); 1711 try testing.expect(containsAtLeast(u8, "aa", 2, "a")); 1712 try testing.expect(!containsAtLeast(u8, "aa", 3, "a")); 1713 1714 try testing.expect(containsAtLeast(u8, "radaradar", 1, "radar")); 1715 try testing.expect(!containsAtLeast(u8, "radaradar", 2, "radar")); 1716 1717 try testing.expect(containsAtLeast(u8, "radarradaradarradar", 3, "radar")); 1718 try testing.expect(!containsAtLeast(u8, "radarradaradarradar", 4, "radar")); 1719 1720 try testing.expect(containsAtLeast(u8, " radar radar ", 2, "radar")); 1721 try testing.expect(!containsAtLeast(u8, " radar radar ", 3, "radar")); 1722 } 1723 1724 /// Deprecated in favor of `containsAtLeastScalar2`. 1725 pub fn containsAtLeastScalar(comptime T: type, list: []const T, minimum: usize, element: T) bool { 1726 return containsAtLeastScalar2(T, list, element, minimum); 1727 } 1728 1729 /// Returns true if `element` appears at least `minimum` number of times in `list`. 1730 // 1731 /// Related: 1732 /// * `containsAtLeast` 1733 /// * `countScalar` 1734 pub fn containsAtLeastScalar2(comptime T: type, list: []const T, element: T, minimum: usize) bool { 1735 const n = list.len; 1736 var i: usize = 0; 1737 var found: usize = 0; 1738 1739 if (use_vectors_for_comparison and 1740 (@typeInfo(T) == .int or @typeInfo(T) == .float) and std.math.isPowerOfTwo(@bitSizeOf(T))) 1741 { 1742 if (std.simd.suggestVectorLength(T)) |block_size| { 1743 const Block = @Vector(block_size, T); 1744 1745 const letter_mask: Block = @splat(element); 1746 while (n - i >= block_size) : (i += block_size) { 1747 const haystack_block: Block = list[i..][0..block_size].*; 1748 found += std.simd.countTrues(letter_mask == haystack_block); 1749 if (found >= minimum) return true; 1750 } 1751 } 1752 } 1753 1754 for (list[i..n]) |item| { 1755 found += @intFromBool(item == element); 1756 if (found >= minimum) return true; 1757 } 1758 1759 return false; 1760 } 1761 1762 test containsAtLeastScalar2 { 1763 try testing.expect(containsAtLeastScalar2(u8, "aa", 'a', 0)); 1764 try testing.expect(containsAtLeastScalar2(u8, "aa", 'a', 1)); 1765 try testing.expect(containsAtLeastScalar2(u8, "aa", 'a', 2)); 1766 try testing.expect(!containsAtLeastScalar2(u8, "aa", 'a', 3)); 1767 1768 try testing.expect(containsAtLeastScalar2(u8, "adadda", 'd', 3)); 1769 try testing.expect(!containsAtLeastScalar2(u8, "adadda", 'd', 4)); 1770 } 1771 1772 /// Reads an integer from memory with size equal to bytes.len. 1773 /// ReturnType specifies the return type, which must be large enough to store 1774 /// the result. 1775 pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: Endian) ReturnType { 1776 assert(@typeInfo(ReturnType).int.bits >= bytes.len * 8); 1777 const bits = @typeInfo(ReturnType).int.bits; 1778 const signedness = @typeInfo(ReturnType).int.signedness; 1779 const WorkType = std.meta.Int(signedness, @max(16, bits)); 1780 var result: WorkType = 0; 1781 switch (endian) { 1782 .big => { 1783 for (bytes) |b| { 1784 result = (result << 8) | b; 1785 } 1786 }, 1787 .little => { 1788 const ShiftType = math.Log2Int(WorkType); 1789 for (bytes, 0..) |b, index| { 1790 result = result | (@as(WorkType, b) << @as(ShiftType, @intCast(index * 8))); 1791 } 1792 }, 1793 } 1794 return @truncate(result); 1795 } 1796 1797 test readVarInt { 1798 try testing.expect(readVarInt(u0, &[_]u8{}, .big) == 0x0); 1799 try testing.expect(readVarInt(u0, &[_]u8{}, .little) == 0x0); 1800 try testing.expect(readVarInt(u8, &[_]u8{0x12}, .big) == 0x12); 1801 try testing.expect(readVarInt(u8, &[_]u8{0xde}, .little) == 0xde); 1802 try testing.expect(readVarInt(u16, &[_]u8{ 0x12, 0x34 }, .big) == 0x1234); 1803 try testing.expect(readVarInt(u16, &[_]u8{ 0x12, 0x34 }, .little) == 0x3412); 1804 1805 try testing.expect(readVarInt(i8, &[_]u8{0xff}, .big) == -1); 1806 try testing.expect(readVarInt(i8, &[_]u8{0xfe}, .little) == -2); 1807 try testing.expect(readVarInt(i16, &[_]u8{ 0xff, 0xfd }, .big) == -3); 1808 try testing.expect(readVarInt(i16, &[_]u8{ 0xfc, 0xff }, .little) == -4); 1809 1810 // Return type can be oversized (bytes.len * 8 < @typeInfo(ReturnType).int.bits) 1811 try testing.expect(readVarInt(u9, &[_]u8{0x12}, .little) == 0x12); 1812 try testing.expect(readVarInt(u9, &[_]u8{0xde}, .big) == 0xde); 1813 try testing.expect(readVarInt(u80, &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }, .big) == 0x123456789abcdef024); 1814 try testing.expect(readVarInt(u80, &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }, .little) == 0xfedcba9876543210ec); 1815 1816 try testing.expect(readVarInt(i9, &[_]u8{0xff}, .big) == 0xff); 1817 try testing.expect(readVarInt(i9, &[_]u8{0xfe}, .little) == 0xfe); 1818 } 1819 1820 /// Loads an integer from packed memory with provided bit_count, bit_offset, and signedness. 1821 /// Asserts that T is large enough to store the read value. 1822 pub fn readVarPackedInt( 1823 comptime T: type, 1824 bytes: []const u8, 1825 bit_offset: usize, 1826 bit_count: usize, 1827 endian: std.builtin.Endian, 1828 signedness: std.builtin.Signedness, 1829 ) T { 1830 const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); 1831 const iN = std.meta.Int(.signed, @bitSizeOf(T)); 1832 const Log2N = std.math.Log2Int(T); 1833 1834 const read_size = (bit_count + (bit_offset % 8) + 7) / 8; 1835 const bit_shift = @as(u3, @intCast(bit_offset % 8)); 1836 const pad = @as(Log2N, @intCast(@bitSizeOf(T) - bit_count)); 1837 1838 const lowest_byte = switch (endian) { 1839 .big => bytes.len - (bit_offset / 8) - read_size, 1840 .little => bit_offset / 8, 1841 }; 1842 const read_bytes = bytes[lowest_byte..][0..read_size]; 1843 1844 if (@bitSizeOf(T) <= 8) { 1845 // These are the same shifts/masks we perform below, but adds `@truncate`/`@intCast` 1846 // where needed since int is smaller than a byte. 1847 const value = if (read_size == 1) b: { 1848 break :b @as(uN, @truncate(read_bytes[0] >> bit_shift)); 1849 } else b: { 1850 const i: u1 = @intFromBool(endian == .big); 1851 const head = @as(uN, @truncate(read_bytes[i] >> bit_shift)); 1852 const tail_shift = @as(Log2N, @intCast(@as(u4, 8) - bit_shift)); 1853 const tail = @as(uN, @truncate(read_bytes[1 - i])); 1854 break :b (tail << tail_shift) | head; 1855 }; 1856 switch (signedness) { 1857 .signed => return @as(T, @intCast((@as(iN, @bitCast(value)) << pad) >> pad)), 1858 .unsigned => return @as(T, @intCast((@as(uN, @bitCast(value)) << pad) >> pad)), 1859 } 1860 } 1861 1862 // Copy the value out (respecting endianness), accounting for bit_shift 1863 var int: uN = 0; 1864 switch (endian) { 1865 .big => { 1866 for (read_bytes[0 .. read_size - 1]) |elem| { 1867 int = elem | (int << 8); 1868 } 1869 int = (read_bytes[read_size - 1] >> bit_shift) | (int << (@as(u4, 8) - bit_shift)); 1870 }, 1871 .little => { 1872 int = read_bytes[0] >> bit_shift; 1873 for (read_bytes[1..], 0..) |elem, i| { 1874 int |= (@as(uN, elem) << @as(Log2N, @intCast((8 * (i + 1) - bit_shift)))); 1875 } 1876 }, 1877 } 1878 switch (signedness) { 1879 .signed => return @as(T, @intCast((@as(iN, @bitCast(int)) << pad) >> pad)), 1880 .unsigned => return @as(T, @intCast((@as(uN, @bitCast(int)) << pad) >> pad)), 1881 } 1882 } 1883 1884 test readVarPackedInt { 1885 const T = packed struct(u16) { a: u3, b: u7, c: u6 }; 1886 var st = T{ .a = 1, .b = 2, .c = 4 }; 1887 const b_field = readVarPackedInt(u64, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 7, builtin.cpu.arch.endian(), .unsigned); 1888 try std.testing.expectEqual(st.b, b_field); 1889 } 1890 1891 /// Reads an integer from memory with bit count specified by T. 1892 /// The bit count of T must be evenly divisible by 8. 1893 /// This function cannot fail and cannot cause undefined behavior. 1894 pub inline fn readInt(comptime T: type, buffer: *const [@divExact(@typeInfo(T).int.bits, 8)]u8, endian: Endian) T { 1895 const value: T = @bitCast(buffer.*); 1896 return if (endian == native_endian) value else @byteSwap(value); 1897 } 1898 1899 test readInt { 1900 try testing.expect(readInt(u0, &[_]u8{}, .big) == 0x0); 1901 try testing.expect(readInt(u0, &[_]u8{}, .little) == 0x0); 1902 1903 try testing.expect(readInt(u8, &[_]u8{0x32}, .big) == 0x32); 1904 try testing.expect(readInt(u8, &[_]u8{0x12}, .little) == 0x12); 1905 1906 try testing.expect(readInt(u16, &[_]u8{ 0x12, 0x34 }, .big) == 0x1234); 1907 try testing.expect(readInt(u16, &[_]u8{ 0x12, 0x34 }, .little) == 0x3412); 1908 1909 try testing.expect(readInt(u72, &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }, .big) == 0x123456789abcdef024); 1910 try testing.expect(readInt(u72, &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }, .little) == 0xfedcba9876543210ec); 1911 1912 try testing.expect(readInt(i8, &[_]u8{0xff}, .big) == -1); 1913 try testing.expect(readInt(i8, &[_]u8{0xfe}, .little) == -2); 1914 1915 try testing.expect(readInt(i16, &[_]u8{ 0xff, 0xfd }, .big) == -3); 1916 try testing.expect(readInt(i16, &[_]u8{ 0xfc, 0xff }, .little) == -4); 1917 1918 try moreReadIntTests(); 1919 try comptime moreReadIntTests(); 1920 } 1921 1922 fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T { 1923 const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); 1924 const Log2N = std.math.Log2Int(T); 1925 1926 const bit_count = @as(usize, @bitSizeOf(T)); 1927 const bit_shift = @as(u3, @intCast(bit_offset % 8)); 1928 1929 const load_size = (bit_count + 7) / 8; 1930 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count)); 1931 const LoadInt = std.meta.Int(.unsigned, load_size * 8); 1932 1933 if (bit_count == 0) 1934 return 0; 1935 1936 // Read by loading a LoadInt, and then follow it up with a 1-byte read 1937 // of the tail if bit_offset pushed us over a byte boundary. 1938 const read_bytes = bytes[bit_offset / 8 ..]; 1939 const val = @as(uN, @truncate(readInt(LoadInt, read_bytes[0..load_size], .little) >> bit_shift)); 1940 if (bit_shift > load_tail_bits) { 1941 const tail_bits = @as(Log2N, @intCast(bit_shift - load_tail_bits)); 1942 const tail_byte = read_bytes[load_size]; 1943 const tail_truncated = if (bit_count < 8) @as(uN, @truncate(tail_byte)) else @as(uN, tail_byte); 1944 return @as(T, @bitCast(val | (tail_truncated << (@as(Log2N, @truncate(bit_count)) -% tail_bits)))); 1945 } else return @as(T, @bitCast(val)); 1946 } 1947 1948 fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T { 1949 const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); 1950 const Log2N = std.math.Log2Int(T); 1951 1952 const bit_count = @as(usize, @bitSizeOf(T)); 1953 const bit_shift = @as(u3, @intCast(bit_offset % 8)); 1954 const byte_count = (@as(usize, bit_shift) + bit_count + 7) / 8; 1955 1956 const load_size = (bit_count + 7) / 8; 1957 const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count)); 1958 const LoadInt = std.meta.Int(.unsigned, load_size * 8); 1959 1960 if (bit_count == 0) 1961 return 0; 1962 1963 // Read by loading a LoadInt, and then follow it up with a 1-byte read 1964 // of the tail if bit_offset pushed us over a byte boundary. 1965 const end = bytes.len - (bit_offset / 8); 1966 const read_bytes = bytes[(end - byte_count)..end]; 1967 const val = @as(uN, @truncate(readInt(LoadInt, bytes[(end - load_size)..end][0..load_size], .big) >> bit_shift)); 1968 if (bit_shift > load_tail_bits) { 1969 const tail_bits = @as(Log2N, @intCast(bit_shift - load_tail_bits)); 1970 const tail_byte = if (bit_count < 8) @as(uN, @truncate(read_bytes[0])) else @as(uN, read_bytes[0]); 1971 return @as(T, @bitCast(val | (tail_byte << (@as(Log2N, @truncate(bit_count)) -% tail_bits)))); 1972 } else return @as(T, @bitCast(val)); 1973 } 1974 1975 /// Deprecated: use readPackedInt(T, bytes, bit_offset, value, .native) 1976 pub const readPackedIntNative = switch (native_endian) { 1977 .little => readPackedIntLittle, 1978 .big => readPackedIntBig, 1979 }; 1980 1981 /// Deprecated: use readPackedInt(T, bytes, bit_offset, value, .foreign) 1982 pub const readPackedIntForeign = switch (native_endian) { 1983 .little => readPackedIntBig, 1984 .big => readPackedIntLittle, 1985 }; 1986 1987 /// Loads an integer from packed memory. 1988 /// Asserts that buffer contains at least bit_offset + @bitSizeOf(T) bits. 1989 pub fn readPackedInt(comptime T: type, bytes: []const u8, bit_offset: usize, endian: Endian) T { 1990 switch (endian) { 1991 .little => return readPackedIntLittle(T, bytes, bit_offset), 1992 .big => return readPackedIntBig(T, bytes, bit_offset), 1993 } 1994 } 1995 1996 test readPackedInt { 1997 const T = packed struct(u16) { a: u3, b: u7, c: u6 }; 1998 var st = T{ .a = 1, .b = 2, .c = 4 }; 1999 const b_field = readPackedInt(u7, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), builtin.cpu.arch.endian()); 2000 try std.testing.expectEqual(st.b, b_field); 2001 } 2002 2003 test "comptime read/write int" { 2004 comptime { 2005 var bytes: [2]u8 = undefined; 2006 writeInt(u16, &bytes, 0x1234, .little); 2007 const result = readInt(u16, &bytes, .big); 2008 try testing.expect(result == 0x3412); 2009 } 2010 comptime { 2011 var bytes: [2]u8 = undefined; 2012 writeInt(u16, &bytes, 0x1234, .big); 2013 const result = readInt(u16, &bytes, .little); 2014 try testing.expect(result == 0x3412); 2015 } 2016 } 2017 2018 /// Writes an integer to memory, storing it in twos-complement. 2019 /// This function always succeeds, has defined behavior for all inputs, but 2020 /// the integer bit width must be divisible by 8. 2021 pub inline fn writeInt(comptime T: type, buffer: *[@divExact(@typeInfo(T).int.bits, 8)]u8, value: T, endian: Endian) void { 2022 buffer.* = @bitCast(if (endian == native_endian) value else @byteSwap(value)); 2023 } 2024 2025 test writeInt { 2026 var buf0: [0]u8 = undefined; 2027 var buf1: [1]u8 = undefined; 2028 var buf2: [2]u8 = undefined; 2029 var buf9: [9]u8 = undefined; 2030 2031 writeInt(u0, &buf0, 0x0, .big); 2032 try testing.expect(eql(u8, buf0[0..], &[_]u8{})); 2033 writeInt(u0, &buf0, 0x0, .little); 2034 try testing.expect(eql(u8, buf0[0..], &[_]u8{})); 2035 2036 writeInt(u8, &buf1, 0x12, .big); 2037 try testing.expect(eql(u8, buf1[0..], &[_]u8{0x12})); 2038 writeInt(u8, &buf1, 0x34, .little); 2039 try testing.expect(eql(u8, buf1[0..], &[_]u8{0x34})); 2040 2041 writeInt(u16, &buf2, 0x1234, .big); 2042 try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0x12, 0x34 })); 2043 writeInt(u16, &buf2, 0x5678, .little); 2044 try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0x78, 0x56 })); 2045 2046 writeInt(u72, &buf9, 0x123456789abcdef024, .big); 2047 try testing.expect(eql(u8, buf9[0..], &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 })); 2048 writeInt(u72, &buf9, 0xfedcba9876543210ec, .little); 2049 try testing.expect(eql(u8, buf9[0..], &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe })); 2050 2051 writeInt(i8, &buf1, -1, .big); 2052 try testing.expect(eql(u8, buf1[0..], &[_]u8{0xff})); 2053 writeInt(i8, &buf1, -2, .little); 2054 try testing.expect(eql(u8, buf1[0..], &[_]u8{0xfe})); 2055 2056 writeInt(i16, &buf2, -3, .big); 2057 try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0xff, 0xfd })); 2058 writeInt(i16, &buf2, -4, .little); 2059 try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0xfc, 0xff })); 2060 } 2061 2062 fn writePackedIntLittle(comptime T: type, bytes: []u8, bit_offset: usize, value: T) void { 2063 const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); 2064 const Log2N = std.math.Log2Int(T); 2065 2066 const bit_count = @as(usize, @bitSizeOf(T)); 2067 const bit_shift = @as(u3, @intCast(bit_offset % 8)); 2068 2069 const store_size = (@bitSizeOf(T) + 7) / 8; 2070 const store_tail_bits = @as(u3, @intCast((store_size * 8) - bit_count)); 2071 const StoreInt = std.meta.Int(.unsigned, store_size * 8); 2072 2073 if (bit_count == 0) 2074 return; 2075 2076 // Write by storing a StoreInt, and then follow it up with a 1-byte tail 2077 // if bit_offset pushed us over a byte boundary. 2078 const write_bytes = bytes[bit_offset / 8 ..]; 2079 const head = write_bytes[0] & ((@as(u8, 1) << bit_shift) - 1); 2080 2081 var write_value = (@as(StoreInt, @as(uN, @bitCast(value))) << bit_shift) | @as(StoreInt, @intCast(head)); 2082 if (bit_shift > store_tail_bits) { 2083 const tail_len = @as(Log2N, @intCast(bit_shift - store_tail_bits)); 2084 write_bytes[store_size] &= ~((@as(u8, 1) << @as(u3, @intCast(tail_len))) - 1); 2085 write_bytes[store_size] |= @as(u8, @intCast((@as(uN, @bitCast(value)) >> (@as(Log2N, @truncate(bit_count)) -% tail_len)))); 2086 } else if (bit_shift < store_tail_bits) { 2087 const tail_len = store_tail_bits - bit_shift; 2088 const tail = write_bytes[store_size - 1] & (@as(u8, 0xfe) << (7 - tail_len)); 2089 write_value |= @as(StoreInt, tail) << (8 * (store_size - 1)); 2090 } 2091 2092 writeInt(StoreInt, write_bytes[0..store_size], write_value, .little); 2093 } 2094 2095 fn writePackedIntBig(comptime T: type, bytes: []u8, bit_offset: usize, value: T) void { 2096 const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); 2097 const Log2N = std.math.Log2Int(T); 2098 2099 const bit_count = @as(usize, @bitSizeOf(T)); 2100 const bit_shift = @as(u3, @intCast(bit_offset % 8)); 2101 const byte_count = (bit_shift + bit_count + 7) / 8; 2102 2103 const store_size = (@bitSizeOf(T) + 7) / 8; 2104 const store_tail_bits = @as(u3, @intCast((store_size * 8) - bit_count)); 2105 const StoreInt = std.meta.Int(.unsigned, store_size * 8); 2106 2107 if (bit_count == 0) 2108 return; 2109 2110 // Write by storing a StoreInt, and then follow it up with a 1-byte tail 2111 // if bit_offset pushed us over a byte boundary. 2112 const end = bytes.len - (bit_offset / 8); 2113 const write_bytes = bytes[(end - byte_count)..end]; 2114 const head = write_bytes[byte_count - 1] & ((@as(u8, 1) << bit_shift) - 1); 2115 2116 var write_value = (@as(StoreInt, @as(uN, @bitCast(value))) << bit_shift) | @as(StoreInt, @intCast(head)); 2117 if (bit_shift > store_tail_bits) { 2118 const tail_len = @as(Log2N, @intCast(bit_shift - store_tail_bits)); 2119 write_bytes[0] &= ~((@as(u8, 1) << @as(u3, @intCast(tail_len))) - 1); 2120 write_bytes[0] |= @as(u8, @intCast((@as(uN, @bitCast(value)) >> (@as(Log2N, @truncate(bit_count)) -% tail_len)))); 2121 } else if (bit_shift < store_tail_bits) { 2122 const tail_len = store_tail_bits - bit_shift; 2123 const tail = write_bytes[0] & (@as(u8, 0xfe) << (7 - tail_len)); 2124 write_value |= @as(StoreInt, tail) << (8 * (store_size - 1)); 2125 } 2126 2127 writeInt(StoreInt, write_bytes[(byte_count - store_size)..][0..store_size], write_value, .big); 2128 } 2129 2130 /// Deprecated: use writePackedInt(T, bytes, bit_offset, value, .native) 2131 pub const writePackedIntNative = switch (native_endian) { 2132 .little => writePackedIntLittle, 2133 .big => writePackedIntBig, 2134 }; 2135 2136 /// Deprecated: use writePackedInt(T, bytes, bit_offset, value, .foreign) 2137 pub const writePackedIntForeign = switch (native_endian) { 2138 .little => writePackedIntBig, 2139 .big => writePackedIntLittle, 2140 }; 2141 2142 /// Stores an integer to packed memory. 2143 /// Asserts that buffer contains at least bit_offset + @bitSizeOf(T) bits. 2144 pub fn writePackedInt(comptime T: type, bytes: []u8, bit_offset: usize, value: T, endian: Endian) void { 2145 switch (endian) { 2146 .little => writePackedIntLittle(T, bytes, bit_offset, value), 2147 .big => writePackedIntBig(T, bytes, bit_offset, value), 2148 } 2149 } 2150 2151 test writePackedInt { 2152 const T = packed struct(u16) { a: u3, b: u7, c: u6 }; 2153 var st = T{ .a = 1, .b = 2, .c = 4 }; 2154 writePackedInt(u7, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 0x7f, builtin.cpu.arch.endian()); 2155 try std.testing.expectEqual(T{ .a = 1, .b = 0x7f, .c = 4 }, st); 2156 } 2157 2158 /// Stores an integer to packed memory with provided bit_offset, bit_count, and signedness. 2159 /// If negative, the written value is sign-extended. 2160 pub fn writeVarPackedInt(bytes: []u8, bit_offset: usize, bit_count: usize, value: anytype, endian: std.builtin.Endian) void { 2161 const T = @TypeOf(value); 2162 const uN = std.meta.Int(.unsigned, @bitSizeOf(T)); 2163 2164 const bit_shift = @as(u3, @intCast(bit_offset % 8)); 2165 const write_size = (bit_count + bit_shift + 7) / 8; 2166 const lowest_byte = switch (endian) { 2167 .big => bytes.len - (bit_offset / 8) - write_size, 2168 .little => bit_offset / 8, 2169 }; 2170 const write_bytes = bytes[lowest_byte..][0..write_size]; 2171 2172 if (write_size == 0) { 2173 return; 2174 } else if (write_size == 1) { 2175 // Single byte writes are handled specially, since we need to mask bits 2176 // on both ends of the byte. 2177 const mask = (@as(u8, 0xff) >> @as(u3, @intCast(8 - bit_count))); 2178 const new_bits = @as(u8, @intCast(@as(uN, @bitCast(value)) & mask)) << bit_shift; 2179 write_bytes[0] = (write_bytes[0] & ~(mask << bit_shift)) | new_bits; 2180 return; 2181 } 2182 2183 var remaining: T = value; 2184 2185 // Iterate bytes forward for Little-endian, backward for Big-endian 2186 const delta: i2 = if (endian == .big) -1 else 1; 2187 const start = if (endian == .big) @as(isize, @intCast(write_bytes.len - 1)) else 0; 2188 2189 var i: isize = start; // isize for signed index arithmetic 2190 2191 // Write first byte, using a mask to protects bits preceding bit_offset 2192 const head_mask = @as(u8, 0xff) >> bit_shift; 2193 write_bytes[@intCast(i)] &= ~(head_mask << bit_shift); 2194 write_bytes[@intCast(i)] |= @as(u8, @intCast(@as(uN, @bitCast(remaining)) & head_mask)) << bit_shift; 2195 remaining = math.shr(T, remaining, @as(u4, 8) - bit_shift); 2196 i += delta; 2197 2198 // Write bytes[1..bytes.len - 1] 2199 if (@bitSizeOf(T) > 8) { 2200 const loop_end = start + delta * (@as(isize, @intCast(write_size)) - 1); 2201 while (i != loop_end) : (i += delta) { 2202 write_bytes[@as(usize, @intCast(i))] = @as(u8, @truncate(@as(uN, @bitCast(remaining)))); 2203 remaining >>= 8; 2204 } 2205 } 2206 2207 // Write last byte, using a mask to protect bits following bit_offset + bit_count 2208 const following_bits = -%@as(u3, @truncate(bit_shift + bit_count)); 2209 const tail_mask = (@as(u8, 0xff) << following_bits) >> following_bits; 2210 write_bytes[@as(usize, @intCast(i))] &= ~tail_mask; 2211 write_bytes[@as(usize, @intCast(i))] |= @as(u8, @intCast(@as(uN, @bitCast(remaining)) & tail_mask)); 2212 } 2213 2214 test writeVarPackedInt { 2215 const T = packed struct(u16) { a: u3, b: u7, c: u6 }; 2216 var st = T{ .a = 1, .b = 2, .c = 4 }; 2217 const value: u64 = 0x7f; 2218 writeVarPackedInt(std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 7, value, builtin.cpu.arch.endian()); 2219 try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st); 2220 } 2221 2222 /// Swap the byte order of all the members of the fields of a struct 2223 /// (Changing their endianness) 2224 pub fn byteSwapAllFields(comptime S: type, ptr: *S) void { 2225 byteSwapAllFieldsAligned(S, .of(S), ptr); 2226 } 2227 2228 /// Swap the byte order of all the members of the fields of a struct 2229 /// (Changing their endianness) 2230 pub fn byteSwapAllFieldsAligned(comptime S: type, comptime a: Alignment, ptr: *align(a.toByteUnits()) S) void { 2231 switch (@typeInfo(S)) { 2232 .@"struct" => |struct_info| { 2233 if (struct_info.backing_integer) |Int| { 2234 ptr.* = @bitCast(@byteSwap(@as(Int, @bitCast(ptr.*)))); 2235 } else inline for (std.meta.fields(S)) |f| { 2236 switch (@typeInfo(f.type)) { 2237 .@"struct" => byteSwapAllFieldsAligned(f.type, .fromByteUnits(f.alignment orelse @alignOf(f.type)), &@field(ptr, f.name)), 2238 .@"union", .array => byteSwapAllFieldsAligned(f.type, .fromByteUnits(f.alignment orelse @alignOf(f.type)), &@field(ptr, f.name)), 2239 .@"enum" => { 2240 @field(ptr, f.name) = @enumFromInt(@byteSwap(@intFromEnum(@field(ptr, f.name)))); 2241 }, 2242 .bool => {}, 2243 .float => |float_info| { 2244 @field(ptr, f.name) = @bitCast(@byteSwap(@as(std.meta.Int(.unsigned, float_info.bits), @bitCast(@field(ptr, f.name))))); 2245 }, 2246 else => { 2247 @field(ptr, f.name) = @byteSwap(@field(ptr, f.name)); 2248 }, 2249 } 2250 } 2251 }, 2252 .@"union" => |union_info| { 2253 if (union_info.tag_type != null) { 2254 @compileError("byteSwapAllFields expects an untagged union"); 2255 } 2256 2257 const first_size = @bitSizeOf(union_info.fields[0].type); 2258 inline for (union_info.fields) |field| { 2259 if (@bitSizeOf(field.type) != first_size) { 2260 @compileError("Unable to byte-swap unions with varying field sizes"); 2261 } 2262 } 2263 2264 const BackingInt = std.meta.Int(.unsigned, @bitSizeOf(S)); 2265 ptr.* = @bitCast(@byteSwap(@as(BackingInt, @bitCast(ptr.*)))); 2266 }, 2267 .array => |info| { 2268 byteSwapAllElements(info.child, ptr); 2269 }, 2270 else => { 2271 ptr.* = @byteSwap(ptr.*); 2272 }, 2273 } 2274 } 2275 2276 test byteSwapAllFields { 2277 const T = extern struct { 2278 f0: u8, 2279 f1: u16, 2280 f2: u32, 2281 f3: [1]u8, 2282 f4: bool, 2283 f5: f32, 2284 f6: extern union { f0: u16, f1: u16 }, 2285 }; 2286 const K = extern struct { 2287 f0: u8, 2288 f1: T, 2289 f2: u16, 2290 f3: [1]u8, 2291 f4: bool, 2292 f5: f32, 2293 }; 2294 const P = packed struct(u32) { 2295 f0: u1, 2296 f1: u7, 2297 f2: u4, 2298 f3: u4, 2299 f4: u16, 2300 }; 2301 const A = extern struct { 2302 f0: u32, 2303 f1: extern struct { 2304 f0: u64, 2305 } align(4), 2306 f2: u32, 2307 }; 2308 var s = T{ 2309 .f0 = 0x12, 2310 .f1 = 0x1234, 2311 .f2 = 0x12345678, 2312 .f3 = .{0x12}, 2313 .f4 = true, 2314 .f5 = @as(f32, @bitCast(@as(u32, 0x4640e400))), 2315 .f6 = .{ .f0 = 0x1234 }, 2316 }; 2317 var k = K{ 2318 .f0 = 0x12, 2319 .f1 = s, 2320 .f2 = 0x1234, 2321 .f3 = .{0x12}, 2322 .f4 = false, 2323 .f5 = @as(f32, @bitCast(@as(u32, 0x45d42800))), 2324 }; 2325 var p: P = @bitCast(@as(u32, 0x01234567)); 2326 var a: A = A{ 2327 .f0 = 0x12345678, 2328 .f1 = .{ .f0 = 0x123456789ABCDEF0 }, 2329 .f2 = 0x87654321, 2330 }; 2331 byteSwapAllFields(T, &s); 2332 byteSwapAllFields(K, &k); 2333 byteSwapAllFields(P, &p); 2334 byteSwapAllFields(A, &a); 2335 try std.testing.expectEqual(T{ 2336 .f0 = 0x12, 2337 .f1 = 0x3412, 2338 .f2 = 0x78563412, 2339 .f3 = .{0x12}, 2340 .f4 = true, 2341 .f5 = @as(f32, @bitCast(@as(u32, 0x00e44046))), 2342 .f6 = .{ .f0 = 0x3412 }, 2343 }, s); 2344 try std.testing.expectEqual(K{ 2345 .f0 = 0x12, 2346 .f1 = s, 2347 .f2 = 0x3412, 2348 .f3 = .{0x12}, 2349 .f4 = false, 2350 .f5 = @as(f32, @bitCast(@as(u32, 0x0028d445))), 2351 }, k); 2352 try std.testing.expectEqual(@as(P, @bitCast(@as(u32, 0x67452301))), p); 2353 try std.testing.expectEqual(A{ 2354 .f0 = 0x78563412, 2355 .f1 = .{ .f0 = 0xF0DEBC9A78563412 }, 2356 .f2 = 0x21436587, 2357 }, a); 2358 } 2359 2360 /// Reverses the byte order of all elements in a slice. 2361 /// Handles structs, unions, arrays, enums, floats, and integers recursively. 2362 /// Useful for converting between little-endian and big-endian representations. 2363 pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { 2364 for (slice) |*elem| { 2365 switch (@typeInfo(@TypeOf(elem.*))) { 2366 .@"struct", .@"union", .array => byteSwapAllFields(@TypeOf(elem.*), elem), 2367 .@"enum" => { 2368 elem.* = @enumFromInt(@byteSwap(@intFromEnum(elem.*))); 2369 }, 2370 .bool => {}, 2371 .float => |float_info| { 2372 elem.* = @bitCast(@byteSwap(@as(std.meta.Int(.unsigned, float_info.bits), @bitCast(elem.*)))); 2373 }, 2374 else => { 2375 elem.* = @byteSwap(elem.*); 2376 }, 2377 } 2378 } 2379 } 2380 2381 /// Returns an iterator that iterates over the slices of `buffer` that are not 2382 /// any of the items in `delimiters`. 2383 /// 2384 /// `tokenizeAny(u8, " abc|def || ghi ", " |")` will return slices 2385 /// for "abc", "def", "ghi", null, in that order. 2386 /// 2387 /// If `buffer` is empty, the iterator will return null. 2388 /// If none of `delimiters` exist in buffer, 2389 /// the iterator will return `buffer`, null, in that order. 2390 /// 2391 /// See also: `tokenizeSequence`, `tokenizeScalar`, 2392 /// `splitSequence`,`splitAny`, `splitScalar`, 2393 /// `splitBackwardsSequence`, `splitBackwardsAny`, and `splitBackwardsScalar` 2394 pub fn tokenizeAny(comptime T: type, buffer: []const T, delimiters: []const T) TokenIterator(T, .any) { 2395 return .{ 2396 .index = 0, 2397 .buffer = buffer, 2398 .delimiter = delimiters, 2399 }; 2400 } 2401 2402 /// Returns an iterator that iterates over the slices of `buffer` that are not 2403 /// the sequence in `delimiter`. 2404 /// 2405 /// `tokenizeSequence(u8, "<>abc><def<><>ghi", "<>")` will return slices 2406 /// for "abc><def", "ghi", null, in that order. 2407 /// 2408 /// If `buffer` is empty, the iterator will return null. 2409 /// If `delimiter` does not exist in buffer, 2410 /// the iterator will return `buffer`, null, in that order. 2411 /// The delimiter length must not be zero. 2412 /// 2413 /// See also: `tokenizeAny`, `tokenizeScalar`, 2414 /// `splitSequence`,`splitAny`, and `splitScalar` 2415 /// `splitBackwardsSequence`, `splitBackwardsAny`, and `splitBackwardsScalar` 2416 pub fn tokenizeSequence(comptime T: type, buffer: []const T, delimiter: []const T) TokenIterator(T, .sequence) { 2417 assert(delimiter.len != 0); 2418 return .{ 2419 .index = 0, 2420 .buffer = buffer, 2421 .delimiter = delimiter, 2422 }; 2423 } 2424 2425 /// Returns an iterator that iterates over the slices of `buffer` that are not 2426 /// `delimiter`. 2427 /// 2428 /// `tokenizeScalar(u8, " abc def ghi ", ' ')` will return slices 2429 /// for "abc", "def", "ghi", null, in that order. 2430 /// 2431 /// If `buffer` is empty, the iterator will return null. 2432 /// If `delimiter` does not exist in buffer, 2433 /// the iterator will return `buffer`, null, in that order. 2434 /// 2435 /// See also: `tokenizeAny`, `tokenizeSequence`, 2436 /// `splitSequence`,`splitAny`, and `splitScalar` 2437 /// `splitBackwardsSequence`, `splitBackwardsAny`, and `splitBackwardsScalar` 2438 pub fn tokenizeScalar(comptime T: type, buffer: []const T, delimiter: T) TokenIterator(T, .scalar) { 2439 return .{ 2440 .index = 0, 2441 .buffer = buffer, 2442 .delimiter = delimiter, 2443 }; 2444 } 2445 2446 test tokenizeScalar { 2447 var it = tokenizeScalar(u8, " abc def ghi ", ' '); 2448 try testing.expect(eql(u8, it.next().?, "abc")); 2449 try testing.expect(eql(u8, it.peek().?, "def")); 2450 try testing.expect(eql(u8, it.next().?, "def")); 2451 try testing.expect(eql(u8, it.next().?, "ghi")); 2452 try testing.expect(it.next() == null); 2453 2454 it = tokenizeScalar(u8, "..\\bob", '\\'); 2455 try testing.expect(eql(u8, it.next().?, "..")); 2456 try testing.expect(eql(u8, "..", "..\\bob"[0..it.index])); 2457 try testing.expect(eql(u8, it.next().?, "bob")); 2458 try testing.expect(it.next() == null); 2459 2460 it = tokenizeScalar(u8, "//a/b", '/'); 2461 try testing.expect(eql(u8, it.next().?, "a")); 2462 try testing.expect(eql(u8, it.next().?, "b")); 2463 try testing.expect(eql(u8, "//a/b", "//a/b"[0..it.index])); 2464 try testing.expect(it.next() == null); 2465 2466 it = tokenizeScalar(u8, "|", '|'); 2467 try testing.expect(it.next() == null); 2468 try testing.expect(it.peek() == null); 2469 2470 it = tokenizeScalar(u8, "", '|'); 2471 try testing.expect(it.next() == null); 2472 try testing.expect(it.peek() == null); 2473 2474 it = tokenizeScalar(u8, "hello", ' '); 2475 try testing.expect(eql(u8, it.next().?, "hello")); 2476 try testing.expect(it.next() == null); 2477 2478 var it16 = tokenizeScalar( 2479 u16, 2480 std.unicode.utf8ToUtf16LeStringLiteral("hello"), 2481 ' ', 2482 ); 2483 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello"))); 2484 try testing.expect(it16.next() == null); 2485 } 2486 2487 test tokenizeAny { 2488 var it = tokenizeAny(u8, "a|b,c/d e", " /,|"); 2489 try testing.expect(eql(u8, it.next().?, "a")); 2490 try testing.expect(eql(u8, it.peek().?, "b")); 2491 try testing.expect(eql(u8, it.next().?, "b")); 2492 try testing.expect(eql(u8, it.next().?, "c")); 2493 try testing.expect(eql(u8, it.next().?, "d")); 2494 try testing.expect(eql(u8, it.next().?, "e")); 2495 try testing.expect(it.next() == null); 2496 try testing.expect(it.peek() == null); 2497 2498 it = tokenizeAny(u8, "hello", ""); 2499 try testing.expect(eql(u8, it.next().?, "hello")); 2500 try testing.expect(it.next() == null); 2501 2502 var it16 = tokenizeAny( 2503 u16, 2504 std.unicode.utf8ToUtf16LeStringLiteral("a|b,c/d e"), 2505 std.unicode.utf8ToUtf16LeStringLiteral(" /,|"), 2506 ); 2507 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a"))); 2508 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b"))); 2509 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c"))); 2510 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d"))); 2511 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e"))); 2512 try testing.expect(it16.next() == null); 2513 } 2514 2515 test tokenizeSequence { 2516 var it = tokenizeSequence(u8, "a<>b<><>c><>d><", "<>"); 2517 try testing.expectEqualStrings("a", it.next().?); 2518 try testing.expectEqualStrings("b", it.peek().?); 2519 try testing.expectEqualStrings("b", it.next().?); 2520 try testing.expectEqualStrings("c>", it.next().?); 2521 try testing.expectEqualStrings("d><", it.next().?); 2522 try testing.expect(it.next() == null); 2523 try testing.expect(it.peek() == null); 2524 2525 var it16 = tokenizeSequence( 2526 u16, 2527 std.unicode.utf8ToUtf16LeStringLiteral("a<>b<><>c><>d><"), 2528 std.unicode.utf8ToUtf16LeStringLiteral("<>"), 2529 ); 2530 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a"))); 2531 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b"))); 2532 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c>"))); 2533 try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d><"))); 2534 try testing.expect(it16.next() == null); 2535 } 2536 2537 test "tokenize (reset)" { 2538 { 2539 var it = tokenizeAny(u8, " abc def ghi ", " "); 2540 try testing.expect(eql(u8, it.next().?, "abc")); 2541 try testing.expect(eql(u8, it.next().?, "def")); 2542 try testing.expect(eql(u8, it.next().?, "ghi")); 2543 2544 it.reset(); 2545 2546 try testing.expect(eql(u8, it.next().?, "abc")); 2547 try testing.expect(eql(u8, it.next().?, "def")); 2548 try testing.expect(eql(u8, it.next().?, "ghi")); 2549 try testing.expect(it.next() == null); 2550 } 2551 { 2552 var it = tokenizeSequence(u8, "<><>abc<>def<><>ghi<>", "<>"); 2553 try testing.expect(eql(u8, it.next().?, "abc")); 2554 try testing.expect(eql(u8, it.next().?, "def")); 2555 try testing.expect(eql(u8, it.next().?, "ghi")); 2556 2557 it.reset(); 2558 2559 try testing.expect(eql(u8, it.next().?, "abc")); 2560 try testing.expect(eql(u8, it.next().?, "def")); 2561 try testing.expect(eql(u8, it.next().?, "ghi")); 2562 try testing.expect(it.next() == null); 2563 } 2564 { 2565 var it = tokenizeScalar(u8, " abc def ghi ", ' '); 2566 try testing.expect(eql(u8, it.next().?, "abc")); 2567 try testing.expect(eql(u8, it.next().?, "def")); 2568 try testing.expect(eql(u8, it.next().?, "ghi")); 2569 2570 it.reset(); 2571 2572 try testing.expect(eql(u8, it.next().?, "abc")); 2573 try testing.expect(eql(u8, it.next().?, "def")); 2574 try testing.expect(eql(u8, it.next().?, "ghi")); 2575 try testing.expect(it.next() == null); 2576 } 2577 } 2578 2579 /// Returns an iterator that iterates over the slices of `buffer` that 2580 /// are separated by the byte sequence in `delimiter`. 2581 /// 2582 /// `splitSequence(u8, "abc||def||||ghi", "||")` will return slices 2583 /// for "abc", "def", "", "ghi", null, in that order. 2584 /// 2585 /// If `delimiter` does not exist in buffer, 2586 /// the iterator will return `buffer`, null, in that order. 2587 /// The delimiter length must not be zero. 2588 /// 2589 /// See also: `splitAny`, `splitScalar`, `splitBackwardsSequence`, 2590 /// `splitBackwardsAny`,`splitBackwardsScalar`, 2591 /// `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`. 2592 pub fn splitSequence(comptime T: type, buffer: []const T, delimiter: []const T) SplitIterator(T, .sequence) { 2593 assert(delimiter.len != 0); 2594 return .{ 2595 .index = 0, 2596 .buffer = buffer, 2597 .delimiter = delimiter, 2598 }; 2599 } 2600 2601 /// Returns an iterator that iterates over the slices of `buffer` that 2602 /// are separated by any item in `delimiters`. 2603 /// 2604 /// `splitAny(u8, "abc,def||ghi", "|,")` will return slices 2605 /// for "abc", "def", "", "ghi", null, in that order. 2606 /// 2607 /// If none of `delimiters` exist in buffer, 2608 /// the iterator will return `buffer`, null, in that order. 2609 /// 2610 /// See also: `splitSequence`, `splitScalar`, `splitBackwardsSequence`, 2611 /// `splitBackwardsAny`,`splitBackwardsScalar`, 2612 /// `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`. 2613 pub fn splitAny(comptime T: type, buffer: []const T, delimiters: []const T) SplitIterator(T, .any) { 2614 return .{ 2615 .index = 0, 2616 .buffer = buffer, 2617 .delimiter = delimiters, 2618 }; 2619 } 2620 2621 /// Returns an iterator that iterates over the slices of `buffer` that 2622 /// are separated by `delimiter`. 2623 /// 2624 /// `splitScalar(u8, "abc|def||ghi", '|')` will return slices 2625 /// for "abc", "def", "", "ghi", null, in that order. 2626 /// 2627 /// If `delimiter` does not exist in buffer, 2628 /// the iterator will return `buffer`, null, in that order. 2629 /// 2630 /// See also: `splitSequence`, `splitAny`, `splitBackwardsSequence`, 2631 /// `splitBackwardsAny`,`splitBackwardsScalar`, 2632 /// `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`. 2633 pub fn splitScalar(comptime T: type, buffer: []const T, delimiter: T) SplitIterator(T, .scalar) { 2634 return .{ 2635 .index = 0, 2636 .buffer = buffer, 2637 .delimiter = delimiter, 2638 }; 2639 } 2640 2641 test splitScalar { 2642 var it = splitScalar(u8, "abc|def||ghi", '|'); 2643 try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); 2644 try testing.expectEqualSlices(u8, it.first(), "abc"); 2645 2646 try testing.expectEqualSlices(u8, it.rest(), "def||ghi"); 2647 try testing.expectEqualSlices(u8, it.peek().?, "def"); 2648 try testing.expectEqualSlices(u8, it.next().?, "def"); 2649 2650 try testing.expectEqualSlices(u8, it.rest(), "|ghi"); 2651 try testing.expectEqualSlices(u8, it.next().?, ""); 2652 2653 try testing.expectEqualSlices(u8, it.rest(), "ghi"); 2654 try testing.expectEqualSlices(u8, it.peek().?, "ghi"); 2655 try testing.expectEqualSlices(u8, it.next().?, "ghi"); 2656 2657 try testing.expectEqualSlices(u8, it.rest(), ""); 2658 try testing.expect(it.peek() == null); 2659 try testing.expect(it.next() == null); 2660 2661 it = splitScalar(u8, "", '|'); 2662 try testing.expectEqualSlices(u8, it.first(), ""); 2663 try testing.expect(it.next() == null); 2664 2665 it = splitScalar(u8, "|", '|'); 2666 try testing.expectEqualSlices(u8, it.first(), ""); 2667 try testing.expectEqualSlices(u8, it.next().?, ""); 2668 try testing.expect(it.peek() == null); 2669 try testing.expect(it.next() == null); 2670 2671 it = splitScalar(u8, "hello", ' '); 2672 try testing.expectEqualSlices(u8, it.first(), "hello"); 2673 try testing.expect(it.next() == null); 2674 2675 var it16 = splitScalar( 2676 u16, 2677 std.unicode.utf8ToUtf16LeStringLiteral("hello"), 2678 ' ', 2679 ); 2680 try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("hello")); 2681 try testing.expect(it16.next() == null); 2682 } 2683 2684 test splitSequence { 2685 var it = splitSequence(u8, "a, b ,, c, d, e", ", "); 2686 try testing.expectEqualSlices(u8, it.first(), "a"); 2687 try testing.expectEqualSlices(u8, it.rest(), "b ,, c, d, e"); 2688 try testing.expectEqualSlices(u8, it.next().?, "b ,"); 2689 try testing.expectEqualSlices(u8, it.next().?, "c"); 2690 try testing.expectEqualSlices(u8, it.next().?, "d"); 2691 try testing.expectEqualSlices(u8, it.next().?, "e"); 2692 try testing.expect(it.next() == null); 2693 2694 var it16 = splitSequence( 2695 u16, 2696 std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), 2697 std.unicode.utf8ToUtf16LeStringLiteral(", "), 2698 ); 2699 try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("a")); 2700 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); 2701 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); 2702 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); 2703 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")); 2704 try testing.expect(it16.next() == null); 2705 } 2706 2707 test splitAny { 2708 var it = splitAny(u8, "a,b, c d e", ", "); 2709 try testing.expectEqualSlices(u8, it.first(), "a"); 2710 try testing.expectEqualSlices(u8, it.rest(), "b, c d e"); 2711 try testing.expectEqualSlices(u8, it.next().?, "b"); 2712 try testing.expectEqualSlices(u8, it.next().?, ""); 2713 try testing.expectEqualSlices(u8, it.next().?, "c"); 2714 try testing.expectEqualSlices(u8, it.next().?, "d"); 2715 try testing.expectEqualSlices(u8, it.next().?, "e"); 2716 try testing.expect(it.next() == null); 2717 2718 it = splitAny(u8, "hello", ""); 2719 try testing.expect(eql(u8, it.next().?, "hello")); 2720 try testing.expect(it.next() == null); 2721 2722 var it16 = splitAny( 2723 u16, 2724 std.unicode.utf8ToUtf16LeStringLiteral("a,b, c d e"), 2725 std.unicode.utf8ToUtf16LeStringLiteral(", "), 2726 ); 2727 try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("a")); 2728 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b")); 2729 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("")); 2730 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); 2731 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); 2732 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")); 2733 try testing.expect(it16.next() == null); 2734 } 2735 2736 test "split (reset)" { 2737 { 2738 var it = splitSequence(u8, "abc def ghi", " "); 2739 try testing.expect(eql(u8, it.first(), "abc")); 2740 try testing.expect(eql(u8, it.next().?, "def")); 2741 try testing.expect(eql(u8, it.next().?, "ghi")); 2742 2743 it.reset(); 2744 2745 try testing.expect(eql(u8, it.first(), "abc")); 2746 try testing.expect(eql(u8, it.next().?, "def")); 2747 try testing.expect(eql(u8, it.next().?, "ghi")); 2748 try testing.expect(it.next() == null); 2749 } 2750 { 2751 var it = splitAny(u8, "abc def,ghi", " ,"); 2752 try testing.expect(eql(u8, it.first(), "abc")); 2753 try testing.expect(eql(u8, it.next().?, "def")); 2754 try testing.expect(eql(u8, it.next().?, "ghi")); 2755 2756 it.reset(); 2757 2758 try testing.expect(eql(u8, it.first(), "abc")); 2759 try testing.expect(eql(u8, it.next().?, "def")); 2760 try testing.expect(eql(u8, it.next().?, "ghi")); 2761 try testing.expect(it.next() == null); 2762 } 2763 { 2764 var it = splitScalar(u8, "abc def ghi", ' '); 2765 try testing.expect(eql(u8, it.first(), "abc")); 2766 try testing.expect(eql(u8, it.next().?, "def")); 2767 try testing.expect(eql(u8, it.next().?, "ghi")); 2768 2769 it.reset(); 2770 2771 try testing.expect(eql(u8, it.first(), "abc")); 2772 try testing.expect(eql(u8, it.next().?, "def")); 2773 try testing.expect(eql(u8, it.next().?, "ghi")); 2774 try testing.expect(it.next() == null); 2775 } 2776 } 2777 2778 /// Returns an iterator that iterates backwards over the slices of `buffer` that 2779 /// are separated by the sequence in `delimiter`. 2780 /// 2781 /// `splitBackwardsSequence(u8, "abc||def||||ghi", "||")` will return slices 2782 /// for "ghi", "", "def", "abc", null, in that order. 2783 /// 2784 /// If `delimiter` does not exist in buffer, 2785 /// the iterator will return `buffer`, null, in that order. 2786 /// The delimiter length must not be zero. 2787 /// 2788 /// See also: `splitBackwardsAny`, `splitBackwardsScalar`, 2789 /// `splitSequence`, `splitAny`,`splitScalar`, 2790 /// `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`. 2791 pub fn splitBackwardsSequence(comptime T: type, buffer: []const T, delimiter: []const T) SplitBackwardsIterator(T, .sequence) { 2792 assert(delimiter.len != 0); 2793 return .{ 2794 .index = buffer.len, 2795 .buffer = buffer, 2796 .delimiter = delimiter, 2797 }; 2798 } 2799 2800 /// Returns an iterator that iterates backwards over the slices of `buffer` that 2801 /// are separated by any item in `delimiters`. 2802 /// 2803 /// `splitBackwardsAny(u8, "abc,def||ghi", "|,")` will return slices 2804 /// for "ghi", "", "def", "abc", null, in that order. 2805 /// 2806 /// If none of `delimiters` exist in buffer, 2807 /// the iterator will return `buffer`, null, in that order. 2808 /// 2809 /// See also: `splitBackwardsSequence`, `splitBackwardsScalar`, 2810 /// `splitSequence`, `splitAny`,`splitScalar`, 2811 /// `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`. 2812 pub fn splitBackwardsAny(comptime T: type, buffer: []const T, delimiters: []const T) SplitBackwardsIterator(T, .any) { 2813 return .{ 2814 .index = buffer.len, 2815 .buffer = buffer, 2816 .delimiter = delimiters, 2817 }; 2818 } 2819 2820 /// Returns an iterator that iterates backwards over the slices of `buffer` that 2821 /// are separated by `delimiter`. 2822 /// 2823 /// `splitBackwardsScalar(u8, "abc|def||ghi", '|')` will return slices 2824 /// for "ghi", "", "def", "abc", null, in that order. 2825 /// 2826 /// If `delimiter` does not exist in buffer, 2827 /// the iterator will return `buffer`, null, in that order. 2828 /// 2829 /// See also: `splitBackwardsSequence`, `splitBackwardsAny`, 2830 /// `splitSequence`, `splitAny`,`splitScalar`, 2831 /// `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`. 2832 pub fn splitBackwardsScalar(comptime T: type, buffer: []const T, delimiter: T) SplitBackwardsIterator(T, .scalar) { 2833 return .{ 2834 .index = buffer.len, 2835 .buffer = buffer, 2836 .delimiter = delimiter, 2837 }; 2838 } 2839 2840 test splitBackwardsScalar { 2841 var it = splitBackwardsScalar(u8, "abc|def||ghi", '|'); 2842 try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); 2843 try testing.expectEqualSlices(u8, it.first(), "ghi"); 2844 2845 try testing.expectEqualSlices(u8, it.rest(), "abc|def|"); 2846 try testing.expectEqualSlices(u8, it.next().?, ""); 2847 2848 try testing.expectEqualSlices(u8, it.rest(), "abc|def"); 2849 try testing.expectEqualSlices(u8, it.next().?, "def"); 2850 2851 try testing.expectEqualSlices(u8, it.rest(), "abc"); 2852 try testing.expectEqualSlices(u8, it.next().?, "abc"); 2853 2854 try testing.expectEqualSlices(u8, it.rest(), ""); 2855 try testing.expect(it.next() == null); 2856 2857 it = splitBackwardsScalar(u8, "", '|'); 2858 try testing.expectEqualSlices(u8, it.first(), ""); 2859 try testing.expect(it.next() == null); 2860 2861 it = splitBackwardsScalar(u8, "|", '|'); 2862 try testing.expectEqualSlices(u8, it.first(), ""); 2863 try testing.expectEqualSlices(u8, it.next().?, ""); 2864 try testing.expect(it.next() == null); 2865 2866 it = splitBackwardsScalar(u8, "hello", ' '); 2867 try testing.expectEqualSlices(u8, it.first(), "hello"); 2868 try testing.expect(it.next() == null); 2869 2870 var it16 = splitBackwardsScalar( 2871 u16, 2872 std.unicode.utf8ToUtf16LeStringLiteral("hello"), 2873 ' ', 2874 ); 2875 try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("hello")); 2876 try testing.expect(it16.next() == null); 2877 } 2878 2879 test splitBackwardsSequence { 2880 var it = splitBackwardsSequence(u8, "a, b ,, c, d, e", ", "); 2881 try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d, e"); 2882 try testing.expectEqualSlices(u8, it.first(), "e"); 2883 2884 try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d"); 2885 try testing.expectEqualSlices(u8, it.next().?, "d"); 2886 2887 try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c"); 2888 try testing.expectEqualSlices(u8, it.next().?, "c"); 2889 2890 try testing.expectEqualSlices(u8, it.rest(), "a, b ,"); 2891 try testing.expectEqualSlices(u8, it.next().?, "b ,"); 2892 2893 try testing.expectEqualSlices(u8, it.rest(), "a"); 2894 try testing.expectEqualSlices(u8, it.next().?, "a"); 2895 2896 try testing.expectEqualSlices(u8, it.rest(), ""); 2897 try testing.expect(it.next() == null); 2898 2899 var it16 = splitBackwardsSequence( 2900 u16, 2901 std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), 2902 std.unicode.utf8ToUtf16LeStringLiteral(", "), 2903 ); 2904 try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("e")); 2905 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); 2906 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); 2907 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); 2908 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")); 2909 try testing.expect(it16.next() == null); 2910 } 2911 2912 test splitBackwardsAny { 2913 var it = splitBackwardsAny(u8, "a,b, c d e", ", "); 2914 try testing.expectEqualSlices(u8, it.rest(), "a,b, c d e"); 2915 try testing.expectEqualSlices(u8, it.first(), "e"); 2916 2917 try testing.expectEqualSlices(u8, it.rest(), "a,b, c d"); 2918 try testing.expectEqualSlices(u8, it.next().?, "d"); 2919 2920 try testing.expectEqualSlices(u8, it.rest(), "a,b, c"); 2921 try testing.expectEqualSlices(u8, it.next().?, "c"); 2922 2923 try testing.expectEqualSlices(u8, it.rest(), "a,b,"); 2924 try testing.expectEqualSlices(u8, it.next().?, ""); 2925 2926 try testing.expectEqualSlices(u8, it.rest(), "a,b"); 2927 try testing.expectEqualSlices(u8, it.next().?, "b"); 2928 2929 try testing.expectEqualSlices(u8, it.rest(), "a"); 2930 try testing.expectEqualSlices(u8, it.next().?, "a"); 2931 2932 try testing.expectEqualSlices(u8, it.rest(), ""); 2933 try testing.expect(it.next() == null); 2934 2935 var it16 = splitBackwardsAny( 2936 u16, 2937 std.unicode.utf8ToUtf16LeStringLiteral("a,b, c d e"), 2938 std.unicode.utf8ToUtf16LeStringLiteral(", "), 2939 ); 2940 try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("e")); 2941 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); 2942 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); 2943 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("")); 2944 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b")); 2945 try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")); 2946 try testing.expect(it16.next() == null); 2947 } 2948 2949 test "splitBackwards (reset)" { 2950 { 2951 var it = splitBackwardsSequence(u8, "abc def ghi", " "); 2952 try testing.expect(eql(u8, it.first(), "ghi")); 2953 try testing.expect(eql(u8, it.next().?, "def")); 2954 try testing.expect(eql(u8, it.next().?, "abc")); 2955 2956 it.reset(); 2957 2958 try testing.expect(eql(u8, it.first(), "ghi")); 2959 try testing.expect(eql(u8, it.next().?, "def")); 2960 try testing.expect(eql(u8, it.next().?, "abc")); 2961 try testing.expect(it.next() == null); 2962 } 2963 { 2964 var it = splitBackwardsAny(u8, "abc def,ghi", " ,"); 2965 try testing.expect(eql(u8, it.first(), "ghi")); 2966 try testing.expect(eql(u8, it.next().?, "def")); 2967 try testing.expect(eql(u8, it.next().?, "abc")); 2968 2969 it.reset(); 2970 2971 try testing.expect(eql(u8, it.first(), "ghi")); 2972 try testing.expect(eql(u8, it.next().?, "def")); 2973 try testing.expect(eql(u8, it.next().?, "abc")); 2974 try testing.expect(it.next() == null); 2975 } 2976 { 2977 var it = splitBackwardsScalar(u8, "abc def ghi", ' '); 2978 try testing.expect(eql(u8, it.first(), "ghi")); 2979 try testing.expect(eql(u8, it.next().?, "def")); 2980 try testing.expect(eql(u8, it.next().?, "abc")); 2981 2982 it.reset(); 2983 2984 try testing.expect(eql(u8, it.first(), "ghi")); 2985 try testing.expect(eql(u8, it.next().?, "def")); 2986 try testing.expect(eql(u8, it.next().?, "abc")); 2987 try testing.expect(it.next() == null); 2988 } 2989 } 2990 2991 /// Returns an iterator with a sliding window of slices for `buffer`. 2992 /// The sliding window has length `size` and on every iteration moves 2993 /// forward by `advance`. 2994 /// 2995 /// Extract data for moving average with: 2996 /// `window(u8, "abcdefg", 3, 1)` will return slices 2997 /// "abc", "bcd", "cde", "def", "efg", null, in that order. 2998 /// 2999 /// Chunk or split every N items with: 3000 /// `window(u8, "abcdefg", 3, 3)` will return slices 3001 /// "abc", "def", "g", null, in that order. 3002 /// 3003 /// Pick every even index with: 3004 /// `window(u8, "abcdefg", 1, 2)` will return slices 3005 /// "a", "c", "e", "g" null, in that order. 3006 /// 3007 /// The `size` and `advance` must be not be zero. 3008 pub fn window(comptime T: type, buffer: []const T, size: usize, advance: usize) WindowIterator(T) { 3009 assert(size != 0); 3010 assert(advance != 0); 3011 return .{ 3012 .index = if (buffer.len > 0) 0 else null, 3013 .buffer = buffer, 3014 .size = size, 3015 .advance = advance, 3016 }; 3017 } 3018 3019 test window { 3020 { 3021 // moving average size 3 3022 var it = window(u8, "abcdefg", 3, 1); 3023 try testing.expectEqualSlices(u8, "abc", it.next().?); 3024 try testing.expectEqualSlices(u8, "bcd", it.next().?); 3025 try testing.expectEqualSlices(u8, "cde", it.next().?); 3026 try testing.expectEqualSlices(u8, "def", it.next().?); 3027 try testing.expectEqualSlices(u8, "efg", it.next().?); 3028 try testing.expectEqual(null, it.next()); 3029 3030 // multibyte 3031 var it16 = window(u16, std.unicode.utf8ToUtf16LeStringLiteral("abcdefg"), 3, 1); 3032 try testing.expectEqualSlices(u16, std.unicode.utf8ToUtf16LeStringLiteral("abc"), it16.next().?); 3033 try testing.expectEqualSlices(u16, std.unicode.utf8ToUtf16LeStringLiteral("bcd"), it16.next().?); 3034 try testing.expectEqualSlices(u16, std.unicode.utf8ToUtf16LeStringLiteral("cde"), it16.next().?); 3035 try testing.expectEqualSlices(u16, std.unicode.utf8ToUtf16LeStringLiteral("def"), it16.next().?); 3036 try testing.expectEqualSlices(u16, std.unicode.utf8ToUtf16LeStringLiteral("efg"), it16.next().?); 3037 try testing.expectEqual(it16.next(), null); 3038 } 3039 3040 { 3041 // chunk/split every 3 3042 var it = window(u8, "abcdefg", 3, 3); 3043 try testing.expectEqualSlices(u8, "abc", it.next().?); 3044 try testing.expectEqualSlices(u8, "def", it.next().?); 3045 try testing.expectEqualSlices(u8, "g", it.next().?); 3046 try testing.expectEqual(null, it.next()); 3047 } 3048 3049 { 3050 // pick even 3051 var it = window(u8, "abcdefg", 1, 2); 3052 try testing.expectEqualSlices(u8, "a", it.next().?); 3053 try testing.expectEqualSlices(u8, "c", it.next().?); 3054 try testing.expectEqualSlices(u8, "e", it.next().?); 3055 try testing.expectEqualSlices(u8, "g", it.next().?); 3056 try testing.expectEqual(null, it.next()); 3057 3058 it = window(u8, "abcdefgh", 1, 2); 3059 try testing.expectEqualSlices(u8, "a", it.next().?); 3060 try testing.expectEqualSlices(u8, "c", it.next().?); 3061 try testing.expectEqualSlices(u8, "e", it.next().?); 3062 try testing.expectEqualSlices(u8, "g", it.next().?); 3063 try testing.expectEqual(null, it.next()); 3064 } 3065 3066 { 3067 // empty 3068 var it = window(u8, "", 1, 1); 3069 try testing.expectEqual(null, it.next()); 3070 3071 it = window(u8, "", 10, 1); 3072 try testing.expectEqual(null, it.next()); 3073 3074 it = window(u8, "", 1, 10); 3075 try testing.expectEqual(null, it.next()); 3076 3077 it = window(u8, "", 10, 10); 3078 try testing.expectEqual(null, it.next()); 3079 } 3080 3081 { 3082 // first 3083 var it = window(u8, "abcdefg", 3, 3); 3084 try testing.expectEqualSlices(u8, "abc", it.next().?); 3085 it.reset(); 3086 try testing.expectEqualSlices(u8, "abc", it.next().?); 3087 } 3088 3089 { 3090 // reset 3091 var it = window(u8, "abcdefg", 3, 3); 3092 try testing.expectEqualSlices(u8, "abc", it.next().?); 3093 try testing.expectEqualSlices(u8, "def", it.next().?); 3094 try testing.expectEqualSlices(u8, "g", it.next().?); 3095 try testing.expectEqual(null, it.next()); 3096 3097 it.reset(); 3098 try testing.expectEqualSlices(u8, "abc", it.next().?); 3099 try testing.expectEqualSlices(u8, "def", it.next().?); 3100 try testing.expectEqualSlices(u8, "g", it.next().?); 3101 try testing.expectEqual(null, it.next()); 3102 } 3103 3104 { 3105 // size > buffer.len 3106 var it = window(u8, "abcdefg", 100, 1); 3107 try testing.expectEqualSlices(u8, "abcdefg", it.next().?); 3108 try testing.expectEqual(null, it.next()); 3109 } 3110 3111 { 3112 // advance >= buffer.len 3113 var it = window(u8, "abcdefg", 1, 7); 3114 try testing.expectEqualSlices(u8, "a", it.next().?); 3115 try testing.expectEqual(null, it.next()); 3116 } 3117 3118 { 3119 // advance == 1 and size == 1 3120 var it = window(u8, "abcdefg", 1, 1); 3121 try testing.expectEqualSlices(u8, "a", it.next().?); 3122 try testing.expectEqualSlices(u8, "b", it.next().?); 3123 try testing.expectEqualSlices(u8, "c", it.next().?); 3124 try testing.expectEqualSlices(u8, "d", it.next().?); 3125 try testing.expectEqualSlices(u8, "e", it.next().?); 3126 try testing.expectEqualSlices(u8, "f", it.next().?); 3127 try testing.expectEqualSlices(u8, "g", it.next().?); 3128 try testing.expectEqual(null, it.next()); 3129 } 3130 3131 { 3132 // advance > size 3133 var it = window(u8, "abcdefg", 2, 3); 3134 try testing.expectEqualSlices(u8, "ab", it.next().?); 3135 try testing.expectEqualSlices(u8, "de", it.next().?); 3136 try testing.expectEqualSlices(u8, "g", it.next().?); 3137 try testing.expectEqual(null, it.next()); 3138 } 3139 } 3140 3141 /// Iterator type returned by the `window` function for sliding window operations. 3142 pub fn WindowIterator(comptime T: type) type { 3143 return struct { 3144 buffer: []const T, 3145 index: ?usize, 3146 size: usize, 3147 advance: usize, 3148 3149 const Self = @This(); 3150 3151 /// Returns a slice of the next window, or null if window is at end. 3152 pub fn next(self: *Self) ?[]const T { 3153 const start = self.index orelse return null; 3154 const next_index = start + self.advance; 3155 const end = if (start + self.size < self.buffer.len) blk: { 3156 self.index = if (next_index < self.buffer.len) next_index else null; 3157 break :blk start + self.size; 3158 } else blk: { 3159 self.index = null; 3160 break :blk self.buffer.len; 3161 }; 3162 return self.buffer[start..end]; 3163 } 3164 3165 /// Resets the iterator to the initial window. 3166 pub fn reset(self: *Self) void { 3167 self.index = 0; 3168 } 3169 }; 3170 } 3171 3172 /// Returns true if haystack starts with needle. 3173 /// Time complexity: O(needle.len) 3174 pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { 3175 return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); 3176 } 3177 3178 test startsWith { 3179 try testing.expect(startsWith(u8, "Bob", "Bo")); 3180 try testing.expect(!startsWith(u8, "Needle in haystack", "haystack")); 3181 } 3182 3183 /// Returns true if haystack ends with needle. 3184 /// Time complexity: O(needle.len) 3185 pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { 3186 return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); 3187 } 3188 3189 test endsWith { 3190 try testing.expect(endsWith(u8, "Needle in haystack", "haystack")); 3191 try testing.expect(!endsWith(u8, "Bob", "Bo")); 3192 } 3193 3194 /// If `slice` starts with `prefix`, returns the rest of `slice` starting at `prefix.len`. 3195 pub fn cutPrefix(comptime T: type, slice: []const T, prefix: []const T) ?[]const T { 3196 return if (startsWith(T, slice, prefix)) slice[prefix.len..] else null; 3197 } 3198 3199 test cutPrefix { 3200 try testing.expectEqualStrings("foo", cutPrefix(u8, "--example=foo", "--example=").?); 3201 try testing.expectEqual(null, cutPrefix(u8, "--example=foo", "-example=")); 3202 } 3203 3204 /// If `slice` ends with `suffix`, returns `slice` from beginning to start of `suffix`. 3205 pub fn cutSuffix(comptime T: type, slice: []const T, suffix: []const T) ?[]const T { 3206 return if (endsWith(T, slice, suffix)) slice[0 .. slice.len - suffix.len] else null; 3207 } 3208 3209 test cutSuffix { 3210 try testing.expectEqualStrings("foo", cutSuffix(u8, "foobar", "bar").?); 3211 try testing.expectEqual(null, cutSuffix(u8, "foobar", "baz")); 3212 } 3213 3214 /// Returns slice of `haystack` before and after first occurrence of `needle`, 3215 /// or `null` if not found. 3216 /// 3217 /// See also: 3218 /// * `cutScalar` 3219 /// * `split` 3220 /// * `tokenizeAny` 3221 pub fn cut(comptime T: type, haystack: []const T, needle: []const T) ?struct { []const T, []const T } { 3222 const index = find(T, haystack, needle) orelse return null; 3223 return .{ haystack[0..index], haystack[index + needle.len ..] }; 3224 } 3225 3226 test cut { 3227 try testing.expectEqual(null, cut(u8, "a b c", "B")); 3228 const before, const after = cut(u8, "a be c", "be") orelse return error.TestFailed; 3229 try testing.expectEqualStrings("a ", before); 3230 try testing.expectEqualStrings(" c", after); 3231 } 3232 3233 /// Returns slice of `haystack` before and after last occurrence of `needle`, 3234 /// or `null` if not found. 3235 /// 3236 /// See also: 3237 /// * `cut` 3238 /// * `cutScalarLast` 3239 pub fn cutLast(comptime T: type, haystack: []const T, needle: []const T) ?struct { []const T, []const T } { 3240 const index = findLast(T, haystack, needle) orelse return null; 3241 return .{ haystack[0..index], haystack[index + needle.len ..] }; 3242 } 3243 3244 test cutLast { 3245 try testing.expectEqual(null, cutLast(u8, "a b c", "B")); 3246 const before, const after = cutLast(u8, "a be c be d", "be") orelse return error.TestFailed; 3247 try testing.expectEqualStrings("a be c ", before); 3248 try testing.expectEqualStrings(" d", after); 3249 } 3250 3251 /// Returns slice of `haystack` before and after first occurrence `needle`, or 3252 /// `null` if not found. 3253 /// 3254 /// See also: 3255 /// * `cut` 3256 /// * `splitScalar` 3257 /// * `tokenizeScalar` 3258 pub fn cutScalar(comptime T: type, haystack: []const T, needle: T) ?struct { []const T, []const T } { 3259 const index = findScalar(T, haystack, needle) orelse return null; 3260 return .{ haystack[0..index], haystack[index + 1 ..] }; 3261 } 3262 3263 test cutScalar { 3264 try testing.expectEqual(null, cutScalar(u8, "a b c", 'B')); 3265 const before, const after = cutScalar(u8, "a b c", 'b') orelse return error.TestFailed; 3266 try testing.expectEqualStrings("a ", before); 3267 try testing.expectEqualStrings(" c", after); 3268 } 3269 3270 /// Returns slice of `haystack` before and after last occurrence of `needle`, 3271 /// or `null` if not found. 3272 /// 3273 /// See also: 3274 /// * `cut` 3275 /// * `splitScalar` 3276 /// * `tokenizeScalar` 3277 pub fn cutScalarLast(comptime T: type, haystack: []const T, needle: T) ?struct { []const T, []const T } { 3278 const index = findScalarLast(T, haystack, needle) orelse return null; 3279 return .{ haystack[0..index], haystack[index + 1 ..] }; 3280 } 3281 3282 test cutScalarLast { 3283 try testing.expectEqual(null, cutScalarLast(u8, "a b c", 'B')); 3284 const before, const after = cutScalarLast(u8, "a b c b d", 'b') orelse return error.TestFailed; 3285 try testing.expectEqualStrings("a b c ", before); 3286 try testing.expectEqualStrings(" d", after); 3287 } 3288 3289 /// Delimiter type for tokenization and splitting operations. 3290 pub const DelimiterType = enum { sequence, any, scalar }; 3291 3292 /// Iterator type for tokenization operations, skipping empty sequences and delimiter sequences. 3293 pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { 3294 return struct { 3295 buffer: []const T, 3296 delimiter: switch (delimiter_type) { 3297 .sequence, .any => []const T, 3298 .scalar => T, 3299 }, 3300 index: usize, 3301 3302 const Self = @This(); 3303 3304 /// Returns a slice of the current token, or null if tokenization is 3305 /// complete, and advances to the next token. 3306 pub fn next(self: *Self) ?[]const T { 3307 const result = self.peek() orelse return null; 3308 self.index += result.len; 3309 return result; 3310 } 3311 3312 /// Returns a slice of the current token, or null if tokenization is 3313 /// complete. Does not advance to the next token. 3314 pub fn peek(self: *Self) ?[]const T { 3315 // move to beginning of token 3316 while (self.index < self.buffer.len and self.isDelimiter(self.index)) : (self.index += switch (delimiter_type) { 3317 .sequence => self.delimiter.len, 3318 .any, .scalar => 1, 3319 }) {} 3320 const start = self.index; 3321 if (start == self.buffer.len) { 3322 return null; 3323 } 3324 3325 // move to end of token 3326 var end = start; 3327 while (end < self.buffer.len and !self.isDelimiter(end)) : (end += 1) {} 3328 3329 return self.buffer[start..end]; 3330 } 3331 3332 /// Returns a slice of the remaining bytes. Does not affect iterator state. 3333 pub fn rest(self: Self) []const T { 3334 // move to beginning of token 3335 var index: usize = self.index; 3336 while (index < self.buffer.len and self.isDelimiter(index)) : (index += switch (delimiter_type) { 3337 .sequence => self.delimiter.len, 3338 .any, .scalar => 1, 3339 }) {} 3340 return self.buffer[index..]; 3341 } 3342 3343 /// Resets the iterator to the initial token. 3344 pub fn reset(self: *Self) void { 3345 self.index = 0; 3346 } 3347 3348 fn isDelimiter(self: Self, index: usize) bool { 3349 switch (delimiter_type) { 3350 .sequence => return startsWith(T, self.buffer[index..], self.delimiter), 3351 .any => { 3352 const item = self.buffer[index]; 3353 for (self.delimiter) |delimiter_item| { 3354 if (item == delimiter_item) { 3355 return true; 3356 } 3357 } 3358 return false; 3359 }, 3360 .scalar => return self.buffer[index] == self.delimiter, 3361 } 3362 } 3363 }; 3364 } 3365 3366 /// Iterator type for splitting operations, including empty sequences between delimiters. 3367 pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { 3368 return struct { 3369 buffer: []const T, 3370 index: ?usize, 3371 delimiter: switch (delimiter_type) { 3372 .sequence, .any => []const T, 3373 .scalar => T, 3374 }, 3375 3376 const Self = @This(); 3377 3378 /// Returns a slice of the first field. 3379 /// Call this only to get the first field and then use `next` to get all subsequent fields. 3380 /// Asserts that iteration has not begun. 3381 pub fn first(self: *Self) []const T { 3382 assert(self.index.? == 0); 3383 return self.next().?; 3384 } 3385 3386 /// Returns a slice of the next field, or null if splitting is complete. 3387 pub fn next(self: *Self) ?[]const T { 3388 const start = self.index orelse return null; 3389 const end = if (switch (delimiter_type) { 3390 .sequence => findPos(T, self.buffer, start, self.delimiter), 3391 .any => findAnyPos(T, self.buffer, start, self.delimiter), 3392 .scalar => findScalarPos(T, self.buffer, start, self.delimiter), 3393 }) |delim_start| blk: { 3394 self.index = delim_start + switch (delimiter_type) { 3395 .sequence => self.delimiter.len, 3396 .any, .scalar => 1, 3397 }; 3398 break :blk delim_start; 3399 } else blk: { 3400 self.index = null; 3401 break :blk self.buffer.len; 3402 }; 3403 return self.buffer[start..end]; 3404 } 3405 3406 /// Returns a slice of the next field, or null if splitting is complete. 3407 /// This method does not alter self.index. 3408 pub fn peek(self: *const Self) ?[]const T { 3409 const start = self.index orelse return null; 3410 const end = if (switch (delimiter_type) { 3411 .sequence => findPos(T, self.buffer, start, self.delimiter), 3412 .any => findAnyPos(T, self.buffer, start, self.delimiter), 3413 .scalar => findScalarPos(T, self.buffer, start, self.delimiter), 3414 }) |delim_start| delim_start else self.buffer.len; 3415 return self.buffer[start..end]; 3416 } 3417 3418 /// Returns a slice of the remaining bytes. Does not affect iterator state. 3419 pub fn rest(self: Self) []const T { 3420 const end = self.buffer.len; 3421 const start = self.index orelse end; 3422 return self.buffer[start..end]; 3423 } 3424 3425 /// Resets the iterator to the initial slice. 3426 pub fn reset(self: *Self) void { 3427 self.index = 0; 3428 } 3429 }; 3430 } 3431 3432 /// Iterator type for splitting operations from the end backwards, including empty sequences. 3433 pub fn SplitBackwardsIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { 3434 return struct { 3435 buffer: []const T, 3436 index: ?usize, 3437 delimiter: switch (delimiter_type) { 3438 .sequence, .any => []const T, 3439 .scalar => T, 3440 }, 3441 3442 const Self = @This(); 3443 3444 /// Returns a slice of the first field. 3445 /// Call this only to get the first field and then use `next` to get all subsequent fields. 3446 /// Asserts that iteration has not begun. 3447 pub fn first(self: *Self) []const T { 3448 assert(self.index.? == self.buffer.len); 3449 return self.next().?; 3450 } 3451 3452 /// Returns a slice of the next field, or null if splitting is complete. 3453 pub fn next(self: *Self) ?[]const T { 3454 const end = self.index orelse return null; 3455 const start = if (switch (delimiter_type) { 3456 .sequence => lastIndexOf(T, self.buffer[0..end], self.delimiter), 3457 .any => lastIndexOfAny(T, self.buffer[0..end], self.delimiter), 3458 .scalar => findScalarLast(T, self.buffer[0..end], self.delimiter), 3459 }) |delim_start| blk: { 3460 self.index = delim_start; 3461 break :blk delim_start + switch (delimiter_type) { 3462 .sequence => self.delimiter.len, 3463 .any, .scalar => 1, 3464 }; 3465 } else blk: { 3466 self.index = null; 3467 break :blk 0; 3468 }; 3469 return self.buffer[start..end]; 3470 } 3471 3472 /// Returns a slice of the remaining bytes. Does not affect iterator state. 3473 pub fn rest(self: Self) []const T { 3474 const end = self.index orelse 0; 3475 return self.buffer[0..end]; 3476 } 3477 3478 /// Resets the iterator to the initial slice. 3479 pub fn reset(self: *Self) void { 3480 self.index = self.buffer.len; 3481 } 3482 }; 3483 } 3484 3485 /// Naively combines a series of slices with a separator. 3486 /// Allocates memory for the result, which must be freed by the caller. 3487 pub fn join(allocator: Allocator, separator: []const u8, slices: []const []const u8) Allocator.Error![]u8 { 3488 return joinMaybeZ(allocator, separator, slices, false); 3489 } 3490 3491 /// Naively combines a series of slices with a separator and null terminator. 3492 /// Allocates memory for the result, which must be freed by the caller. 3493 pub fn joinZ(allocator: Allocator, separator: []const u8, slices: []const []const u8) Allocator.Error![:0]u8 { 3494 const out = try joinMaybeZ(allocator, separator, slices, true); 3495 return out[0 .. out.len - 1 :0]; 3496 } 3497 3498 fn joinMaybeZ(allocator: Allocator, separator: []const u8, slices: []const []const u8, zero: bool) Allocator.Error![]u8 { 3499 if (slices.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{}; 3500 3501 const total_len = blk: { 3502 var sum: usize = separator.len * (slices.len - 1); 3503 for (slices) |slice| sum += slice.len; 3504 if (zero) sum += 1; 3505 break :blk sum; 3506 }; 3507 3508 const buf = try allocator.alloc(u8, total_len); 3509 errdefer allocator.free(buf); 3510 3511 @memcpy(buf[0..slices[0].len], slices[0]); 3512 var buf_index: usize = slices[0].len; 3513 for (slices[1..]) |slice| { 3514 @memcpy(buf[buf_index .. buf_index + separator.len], separator); 3515 buf_index += separator.len; 3516 @memcpy(buf[buf_index .. buf_index + slice.len], slice); 3517 buf_index += slice.len; 3518 } 3519 3520 if (zero) buf[buf.len - 1] = 0; 3521 3522 // No need for shrink since buf is exactly the correct size. 3523 return buf; 3524 } 3525 3526 test join { 3527 { 3528 const str = try join(testing.allocator, ",", &[_][]const u8{}); 3529 defer testing.allocator.free(str); 3530 try testing.expect(eql(u8, str, "")); 3531 } 3532 { 3533 const str = try join(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" }); 3534 defer testing.allocator.free(str); 3535 try testing.expect(eql(u8, str, "a,b,c")); 3536 } 3537 { 3538 const str = try join(testing.allocator, ",", &[_][]const u8{"a"}); 3539 defer testing.allocator.free(str); 3540 try testing.expect(eql(u8, str, "a")); 3541 } 3542 { 3543 const str = try join(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" }); 3544 defer testing.allocator.free(str); 3545 try testing.expect(eql(u8, str, "a,,b,,c")); 3546 } 3547 } 3548 3549 test joinZ { 3550 { 3551 const str = try joinZ(testing.allocator, ",", &[_][]const u8{}); 3552 defer testing.allocator.free(str); 3553 try testing.expect(eql(u8, str, "")); 3554 try testing.expectEqual(str[str.len], 0); 3555 } 3556 { 3557 const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" }); 3558 defer testing.allocator.free(str); 3559 try testing.expect(eql(u8, str, "a,b,c")); 3560 try testing.expectEqual(str[str.len], 0); 3561 } 3562 { 3563 const str = try joinZ(testing.allocator, ",", &[_][]const u8{"a"}); 3564 defer testing.allocator.free(str); 3565 try testing.expect(eql(u8, str, "a")); 3566 try testing.expectEqual(str[str.len], 0); 3567 } 3568 { 3569 const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" }); 3570 defer testing.allocator.free(str); 3571 try testing.expect(eql(u8, str, "a,,b,,c")); 3572 try testing.expectEqual(str[str.len], 0); 3573 } 3574 } 3575 3576 /// Copies each T from slices into a new slice that exactly holds all the elements. 3577 pub fn concat(allocator: Allocator, comptime T: type, slices: []const []const T) Allocator.Error![]T { 3578 return concatMaybeSentinel(allocator, T, slices, null); 3579 } 3580 3581 /// Copies each T from slices into a new slice that exactly holds all the elements. 3582 pub fn concatWithSentinel(allocator: Allocator, comptime T: type, slices: []const []const T, comptime s: T) Allocator.Error![:s]T { 3583 const ret = try concatMaybeSentinel(allocator, T, slices, s); 3584 return ret[0 .. ret.len - 1 :s]; 3585 } 3586 3587 /// Copies each T from slices into a new slice that exactly holds all the elements as well as the sentinel. 3588 pub fn concatMaybeSentinel(allocator: Allocator, comptime T: type, slices: []const []const T, comptime s: ?T) Allocator.Error![]T { 3589 if (slices.len == 0) return if (s) |sentinel| try allocator.dupe(T, &[1]T{sentinel}) else &[0]T{}; 3590 3591 const total_len = blk: { 3592 var sum: usize = 0; 3593 for (slices) |slice| { 3594 sum += slice.len; 3595 } 3596 3597 if (s) |_| { 3598 sum += 1; 3599 } 3600 3601 break :blk sum; 3602 }; 3603 3604 const buf = try allocator.alloc(T, total_len); 3605 errdefer allocator.free(buf); 3606 3607 var buf_index: usize = 0; 3608 for (slices) |slice| { 3609 @memcpy(buf[buf_index .. buf_index + slice.len], slice); 3610 buf_index += slice.len; 3611 } 3612 3613 if (s) |sentinel| { 3614 buf[buf.len - 1] = sentinel; 3615 } 3616 3617 // No need for shrink since buf is exactly the correct size. 3618 return buf; 3619 } 3620 3621 test concat { 3622 { 3623 const str = try concat(testing.allocator, u8, &[_][]const u8{ "abc", "def", "ghi" }); 3624 defer testing.allocator.free(str); 3625 try testing.expect(eql(u8, str, "abcdefghi")); 3626 } 3627 { 3628 const str = try concat(testing.allocator, u32, &[_][]const u32{ 3629 &[_]u32{ 0, 1 }, 3630 &[_]u32{ 2, 3, 4 }, 3631 &[_]u32{}, 3632 &[_]u32{5}, 3633 }); 3634 defer testing.allocator.free(str); 3635 try testing.expect(eql(u32, str, &[_]u32{ 0, 1, 2, 3, 4, 5 })); 3636 } 3637 { 3638 const str = try concatWithSentinel(testing.allocator, u8, &[_][]const u8{ "abc", "def", "ghi" }, 0); 3639 defer testing.allocator.free(str); 3640 try testing.expectEqualSentinel(u8, 0, str, "abcdefghi"); 3641 } 3642 { 3643 const slice = try concatWithSentinel(testing.allocator, u8, &[_][]const u8{}, 0); 3644 defer testing.allocator.free(slice); 3645 try testing.expectEqualSentinel(u8, 0, slice, &[_:0]u8{}); 3646 } 3647 { 3648 const slice = try concatWithSentinel(testing.allocator, u32, &[_][]const u32{ 3649 &[_]u32{ 0, 1 }, 3650 &[_]u32{ 2, 3, 4 }, 3651 &[_]u32{}, 3652 &[_]u32{5}, 3653 }, 2); 3654 defer testing.allocator.free(slice); 3655 try testing.expectEqualSentinel(u32, 2, slice, &[_:2]u32{ 0, 1, 2, 3, 4, 5 }); 3656 } 3657 } 3658 3659 fn moreReadIntTests() !void { 3660 { 3661 const bytes = [_]u8{ 3662 0x12, 3663 0x34, 3664 0x56, 3665 0x78, 3666 }; 3667 try testing.expect(readInt(u32, &bytes, .big) == 0x12345678); 3668 try testing.expect(readInt(u32, &bytes, .big) == 0x12345678); 3669 try testing.expect(readInt(i32, &bytes, .big) == 0x12345678); 3670 try testing.expect(readInt(u32, &bytes, .little) == 0x78563412); 3671 try testing.expect(readInt(u32, &bytes, .little) == 0x78563412); 3672 try testing.expect(readInt(i32, &bytes, .little) == 0x78563412); 3673 } 3674 { 3675 const buf = [_]u8{ 3676 0x00, 3677 0x00, 3678 0x12, 3679 0x34, 3680 }; 3681 const answer = readInt(u32, &buf, .big); 3682 try testing.expect(answer == 0x00001234); 3683 } 3684 { 3685 const buf = [_]u8{ 3686 0x12, 3687 0x34, 3688 0x00, 3689 0x00, 3690 }; 3691 const answer = readInt(u32, &buf, .little); 3692 try testing.expect(answer == 0x00003412); 3693 } 3694 { 3695 const bytes = [_]u8{ 3696 0xff, 3697 0xfe, 3698 }; 3699 try testing.expect(readInt(u16, &bytes, .big) == 0xfffe); 3700 try testing.expect(readInt(i16, &bytes, .big) == -0x0002); 3701 try testing.expect(readInt(u16, &bytes, .little) == 0xfeff); 3702 try testing.expect(readInt(i16, &bytes, .little) == -0x0101); 3703 } 3704 } 3705 3706 /// Returns the smallest number in a slice. O(n). 3707 /// `slice` must not be empty. 3708 pub fn min(comptime T: type, slice: []const T) T { 3709 assert(slice.len > 0); 3710 var best = slice[0]; 3711 for (slice[1..]) |item| { 3712 best = @min(best, item); 3713 } 3714 return best; 3715 } 3716 3717 test min { 3718 try testing.expectEqual(min(u8, "abcdefg"), 'a'); 3719 try testing.expectEqual(min(u8, "bcdefga"), 'a'); 3720 try testing.expectEqual(min(u8, "a"), 'a'); 3721 } 3722 3723 /// Returns the largest number in a slice. O(n). 3724 /// `slice` must not be empty. 3725 pub fn max(comptime T: type, slice: []const T) T { 3726 assert(slice.len > 0); 3727 var best = slice[0]; 3728 for (slice[1..]) |item| { 3729 best = @max(best, item); 3730 } 3731 return best; 3732 } 3733 3734 test max { 3735 try testing.expectEqual(max(u8, "abcdefg"), 'g'); 3736 try testing.expectEqual(max(u8, "gabcdef"), 'g'); 3737 try testing.expectEqual(max(u8, "g"), 'g'); 3738 } 3739 3740 /// Finds the smallest and largest number in a slice. O(n). 3741 /// Returns an anonymous struct with the fields `min` and `max`. 3742 /// `slice` must not be empty. 3743 pub fn minMax(comptime T: type, slice: []const T) struct { T, T } { 3744 assert(slice.len > 0); 3745 var running_minimum = slice[0]; 3746 var running_maximum = slice[0]; 3747 for (slice[1..]) |item| { 3748 running_minimum = @min(running_minimum, item); 3749 running_maximum = @max(running_maximum, item); 3750 } 3751 return .{ running_minimum, running_maximum }; 3752 } 3753 3754 test minMax { 3755 { 3756 const actual_min, const actual_max = minMax(u8, "abcdefg"); 3757 try testing.expectEqual(@as(u8, 'a'), actual_min); 3758 try testing.expectEqual(@as(u8, 'g'), actual_max); 3759 } 3760 { 3761 const actual_min, const actual_max = minMax(u8, "bcdefga"); 3762 try testing.expectEqual(@as(u8, 'a'), actual_min); 3763 try testing.expectEqual(@as(u8, 'g'), actual_max); 3764 } 3765 { 3766 const actual_min, const actual_max = minMax(u8, "a"); 3767 try testing.expectEqual(@as(u8, 'a'), actual_min); 3768 try testing.expectEqual(@as(u8, 'a'), actual_max); 3769 } 3770 } 3771 3772 /// Deprecated in favor of `findMin`. 3773 pub const indexOfMin = findMin; 3774 3775 /// Returns the index of the smallest number in a slice. O(n). 3776 /// `slice` must not be empty. 3777 pub fn findMin(comptime T: type, slice: []const T) usize { 3778 assert(slice.len > 0); 3779 var best = slice[0]; 3780 var index: usize = 0; 3781 for (slice[1..], 0..) |item, i| { 3782 if (item < best) { 3783 best = item; 3784 index = i + 1; 3785 } 3786 } 3787 return index; 3788 } 3789 3790 test findMin { 3791 try testing.expectEqual(findMin(u8, "abcdefg"), 0); 3792 try testing.expectEqual(findMin(u8, "bcdefga"), 6); 3793 try testing.expectEqual(findMin(u8, "a"), 0); 3794 } 3795 3796 pub const indexOfMax = findMax; 3797 3798 /// Returns the index of the largest number in a slice. O(n). 3799 /// `slice` must not be empty. 3800 pub fn findMax(comptime T: type, slice: []const T) usize { 3801 assert(slice.len > 0); 3802 var best = slice[0]; 3803 var index: usize = 0; 3804 for (slice[1..], 0..) |item, i| { 3805 if (item > best) { 3806 best = item; 3807 index = i + 1; 3808 } 3809 } 3810 return index; 3811 } 3812 3813 test findMax { 3814 try testing.expectEqual(findMax(u8, "abcdefg"), 6); 3815 try testing.expectEqual(findMax(u8, "gabcdef"), 0); 3816 try testing.expectEqual(findMax(u8, "a"), 0); 3817 } 3818 3819 /// Deprecated in favor of `findMinMax`. 3820 pub const indexOfMinMax = findMinMax; 3821 3822 /// Finds the indices of the smallest and largest number in a slice. O(n). 3823 /// Returns the indices of the smallest and largest numbers in that order. 3824 /// `slice` must not be empty. 3825 pub fn findMinMax(comptime T: type, slice: []const T) struct { usize, usize } { 3826 assert(slice.len > 0); 3827 var minVal = slice[0]; 3828 var maxVal = slice[0]; 3829 var minIdx: usize = 0; 3830 var maxIdx: usize = 0; 3831 for (slice[1..], 0..) |item, i| { 3832 if (item < minVal) { 3833 minVal = item; 3834 minIdx = i + 1; 3835 } 3836 if (item > maxVal) { 3837 maxVal = item; 3838 maxIdx = i + 1; 3839 } 3840 } 3841 return .{ minIdx, maxIdx }; 3842 } 3843 3844 test findMinMax { 3845 try testing.expectEqual(.{ 0, 6 }, findMinMax(u8, "abcdefg")); 3846 try testing.expectEqual(.{ 1, 0 }, findMinMax(u8, "gabcdef")); 3847 try testing.expectEqual(.{ 0, 0 }, findMinMax(u8, "a")); 3848 } 3849 3850 /// Exchanges contents of two memory locations. 3851 pub fn swap(comptime T: type, noalias a: *T, noalias b: *T) void { 3852 if (@inComptime()) { 3853 // In comptime, accessing bytes of values with no defined layout is a compile error. 3854 const tmp = a.*; 3855 a.* = b.*; 3856 b.* = tmp; 3857 } else { 3858 // Swapping in streaming nature from start to end instead of swapping 3859 // everything in one step allows easier optimizations and less stack usage. 3860 const a_bytes: []align(@alignOf(T)) u8 = @ptrCast(a); 3861 const b_bytes: []align(@alignOf(T)) u8 = @ptrCast(b); 3862 for (a_bytes, b_bytes) |*ab, *bb| { 3863 const tmp = ab.*; 3864 ab.* = bb.*; 3865 bb.* = tmp; 3866 } 3867 } 3868 } 3869 3870 test "swap works at comptime with types with no defined layout" { 3871 comptime { 3872 const T = struct { val: u64 }; 3873 var a: T = .{ .val = 0 }; 3874 var b: T = .{ .val = 1 }; 3875 swap(T, &a, &b); 3876 try testing.expectEqual(T{ .val = 1 }, a); 3877 try testing.expectEqual(T{ .val = 0 }, b); 3878 } 3879 } 3880 3881 inline fn reverseVector(comptime N: usize, comptime T: type, a: []T) [N]T { 3882 var res: [N]T = undefined; 3883 inline for (0..N) |i| { 3884 res[i] = a[N - i - 1]; 3885 } 3886 return res; 3887 } 3888 3889 /// In-place order reversal of a slice 3890 pub fn reverse(comptime T: type, items: []T) void { 3891 var i: usize = 0; 3892 const end = items.len / 2; 3893 if (use_vectors and 3894 !@inComptime() and 3895 @bitSizeOf(T) > 0 and 3896 std.math.isPowerOfTwo(@bitSizeOf(T))) 3897 { 3898 if (std.simd.suggestVectorLength(T)) |simd_size| { 3899 if (simd_size <= end) { 3900 const simd_end = end - (simd_size - 1); 3901 while (i < simd_end) : (i += simd_size) { 3902 const left_slice = items[i .. i + simd_size]; 3903 const right_slice = items[items.len - i - simd_size .. items.len - i]; 3904 3905 const left_shuffled: [simd_size]T = reverseVector(simd_size, T, left_slice); 3906 const right_shuffled: [simd_size]T = reverseVector(simd_size, T, right_slice); 3907 3908 @memcpy(right_slice, &left_shuffled); 3909 @memcpy(left_slice, &right_shuffled); 3910 } 3911 } 3912 } 3913 } 3914 3915 while (i < end) : (i += 1) { 3916 swap(T, &items[i], &items[items.len - i - 1]); 3917 } 3918 } 3919 3920 test reverse { 3921 { 3922 var arr = [_]i32{ 5, 3, 1, 2, 4 }; 3923 reverse(i32, arr[0..]); 3924 try testing.expectEqualSlices(i32, &arr, &.{ 4, 2, 1, 3, 5 }); 3925 } 3926 { 3927 var arr = [_]u0{}; 3928 reverse(u0, arr[0..]); 3929 try testing.expectEqualSlices(u0, &arr, &.{}); 3930 } 3931 { 3932 var arr = [_]i64{ 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18 }; 3933 reverse(i64, arr[0..]); 3934 try testing.expectEqualSlices(i64, &arr, &.{ 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 }); 3935 } 3936 { 3937 var arr = [_][]const u8{ "a", "b", "c", "d" }; 3938 reverse([]const u8, arr[0..]); 3939 try testing.expectEqualSlices([]const u8, &arr, &.{ "d", "c", "b", "a" }); 3940 } 3941 { 3942 const MyType = union(enum) { 3943 a: [3]u8, 3944 b: u24, 3945 c, 3946 }; 3947 var arr = [_]MyType{ .{ .a = .{ 0, 0, 0 } }, .{ .b = 0 }, .c }; 3948 reverse(MyType, arr[0..]); 3949 try testing.expectEqualSlices(MyType, &arr, &([_]MyType{ .c, .{ .b = 0 }, .{ .a = .{ 0, 0, 0 } } })); 3950 } 3951 } 3952 3953 /// Returned by `reverseIterator`. 3954 pub fn ReverseIterator(comptime T: type) type { 3955 const ptr = switch (@typeInfo(T)) { 3956 .pointer => |ptr| ptr, 3957 else => @compileError("expected slice or pointer to array, found '" ++ @typeName(T) ++ "'"), 3958 }; 3959 switch (ptr.size) { 3960 .slice => {}, 3961 .one => if (@typeInfo(ptr.child) != .array) @compileError("expected slice or pointer to array, found '" ++ @typeName(T) ++ "'"), 3962 .many, .c => @compileError("expected slice or pointer to array, found '" ++ @typeName(T) ++ "'"), 3963 } 3964 const Element = std.meta.Elem(T); 3965 const attrs: std.builtin.Type.Pointer.Attributes = .{ 3966 .@"const" = ptr.is_const, 3967 .@"volatile" = ptr.is_volatile, 3968 .@"allowzero" = ptr.is_allowzero, 3969 .@"align" = ptr.alignment, 3970 .@"addrspace" = ptr.address_space, 3971 }; 3972 const Pointer = @Pointer(.many, attrs, Element, std.meta.sentinel(T)); 3973 const ElementPointer = @Pointer(.one, attrs, Element, null); 3974 return struct { 3975 ptr: Pointer, 3976 index: usize, 3977 pub fn next(self: *@This()) ?Element { 3978 if (self.index == 0) return null; 3979 self.index -= 1; 3980 return self.ptr[self.index]; 3981 } 3982 pub fn nextPtr(self: *@This()) ?ElementPointer { 3983 if (self.index == 0) return null; 3984 self.index -= 1; 3985 return &self.ptr[self.index]; 3986 } 3987 }; 3988 } 3989 3990 /// Iterates over a slice in reverse. 3991 pub fn reverseIterator(slice: anytype) ReverseIterator(@TypeOf(slice)) { 3992 return .{ .ptr = slice.ptr, .index = slice.len }; 3993 } 3994 3995 test reverseIterator { 3996 { 3997 var it = reverseIterator("abc"); 3998 try testing.expectEqual(@as(?u8, 'c'), it.next()); 3999 try testing.expectEqual(@as(?u8, 'b'), it.next()); 4000 try testing.expectEqual(@as(?u8, 'a'), it.next()); 4001 try testing.expectEqual(@as(?u8, null), it.next()); 4002 } 4003 { 4004 var array = [2]i32{ 3, 7 }; 4005 const slice: []const i32 = &array; 4006 var it = reverseIterator(slice); 4007 try testing.expectEqual(@as(?i32, 7), it.next()); 4008 try testing.expectEqual(@as(?i32, 3), it.next()); 4009 try testing.expectEqual(@as(?i32, null), it.next()); 4010 4011 it = reverseIterator(slice); 4012 try testing.expect(*const i32 == @TypeOf(it.nextPtr().?)); 4013 try testing.expectEqual(@as(?i32, 7), it.nextPtr().?.*); 4014 try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*); 4015 try testing.expectEqual(@as(?*const i32, null), it.nextPtr()); 4016 4017 const mut_slice: []i32 = &array; 4018 var mut_it = reverseIterator(mut_slice); 4019 mut_it.nextPtr().?.* += 1; 4020 mut_it.nextPtr().?.* += 2; 4021 try testing.expectEqual([2]i32{ 5, 8 }, array); 4022 } 4023 { 4024 var array = [2]i32{ 3, 7 }; 4025 const ptr_to_array: *const [2]i32 = &array; 4026 var it = reverseIterator(ptr_to_array); 4027 try testing.expectEqual(@as(?i32, 7), it.next()); 4028 try testing.expectEqual(@as(?i32, 3), it.next()); 4029 try testing.expectEqual(@as(?i32, null), it.next()); 4030 4031 it = reverseIterator(ptr_to_array); 4032 try testing.expect(*const i32 == @TypeOf(it.nextPtr().?)); 4033 try testing.expectEqual(@as(?i32, 7), it.nextPtr().?.*); 4034 try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*); 4035 try testing.expectEqual(@as(?*const i32, null), it.nextPtr()); 4036 4037 const mut_ptr_to_array: *[2]i32 = &array; 4038 var mut_it = reverseIterator(mut_ptr_to_array); 4039 mut_it.nextPtr().?.* += 1; 4040 mut_it.nextPtr().?.* += 2; 4041 try testing.expectEqual([2]i32{ 5, 8 }, array); 4042 } 4043 } 4044 4045 /// In-place rotation of the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1) 4046 /// Assumes 0 <= amount <= items.len 4047 pub fn rotate(comptime T: type, items: []T, amount: usize) void { 4048 reverse(T, items[0..amount]); 4049 reverse(T, items[amount..]); 4050 reverse(T, items); 4051 } 4052 4053 test rotate { 4054 var arr = [_]i32{ 5, 3, 1, 2, 4 }; 4055 rotate(i32, arr[0..], 2); 4056 4057 try testing.expect(eql(i32, &arr, &[_]i32{ 1, 2, 4, 5, 3 })); 4058 } 4059 4060 /// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of 4061 /// appropriate size. Use replacementSize to calculate an appropriate buffer size. 4062 /// The `input` and `output` slices must not overlap. 4063 /// The needle must not be empty. 4064 /// Returns the number of replacements made. 4065 pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize { 4066 // Empty needle will loop until output buffer overflows. 4067 assert(needle.len > 0); 4068 4069 var i: usize = 0; 4070 var slide: usize = 0; 4071 var replacements: usize = 0; 4072 while (slide < input.len) { 4073 if (mem.startsWith(T, input[slide..], needle)) { 4074 @memcpy(output[i..][0..replacement.len], replacement); 4075 i += replacement.len; 4076 slide += needle.len; 4077 replacements += 1; 4078 } else { 4079 output[i] = input[slide]; 4080 i += 1; 4081 slide += 1; 4082 } 4083 } 4084 4085 return replacements; 4086 } 4087 4088 test replace { 4089 var output: [29]u8 = undefined; 4090 var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]); 4091 var expected: []const u8 = "All your Zig are belong to us"; 4092 try testing.expect(replacements == 1); 4093 try testing.expectEqualStrings(expected, output[0..expected.len]); 4094 4095 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]); 4096 expected = "Favor reading over writing ."; 4097 try testing.expect(replacements == 2); 4098 try testing.expectEqualStrings(expected, output[0..expected.len]); 4099 4100 // Empty needle is not allowed but input may be empty. 4101 replacements = replace(u8, "", "x", "y", output[0..0]); 4102 expected = ""; 4103 try testing.expect(replacements == 0); 4104 try testing.expectEqualStrings(expected, output[0..expected.len]); 4105 4106 // Adjacent replacements. 4107 4108 replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]); 4109 expected = "\n\n"; 4110 try testing.expect(replacements == 2); 4111 try testing.expectEqualStrings(expected, output[0..expected.len]); 4112 4113 replacements = replace(u8, "abbba", "b", "cd", output[0..]); 4114 expected = "acdcdcda"; 4115 try testing.expect(replacements == 3); 4116 try testing.expectEqualStrings(expected, output[0..expected.len]); 4117 } 4118 4119 /// Replace all occurrences of `match` with `replacement`. 4120 pub fn replaceScalar(comptime T: type, slice: []T, match: T, replacement: T) void { 4121 for (slice) |*e| { 4122 if (e.* == match) 4123 e.* = replacement; 4124 } 4125 } 4126 4127 /// Collapse consecutive duplicate elements into one entry. 4128 pub fn collapseRepeatsLen(comptime T: type, slice: []T, elem: T) usize { 4129 if (slice.len == 0) return 0; 4130 var write_idx: usize = 1; 4131 var read_idx: usize = 1; 4132 while (read_idx < slice.len) : (read_idx += 1) { 4133 if (slice[read_idx - 1] != elem or slice[read_idx] != elem) { 4134 slice[write_idx] = slice[read_idx]; 4135 write_idx += 1; 4136 } 4137 } 4138 return write_idx; 4139 } 4140 4141 /// Collapse consecutive duplicate elements into one entry. 4142 pub fn collapseRepeats(comptime T: type, slice: []T, elem: T) []T { 4143 return slice[0..collapseRepeatsLen(T, slice, elem)]; 4144 } 4145 4146 fn testCollapseRepeats(str: []const u8, elem: u8, expected: []const u8) !void { 4147 const mutable = try std.testing.allocator.dupe(u8, str); 4148 defer std.testing.allocator.free(mutable); 4149 try testing.expect(std.mem.eql(u8, collapseRepeats(u8, mutable, elem), expected)); 4150 } 4151 test collapseRepeats { 4152 try testCollapseRepeats("", '/', ""); 4153 try testCollapseRepeats("a", '/', "a"); 4154 try testCollapseRepeats("/", '/', "/"); 4155 try testCollapseRepeats("//", '/', "/"); 4156 try testCollapseRepeats("/a", '/', "/a"); 4157 try testCollapseRepeats("//a", '/', "/a"); 4158 try testCollapseRepeats("a/", '/', "a/"); 4159 try testCollapseRepeats("a//", '/', "a/"); 4160 try testCollapseRepeats("a/a", '/', "a/a"); 4161 try testCollapseRepeats("a//a", '/', "a/a"); 4162 try testCollapseRepeats("//a///a////", '/', "/a/a/"); 4163 } 4164 4165 /// Calculate the size needed in an output buffer to perform a replacement. 4166 /// The needle must not be empty. 4167 pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize { 4168 // Empty needle will loop forever. 4169 assert(needle.len > 0); 4170 4171 var i: usize = 0; 4172 var size: usize = input.len; 4173 while (i < input.len) { 4174 if (mem.startsWith(T, input[i..], needle)) { 4175 size = size - needle.len + replacement.len; 4176 i += needle.len; 4177 } else { 4178 i += 1; 4179 } 4180 } 4181 4182 return size; 4183 } 4184 4185 test replacementSize { 4186 try testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29); 4187 try testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29); 4188 try testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41); 4189 4190 // Empty needle is not allowed but input may be empty. 4191 try testing.expect(replacementSize(u8, "", "x", "y") == 0); 4192 4193 // Adjacent replacements. 4194 try testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2); 4195 try testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8); 4196 } 4197 4198 /// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory. 4199 pub fn replaceOwned(comptime T: type, allocator: Allocator, input: []const T, needle: []const T, replacement: []const T) Allocator.Error![]T { 4200 const output = try allocator.alloc(T, replacementSize(T, input, needle, replacement)); 4201 _ = replace(T, input, needle, replacement, output); 4202 return output; 4203 } 4204 4205 test replaceOwned { 4206 const gpa = std.testing.allocator; 4207 4208 const base_replace = replaceOwned(u8, gpa, "All your base are belong to us", "base", "Zig") catch @panic("out of memory"); 4209 defer gpa.free(base_replace); 4210 try testing.expect(eql(u8, base_replace, "All your Zig are belong to us")); 4211 4212 const zen_replace = replaceOwned(u8, gpa, "Favor reading code over writing code.", " code", "") catch @panic("out of memory"); 4213 defer gpa.free(zen_replace); 4214 try testing.expect(eql(u8, zen_replace, "Favor reading over writing.")); 4215 } 4216 4217 /// Converts a little-endian integer to host endianness. 4218 pub fn littleToNative(comptime T: type, x: T) T { 4219 return switch (native_endian) { 4220 .little => x, 4221 .big => @byteSwap(x), 4222 }; 4223 } 4224 4225 /// Converts a big-endian integer to host endianness. 4226 pub fn bigToNative(comptime T: type, x: T) T { 4227 return switch (native_endian) { 4228 .little => @byteSwap(x), 4229 .big => x, 4230 }; 4231 } 4232 4233 /// Converts an integer from specified endianness to host endianness. 4234 pub fn toNative(comptime T: type, x: T, endianness_of_x: Endian) T { 4235 return switch (endianness_of_x) { 4236 .little => littleToNative(T, x), 4237 .big => bigToNative(T, x), 4238 }; 4239 } 4240 4241 /// Converts an integer which has host endianness to the desired endianness. 4242 pub fn nativeTo(comptime T: type, x: T, desired_endianness: Endian) T { 4243 return switch (desired_endianness) { 4244 .little => nativeToLittle(T, x), 4245 .big => nativeToBig(T, x), 4246 }; 4247 } 4248 4249 /// Converts an integer which has host endianness to little endian. 4250 pub fn nativeToLittle(comptime T: type, x: T) T { 4251 return switch (native_endian) { 4252 .little => x, 4253 .big => @byteSwap(x), 4254 }; 4255 } 4256 4257 /// Converts an integer which has host endianness to big endian. 4258 pub fn nativeToBig(comptime T: type, x: T) T { 4259 return switch (native_endian) { 4260 .little => @byteSwap(x), 4261 .big => x, 4262 }; 4263 } 4264 4265 /// Returns the number of elements that, if added to the given pointer, align it 4266 /// to a multiple of the given quantity, or `null` if one of the following 4267 /// conditions is met: 4268 /// - The aligned pointer would not fit the address space, 4269 /// - The delta required to align the pointer is not a multiple of the pointee's 4270 /// type. 4271 pub fn alignPointerOffset(ptr: anytype, align_to: usize) ?usize { 4272 assert(isValidAlign(align_to)); 4273 4274 const T = @TypeOf(ptr); 4275 const info = @typeInfo(T); 4276 if (info != .pointer or info.pointer.size != .many) 4277 @compileError("expected many item pointer, got " ++ @typeName(T)); 4278 4279 // Do nothing if the pointer is already well-aligned. 4280 if (align_to <= info.pointer.alignment orelse @alignOf(info.pointer.child)) 4281 return 0; 4282 4283 // Calculate the aligned base address with an eye out for overflow. 4284 const addr = @intFromPtr(ptr); 4285 var ov = @addWithOverflow(addr, align_to - 1); 4286 if (ov[1] != 0) return null; 4287 ov[0] &= ~@as(usize, align_to - 1); 4288 4289 // The delta is expressed in terms of bytes, turn it into a number of child 4290 // type elements. 4291 const delta = ov[0] - addr; 4292 const pointee_size = @sizeOf(info.pointer.child); 4293 if (delta % pointee_size != 0) return null; 4294 return delta / pointee_size; 4295 } 4296 4297 /// Aligns a given pointer value to a specified alignment factor. 4298 /// Returns an aligned pointer or null if one of the following conditions is 4299 /// met: 4300 /// - The aligned pointer would not fit the address space, 4301 /// - The delta required to align the pointer is not a multiple of the pointee's 4302 /// type. 4303 pub fn alignPointer(ptr: anytype, align_to: usize) ?@TypeOf(ptr) { 4304 const adjust_off = alignPointerOffset(ptr, align_to) orelse return null; 4305 // Avoid the use of ptrFromInt to avoid losing the pointer provenance info. 4306 return @alignCast(ptr + adjust_off); 4307 } 4308 4309 test alignPointer { 4310 const S = struct { 4311 fn checkAlign(comptime T: type, base: usize, align_to: usize, expected: usize) !void { 4312 const ptr: T = @ptrFromInt(base); 4313 const aligned = alignPointer(ptr, align_to); 4314 try testing.expectEqual(expected, @intFromPtr(aligned)); 4315 } 4316 }; 4317 4318 try S.checkAlign([*]u8, 0x123, 0x200, 0x200); 4319 try S.checkAlign([*]align(4) u8, 0x10, 2, 0x10); 4320 try S.checkAlign([*]u32, 0x10, 2, 0x10); 4321 try S.checkAlign([*]u32, 0x4, 16, 0x10); 4322 // Misaligned. 4323 try S.checkAlign([*]align(1) u32, 0x3, 2, 0); 4324 // Overflow. 4325 try S.checkAlign([*]u32, math.maxInt(usize) - 3, 8, 0); 4326 } 4327 4328 fn CopyPtrAttrs( 4329 comptime source: type, 4330 comptime size: std.builtin.Type.Pointer.Size, 4331 comptime child: type, 4332 ) type { 4333 const ptr = @typeInfo(source).pointer; 4334 return @Pointer(size, .{ 4335 .@"const" = ptr.is_const, 4336 .@"volatile" = ptr.is_volatile, 4337 .@"allowzero" = ptr.is_allowzero, 4338 .@"align" = ptr.alignment orelse a: { 4339 // If the new child is aligned differently than the old one, explicitly align the type. 4340 const want = @alignOf(ptr.child); 4341 break :a if (@alignOf(child) == want) null else want; 4342 }, 4343 .@"addrspace" = ptr.address_space, 4344 }, child, null); 4345 } 4346 4347 fn AsBytesReturnType(comptime P: type) type { 4348 const pointer = @typeInfo(P).pointer; 4349 assert(pointer.size == .one); 4350 const size = @sizeOf(pointer.child); 4351 return CopyPtrAttrs(P, .one, [size]u8); 4352 } 4353 4354 /// Given a pointer to a single item, returns a slice of the underlying bytes, preserving pointer attributes. 4355 pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) { 4356 return @ptrCast(@alignCast(ptr)); 4357 } 4358 4359 test asBytes { 4360 const deadbeef = @as(u32, 0xDEADBEEF); 4361 const deadbeef_bytes = switch (native_endian) { 4362 .big => "\xDE\xAD\xBE\xEF", 4363 .little => "\xEF\xBE\xAD\xDE", 4364 }; 4365 4366 try testing.expect(eql(u8, asBytes(&deadbeef), deadbeef_bytes)); 4367 4368 var codeface = @as(u32, 0xC0DEFACE); 4369 for (asBytes(&codeface)) |*b| 4370 b.* = 0; 4371 try testing.expect(codeface == 0); 4372 4373 const S = packed struct { 4374 a: u8, 4375 b: u8, 4376 c: u8, 4377 d: u8, 4378 }; 4379 4380 const inst = S{ 4381 .a = 0xBE, 4382 .b = 0xEF, 4383 .c = 0xDE, 4384 .d = 0xA1, 4385 }; 4386 switch (native_endian) { 4387 .little => { 4388 try testing.expect(eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1")); 4389 }, 4390 .big => { 4391 try testing.expect(eql(u8, asBytes(&inst), "\xA1\xDE\xEF\xBE")); 4392 }, 4393 } 4394 4395 const ZST = struct {}; 4396 const zero = ZST{}; 4397 try testing.expect(eql(u8, asBytes(&zero), "")); 4398 } 4399 4400 test "asBytes preserves pointer attributes" { 4401 const inArr: u32 align(16) = 0xDEADBEEF; 4402 const inPtr = @as(*align(16) const volatile u32, @ptrCast(&inArr)); 4403 const outSlice = asBytes(inPtr); 4404 4405 const in = @typeInfo(@TypeOf(inPtr)).pointer; 4406 const out = @typeInfo(@TypeOf(outSlice)).pointer; 4407 4408 try testing.expectEqual(in.is_const, out.is_const); 4409 try testing.expectEqual(in.is_volatile, out.is_volatile); 4410 try testing.expectEqual(in.is_allowzero, out.is_allowzero); 4411 try testing.expectEqual(in.alignment, out.alignment); 4412 } 4413 4414 /// Given any value, returns a copy of its bytes in an array. 4415 pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 { 4416 return asBytes(&value).*; 4417 } 4418 4419 test toBytes { 4420 var my_bytes = toBytes(@as(u32, 0x12345678)); 4421 switch (native_endian) { 4422 .big => try testing.expect(eql(u8, &my_bytes, "\x12\x34\x56\x78")), 4423 .little => try testing.expect(eql(u8, &my_bytes, "\x78\x56\x34\x12")), 4424 } 4425 4426 my_bytes[0] = '\x99'; 4427 switch (native_endian) { 4428 .big => try testing.expect(eql(u8, &my_bytes, "\x99\x34\x56\x78")), 4429 .little => try testing.expect(eql(u8, &my_bytes, "\x99\x56\x34\x12")), 4430 } 4431 } 4432 4433 fn BytesAsValueReturnType(comptime T: type, comptime B: type) type { 4434 return CopyPtrAttrs(B, .one, T); 4435 } 4436 4437 /// Given a pointer to an array of bytes, returns a pointer to a value of the specified type 4438 /// backed by those bytes, preserving pointer attributes. 4439 pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) { 4440 return @ptrCast(bytes); 4441 } 4442 4443 test bytesAsValue { 4444 const deadbeef = @as(u32, 0xDEADBEEF); 4445 const deadbeef_bytes = switch (native_endian) { 4446 .big => "\xDE\xAD\xBE\xEF", 4447 .little => "\xEF\xBE\xAD\xDE", 4448 }; 4449 4450 try testing.expect(deadbeef == bytesAsValue(u32, deadbeef_bytes).*); 4451 4452 var codeface_bytes: [4]u8 = switch (native_endian) { 4453 .big => "\xC0\xDE\xFA\xCE", 4454 .little => "\xCE\xFA\xDE\xC0", 4455 }.*; 4456 const codeface = bytesAsValue(u32, &codeface_bytes); 4457 try testing.expect(codeface.* == 0xC0DEFACE); 4458 codeface.* = 0; 4459 for (codeface_bytes) |b| 4460 try testing.expect(b == 0); 4461 4462 const S = packed struct { 4463 a: u8, 4464 b: u8, 4465 c: u8, 4466 d: u8, 4467 }; 4468 4469 const inst = S{ 4470 .a = 0xBE, 4471 .b = 0xEF, 4472 .c = 0xDE, 4473 .d = 0xA1, 4474 }; 4475 const inst_bytes = switch (native_endian) { 4476 .little => "\xBE\xEF\xDE\xA1", 4477 .big => "\xA1\xDE\xEF\xBE", 4478 }; 4479 const inst2 = bytesAsValue(S, inst_bytes); 4480 try testing.expect(std.meta.eql(inst, inst2.*)); 4481 } 4482 4483 test "bytesAsValue preserves pointer attributes" { 4484 const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; 4485 const inSlice = @as(*align(16) const volatile [4]u8, @ptrCast(&inArr))[0..]; 4486 const outPtr = bytesAsValue(u32, inSlice); 4487 4488 const in = @typeInfo(@TypeOf(inSlice)).pointer; 4489 const out = @typeInfo(@TypeOf(outPtr)).pointer; 4490 4491 try testing.expectEqual(in.is_const, out.is_const); 4492 try testing.expectEqual(in.is_volatile, out.is_volatile); 4493 try testing.expectEqual(in.is_allowzero, out.is_allowzero); 4494 try testing.expectEqual(in.alignment, out.alignment); 4495 } 4496 4497 /// Given a pointer to an array of bytes, returns a value of the specified type backed by a 4498 /// copy of those bytes. 4499 pub fn bytesToValue(comptime T: type, bytes: anytype) T { 4500 return bytesAsValue(T, bytes).*; 4501 } 4502 test bytesToValue { 4503 const deadbeef_bytes = switch (native_endian) { 4504 .big => "\xDE\xAD\xBE\xEF", 4505 .little => "\xEF\xBE\xAD\xDE", 4506 }; 4507 4508 const deadbeef = bytesToValue(u32, deadbeef_bytes); 4509 try testing.expect(deadbeef == @as(u32, 0xDEADBEEF)); 4510 } 4511 4512 fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { 4513 return CopyPtrAttrs(bytesType, .slice, T); 4514 } 4515 4516 /// Given a slice of bytes, returns a slice of the specified type 4517 /// backed by those bytes, preserving pointer attributes. 4518 /// If `T` is zero-bytes sized, the returned slice has a len of zero. 4519 pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { 4520 // let's not give an undefined pointer to @ptrCast 4521 // it may be equal to zero and fail a null check 4522 if (bytes.len == 0 or @sizeOf(T) == 0) { 4523 return &[0]T{}; 4524 } 4525 4526 const cast_target = CopyPtrAttrs(@TypeOf(bytes), .many, T); 4527 4528 return @as(cast_target, @ptrCast(bytes))[0..@divExact(bytes.len, @sizeOf(T))]; 4529 } 4530 4531 test bytesAsSlice { 4532 { 4533 const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; 4534 const slice = bytesAsSlice(u16, bytes[0..]); 4535 try testing.expect(slice.len == 2); 4536 try testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); 4537 try testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); 4538 } 4539 { 4540 const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; 4541 var runtime_zero: usize = 0; 4542 _ = &runtime_zero; 4543 const slice = bytesAsSlice(u16, bytes[runtime_zero..]); 4544 try testing.expect(slice.len == 2); 4545 try testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); 4546 try testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); 4547 } 4548 } 4549 4550 test "bytesAsSlice keeps pointer alignment" { 4551 { 4552 var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; 4553 const numbers = bytesAsSlice(u32, bytes[0..]); 4554 try comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); 4555 } 4556 { 4557 var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; 4558 var runtime_zero: usize = 0; 4559 _ = &runtime_zero; 4560 const numbers = bytesAsSlice(u32, bytes[runtime_zero..]); 4561 try comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); 4562 } 4563 } 4564 4565 test "bytesAsSlice on a packed struct" { 4566 const F = packed struct { 4567 a: u8, 4568 }; 4569 4570 const b: [1]u8 = .{9}; 4571 const f = bytesAsSlice(F, &b); 4572 try testing.expect(f[0].a == 9); 4573 } 4574 4575 test "bytesAsSlice with specified alignment" { 4576 var bytes align(4) = [_]u8{ 4577 0x33, 4578 0x33, 4579 0x33, 4580 0x33, 4581 }; 4582 const slice: []u32 = std.mem.bytesAsSlice(u32, bytes[0..]); 4583 try testing.expect(slice[0] == 0x33333333); 4584 } 4585 4586 test "bytesAsSlice preserves pointer attributes" { 4587 const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; 4588 const inSlice = @as(*align(16) const volatile [4]u8, @ptrCast(&inArr))[0..]; 4589 const outSlice = bytesAsSlice(u16, inSlice); 4590 4591 const in = @typeInfo(@TypeOf(inSlice)).pointer; 4592 const out = @typeInfo(@TypeOf(outSlice)).pointer; 4593 4594 try testing.expectEqual(in.is_const, out.is_const); 4595 try testing.expectEqual(in.is_volatile, out.is_volatile); 4596 try testing.expectEqual(in.is_allowzero, out.is_allowzero); 4597 try testing.expectEqual(in.alignment, out.alignment); 4598 } 4599 4600 test "bytesAsSlice with zero-bit element type" { 4601 { 4602 const bytes = [_]u8{}; 4603 const slice = bytesAsSlice(void, &bytes); 4604 try testing.expectEqual(0, slice.len); 4605 } 4606 { 4607 const bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; 4608 const slice = bytesAsSlice(u0, &bytes); 4609 try testing.expectEqual(0, slice.len); 4610 } 4611 } 4612 4613 fn SliceAsBytesReturnType(comptime Slice: type) type { 4614 return CopyPtrAttrs(Slice, .slice, u8); 4615 } 4616 4617 /// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes. 4618 pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { 4619 const Slice = @TypeOf(slice); 4620 4621 // a slice of zero-bit values always occupies zero bytes 4622 if (@sizeOf(std.meta.Elem(Slice)) == 0) return &[0]u8{}; 4623 4624 // let's not give an undefined pointer to @ptrCast 4625 // it may be equal to zero and fail a null check 4626 if (slice.len == 0 and std.meta.sentinel(Slice) == null) return &[0]u8{}; 4627 4628 const cast_target = CopyPtrAttrs(Slice, .many, u8); 4629 4630 return @as(cast_target, @ptrCast(slice))[0 .. slice.len * @sizeOf(std.meta.Elem(Slice))]; 4631 } 4632 4633 test sliceAsBytes { 4634 const bytes = [_]u16{ 0xDEAD, 0xBEEF }; 4635 const slice = sliceAsBytes(bytes[0..]); 4636 try testing.expect(slice.len == 4); 4637 try testing.expect(eql(u8, slice, switch (native_endian) { 4638 .big => "\xDE\xAD\xBE\xEF", 4639 .little => "\xAD\xDE\xEF\xBE", 4640 })); 4641 } 4642 4643 test "sliceAsBytes with sentinel slice" { 4644 const empty_string: [:0]const u8 = ""; 4645 const bytes = sliceAsBytes(empty_string); 4646 try testing.expect(bytes.len == 0); 4647 } 4648 4649 test "sliceAsBytes with zero-bit element type" { 4650 const lots_of_nothing = [1]void{{}} ** 10_000; 4651 const bytes = sliceAsBytes(&lots_of_nothing); 4652 try testing.expect(bytes.len == 0); 4653 } 4654 4655 test "sliceAsBytes packed struct at runtime and comptime" { 4656 const Foo = packed struct { 4657 a: u4, 4658 b: u4, 4659 }; 4660 const S = struct { 4661 fn doTheTest() !void { 4662 var foo: Foo = undefined; 4663 var slice = sliceAsBytes(@as(*[1]Foo, &foo)[0..1]); 4664 slice[0] = 0x13; 4665 try testing.expect(foo.a == 0x3); 4666 try testing.expect(foo.b == 0x1); 4667 } 4668 }; 4669 try S.doTheTest(); 4670 try comptime S.doTheTest(); 4671 } 4672 4673 test "sliceAsBytes and bytesAsSlice back" { 4674 try testing.expect(@sizeOf(i32) == 4); 4675 4676 var big_thing_array = [_]i32{ 1, 2, 3, 4 }; 4677 const big_thing_slice: []i32 = big_thing_array[0..]; 4678 4679 const bytes = sliceAsBytes(big_thing_slice); 4680 try testing.expect(bytes.len == 4 * 4); 4681 4682 bytes[4] = 0; 4683 bytes[5] = 0; 4684 bytes[6] = 0; 4685 bytes[7] = 0; 4686 try testing.expect(big_thing_slice[1] == 0); 4687 4688 const big_thing_again = bytesAsSlice(i32, bytes); 4689 try testing.expect(big_thing_again[2] == 3); 4690 4691 big_thing_again[2] = -1; 4692 try testing.expect(bytes[8] == math.maxInt(u8)); 4693 try testing.expect(bytes[9] == math.maxInt(u8)); 4694 try testing.expect(bytes[10] == math.maxInt(u8)); 4695 try testing.expect(bytes[11] == math.maxInt(u8)); 4696 } 4697 4698 test "sliceAsBytes preserves pointer attributes" { 4699 const inArr align(16) = [2]u16{ 0xDEAD, 0xBEEF }; 4700 const inSlice = @as(*align(16) const volatile [2]u16, @ptrCast(&inArr))[0..]; 4701 const outSlice = sliceAsBytes(inSlice); 4702 4703 const in = @typeInfo(@TypeOf(inSlice)).pointer; 4704 const out = @typeInfo(@TypeOf(outSlice)).pointer; 4705 4706 try testing.expectEqual(in.is_const, out.is_const); 4707 try testing.expectEqual(in.is_volatile, out.is_volatile); 4708 try testing.expectEqual(in.is_allowzero, out.is_allowzero); 4709 try testing.expectEqual(in.alignment, out.alignment); 4710 } 4711 4712 /// Round an address down to the next (or current) aligned address. 4713 /// Unlike `alignForward`, `alignment` can be any positive number, not just a power of 2. 4714 pub fn alignForwardAnyAlign(comptime T: type, addr: T, alignment: T) T { 4715 if (isValidAlignGeneric(T, alignment)) 4716 return alignForward(T, addr, alignment); 4717 assert(alignment != 0); 4718 return alignBackwardAnyAlign(T, addr + (alignment - 1), alignment); 4719 } 4720 4721 /// Round an address up to the next (or current) aligned address. 4722 /// The alignment must be a power of 2 and greater than 0. 4723 /// Asserts that rounding up the address does not cause integer overflow. 4724 pub fn alignForward(comptime T: type, addr: T, alignment: T) T { 4725 assert(isValidAlignGeneric(T, alignment)); 4726 return alignBackward(T, addr + (alignment - 1), alignment); 4727 } 4728 4729 /// Rounds an address up to the next alignment boundary using log2 representation. 4730 /// Equivalent to alignForward with alignment = 1 << log2_alignment. 4731 /// More efficient when alignment is known to be a power of 2. 4732 pub fn alignForwardLog2(addr: usize, log2_alignment: u8) usize { 4733 const alignment = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(log2_alignment)); 4734 return alignForward(usize, addr, alignment); 4735 } 4736 4737 /// Force an evaluation of the expression; this tries to prevent 4738 /// the compiler from optimizing the computation away even if the 4739 /// result eventually gets discarded. 4740 // TODO: use @declareSideEffect() when it is available - https://github.com/ziglang/zig/issues/6168 4741 pub fn doNotOptimizeAway(val: anytype) void { 4742 if (@inComptime()) return; 4743 4744 const max_gp_register_bits = @bitSizeOf(c_long); 4745 const t = @typeInfo(@TypeOf(val)); 4746 switch (t) { 4747 .void, .null, .comptime_int, .comptime_float => return, 4748 .@"enum" => doNotOptimizeAway(@intFromEnum(val)), 4749 .bool => doNotOptimizeAway(@intFromBool(val)), 4750 .int => { 4751 const bits = t.int.bits; 4752 if (bits <= max_gp_register_bits and builtin.zig_backend != .stage2_c) { 4753 const val2 = @as( 4754 std.meta.Int(t.int.signedness, @max(8, std.math.ceilPowerOfTwoAssert(u16, bits))), 4755 val, 4756 ); 4757 asm volatile ("" 4758 : 4759 : [_] "r" (val2), 4760 ); 4761 } else doNotOptimizeAway(&val); 4762 }, 4763 .float => { 4764 // https://github.com/llvm/llvm-project/issues/159200 4765 if ((t.float.bits == 32 or t.float.bits == 64) and builtin.zig_backend != .stage2_c and !builtin.cpu.arch.isLoongArch()) { 4766 asm volatile ("" 4767 : 4768 : [_] "rm" (val), 4769 ); 4770 } else doNotOptimizeAway(&val); 4771 }, 4772 .pointer => { 4773 if (builtin.zig_backend == .stage2_c) { 4774 doNotOptimizeAwayC(val); 4775 } else { 4776 asm volatile ("" 4777 : 4778 : [_] "m" (val), 4779 : .{ .memory = true }); 4780 } 4781 }, 4782 .array => { 4783 if (t.array.len * @sizeOf(t.array.child) <= 64) { 4784 for (val) |v| doNotOptimizeAway(v); 4785 } else doNotOptimizeAway(&val); 4786 }, 4787 else => doNotOptimizeAway(&val), 4788 } 4789 } 4790 4791 /// .stage2_c doesn't support asm blocks yet, so use volatile stores instead 4792 var deopt_target: if (builtin.zig_backend == .stage2_c) u8 else void = undefined; 4793 fn doNotOptimizeAwayC(ptr: anytype) void { 4794 const dest = @as(*volatile u8, @ptrCast(&deopt_target)); 4795 for (asBytes(ptr)) |b| { 4796 dest.* = b; 4797 } 4798 dest.* = 0; 4799 } 4800 4801 test doNotOptimizeAway { 4802 comptime doNotOptimizeAway("test"); 4803 4804 doNotOptimizeAway(null); 4805 doNotOptimizeAway(true); 4806 doNotOptimizeAway(0); 4807 doNotOptimizeAway(0.0); 4808 doNotOptimizeAway(@as(u1, 0)); 4809 doNotOptimizeAway(@as(u3, 0)); 4810 doNotOptimizeAway(@as(u8, 0)); 4811 doNotOptimizeAway(@as(u16, 0)); 4812 doNotOptimizeAway(@as(u32, 0)); 4813 doNotOptimizeAway(@as(u64, 0)); 4814 doNotOptimizeAway(@as(u128, 0)); 4815 doNotOptimizeAway(@as(u13, 0)); 4816 doNotOptimizeAway(@as(u37, 0)); 4817 doNotOptimizeAway(@as(u96, 0)); 4818 doNotOptimizeAway(@as(u200, 0)); 4819 doNotOptimizeAway(@as(f32, 0.0)); 4820 doNotOptimizeAway(@as(f64, 0.0)); 4821 doNotOptimizeAway([_]u8{0} ** 4); 4822 doNotOptimizeAway([_]u8{0} ** 100); 4823 doNotOptimizeAway(@as(std.builtin.Endian, .little)); 4824 } 4825 4826 test alignForward { 4827 try testing.expect(alignForward(usize, 1, 1) == 1); 4828 try testing.expect(alignForward(usize, 2, 1) == 2); 4829 try testing.expect(alignForward(usize, 1, 2) == 2); 4830 try testing.expect(alignForward(usize, 2, 2) == 2); 4831 try testing.expect(alignForward(usize, 3, 2) == 4); 4832 try testing.expect(alignForward(usize, 4, 2) == 4); 4833 try testing.expect(alignForward(usize, 7, 8) == 8); 4834 try testing.expect(alignForward(usize, 8, 8) == 8); 4835 try testing.expect(alignForward(usize, 9, 8) == 16); 4836 try testing.expect(alignForward(usize, 15, 8) == 16); 4837 try testing.expect(alignForward(usize, 16, 8) == 16); 4838 try testing.expect(alignForward(usize, 17, 8) == 24); 4839 } 4840 4841 /// Round an address down to the previous (or current) aligned address. 4842 /// Unlike `alignBackward`, `alignment` can be any positive number, not just a power of 2. 4843 pub fn alignBackwardAnyAlign(comptime T: type, addr: T, alignment: T) T { 4844 if (isValidAlignGeneric(T, alignment)) 4845 return alignBackward(T, addr, alignment); 4846 assert(alignment != 0); 4847 return addr - @mod(addr, alignment); 4848 } 4849 4850 /// Round an address down to the previous (or current) aligned address. 4851 /// The alignment must be a power of 2 and greater than 0. 4852 pub fn alignBackward(comptime T: type, addr: T, alignment: T) T { 4853 assert(isValidAlignGeneric(T, alignment)); 4854 // 000010000 // example alignment 4855 // 000001111 // subtract 1 4856 // 111110000 // binary not 4857 return addr & ~(alignment - 1); 4858 } 4859 4860 /// Returns whether `alignment` is a valid alignment, meaning it is 4861 /// a positive power of 2. 4862 pub fn isValidAlign(alignment: usize) bool { 4863 return isValidAlignGeneric(usize, alignment); 4864 } 4865 4866 /// Returns whether `alignment` is a valid alignment, meaning it is 4867 /// a positive power of 2. 4868 pub fn isValidAlignGeneric(comptime T: type, alignment: T) bool { 4869 return alignment > 0 and std.math.isPowerOfTwo(alignment); 4870 } 4871 4872 /// Returns true if i is aligned to the given alignment. 4873 /// Works with any positive alignment value, not just powers of 2. 4874 /// For power-of-2 alignments, `isAligned` is more efficient. 4875 pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { 4876 if (isValidAlign(alignment)) 4877 return isAligned(i, alignment); 4878 assert(alignment != 0); 4879 return 0 == @mod(i, alignment); 4880 } 4881 4882 /// Returns true if addr is aligned to 2^log2_alignment. 4883 /// More efficient than `isAligned` when alignment is known to be a power of 2. 4884 /// log2_alignment must be < @bitSizeOf(usize). 4885 pub fn isAlignedLog2(addr: usize, log2_alignment: u8) bool { 4886 return @ctz(addr) >= log2_alignment; 4887 } 4888 4889 /// Given an address and an alignment, return true if the address is a multiple of the alignment 4890 /// The alignment must be a power of 2 and greater than 0. 4891 pub fn isAligned(addr: usize, alignment: usize) bool { 4892 return isAlignedGeneric(u64, addr, alignment); 4893 } 4894 4895 /// Generic version of `isAligned` that works with any integer type. 4896 /// Returns true if addr is aligned to the given alignment. 4897 /// Alignment must be a power of 2 and greater than 0. 4898 pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool { 4899 return alignBackward(T, addr, alignment) == addr; 4900 } 4901 4902 test isAligned { 4903 try testing.expect(isAligned(0, 4)); 4904 try testing.expect(isAligned(1, 1)); 4905 try testing.expect(isAligned(2, 1)); 4906 try testing.expect(isAligned(2, 2)); 4907 try testing.expect(!isAligned(2, 4)); 4908 try testing.expect(isAligned(3, 1)); 4909 try testing.expect(!isAligned(3, 2)); 4910 try testing.expect(!isAligned(3, 4)); 4911 try testing.expect(isAligned(4, 4)); 4912 try testing.expect(isAligned(4, 2)); 4913 try testing.expect(isAligned(4, 1)); 4914 try testing.expect(!isAligned(4, 8)); 4915 try testing.expect(!isAligned(4, 16)); 4916 } 4917 4918 test "freeing empty string with null-terminated sentinel" { 4919 const empty_string = try testing.allocator.dupeZ(u8, ""); 4920 testing.allocator.free(empty_string); 4921 } 4922 4923 /// Returns a slice with the given new alignment, 4924 /// all other pointer attributes copied from `AttributeSource`. 4925 fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: usize) type { 4926 const ptr = @typeInfo(AttributeSource).pointer; 4927 return @Pointer(.slice, .{ 4928 .@"const" = ptr.is_const, 4929 .@"volatile" = ptr.is_volatile, 4930 .@"allowzero" = ptr.is_allowzero, 4931 .@"align" = new_alignment, 4932 .@"addrspace" = ptr.address_space, 4933 }, ptr.child, null); 4934 } 4935 4936 /// Returns the largest slice in the given bytes that conforms to the new alignment, 4937 /// or `null` if the given bytes contain no conforming address. 4938 pub fn alignInBytes(bytes: []u8, comptime new_alignment: usize) ?[]align(new_alignment) u8 { 4939 const begin_address = @intFromPtr(bytes.ptr); 4940 const end_address = begin_address + bytes.len; 4941 4942 const begin_address_aligned = mem.alignForward(usize, begin_address, new_alignment); 4943 const new_length = std.math.sub(usize, end_address, begin_address_aligned) catch |e| switch (e) { 4944 error.Overflow => return null, 4945 }; 4946 const alignment_offset = begin_address_aligned - begin_address; 4947 return @alignCast(bytes[alignment_offset .. alignment_offset + new_length]); 4948 } 4949 4950 /// Returns the largest sub-slice within the given slice that conforms to the new alignment, 4951 /// or `null` if the given slice contains no conforming address. 4952 pub fn alignInSlice(slice: anytype, comptime new_alignment: usize) ?AlignedSlice(@TypeOf(slice), new_alignment) { 4953 const bytes = sliceAsBytes(slice); 4954 const aligned_bytes = alignInBytes(bytes, new_alignment) orelse return null; 4955 4956 const Element = @TypeOf(slice[0]); 4957 const slice_length_bytes = aligned_bytes.len - (aligned_bytes.len % @sizeOf(Element)); 4958 const aligned_slice = bytesAsSlice(Element, aligned_bytes[0..slice_length_bytes]); 4959 return @alignCast(aligned_slice); 4960 } 4961 4962 test "read/write(Var)PackedInt" { 4963 switch (builtin.cpu.arch) { 4964 // This test generates too much code to execute on WASI. 4965 // LLVM backend fails with "too many locals: locals exceed maximum" 4966 .wasm32, .wasm64 => return error.SkipZigTest, 4967 else => {}, 4968 } 4969 4970 const foreign_endian: Endian = if (native_endian == .big) .little else .big; 4971 const expect = std.testing.expect; 4972 var prng = std.Random.DefaultPrng.init(1234); 4973 const random = prng.random(); 4974 4975 @setEvalBranchQuota(10_000); 4976 inline for ([_]type{ u8, u16, u32, u128 }) |BackingType| { 4977 for ([_]BackingType{ 4978 @as(BackingType, 0), // all zeros 4979 -%@as(BackingType, 1), // all ones 4980 random.int(BackingType), // random 4981 random.int(BackingType), // random 4982 random.int(BackingType), // random 4983 }) |init_value| { 4984 const uTs = [_]type{ u1, u3, u7, u8, u9, u10, u15, u16, u86 }; 4985 const iTs = [_]type{ i1, i3, i7, i8, i9, i10, i15, i16, i86 }; 4986 inline for (uTs ++ iTs) |PackedType| { 4987 if (@bitSizeOf(PackedType) > @bitSizeOf(BackingType)) 4988 continue; 4989 4990 const iPackedType = std.meta.Int(.signed, @bitSizeOf(PackedType)); 4991 const uPackedType = std.meta.Int(.unsigned, @bitSizeOf(PackedType)); 4992 const Log2T = std.math.Log2Int(BackingType); 4993 4994 const offset_at_end = @bitSizeOf(BackingType) - @bitSizeOf(PackedType); 4995 for ([_]usize{ 0, 1, 7, 8, 9, 10, 15, 16, 86, offset_at_end }) |offset| { 4996 if (offset > offset_at_end or offset == @bitSizeOf(BackingType)) 4997 continue; 4998 4999 for ([_]PackedType{ 5000 ~@as(PackedType, 0), // all ones: -1 iN / maxInt uN 5001 @as(PackedType, 0), // all zeros: 0 iN / 0 uN 5002 @as(PackedType, @bitCast(@as(iPackedType, math.maxInt(iPackedType)))), // maxInt iN 5003 @as(PackedType, @bitCast(@as(iPackedType, math.minInt(iPackedType)))), // maxInt iN 5004 random.int(PackedType), // random 5005 random.int(PackedType), // random 5006 }) |write_value| { 5007 { // Fixed-size Read/Write (Native-endian) 5008 5009 // Initialize Value 5010 var value: BackingType = init_value; 5011 5012 // Read 5013 const read_value1 = readPackedInt(PackedType, asBytes(&value), offset, native_endian); 5014 try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset))))))); 5015 5016 // Write 5017 writePackedInt(PackedType, asBytes(&value), offset, write_value, native_endian); 5018 try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset))))))); 5019 5020 // Read again 5021 const read_value2 = readPackedInt(PackedType, asBytes(&value), offset, native_endian); 5022 try expect(read_value2 == write_value); 5023 5024 // Verify bits outside of the target integer are unmodified 5025 const diff_bits = init_value ^ value; 5026 if (offset != offset_at_end) 5027 try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0); 5028 if (offset != 0) 5029 try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0); 5030 } 5031 5032 { // Fixed-size Read/Write (Foreign-endian) 5033 5034 // Initialize Value 5035 var value: BackingType = @byteSwap(init_value); 5036 5037 // Read 5038 const read_value1 = readPackedInt(PackedType, asBytes(&value), offset, foreign_endian); 5039 try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset))))))); 5040 5041 // Write 5042 writePackedInt(PackedType, asBytes(&value), offset, write_value, foreign_endian); 5043 try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset))))))); 5044 5045 // Read again 5046 const read_value2 = readPackedInt(PackedType, asBytes(&value), offset, foreign_endian); 5047 try expect(read_value2 == write_value); 5048 5049 // Verify bits outside of the target integer are unmodified 5050 const diff_bits = init_value ^ @byteSwap(value); 5051 if (offset != offset_at_end) 5052 try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0); 5053 if (offset != 0) 5054 try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0); 5055 } 5056 5057 const signedness = @typeInfo(PackedType).int.signedness; 5058 const NextPowerOfTwoInt = std.meta.Int(signedness, try comptime std.math.ceilPowerOfTwo(u16, @bitSizeOf(PackedType))); 5059 const ui64 = std.meta.Int(signedness, 64); 5060 inline for ([_]type{ PackedType, NextPowerOfTwoInt, ui64 }) |U| { 5061 { // Variable-size Read/Write (Native-endian) 5062 5063 if (@bitSizeOf(U) < @bitSizeOf(PackedType)) 5064 continue; 5065 5066 // Initialize Value 5067 var value: BackingType = init_value; 5068 5069 // Read 5070 const read_value1 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), native_endian, signedness); 5071 try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset))))))); 5072 5073 // Write 5074 writeVarPackedInt(asBytes(&value), offset, @bitSizeOf(PackedType), @as(U, write_value), native_endian); 5075 try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset))))))); 5076 5077 // Read again 5078 const read_value2 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), native_endian, signedness); 5079 try expect(read_value2 == write_value); 5080 5081 // Verify bits outside of the target integer are unmodified 5082 const diff_bits = init_value ^ value; 5083 if (offset != offset_at_end) 5084 try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0); 5085 if (offset != 0) 5086 try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0); 5087 } 5088 5089 { // Variable-size Read/Write (Foreign-endian) 5090 5091 if (@bitSizeOf(U) < @bitSizeOf(PackedType)) 5092 continue; 5093 5094 // Initialize Value 5095 var value: BackingType = @byteSwap(init_value); 5096 5097 // Read 5098 const read_value1 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), foreign_endian, signedness); 5099 try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset))))))); 5100 5101 // Write 5102 writeVarPackedInt(asBytes(&value), offset, @bitSizeOf(PackedType), @as(U, write_value), foreign_endian); 5103 try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset))))))); 5104 5105 // Read again 5106 const read_value2 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), foreign_endian, signedness); 5107 try expect(read_value2 == write_value); 5108 5109 // Verify bits outside of the target integer are unmodified 5110 const diff_bits = init_value ^ @byteSwap(value); 5111 if (offset != offset_at_end) 5112 try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0); 5113 if (offset != 0) 5114 try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0); 5115 } 5116 } 5117 } 5118 } 5119 } 5120 } 5121 } 5122 }