1 module m3.m3;
2 
3 private:
4 
5 static import core.stdc.stdlib;
6 alias malloc = core.stdc.stdlib.malloc;
7 alias realloc = core.stdc.stdlib.realloc;
8 alias free = core.stdc.stdlib.free;
9 
10 static import std.traits;
11 alias isArray = std.traits.isArray;
12 
13 static import std.typecons;
14 alias TypeTuple = std.typecons.TypeTuple;
15 
16 enum CTOR = "__ctor";
17 enum DTOR = "__dtor";
18 
19 package:
20 
21 debug(m3) static import core.stdc.stdio;
22 debug(m3) alias printf = core.stdc.stdio.printf;
23 
24 public:
25 
26 /* Class and Struct */
27 
28 template DimOf(T) {
29     static assert(!is(T : V[K], V, K), "Cannot figure out the dimension of an assocative array.");
30 
31     static if (is(T : U[], U))
32         enum size_t DimOf = 1 + DimOf!(U);
33     else static if (is(T : U*, U))
34         enum size_t DimOf = 1 + DimOf!(U);
35     else
36         enum size_t DimOf = 0;
37 }
38 
39 unittest {
40     static assert(DimOf!(void) == 0);
41     static assert(DimOf!(void[]) == 1);
42     static assert(DimOf!(void[][]) == 2);
43 
44     static assert(DimOf!(int) == 0);
45     static assert(DimOf!(int[]) == 1);
46     static assert(DimOf!(int[][]) == 2);
47     static assert(DimOf!(int[][][]) == 3);
48 
49     static assert(DimOf!(string) == 1);
50     static assert(DimOf!(string[]) == 2);
51 
52     static assert(DimOf!(int*) == 1);
53     static assert(DimOf!(int**) == 2);
54     static assert(DimOf!(int[]**) == 3);
55     static assert(DimOf!(int[]*[]*) == 4);
56 }
57 
58 template BasicTypeOf(T) {
59     static assert(!is(T : V[K], V, K), "Cannot figure out the basic type of an assocative array.");
60 
61     static if (is(T : U[], U))
62         alias BasicTypeOf = BasicTypeOf!(U);
63     else
64         alias BasicTypeOf = T;
65 }
66 
67 unittest {
68     static assert(is(BasicTypeOf!(void) == void));
69     static assert(is(BasicTypeOf!(void[]) == void));
70     static assert(is(BasicTypeOf!(void[][]) == void));
71 
72     static assert(is(BasicTypeOf!(int) == int));
73     static assert(is(BasicTypeOf!(int[]) == int));
74     static assert(is(BasicTypeOf!(int[][]) == int));
75     static assert(is(BasicTypeOf!(int[][][]) == int));
76 
77     static assert(is(BasicTypeOf!(string) == immutable(char)));
78     static assert(is(BasicTypeOf!(string[]) == immutable(char)));
79 }
80 
81 template NextTypeOf(T) {
82     static assert(!is(T : V[K], V, K), "Cannot figure out the next type of an assocative array.");
83 
84     static if (is(T : U[], U))
85         alias NextTypeOf = U;
86     else
87         alias NextTypeOf = T;
88 }
89 
90 unittest {
91     static assert(is(NextTypeOf!(void) == void));
92     static assert(is(NextTypeOf!(void[]) == void));
93     static assert(is(NextTypeOf!(void[][]) == void[]));
94 
95     static assert(is(NextTypeOf!(int) == int));
96     static assert(is(NextTypeOf!(int[]) == int));
97     static assert(is(NextTypeOf!(int[][]) == int[]));
98     static assert(is(NextTypeOf!(int[][][]) == int[][]));
99 
100     static assert(is(NextTypeOf!(string) == immutable(char)));
101     static assert(is(NextTypeOf!(string[]) == string));
102 }
103 
104 template SizeOf(T) {
105     static assert(!is(T : V[K], V, K), "Cannot figure out the size of an assocative array.");
106 
107     static if (is(T == class))
108         enum size_t SizeOf = __traits(classInstanceSize, T);
109     else
110         enum size_t SizeOf = T.sizeof;
111 }
112 
113 unittest {
114     static assert(SizeOf!(void) == 1);
115     static assert(SizeOf!(void[]) == 8);
116     static assert(SizeOf!(void[][]) == 8);
117 
118     static assert(SizeOf!(int) == 4);
119     static assert(SizeOf!(int[]) == 8);
120     static assert(SizeOf!(int[][]) == 8);
121     static assert(SizeOf!(int[][][]) == 8);
122 
123     static assert(SizeOf!(string) == 8);
124     static assert(SizeOf!(string[]) == 8);
125 }
126 
127 enum TypeOfClass : ubyte {
128     AsClass,
129     AsVoid
130 }
131 
132 template TypeOf(T, TypeOfClass toc = TypeOfClass.AsClass) {
133     static assert(!is(T : V[K], V, K), "Cannot figure out the type of an assocative array.");
134 
135     static if (isArray!(T)) {
136         enum size_t DIM = DimOf!(T);
137         static assert(DIM < 5, "Too high dimension");
138 
139         alias Base = BasicTypeOf!(T);
140         static if (is(Base == class))
141             alias Bases = TypeTuple!(void**, void**, void***, void****, void*****);
142         else
143             alias Bases = TypeTuple!(Base*, Base*, Base**, Base***, Base****);
144 
145         alias TypeOf = Bases[DIM];
146     } else static if (is(T == class)) {
147         static if (toc == TypeOfClass.AsVoid)
148             alias TypeOf = void*;
149         else
150             alias TypeOf = T;
151     } else
152         alias TypeOf = T*;
153 }
154 
155 unittest {
156     static assert(is(TypeOf!(void) == void*));
157     static assert(is(TypeOf!(void[]) == void*));
158     static assert(is(TypeOf!(void[][]) == void**));
159 
160     static assert(is(TypeOf!(int) == int*));
161     static assert(is(TypeOf!(int[]) == int*));
162     static assert(is(TypeOf!(int[][]) == int**));
163     static assert(is(TypeOf!(int[][][]) == int***));
164 
165     static assert(is(TypeOf!(string) == immutable(char)*));
166     static assert(is(TypeOf!(string[]) == immutable(char)**));
167 }
168 
169 template ArrayPtrTypeOf(T) {
170     static if (isArray!(T))
171         alias ArrayPtrTypeOf = TypeOf!(T);
172     else static if (is(T == class))
173         alias ArrayPtrTypeOf = void**;
174     else
175         alias ArrayPtrTypeOf = T*;
176 }
177 
178 unittest {
179     class _C { }
180     struct _D { }
181 
182     static assert(is(ArrayPtrTypeOf!(_C) == void**));
183     static assert(is(ArrayPtrTypeOf!(_D) == _D*));
184     static assert(is(ArrayPtrTypeOf!(int) == int*));
185     static assert(is(ArrayPtrTypeOf!(void) == void*));
186     static assert(is(ArrayPtrTypeOf!(int[]) == int*));
187     static assert(is(ArrayPtrTypeOf!(void[]) == void*));
188 }
189 
190 @nogc
191 auto emplace(T, Args...)(void[] buf, auto ref Args args) if (is(T == class) || is(T == struct)) {
192     enum size_t SIZE = SizeOf!(T);
193     assert(buf.length == SIZE, "No enough space in buf");
194     alias Type = TypeOf!(T);
195 
196     static if (is(T == class)) {
197         buf[] = typeid(T).init[];
198         debug(m3) printf("Emplace class %s\n", &T.stringof[0]);
199     }
200 
201     Type tp = cast(Type) buf.ptr;
202 
203     static if (is(T == struct)) {
204         *tp = T.init;
205         debug(m3) printf("Emplace struct %s\n", &T.stringof[0]);
206     }
207 
208     static if (args.length != 0) {
209         static assert(__traits(hasMember, T, CTOR), "No CTor for type " ~ T.stringof);
210         tp.__ctor(args);
211     }
212 
213     return tp;
214 }
215 
216 @nogc
217 auto make(T, Args...)(auto ref Args args) if (is(T == class) || is(T == struct)) {
218     enum size_t SIZE = SizeOf!(T);
219     void* p = malloc(SIZE);
220 
221     debug(m3) printf("Make %s : %p\n", &T.stringof[0], p);
222         
223     return emplace!(T)(p[0 .. SIZE], args);
224 }
225 
226 @nogc
227 auto make(T, Args...)(auto ref Args args) nothrow if (!isArray!(T) && !is(T == class) && !is(T == struct)) {
228     enum size_t SIZE = SizeOf!(T);
229     T* p = cast(T*) malloc(SIZE);
230 
231     static if (!is(T == void)) {
232         static if (args.length == 0)
233             *p = T.init;
234         else {
235             static assert(args.length == 1, "Too many parameters!");
236             *p = args[0];
237         }
238     } else {
239         static assert(args.length == 0, "void cannot have arguments");
240     }
241 
242     return p;
243 }
244 
245 @nogc
246 void destruct(T)(T obj) if (is(T == class)) {
247     if (obj) {
248         static if (__traits(hasMember, T, DTOR))
249             obj.__dtor();
250         debug(m3) printf("Release class %s : %p\n", &T.stringof[0], cast(void*) obj);
251         
252         free(cast(void*) obj);
253         obj = null;
254     }
255 }
256 
257 @nogc
258 void destruct(T)(T* p) if (!is(T == class)) {
259     if (p) {
260         static if (is(T == struct)) {
261             static if (__traits(hasMember, T, DTOR))
262                 p.__dtor();
263             debug(m3) printf("Release struct %s: %p\n", &T.stringof[0], p);
264         }
265 
266         free(p);
267         p = null;
268     }
269 }
270 
271 /* Array */
272 
273 @nogc
274 T make(T)(size_t n) nothrow if (isArray!(T)) {
275     alias Base = BasicTypeOf!(T);
276     alias Next = NextTypeOf!(T);
277     enum size_t SIZE = SizeOf!(Next);
278 
279     void* p = malloc(n * SIZE);
280 
281     static if (is(Base == class)) {
282         alias Type = TypeOf!(T);
283 
284         T arr = cast(T) (cast(Type) p)[0 .. n];
285     }  else
286         T arr = (cast(Next*) p)[0 .. n];
287 
288     static if (!is(Base == void))
289         arr[0 .. n] = Next.init;
290 
291     return arr;
292 }
293 
294 @nogc
295 T* reserve(T)(T* ptr, size_t size) nothrow if (!isArray!(T) && !is(T == class)) {
296     enum size_t SIZE = SizeOf!(T);
297 
298     return cast(T*) realloc(ptr, size * SIZE);
299 }
300 
301 @nogc
302 T reserve(T)(ref T arr, size_t size) if (isArray!(T)) {
303     alias Base = BasicTypeOf!(T);
304     alias Next = NextTypeOf!(T);
305     
306     immutable size_t olen = arr.length;
307     immutable size_t nlen = olen + size;
308 
309     static if (is(Base == class)) {
310         alias Type = TypeOf!(T);
311 
312         arr = cast(T) reserve(cast(Type) arr.ptr, nlen)[0 .. nlen];
313     } else
314         arr = reserve(arr.ptr, nlen)[0 .. nlen];
315 
316     static if (!is(Base == void)) {
317         for (size_t i = olen; i < nlen; i++) {
318             arr[i] = Next.init;
319         }
320     }
321 
322     return arr;
323 }
324 
325 @nogc
326 T append(T, Args...)(ref T arr, auto ref Args args) if (isArray!(T)) {
327     if (arr.length != 0 && args.length != 0) {
328         immutable size_t olen = arr.length;
329         immutable size_t nlen = olen + args.length;
330         
331         alias Base = BasicTypeOf!(T);
332         static if (is(Base == class)) {
333             alias Type = TypeOf!(T);
334 
335             arr = reserve(cast(Type) arr.ptr, nlen)[0 .. nlen];
336         } else
337             arr = reserve(arr.ptr, nlen)[0 .. nlen];
338         
339         size_t i = olen;
340         foreach (arg; args) {
341             arr[i++] = arg;
342         }
343     }
344                
345     return arr;
346 }
347 
348 @nogc
349 void destruct(T)(ref T arr) if (isArray!(T)) {
350     if (arr.ptr) {
351         alias Base = BasicTypeOf!(T);
352 
353         static if (__traits(hasMember, Base, DTOR)) {
354             enum size_t DIM = DimOf!(T);
355 
356             static if (DIM > 1) {
357                 for (size_t i = 0; i < DIM; i++) {
358                     foreach (ref Base item; arr[i]) {
359                         item.__dtor();
360                     }
361                 }
362             } else {
363                 foreach (ref Base item; arr) {
364                     item.__dtor();
365                 }
366             }
367         }
368         
369         free(arr.ptr);
370         arr = null;
371     }
372 }
373 
374 version (unittest) {
375     class A {
376     @nogc:
377         int id = 23;
378 
379         this(int i) {
380             this.id = i;
381         }
382         
383         ~this() {
384             debug(m3) printf("DTor A\n");
385         }
386         
387         int getId() const {
388             return this.id;
389         }
390     }
391 
392     class B : A {
393     @nogc:
394         this(int i) {
395             super(i);
396         }
397         
398         ~this() {
399             debug(m3) printf("DTor B\n");
400         }
401     }
402 
403     struct C {
404     @nogc:
405         int id = 42;
406         
407         this(int i) {
408             this.id = i;
409         }
410 
411         ~this() {
412             debug(m3) printf("DTor C\n");
413         }
414         
415         int getId() const {
416             return this.id;
417         }
418     }
419 }
420 
421 @nogc
422 unittest {
423     void[] mem = make!(void[])(23);
424     scope(exit) destruct(mem);
425 
426     assert(mem.length == 23);
427 
428     int[] arr = make!(int[])(42);
429     scope(exit) destruct(arr);
430 
431     assert(arr.length == 42);
432 
433     int[] slice = arr[10 .. 20];
434     assert(slice.length == 10);
435 
436     string[] arr2 = make!(string[])(2);
437     scope(exit) destruct(arr2);
438 
439     assert(arr2.length == 2);
440 
441     assert(arr2[0].length == 0);
442     assert(arr2[1].length == 0);
443 
444     arr2[0] = "Foo";
445     arr2[1] = "Quatz";
446 
447     assert(arr2[0].length == 3);
448     assert(arr2[1].length == 5);
449 
450     assert(arr2[0] == "Foo");
451     assert(arr2[1] == "Quatz");
452 
453     int* p1 = make!(int)();
454     int* p2 = make!(int)(1);
455     int** p3 = make!(int*)(make!(int)(42));
456 
457     assert(p1);
458     assert(p2);
459     assert(p3);
460     assert(*p3);
461 
462     assert(*p1 == 0);
463     assert(*p2 == 1);
464     assert(**p3 == 42);
465 
466     debug(m3) printf("A.sizeof = %d, B.sizeof = %d, C.sizeof = %d\n",
467         __traits(classInstanceSize, A), __traits(classInstanceSize, B), C.sizeof);
468 
469     arr[0] = 42;
470     assert(arr.length == 42);
471     assert(arr[0] == 42);
472 
473     arr.append(23);
474     assert(arr.length == 43);
475     assert(arr[0] == 42);
476     assert(arr[$ - 1] == 23);
477 
478     arr.append(4, 2, 3);
479     assert(arr.length == 46);
480     assert(arr[0] == 42);
481     assert(arr[$ - 1] == 3);
482     assert(arr[$ - 2] == 2);
483     assert(arr[$ - 3] == 4);
484     assert(arr[$ - 4] == 23);
485     
486     A a = make!(A)(42);
487     assert(a.id == 42 && a.getId() == 42);
488     
489     B b = make!(B)(23);
490     assert(b.id == 23 && b.getId() == 23);
491     
492     destruct(a);
493     destruct(b);
494     
495     C* c = make!(C);
496     assert(c.id == 42 && c.getId() == 42);
497     
498     destruct(c);
499 
500     void[__traits(classInstanceSize, A)] buf = void;
501     A as = emplace!(A)(buf[]);
502     assert(as.id == 23 && as.getId() == 23);
503 
504     void[__traits(classInstanceSize, A)] buf2 = void;
505     A as2 = emplace!(A)(buf2[], 42);
506     assert(as2.id == 42 && as2.getId() == 42);
507 
508     void[SizeOf!(A)] buf3 = void;
509     A as3 = emplace!(A)(buf3[], 23);
510     assert(as3.id == 23 && as3.getId() == 23);
511 
512     /** class array #1 */
513 
514     A[] aarr;
515     aarr.reserve(42);
516     scope(exit) destruct(aarr);
517 
518     debug(m3) printf("aarr.length = %d\n", aarr.length);
519 
520     assert(aarr.length == 42);
521     for (size_t i = 0; i < 42; i++) {
522         assert(aarr[i] is null);
523     }
524 
525     aarr[0] = as;
526     aarr[1] = as2;
527     aarr[2] = as3;
528 
529     assert(aarr[0] is as);
530     assert(aarr[0] !is null);
531 
532     assert(aarr[1] is as2);
533     assert(aarr[1] !is null);
534 
535     assert(aarr[2] is as3);
536     assert(aarr[2] !is null);
537 
538     assert(aarr.length == 42);
539     for (size_t i = 3; i < 42; i++) {
540         assert(aarr[i] is null);
541     }
542 
543     /** class array #2 */
544 
545     A[] aarr2 = make!(A[])(42);
546     scope(exit) destruct(aarr2);
547 
548     debug(m3) printf("aarr2.length = %d\n", aarr2.length);
549 
550     assert(aarr2.length == 42);
551     for (size_t i = 0; i < 42; i++) {
552         assert(aarr2[i] is null);
553     }
554 
555     aarr2[0] = as;
556     aarr2[1] = as2;
557     aarr2[2] = as3;
558 
559     assert(aarr2[0] is as);
560     assert(aarr2[0] !is null);
561 
562     assert(aarr2[1] is as2);
563     assert(aarr2[1] !is null);
564     
565     assert(aarr2[2] is as3);
566     assert(aarr2[2] !is null);
567 
568     assert(aarr2.length == 42);
569     for (size_t i = 3; i < 42; i++) {
570         assert(aarr2[i] is null);
571     }
572 
573     /** struct array #1 */
574 
575     C[] carr;
576     carr.reserve(23);
577     scope(exit) destruct(carr);
578 
579     debug(m3) printf("carr.length = %d\n", carr.length);
580 
581     assert(carr.length == 23);
582     for (size_t i = 0; i < 23; i++) {
583         assert(carr[i].id == 42);
584     }
585 
586     carr[0].id = 1;
587     carr[1].id = 2;
588     carr[2].id = 3;
589 
590     assert(carr[0].id == 1);
591     assert(carr[1].id == 2);
592     assert(carr[2].id == 3);
593 
594     assert(carr.length == 23);
595     for (size_t i = 3; i < 23; i++) {
596         assert(carr[i].id == 42);
597     }
598     
599     /** struct array #2 */
600 
601     C[] carr2 = make!(C[])(23);
602     scope(exit) destruct(carr2);
603 
604     debug(m3) printf("carr2.length = %d\n", carr2.length);
605 
606     assert(carr2.length == 23);
607     for (size_t i = 0; i < 23; i++) {
608         assert(carr2[i].id == 42);
609     }
610 
611     carr2[0].id = 1;
612     carr2[1].id = 2;
613     carr2[2].id = 3;
614 
615     assert(carr2[0].id == 1);
616     assert(carr2[1].id == 2);
617     assert(carr2[2].id == 3);
618 
619     assert(carr2.length == 23);
620     for (size_t i = 3; i < 23; i++) {
621         assert(carr2[i].id == 42);
622     }
623 }