1 /// Authors: Chance Snow
2 /// Copyright: Copyright © 2024 Chance Snow. All rights reserved.
3 /// License: MIT License
4 module dbpf.types;
5 
6 import std.conv : castFrom, to;
7 
8 /// A fixed-sized string.
9 /// Params:
10 /// Size: Size of string, in bytes.
11 struct str(int Size) {
12 align(1):
13   private ubyte[Size] payload;
14   alias value this;
15 
16   ///
17   this(string str) {
18     value = str;
19   }
20 
21   string value() inout @property {
22     import std.string : fromStringz;
23     return castFrom!(inout ubyte[4]).to!(inout char*)(payload).fromStringz.to!string;
24   }
25 
26   string value(string str) @property {
27     import std.algorithm : copy;
28     import std.conv : text;
29     import std.string : toStringz;
30 
31     assert(str.length <= payload.sizeof, "Value is too long.");
32     // Ensure the null-terminator is leftover
33     assert(str.toStringz[0..str.length].copy(payload[]).length == 1);
34     return str;
35   }
36 }
37 
38 static assert(str!4.alignof == 1);
39 static assert(str!4.sizeof == 4);
40 
41 unittest {
42   import std.algorithm : equal;
43 
44   str!4 id = "foo";
45   assert(id.length == 3);
46   assert(id.value.equal("foo"));
47 }
48 
49 /// A 24-bit signed integer.
50 /// See_Also: <a href="https://forum.dlang.org/thread/sarwanlrindyawtztlgh@forum.dlang.org"24-bit int</a> (D Forum)
51 struct int24 {
52 align(1):
53   private ubyte[3] payload;
54   alias value this;
55 
56   ///
57   this(int x) {
58     value = x;
59   }
60 
61   ///
62   int value() inout @property {
63     int val = *cast(int*)&payload & 0xFFFFFF;
64     if (val & 0x800000)
65       val |= 0xFF000000;
66     return val;
67   }
68 
69   ///
70   int value(int x) @property {
71     version(BigEndian) payload = (cast(ubyte*)&x)[1..4];
72     else payload = (cast(ubyte*)&x)[0 .. 3];
73     return value;
74   }
75 
76   ///
77   auto opUnary(string op)() {
78     static if (op == "++")
79       value = value + 1;
80     else static if (op == "--")
81       value = value - 1;
82     else static if (op == "+")
83       return value;
84     else static if (op == "-")
85       return -value;
86     else static if (op == "~")
87       return ~value;
88     else
89       static assert(0, "Unary operator '" ~ op ~ "' is not supported by `int24`.");
90   }
91 
92   ///
93   auto opOpAssign(string op)(int x) {
94     static const error = "Binary operator '" ~ op ~ "' is not supported by `int24`.";
95     static assert(__traits(compiles, { mixin("value = value " ~ op ~ " x;"); }), error);
96 
97     mixin("value = value " ~ op ~ " x;");
98     return this;
99   }
100 }
101 
102 static assert(int24.sizeof == 3);
103 static assert(int24.alignof == 1);
104 
105 unittest {
106   int24[3] a;
107   assert(a.sizeof == 9);
108 
109   // Max value
110   a[1] = 8_388_607;
111   assert(a[1] == 8_388_607);
112   // Test for buffer overflow:
113   assert(a[0] == 0);
114   assert(a[2] == 0);
115 
116   // Overflow
117   a[1] = 8_388_608;
118   assert(a[1] == -8_388_608);
119   // Test for buffer overflow:
120   assert(a[0] == 0);
121   assert(a[2] == 0);
122 
123   // Negative value
124   a[1] = -1;
125   assert(a[1] == -1);
126   // Test for buffer overflow:
127   assert(a[0] == 0);
128   assert(a[2] == 0);
129 
130   // Unary operators
131   a[1] = 0;
132   assert(~a[1] == -1);
133   a[1]--;
134   assert(a[1] == -1);
135   assert(-a[1] == 1);
136   assert(+a[1] == -1);
137   a[1]++;
138   assert(a[1] == 0);
139 
140   // Binary operators
141   a[1] = 0;
142   a[1] = a[1] + 1;
143   assert(a[1] == 1);
144   a[1] += 1;
145   assert(a[1] == 2);
146   a[1] = a[1] - 1;
147   assert(a[1] == 1);
148   a[1] -= 1;
149   assert(a[1] == 0);
150 
151   a[1] = 3;
152   a[1] = a[1] * 2;
153   assert(a[1] == 6);
154   a[1] = a[1] / 2;
155   assert(a[1] == 3);
156   a[1] *= 2;
157   assert(a[1] == 6);
158   a[1] /= 2;
159   assert(a[1] == 3);
160   a[1] = a[1] << 1;
161   assert(a[1] == 6);
162   a[1] <<= 1;
163   assert(a[1] == 12);
164   a[1] = a[1] >> 1;
165   assert(a[1] == 6);
166   a[1] >>= 1;
167   assert(a[1] == 3);
168 
169   a[1] |= 4;
170   assert(a[1] == 7);
171   a[1] &= 5;
172   assert(a[1] == 5);
173   a[1] = a[1] | 2;
174   assert(a[1] == 7);
175   a[1] = a[1] & 3;
176   assert(a[1] == 3);
177 }