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