lib::nanbox.zig (4861B)
1 const std = @import("std"); 2 const utils = @import("lib::utils.zig"); 3 4 const Flag = struct { 5 flag: u64, 6 mask: u64, 7 true: bool = true, 8 shift: u6 = 0, 9 10 pub fn check(self: *const @This(), val: u64) bool { 11 return self.true == ((val & self.mask) == self.flag); 12 } 13 14 pub fn decode(self: *const @This(), val: u64) u64 { 15 return if (self.true) (val & ~self.mask) << self.shift else val; 16 } 17 18 pub fn encode(self: *const @This(), val: u64) u64 { 19 return if (self.true) ((val >> self.shift) & ~self.mask) | self.flag else val; 20 } 21 }; 22 23 pub fn Packer(comptime MAX: u6) type { 24 const MAXU64: u64 = std.math.maxInt(u64); 25 const MASK: u64 = ~(MAXU64 << MAX); 26 27 return struct { 28 matrix: [MAX]?u64, 29 30 pub fn init() @This() { 31 return .{ .matrix = @splat(0) }; 32 } 33 34 fn mask(idx: u6) u64 { 35 return MASK & (MAXU64 << idx); 36 } 37 38 fn inc(val: u64, idx: u6) ?u64 { 39 return if ((val & mask(idx)) == mask(idx)) 40 null 41 else 42 val + (@as(u64, 1) << idx); 43 } 44 45 pub fn next(self: *@This(), idx: u6) ?u64 { 46 if (self.matrix[idx]) |ret| { 47 var i: u6 = idx; 48 while (i < MAX) : (i += 1) { 49 if (self.matrix[i]) |val| { 50 if (val == (ret & mask(i))) 51 self.matrix[i] = inc(val, i) 52 else 53 break; 54 } else break; 55 } 56 57 i = idx; 58 while (i > 0) { 59 i -= 1; 60 if (self.matrix[i]) |val| { 61 if (val == (ret & mask(i))) 62 self.matrix[i] = self.matrix[idx] 63 else 64 break; 65 } else @panic("The Matrix has been breached!"); 66 } 67 68 return ret; 69 } 70 return null; 71 } 72 }; 73 } 74 75 pub fn NaNBox(comptime fields: anytype) type { 76 const Fields = @TypeOf(fields); 77 78 if (!utils.is_type(Fields, "struct")) 79 @compileError("The fields have to be a struct of types"); 80 81 const flags = blk: { 82 const info = @typeInfo(Fields).@"struct"; 83 84 var flags: [info.field_names.len]Flag = undefined; 85 86 var float = false; 87 88 const QNAN: u64 = 0x7ffc000000000000; 89 const SIGN: u64 = 0x8000000000000000; 90 91 const Pack = Packer(51); 92 var packer = Pack.init(); 93 94 inline for (info.field_names, info.field_types, &flags) |name, ftype, *flag| { 95 if (ftype != type) 96 @compileError("The fields have to be types, but field " ++ name ++ " is " ++ @typeName(ftype)); 97 98 const field = @field(fields, name); 99 const is_ptr = utils.is_type(field, "pointer"); 100 101 if (field == f64) { 102 if (float) 103 @compileError("There can only be one f64 field"); 104 105 flag.* = .{ .flag = QNAN, .mask = QNAN, .true = false }; 106 float = true; 107 } else if (is_ptr or @bitSizeOf(field) < 51) { 108 const bitsz: u6 = if (is_ptr) 45 else @bitSizeOf(field); 109 110 if (packer.next(bitsz)) |pack| { 111 flag.* = Flag{ 112 .flag = (SIGN & (pack << 13)) | QNAN | pack, 113 .mask = SIGN | QNAN | Pack.mask(bitsz), 114 .shift = if (is_ptr) 3 else 0, 115 }; 116 } else { 117 @compileError("Too many fields to fit"); 118 } 119 } else { 120 @compileError("The field " ++ name ++ " has more than 50 bits"); 121 } 122 } 123 124 if (!float) 125 @compileError("At least one f64 field is required"); 126 127 break :blk flags; 128 }; 129 130 return struct { 131 const Self = @This(); 132 133 pub const Type = utils.enumFromStruct(Fields, usize); 134 pub const Error = error{WrongTag}; 135 136 value: u64, 137 138 pub fn flag(comptime tag: Type) Flag { 139 return flags[@backingInt(tag)]; 140 } 141 142 pub fn typeof(comptime tag: Type) type { 143 return @field(fields, @tagName(tag)); 144 } 145 146 pub fn of(comptime tag: Type, val: typeof(tag)) Self { 147 return Self{ 148 .value = flag(tag).encode(utils.typepun(u64, val)), 149 }; 150 } 151 152 pub fn to(self: *const Self, comptime tag: Type) !typeof(tag) { 153 return if (self.is(tag)) 154 utils.typepun(typeof(tag), flag(tag).decode(self.value)) 155 else 156 Error.WrongTag; 157 } 158 159 pub fn is(self: *const Self, comptime tag: Type) bool { 160 return flag(tag).check(self.value); 161 } 162 }; 163 }