1 /****************************************************************************** 2 3 Templates to generate a a hash or a string that describes the binary layout 4 of a value type, fully recursing into aggregates. 5 6 The data layout identifier hash is the 64-bit Fnv1a hash value of a string 7 that is generated from a struct or union by concatenating the offsets 8 and types of each field in order of appearance, recursing into structs, 9 unions and function/delegate parameter lists and using the base type of 10 enums and typedefs. 11 12 The type identifier of a non-aggregate type is the `.stringof` of that type 13 (or its base if it is a `typedef` or `enum`). 14 15 Copyright: 16 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 17 All rights reserved. 18 19 License: 20 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 21 Alternatively, this file may be distributed under the terms of the Tango 22 3-Clause BSD License (see LICENSE_BSD.txt for details). 23 24 ******************************************************************************/ 25 26 module ocean.io.serialize.TypeId; 27 28 import ocean.io.digest.Fnv1: StaticFnv1a64, Fnv164Const; 29 import ocean.meta.types.Typedef; 30 import ocean.meta.traits.Basic /* : isTypedef */; 31 32 /// Usage example 33 unittest 34 { 35 static struct S 36 { 37 mixin(Typedef!(int, `Spam`)); 38 39 uint spam; 40 41 static struct T 42 { 43 enum Eggs : ushort 44 { 45 Ham = 7 46 } 47 48 Eggs eggs; // at offset 0 49 char[] str; // at offset 8 50 } 51 52 Spam x; // at offset 0 53 T[][5] y; // at offset 8 54 Spam delegate ( T ) dg; // at offset 88 55 T*[float function(Spam, T.Eggs)] a; // at offset 104 56 } 57 58 static immutable id = TypeId!(S); 59 static assert(id == 60 `struct{` ~ 61 `0LU` ~ `uint` ~ 62 `4LU` ~ `int` ~ 63 `8LU` ~ `struct{` ~ 64 `0LU` ~ `ushort` ~ 65 `8LU` ~ `char[]` ~ 66 `}[][5LU]` ~ 67 `88LU` ~ `intdelegate(` ~ 68 `struct{` ~ 69 `0LU` ~ `ushort` ~ 70 `8LU` ~ `char[]` ~ 71 `}` ~ 72 `)` ~ 73 `104LU` ~ `struct{` ~ 74 `0LU` ~ `ushort` ~ 75 `8LU` ~ `char[]` ~ 76 `}*[floatfunction(intushort)]` ~ 77 `}`); 78 79 static immutable hash = TypeHash!(S); 80 static assert(hash == 0x3ff282c0d315761b); 81 } 82 83 84 /****************************************************************************** 85 86 Evaluates to the type identifier of T, fully recursing into structs, unions 87 and function/delegate parameter lists. T may be or contain any type except 88 a class or interface. 89 90 ******************************************************************************/ 91 92 template TypeId ( T ) 93 { 94 static if (is (T == struct) && !isTypedef!(T)) 95 { 96 static immutable TypeId = "struct{" ~ AggregateId!(CheckedBaseType!(T)) ~ "}"; 97 } 98 else static if (is (T == union)) 99 { 100 static immutable TypeId = "union{" ~ AggregateId!(CheckedBaseType!(T)) ~ "}"; 101 } 102 else static if (is (T Base : Base[])) 103 { 104 static if (is (T == Base[])) 105 { 106 static immutable TypeId = TypeId!(Base) ~ "[]"; 107 } 108 else 109 { 110 static immutable TypeId = TypeId!(Base) ~ "[" ~ T.length.stringof ~ "]"; 111 } 112 } 113 else static if (is (T Base == Base*)) 114 { 115 static if (is (Base Args == function) && is (Base R == return)) 116 { 117 static immutable TypeId = TypeId!(R) ~ "function(" ~ TupleId!(Args) ~ ")"; 118 } 119 else 120 { 121 static immutable TypeId = TypeId!(Base) ~ "*"; 122 } 123 } 124 else static if (is (T Func == delegate) && 125 is (Func Args == function) && is (Func R == return)) 126 { 127 static immutable TypeId = TypeId!(R) ~ "delegate(" ~ TupleId!(Args) ~ ")"; 128 } 129 else static if (is (typeof (T.init.values[0]) V) && 130 is (typeof (T.init.keys[0]) K) && 131 is (V[K] == T)) 132 { 133 static immutable TypeId = TypeId!(V) ~ "[" ~ TypeId!(K) ~ "]"; 134 } 135 else 136 { 137 static immutable TypeId = CheckedBaseType!(T).stringof; 138 } 139 } 140 141 unittest 142 { 143 static struct Sample 144 { 145 int[4] arr; 146 int a; 147 double b; 148 char* c; 149 } 150 151 152 static immutable x = TypeId!(Sample); 153 static immutable ExpectedSampleStr = `struct{0LUint[4LU]16LUint24LUdouble32LUchar*}`; 154 static assert(x == ExpectedSampleStr); 155 156 // This looks like a bug 157 mixin(Typedef!(Sample, `NestedTypedef`)); 158 static assert (TypeId!(NestedTypedef) == `Sample`); 159 160 static struct Bar { NestedTypedef f; } 161 static assert(TypeId!(Bar) == `struct{0LUSample}`); 162 163 union Foo { char* ptr; ulong val; } 164 static assert(TypeId!(Foo) == `union{0LUchar*0LUulong}`); 165 166 interface IFoo {} 167 mixin(Typedef!(IFoo, `DasInterface`)); 168 static assert (!is(typeof(TypeId!(IFoo)))); 169 static assert (!is(typeof(TypeId!(DasInterface)))); 170 171 mixin(Typedef!(Object, `Klass`)); 172 static assert (!is(typeof(TypeId!(Object)))); 173 static assert (!is(typeof(TypeId!(Klass)))); 174 } 175 176 /****************************************************************************** 177 178 Evaluates to the type hash of T, which is the 64-bit Fnv1a hash of the 179 string that would be generated by TypeId!(T). 180 181 ******************************************************************************/ 182 183 template TypeHash ( T ) 184 { 185 static immutable TypeHash = TypeHash!(Fnv164Const.INIT, T); 186 } 187 188 unittest 189 { 190 static struct Sample 191 { 192 int[4] arr; 193 int a; 194 double b; 195 char* c; 196 } 197 198 static immutable hash = TypeHash!(Sample); 199 static immutable ExpectedSampleHash = 0x25E3D303374B7838; 200 static assert(hash == ExpectedSampleHash); 201 202 // This looks like a bug 203 mixin(Typedef!(Sample, `NestedTypedef`)); 204 static assert (TypeHash!(NestedTypedef) == StaticFnv1a64!(`Sample`)); 205 206 static struct Bar { NestedTypedef f; } 207 static assert(TypeHash!(Bar) == 0xB3F1A91424ABC725); 208 209 union Foo { char* ptr; ulong val; } 210 static assert(TypeHash!(Foo) == 0xC4BD15CE20899C30); 211 212 interface IFoo {} 213 mixin(Typedef!(IFoo, `DasInterface`)); 214 static assert (!is(typeof(TypeHash!(IFoo)))); 215 static assert (!is(typeof(TypeHash!(DasInterface)))); 216 217 mixin(Typedef!(Object, `Klass`)); 218 static assert (!is(typeof(TypeHash!(Object)))); 219 static assert (!is(typeof(TypeHash!(Klass)))); 220 } 221 222 /****************************************************************************** 223 224 Evaluates to the type hash of T, which is the 64-bit Fnv1a hash of the 225 string that would be generated by TypeId!(T), using hash as initial hash 226 value so that TypeHash!(TypeHash!(A), B) evaluates to the 64-bit Fvn1a hash 227 value of TypeId!(A) ~ TypeId!(B). 228 229 ******************************************************************************/ 230 231 template TypeHash ( ulong hash, T ) 232 { 233 static if (is (T == struct) && !isTypedef!(T)) 234 { 235 static immutable TypeHash = StaticFnv1a64!(AggregateHash!(StaticFnv1a64!(hash, "struct{"), CheckedBaseType!(T)), "}"); 236 } 237 else static if (is (T == union)) 238 { 239 static immutable TypeHash = StaticFnv1a64!(AggregateHash!(StaticFnv1a64!(hash, "union{"), CheckedBaseType!(T)), "}"); 240 } 241 else static if (is (T Base : Base[])) 242 { 243 static if (is (T == Base[])) 244 { 245 static immutable TypeHash = StaticFnv1a64!(TypeHash!(hash, Base), "[]"); 246 } 247 else 248 { 249 static immutable TypeHash = StaticFnv1a64!(TypeHash!(hash, Base), "[" ~ T.length.stringof ~ "]"); 250 } 251 } 252 else static if (is (T Base == Base*)) 253 { 254 static if (is (Base Args == function) && is (Base R == return)) 255 { 256 static immutable TypeHash = StaticFnv1a64!(TupleHash!(StaticFnv1a64!(TypeHash!(hash, R), "function("), Args), ")"); 257 } 258 else 259 { 260 static immutable TypeHash = StaticFnv1a64!(TypeHash!(Base), "*"); 261 } 262 } 263 else static if (is (T Func == delegate) && 264 is (Func Args == function) && is (Func R == return)) 265 { 266 static immutable TypeHash = StaticFnv1a64!(TupleHash!(StaticFnv1a64!(TypeHash!(hash, R), "delegate("), Args), ")"); 267 } 268 else static if (is (typeof (T.init.values[0]) V) && 269 is (typeof (T.init.keys[0]) K) && 270 is (V[K] == T)) 271 { 272 static immutable TypeHash = StaticFnv1a64!(TypeHash!(StaticFnv1a64!(TypeHash!(hash, V), "["), K), "]"); 273 } 274 else 275 { 276 static immutable TypeHash = StaticFnv1a64!(hash, CheckedBaseType!(T).stringof); 277 } 278 } 279 280 /****************************************************************************** 281 282 Evaluates to the concatenated type identifiers of the fields of T, starting 283 with the n-th field. T must be a struct or union. 284 285 ******************************************************************************/ 286 287 template AggregateId ( T, size_t n = 0 ) 288 { 289 static if (n < T.tupleof.length) 290 { 291 static immutable AggregateId = T.tupleof[n].offsetof.stringof ~ TypeId!(typeof (T.tupleof[n])) ~ AggregateId!(T, n + 1); 292 } 293 else 294 { 295 static immutable AggregateId = ""; 296 } 297 } 298 299 /****************************************************************************** 300 301 Evaluates to the concatenated type identifiers of the elements of T. 302 303 ******************************************************************************/ 304 305 template TupleId ( T ... ) 306 { 307 static if (T.length) 308 { 309 static immutable TupleId = TypeId!(T[0]) ~ TupleId!(T[1 .. $]); 310 } 311 else 312 { 313 static immutable TupleId = ""; 314 } 315 } 316 317 /****************************************************************************** 318 319 Evaluates to the hash value of the type identifiers of the fields of T, 320 starting with the n-th field, using hash as initial hash value. T must be a 321 struct or union. 322 323 ******************************************************************************/ 324 325 template AggregateHash ( ulong hash, T, size_t n = 0 ) 326 { 327 static if (n < T.tupleof.length) 328 { 329 static immutable AggregateHash = AggregateHash!(TypeHash!(StaticFnv1a64!(hash, T.tupleof[n].offsetof.stringof), typeof (T.tupleof[n])), T, n + 1); 330 } 331 else 332 { 333 static immutable AggregateHash = hash; 334 } 335 } 336 337 /****************************************************************************** 338 339 Evaluates to the hash value of the concatenated type identifiers of the 340 elements of T, using hash as initial hash value. 341 342 ******************************************************************************/ 343 344 template TupleHash ( ulong hash, T ... ) 345 { 346 static if (T.length) 347 { 348 static immutable TupleHash = TupleHash!(TypeHash!(hash, T[0]), T[1 .. $]); 349 } 350 else 351 { 352 static immutable TupleHash = hash; 353 } 354 } 355 356 /****************************************************************************** 357 358 Aliases the base type of T, if T is a typedef or enum, or T otherwise. 359 Recurses into further typedefs/enums if required. 360 Veryfies that the aliased type is not a class, pointer, function, delegate 361 or associative array (a reference type other than a dynamic array). 362 363 ******************************************************************************/ 364 365 template CheckedBaseType ( T ) 366 { 367 alias BaseType!(T) CheckedBaseType; 368 369 static assert (!(is (CheckedBaseType == class) || 370 is (CheckedBaseType == interface)), TypeErrorMsg!(T, CheckedBaseType)); 371 } 372 373 /****************************************************************************** 374 375 Aliases the base type of T, if T is a typedef or enum, or T otherwise. 376 Recurses into further typedefs/enums if required. 377 378 ******************************************************************************/ 379 380 template BaseType ( T ) 381 { 382 static if (isTypedef!(T)) 383 alias TypedefBaseType!(T) BaseType; 384 else static if (is (T Base == enum)) 385 { 386 alias BaseType!(Base) BaseType; 387 } 388 else 389 { 390 alias T BaseType; 391 } 392 } 393 394 /****************************************************************************** 395 396 Evaluates to an error messsage used by CheckedBaseType. 397 398 ******************************************************************************/ 399 400 template TypeErrorMsg ( T, Base ) 401 { 402 static if (is (T == Base)) 403 { 404 static immutable TypeErrorMsg = Base.stringof ~ " is not supported because it is a class or interface"; 405 } 406 else 407 { 408 static immutable TypeErrorMsg = T.stringof ~ " is a typedef of " ~ Base.stringof ~ " which is not supported because it is a class or interface"; 409 } 410 }