const expect = @import("std").testing.expect; // Arrays, slices and many pointers may be terminated by a value of their child type. // This is known as sentinel termination. These follow the syntax `[N:t]T`, `[:t]T`, and `[*:t]T`, // where `t` is a value of the child type `T`. // An example of a sentinel terminated array. The built-in `@ptrCast` is used to perform an unsafe type conversion. // This shows us that the last element of the array is followed by a 0 byte. test "sentinel termination" { const terminated = [3:0]u8{ 3, 2, 1 }; try expect(terminated.len == 3); try expect(@as(*const [4]u8, @ptrCast(&terminated))[3] == 0); } // The types of string literals is `*const [N:0]u8`, where N is the length of the string. // This allows string literals to coerce to sentinel terminated slices, and sentinel terminated many pointers. // Note: string literals are UTF-8 encoded. test "string literal" { try expect(@TypeOf("hello") == *const [5:0]u8); } // `[*:0]u8` and `[*:0]const u8` perfectly model C's strings. const eql = @import("std").mem.eql; test "C string" { const c_string: [*:0]const u8 = "hello"; var array: [5]u8 = undefined; var i: usize = 0; while (c_string[i] != 0) : (i += 1) { array[i] = c_string[i]; } // XXX: Maybe we should use `c_string[0..i]` instead of `c_string[0..5]`? // try expect(eql(u8, &array, c_string[0..i])); } // Sentinel terminated types coerce to their non-sentinel-terminated counterparts. test "coercion" { const a: [*:0]u8 = undefined; const b: [*]u8 = a; const c: [5:0]u8 = undefined; const d: [5]u8 = c; const e: [:0]f32 = undefined; const f: []f32 = e; _ = .{ b, d, f }; //ignore unused } // Sentinel terminated slicing is provided // which can be used to create a sentinel terminated slice with the syntax `x[n..m:t]`, // where `t` is the terminator value. // Doing this is an assertion from the programmer that the memory is terminated where it should be // - getting this wrong is detectable illegal behaviour. test "sentinel terminated slicing" { var x = [_:0]u8{255} ** 3; const y = x[0..3 :0]; _ = y; } // My tests here to understand the concept better const print = @import("std").debug.print; test "sentianel checking" { const x = [_:0]u8{ 'h', 'i', @as(u8, 0), 'w', 'o', 'r', 'l', 'd', @as(u8, 0) }; // To validate the memory is terminated where it should be, we can use the `x[n..m:t]` syntax, const y = x[0..2 :@as(u8, 0)]; // print("\n", .{}); // print("x: {any}\n", .{x}); // print("y: {any}\n", .{y}); try expect(x[2] == 0); try expect(y.len == 2); try expect(y[0] == 'h'); try expect(y[1] == 'i'); try expect(y[2] == 0); }