1 /// 2 module sbin.type; 3 4 package import std.traits; 5 package import std.range; 6 7 import std.bitmanip : nativeToLittleEndian, littleEndianToNative; 8 9 import sbin.repr; 10 11 /// UDA for marking struct fields to be skipped. 12 enum sbinSkip; 13 14 /// variable length uint 15 struct vluint 16 { 17 ulong value; /// 18 /// 19 this()(ulong v) { value = v; } 20 alias value this; /// 21 } 22 23 /// variable length int 24 struct vlint 25 { 26 long value; /// 27 /// 28 this()(long v) { value = v; } 29 alias value this; /// 30 } 31 32 package alias pack = nativeToLittleEndian; 33 34 package auto unpack(T, size_t N)(ubyte[N] arr) 35 if (N == T.sizeof) 36 { 37 static if (T.sizeof == 1) return cast(T)arr[0]; 38 else return littleEndianToNative!T(arr); 39 } 40 41 version (Have_taggedalgebraic) package import taggedalgebraic; 42 version (Have_mir_core) package import mir.algebraic; 43 version (Have_sumtype) package import sumtype; 44 45 template isTagged(T) 46 { 47 version (Have_taggedalgebraic) 48 enum isTaggedAlgebraic = is(T == TaggedUnion!X, X) || is(T == TaggedAlgebraic!Y, Y); 49 else 50 enum isTaggedAlgebraic = false; 51 52 version (Have_mir_core) 53 enum isMirAlgebraic = isVariant!T; 54 else 55 enum isMirAlgebraic = false; 56 57 version (Have_sumtype) 58 enum isSumType = is(T == SumType!Args, Args...); 59 else 60 enum isSumType = false; 61 62 enum any = isTaggedAlgebraic || isMirAlgebraic || isSumType; 63 } 64 65 package(sbin) 66 template taggedMatch(handlers...) 67 { 68 auto taggedMatch(T)(auto ref T val) if (isTagged!(T).any) 69 { 70 static if (isTagged!(T).isSumType) 71 return sumtype.match!(handlers)(val); 72 else 73 static if (isTagged!(T).isTaggedAlgebraic) 74 { 75 static if (is(T == TaggedUnion!X, X)) 76 return taggedalgebraic.visit.visit!(handlers)(val); 77 else static if (is(T == TaggedAlgebraic!Y, Y)) 78 return taggedalgebraic.visit.visit!(handlers)(val.get!(TaggedUnion!Y)); 79 } 80 else 81 static if (isTagged!(T).isMirAlgebraic) 82 return mir.algebraic.match!(handlers)(val); 83 else 84 static assert(0, "??"); 85 } 86 } 87 88 package(sbin) 89 template TaggedTagType(T) if (isTagged!(T).any) 90 { 91 static if (isTagged!(T).isTaggedAlgebraic || isTagged!(T).isMirAlgebraic) 92 alias TaggedTagType = T.Kind; 93 else static if (isTagged!(T).isSumType) 94 { 95 import std : AliasSeq, Filter; 96 enum bool canHoldTag(X) = T.Types.length <= X.max; 97 alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong); 98 alias TaggedTagType = Filter!(canHoldTag, unsignedInts)[0]; 99 } 100 } 101 102 package(sbin) 103 auto getTaggedAllTags(T)() if (isTagged!(T).any) 104 { 105 import std : EnumMembers; 106 107 static if (isTagged!(T).isSumType) 108 { 109 alias TTT = TaggedTagType!T; 110 TTT[T.Types.length] ret; 111 foreach (i, ref v; ret) v = cast(TTT)i; 112 return ret; 113 } 114 else 115 static if (isTagged!(T).isTaggedAlgebraic || isTagged!(T).isMirAlgebraic) 116 return [EnumMembers!(T.Kind)]; 117 else 118 static assert(0, "??"); 119 } 120 121 package(sbin) 122 template TaggedTypeByTag(T, alias tag) if (isTagged!(T).any) 123 { 124 static if (isTagged!(T).isTaggedAlgebraic) 125 alias r = TypeOf!tag; 126 else 127 static if (isTagged!(T).isMirAlgebraic) 128 { 129 ptrdiff_t indexOf(T.Kind[] lst, T.Kind kind) 130 { 131 foreach (i, v; lst) if (v == kind) return i; 132 assert(0); 133 } 134 alias r = T.AllowedTypes[indexOf(getTaggedAllTags!T, tag)]; 135 } 136 else 137 static if (isTagged!(T).isSumType) 138 alias r = T.Types[tag]; 139 140 alias TaggedTypeByTag = r; 141 } 142 143 package(sbin) 144 auto getTaggedTag(T)(in T val) if (isTagged!(T).any) 145 { 146 static if (isTagged!(T).isSumType) 147 return cast(TaggedTagType!(T))(val.typeIndex); 148 else 149 static if (isTagged!(T).isMirAlgebraic || isTagged!(T).isTaggedAlgebraic) 150 return val.kind; 151 else static assert(0, "unsupported " ~ T.stringof); 152 } 153 154 template isVoidArray(T) 155 { 156 static if( (is(T U == U[]) || is(T U == U[N], size_t N)) && 157 is(Unqual!U == void)) enum isVoidArray = true; 158 else enum isVoidArray = false; 159 } 160 161 unittest 162 { 163 assert (isVoidArray!(void[])); 164 assert (isVoidArray!(void[2])); 165 assert (isVoidArray!(const(void)[])); 166 assert (isVoidArray!(const(void[]))); 167 assert (isVoidArray!(const(void)[2])); 168 assert (isVoidArray!(const(void[2]))); 169 assert (isVoidArray!(immutable(void)[])); 170 assert (isVoidArray!(immutable(void[]))); 171 assert (isVoidArray!(immutable(void)[2])); 172 assert (isVoidArray!(immutable(void[2]))); 173 174 assert (!isVoidArray!(long[])); 175 assert (!isVoidArray!(long[2])); 176 assert (!isVoidArray!(byte[])); 177 assert (!isVoidArray!(byte[2])); 178 } 179 180 template EnumNumType(T) if (is(T == enum)) 181 { 182 enum C = [EnumMembers!T].length; 183 static if (C <= ubyte.max) alias EnumNumType = ubyte; 184 else static if (C <= ushort.max) alias EnumNumType = ushort; 185 else static if (C <= uint.max) alias EnumNumType = uint; 186 else alias EnumNumType = ulong; 187 } 188 189 unittest 190 { 191 enum E { e } 192 static assert(is(E == enum)); 193 static assert(is(EnumNumType!E == ubyte)); 194 } 195 196 unittest 197 { 198 enum Color 199 { 200 black = "#000000", 201 red = "#ff0000", 202 green = "#00ff00", 203 blue = "#0000ff", 204 white = "#ffffff" 205 } 206 207 static assert(is(EnumNumType!Color == ubyte)); 208 209 enum Level { low, medium, high } 210 211 static assert(is(EnumNumType!Level == ubyte)); 212 213 static string bigElems() pure 214 { 215 import std.format : formattedWrite; 216 import std.array : appender; 217 import std.range : put; 218 219 auto buf = appender!(char[]); 220 formattedWrite(buf, "enum Big { "); 221 foreach (i; 0 .. 300) 222 formattedWrite(buf, "e%d,", i); 223 buf.put(" }"); 224 return buf.data.idup; 225 } 226 227 mixin(bigElems()); 228 229 static assert(is(EnumNumType!Big == ushort)); 230 assert(is(EnumNumType!Big == ushort)); 231 } 232 233 /// 234 auto getEnumNum(T)(T val) @safe @nogc pure nothrow 235 if (is(T == enum)) 236 { 237 alias Ret = EnumNumType!T; 238 static foreach (Ret i; 0 .. [EnumMembers!T].length) 239 if ((EnumMembers!T)[i] == val) return i; 240 return Ret.max; 241 } 242 243 unittest 244 { 245 enum Foo { one, two } 246 assert (getEnumNum(Foo.two) == 1); 247 static assert(is(typeof(getEnumNum(Foo.two)) == ubyte)); 248 assert(is(typeof(getEnumNum(Foo.two)) == ubyte)); 249 } 250 251 /// 252 template hasCustomRepr(T, RH=EmptyReprHandler) 253 { 254 static if (hasMember!(T, "sbinCustomRepr") && hasMember!(T, "sbinFromCustomRepr")) 255 { 256 import sbin.serialize : sbinSerialize; 257 258 alias Repr = ReturnType!(() => T.init.sbinCustomRepr); 259 260 enum hasCustomRepr = is(typeof(sbinSerialize!RH(Repr.init))) && 261 is(typeof(((){ return T.sbinFromCustomRepr(Repr.init); })()) == Unqual!T); 262 } 263 else enum hasCustomRepr = false; 264 } 265 266 unittest 267 { 268 static class Foo { ulong id; } 269 static assert (hasCustomRepr!Foo == false); 270 } 271 272 unittest 273 { 274 static class Foo 275 { 276 ulong id; 277 ulong sbinCustomRepr() const @property { return id; } 278 } 279 static assert (hasCustomRepr!Foo == false); 280 assert (hasCustomRepr!Foo == false); 281 } 282 283 unittest 284 { 285 static class Foo 286 { 287 ulong id; 288 this(ulong v) { id = v; } 289 ulong sbinCustomRepr() const @property { return id; } 290 static Foo sbinFromCustomRepr(ulong v) { return new Foo(v); } 291 } 292 static assert (hasCustomRepr!Foo == true); 293 assert (hasCustomRepr!Foo == true); 294 auto foo = new Foo(12); 295 assert (foo.sbinCustomRepr == 12); 296 auto foo2 = Foo.sbinFromCustomRepr(foo.sbinCustomRepr); 297 assert (foo.id == foo2.id); 298 } 299 300 unittest 301 { 302 static class Bar { ulong id; this(ulong v) { id = v; } } 303 static class Foo 304 { 305 ulong id; 306 this(ulong v) { id = v; } 307 Bar sbinCustomRepr() const @property { return new Bar(id); } 308 static Foo sbinFromCustomRepr(Bar v) { return new Foo(v.id); } 309 } 310 static assert (hasCustomRepr!Foo == false); 311 } 312 313 unittest 314 { 315 static struct Bar { ulong id; } 316 static class Foo 317 { 318 ulong id; 319 this(ulong v) { id = v; } 320 Bar sbinCustomRepr() const @property { return Bar(id); } 321 static Foo sbinFromCustomRepr()(auto ref const Bar v) { return new Foo(v.id); } 322 } 323 import sbin.serialize : sbinSerialize; 324 static assert (is(typeof(sbinSerialize(Bar.init)))); 325 static assert (hasCustomRepr!Foo == true); 326 } 327 328 unittest 329 { 330 static class Foo 331 { 332 ulong id; 333 this(ulong v) { id = v; } 334 ulong sbinCustomRepr() const @property { return id; } 335 static Foo sbinFromCustomRepr(ulong v, int add) { return new Foo(v+add); } 336 } 337 static assert (hasCustomRepr!Foo == false); 338 } 339 340 unittest 341 { 342 static class Foo 343 { 344 ulong id; 345 this(ulong v) { id = v; } 346 ulong sbinCustomRepr() const @property { return id; } 347 static Foo sbinFromCustomRepr(ulong v, int add=1) { return new Foo(v+add); } 348 } 349 static assert (hasCustomRepr!Foo == true); 350 }