Commit 0972b2a8fe

Bas van den Berg <BarabasGitHub@users.noreply.github.com>
2020-02-24 20:25:59
implement zeroes for non extern structs and native types
1 parent dca19b6
Changed files (1)
lib
lib/std/mem.zig
@@ -279,15 +279,69 @@ pub fn set(comptime T: type, dest: []T, value: T) void {
 /// Zero initializes the type.
 /// This can be used to zero initialize a C-struct.
 pub fn zeroes(comptime T: type) T {
-    if (@sizeOf(T) == 0) return T{};
-
-    if (comptime meta.containerLayout(T) != .Extern) {
-        @compileError("TODO: Currently this only works for extern types");
+    switch (@typeInfo(T)) {
+        .ComptimeInt, .Int, .ComptimeFloat, .Float => {
+            return @as(T, 0);
+        },
+        .Enum, .EnumLiteral => {
+            return @intToEnum(T, 0);
+        },
+        .Void => {
+            return {};
+        },
+        .Bool => {
+            return false;
+        },
+        .Optional, .Null => {
+            return null;
+        },
+        .Struct => {
+            if (@sizeOf(T) == 0) return T{};
+            if (comptime meta.containerLayout(T) == .Extern) {
+                var item: T = undefined;
+                @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T));
+                return item;
+            } else {
+                var structure: T = undefined;
+                comptime var field_i = 0;
+                inline while (field_i < @memberCount(T)) : (field_i += 1) {
+                    @field(structure, @memberName(T, field_i)) = zeroes(@TypeOf(@field(structure, @memberName(T, field_i))));
+                }
+                return structure;
+            }
+        },
+        .Pointer => |ptr_info| {
+            if (ptr_info.is_allowzero) {
+                return null;
+            } else {
+                switch (ptr_info.size) {
+                    .Slice => {
+                        return &[_]ptr_info.child{};
+                    },
+                    .One, .Many, .C => {
+                        @compileError("Can't set a non nullable pointer to zero.");
+                    },
+                }
+            }
+        },
+        .Array => |info| {
+            var array: T = undefined;
+            for (array) |*element| {
+                element.* = zeroes(info.child);
+            }
+            return array;
+        },
+        .Vector => |info| {
+            var vector: T = undefined;
+            for (vector) |*element| {
+                *element = zeroes(info.child);
+            }
+            return vector;
+        },
+        .ErrorUnion, .ErrorSet, .Union, .Fn, .BoundFn, .Type, .NoReturn, .Undefined, .Opaque, .Frame, .AnyFrame,  => {
+            @compileError("Can't set a "++ @typeName(T) ++" to zero.");
+        },
     }
-
-    var item: T = undefined;
-    @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T));
-    return item;
 }
 
 test "mem.zeroes" {
@@ -301,6 +355,60 @@ test "mem.zeroes" {
 
     testing.expect(a.x == 0);
     testing.expect(a.y == 10);
+
+    const ZigStruct = struct {
+        const IntegralTypes = struct {
+            integer_0: i0,
+            integer_8: i8,
+            integer_16: i16,
+            integer_32: i32,
+            integer_64: i64,
+            integer_128: i128,
+            unsigned_0: u0,
+            unsigned_8: u8,
+            unsigned_16: u16,
+            unsigned_32: u32,
+            unsigned_64: u64,
+            unsigned_128: u128,
+
+            float_32: f32,
+            float_64: f64,
+        };
+
+        integral_types: IntegralTypes,
+
+        const Pointers = struct {
+            optional: ?*u8,
+            slice: []u8,
+        };
+        pointers: Pointers,
+
+        array: [2]u32,
+        optional_int: ?u8,
+        empty: void,
+    };
+
+    const b = zeroes(ZigStruct);
+    testing.expectEqual(@as(i8, 0), b.integral_types.integer_0);
+    testing.expectEqual(@as(i8, 0), b.integral_types.integer_8);
+    testing.expectEqual(@as(i16, 0), b.integral_types.integer_16);
+    testing.expectEqual(@as(i32, 0), b.integral_types.integer_32);
+    testing.expectEqual(@as(i64, 0), b.integral_types.integer_64);
+    testing.expectEqual(@as(i128, 0), b.integral_types.integer_128);
+    testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_0);
+    testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_8);
+    testing.expectEqual(@as(u16, 0), b.integral_types.unsigned_16);
+    testing.expectEqual(@as(u32, 0), b.integral_types.unsigned_32);
+    testing.expectEqual(@as(u64, 0), b.integral_types.unsigned_64);
+    testing.expectEqual(@as(u128, 0), b.integral_types.unsigned_128);
+    testing.expectEqual(@as(f32, 0), b.integral_types.float_32);
+    testing.expectEqual(@as(f64, 0), b.integral_types.float_64);
+    testing.expectEqual(@as(?*u8, null), b.pointers.optional);
+    testing.expectEqual(@as([]u8, &[_]u8{}), b.pointers.slice);
+    for (b.array) |e| {
+        testing.expectEqual(@as(u32, 0), e);
+    }
+    testing.expectEqual(@as(?u8, null), b.optional_int);
 }
 
 pub fn secureZero(comptime T: type, s: []T) void {