DzLox

:)
git clone https://git.sr.ht/~ashymad/DzLox
Log | Files | Refs | Submodules | LICENSE

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 }