1 /++
2 This is a submodule of $(MREF mir,ndslice).
3 
4 It contains allocation utilities.
5 
6 
7 $(BOOKTABLE $(H2 Common utilities),
8 $(T2 shape, Returns a shape of a common n-dimensional array. )
9 )
10 
11 $(BOOKTABLE $(H2 GC Allocation utilities),
12 $(TR $(TH Function Name) $(TH Description))
13 $(T2 slice, Allocates a slice using GC.)
14 $(T2 bitSlice, GC-Allocates a bitwise packed n-dimensional boolean slice.)
15 $(T2 ndarray, Allocates a common n-dimensional array from a slice. )
16 $(T2 uninitSlice, Allocates an uninitialized slice using GC. )
17 )
18 
19 $(BOOKTABLE $(H2 Ref counted allocation utilities),
20 $(T2 rcslice, Allocates an n-dimensional reference-counted (thread-safe) slice.)
21 $(T2 bitRcslice, Allocates a bitwise packed n-dimensional reference-counted (thread-safe) boolean slice.)
22 $(T2 mininitRcslice, Allocates a minimally initialized n-dimensional reference-counted (thread-safe) slice.)
23 )
24 
25 $(BOOKTABLE $(H2 Custom allocation utilities),
26 $(TR $(TH Function Name) $(TH Description))
27 $(T2 makeNdarray, Allocates a common n-dimensional array from a slice using an allocator. )
28 $(T2 makeSlice, Allocates a slice using an allocator. )
29 $(T2 makeUninitSlice, Allocates an uninitialized slice using an allocator. )
30 )
31 
32 $(BOOKTABLE $(H2 CRuntime allocation utilities),
33 $(TR $(TH Function Name) $(TH Description))
34 $(T2 stdcSlice, Allocates a slice copy using `core.stdc.stdlib.malloc`)
35 $(T2 stdcUninitSlice, Allocates an uninitialized slice using `core.stdc.stdlib.malloc`.)
36 $(T2 stdcFreeSlice, Frees memory using `core.stdc.stdlib.free`)
37 )
38 
39 $(BOOKTABLE $(H2 Aligned allocation utilities),
40 $(TR $(TH Function Name) $(TH Description))
41 $(T2 uninitAlignedSlice, Allocates an uninitialized aligned slice using GC. )
42 $(T2 stdcUninitAlignedSlice, Allocates an uninitialized aligned slice using CRuntime.)
43 $(T2 stdcFreeAlignedSlice, Frees memory using CRuntime)
44 )
45 
46 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
47 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
48 Authors: Ilya Yaroshenko
49 
50 Macros:
51 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
52 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
53 +/
54 module mir.ndslice.allocation;
55 
56 import mir.math.common: optmath;
57 import mir.ndslice.concatenation;
58 import mir.ndslice.field: BitField;
59 import mir.ndslice.internal;
60 import mir.ndslice.iterator: FieldIterator;
61 import mir.ndslice.slice;
62 import mir.rc.array;
63 import std.traits;
64 import std.meta: staticMap;
65 
66 @optmath:
67 
68 /++
69 Allocates an n-dimensional reference-counted (thread-safe) slice.
70 Params:
71     lengths = List of lengths for each dimension.
72     init = Value to initialize with (optional).
73     slice = Slice to copy shape and data from (optional).
74 Returns:
75     n-dimensional slice
76 +/
77 Slice!(RCI!T, N)
78     rcslice(T, size_t N)(size_t[N] lengths...)
79 {
80     immutable len = lengths.lengthsProduct;
81     auto _lengths = lengths;
82     return typeof(return)(_lengths, RCI!T(RCArray!T(len)));
83 }
84 
85 /// ditto
86 Slice!(RCI!T, N)
87     rcslice(T, size_t N)(size_t[N] lengths, T init)
88 {
89     auto ret = (()@trusted => mininitRcslice!T(lengths))();
90     ret.lightScope.field[] = init;
91     return ret;
92 }
93 
94 /// ditto
95 auto rcslice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
96 {
97     import mir.conv: emplaceRef;
98     alias E = slice.DeepElement;
99 
100     auto result = (() @trusted => slice.shape.mininitRcslice!(Unqual!E))();
101 
102     import mir.algorithm.iteration: each;
103     each!(emplaceRef!E)(result.lightScope, slice.lightScope);
104 
105     return *(() @trusted => cast(Slice!(RCI!E, N)*) &result)();
106 }
107 
108 /// ditto
109 auto rcslice(T)(T[] array)
110 {
111     return rcslice(array.sliced);
112 }
113 
114 /// ditto
115 auto rcslice(T, I)(I[] array)
116     if  (!isImplicitlyConvertible!(I[], T[]))
117 {
118     import mir.ndslice.topology: as;
119     return rcslice(array.sliced.as!T);
120 }
121 
122 ///
123 version(mir_test)
124 @safe pure nothrow @nogc unittest
125 {
126     import mir.ndslice.slice: Slice;
127     import mir.rc.array: RCI;
128     auto tensor = rcslice!int(5, 6, 7);
129     assert(tensor.length == 5);
130     assert(tensor.elementCount == 5 * 6 * 7);
131     static assert(is(typeof(tensor) == Slice!(RCI!int, 3)));
132 
133     // creates duplicate using `rcslice`
134     auto dup = tensor.rcslice;
135     assert(dup == tensor);
136 }
137 
138 ///
139 version(mir_test)
140 @safe pure nothrow @nogc unittest
141 {
142     import mir.ndslice.slice: Slice;
143     import mir.rc.array: RCI;
144     auto tensor = rcslice([2, 3], 5);
145     assert(tensor.elementCount == 2 * 3);
146     assert(tensor[1, 1] == 5);
147 
148     import mir.rc.array;
149     static assert(is(typeof(tensor) == Slice!(RCI!int, 2)));
150 }
151 
152 /// ditto
153 auto rcslice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation)
154 {
155     alias T = Unqual!(concatenation.DeepElement);
156     auto ret = (()@trusted => mininitRcslice!T(concatenation.shape))();
157     ret.lightScope.opIndexAssign(concatenation);
158     return ret;
159 }
160 
161 ///
162 version(mir_test)
163 @safe pure nothrow @nogc unittest
164 {
165     import mir.rc.array: RCI;
166     import mir.ndslice.slice: Slice;
167     import mir.ndslice.topology : iota;
168     import mir.ndslice.concatenation;
169     auto tensor = concatenation([2, 3].iota, [3].iota(6)).rcslice;
170     assert(tensor == [3, 3].iota);
171 
172     static assert(is(typeof(tensor) == Slice!(RCI!ptrdiff_t, 2)));
173 }
174 
175 /++
176 Allocates an n-dimensional reference-counted (thread-safe) slice without memory initialisation.
177 Params:
178     lengths = List of lengths for each dimension.
179 Returns:
180     n-dimensional slice
181 +/
182 Slice!(RCI!T, N)
183     uninitRCslice(T, size_t N)(size_t[N] lengths...)
184 {
185     immutable len = lengths.lengthsProduct;
186     auto _lengths = lengths;
187     return typeof(return)(_lengths, RCI!T(RCArray!T(len, false)));
188 }
189 
190 ///
191 version(mir_test)
192 @safe pure nothrow @nogc unittest
193 {
194     import mir.ndslice.slice: Slice;
195     import mir.rc.array: RCI;
196     auto tensor = uninitRCslice!int(5, 6, 7);
197     tensor[] = 1;
198     assert(tensor.length == 5);
199     assert(tensor.elementCount == 5 * 6 * 7);
200     static assert(is(typeof(tensor) == Slice!(RCI!int, 3)));
201 }
202 
203 /++
204 Allocates a bitwise packed n-dimensional reference-counted (thread-safe) boolean slice.
205 Params:
206     lengths = List of lengths for each dimension.
207 Returns:
208     n-dimensional bitwise rcslice
209 See_also: $(SUBREF topology, bitwise).
210 +/
211 Slice!(FieldIterator!(BitField!(RCI!size_t)), N) bitRcslice(size_t N)(size_t[N] lengths...)
212 {
213     import mir.ndslice.topology: bitwise;
214     enum elen = size_t.sizeof * 8;
215     immutable len = lengths.lengthsProduct;
216     immutable dlen = (len / elen + (len % elen != 0));
217     return RCArray!size_t(dlen).asSlice.bitwise[0 .. len].sliced(lengths);
218 }
219 
220 /// 1D
221 @safe pure nothrow @nogc
222 version(mir_test) unittest
223 {
224     auto bitarray = 100.bitRcslice; // allocates 16 bytes total (plus RC context)
225     assert(bitarray.shape == cast(size_t[1])[100]);
226     assert(bitarray[72] == false);
227     bitarray[72] = true;
228     assert(bitarray[72] == true);
229 }
230 
231 /// 2D
232 @safe pure nothrow @nogc
233 version(mir_test) unittest
234 {
235     auto bitmatrix = bitRcslice(20, 6); // allocates 16 bytes total (plus RC context)
236     assert(bitmatrix.shape == cast(size_t[2])[20, 6]);
237     assert(bitmatrix[3, 4] == false);
238     bitmatrix[3, 4] = true;
239     assert(bitmatrix[3, 4] == true);
240 }
241 
242 /++
243 Allocates a minimally initialized n-dimensional reference-counted (thread-safe) slice.
244 Params:
245     lengths = list of lengths for each dimension
246 Returns:
247     contiguous minimally initialized n-dimensional reference-counted (thread-safe) slice
248 +/
249 Slice!(RCI!T, N) mininitRcslice(T, size_t N)(size_t[N] lengths...)
250 {
251     immutable len = lengths.lengthsProduct;
252     auto _lengths = lengths;
253     return Slice!(RCI!T, N)(_lengths, RCI!T(mininitRcarray!T(len)));
254 }
255 
256 ///
257 version(mir_test)
258 pure nothrow @nogc unittest
259 {
260     import mir.ndslice.slice: Slice;
261     import mir.rc.array: RCI;
262     auto tensor = mininitRcslice!int(5, 6, 7);
263     assert(tensor.length == 5);
264     assert(tensor.elementCount == 5 * 6 * 7);
265     static assert(is(typeof(tensor) == Slice!(RCI!int, 3)));
266 }
267 
268 private alias Pointer(T) = T*;
269 private alias Pointers(Args...) = staticMap!(Pointer, Args);
270 
271 /++
272 GC-Allocates an n-dimensional slice.
273 +/
274 template slice(Args...)
275     if (Args.length)
276 {
277     ///
278     alias LabelTypes = Args[1 .. $];
279     ///
280     alias T = Args[0];
281 
282     /++
283     Params:
284         lengths = List of lengths for each dimension.
285         init = Value to initialize with (optional).
286     Returns:
287         initialzed n-dimensional slice
288     +/
289     Slice!(T*, N, Contiguous, Pointers!LabelTypes)
290         slice(size_t N)(size_t[N] lengths...)
291         if (N >= LabelTypes.length)
292     {
293         auto shape = lengths; // DMD variadic bug workaround
294         immutable len = shape.lengthsProduct;
295         auto ret = typeof(return)(shape, len == 0 ? null : (()@trusted=>new T[len].ptr)());
296         foreach (i, L; LabelTypes) // static
297             ret._labels[i] = (()@trusted=>new L[shape[i]].ptr)();
298         return ret;
299     }
300 
301     /// ditto
302     Slice!(T*, N, Contiguous, Pointers!LabelTypes)
303         slice(size_t N)(size_t[N] lengths, T init)
304         if (N >= LabelTypes.length)
305     {
306         import mir.conv: emplaceRef;
307         import std.array : uninitializedArray;
308         immutable len = lengths.lengthsProduct;
309         auto arr = uninitializedArray!(Unqual!T[])(len);
310         foreach (ref e; arr)
311             emplaceRef(e, init);
312         auto ret = typeof(return)(lengths, len == 0 ? null : (()@trusted=>cast(T*)arr.ptr)());
313         foreach (i, L; LabelTypes) // static
314             ret._labels[i] = (()@trusted=>new L[shape[i]].ptr)();
315         return ret;
316     }
317 }
318 
319 ///
320 version(mir_test)
321 @safe pure nothrow unittest
322 {
323     import mir.ndslice.slice: Slice;
324     auto tensor = slice!int(5, 6, 7);
325     assert(tensor.length == 5);
326     assert(tensor.length!1 == 6);
327     assert(tensor.elementCount == 5 * 6 * 7);
328     static assert(is(typeof(tensor) == Slice!(int*, 3)));
329 }
330 
331 /// 2D DataFrame example
332 version(mir_test)
333 @safe pure unittest
334 {
335     import mir.ndslice.slice;
336     import mir.ndslice.allocation: slice;
337     import mir.date: Date;
338 
339     auto dataframe = slice!(double, Date, string)(4, 3);
340     assert(dataframe.length == 4);
341     assert(dataframe.length!1 == 3);
342     assert(dataframe.elementCount == 4 * 3);
343 
344     static assert(is(typeof(dataframe) ==
345         Slice!(double*, 2, Contiguous, Date*, string*)));
346 
347     // Dataframe labels are contiguous 1-dimensional slices.
348 
349     // Fill row labels
350     dataframe.label[] = [
351         Date(2019, 1, 24),
352         Date(2019, 2, 2),
353         Date(2019, 2, 4),
354         Date(2019, 2, 5),
355     ];
356 
357     assert(dataframe.label!0[2] == Date(2019, 2, 4));
358 
359     // Fill column labels
360     dataframe.label!1[] = ["income", "outcome", "balance"];
361 
362     assert(dataframe.label!1[2] == "balance");
363 
364     // Change label element
365     dataframe.label!1[2] = "total";
366     assert(dataframe.label!1[2] == "total");
367 
368     // Attach a newly allocated label
369     dataframe.label!1 = ["Income", "Outcome", "Balance"].sliced;
370 
371     assert(dataframe.label!1[2] == "Balance");
372 }
373 
374 /++
375 GC-Allocates an n-dimensional slice.
376 Params:
377     lengths = List of lengths for each dimension.
378     init = Value to initialize with (optional).
379 Returns:
380     initialzed n-dimensional slice
381 +/
382 Slice!(T*, N)
383     slice(size_t N, T)(size_t[N] lengths, T init)
384 {
385     return .slice!T(lengths, init);
386 }
387 
388 // TODO: make it a dataframe compatible. This function performs copy.
389 /// ditto
390 auto slice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
391 {
392     if (__ctfe)
393     {
394         import mir.ndslice.topology: flattened;
395         import mir.array.allocation: array;
396         return slice.flattened.array.sliced(slice.shape);
397     }
398     else
399     {
400         import mir.conv: emplaceRef;
401         alias E = slice.DeepElement;
402 
403         auto result = (() @trusted => slice.shape.uninitSlice!(Unqual!E))();
404 
405         import mir.algorithm.iteration: each;
406         each!(emplaceRef!E)(result, slice);
407 
408         return (() @trusted => cast(Slice!(E*, N)) result)();
409     }
410 }
411 
412 ///
413 version(mir_test)
414 @safe pure nothrow unittest
415 {
416     auto tensor = slice([2, 3], 5);
417     assert(tensor.elementCount == 2 * 3);
418     assert(tensor[1, 1] == 5);
419 
420     // creates duplicate using `slice`
421     auto dup = tensor.slice;
422     assert(dup == tensor);
423 }
424 
425 /// ditto
426 auto slice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation)
427 {
428     alias T = Unqual!(concatenation.DeepElement);
429     static if (hasElaborateAssign!T)
430         alias fun = .slice;
431     else
432         alias fun = .uninitSlice;
433     auto ret = (()@trusted => fun!T(concatenation.shape))();
434     ret.opIndexAssign(concatenation);
435     return ret;
436 }
437 
438 ///
439 version(mir_test)
440 @safe pure nothrow unittest
441 {
442     import mir.ndslice.slice: Slice;
443     import mir.ndslice.topology : iota;
444     import mir.ndslice.concatenation;
445     auto tensor = concatenation([2, 3].iota, [3].iota(6)).slice;
446     assert(tensor == [3, 3].iota);
447 
448     static assert(is(typeof(tensor) == Slice!(ptrdiff_t*, 2)));
449 }
450 
451 /++
452 GC-Allocates a bitwise packed n-dimensional boolean slice.
453 Params:
454     lengths = List of lengths for each dimension.
455 Returns:
456     n-dimensional bitwise slice
457 See_also: $(SUBREF topology, bitwise).
458 +/
459 Slice!(FieldIterator!(BitField!(size_t*)), N) bitSlice(size_t N)(size_t[N] lengths...)
460 {
461     import mir.ndslice.topology: bitwise;
462     enum elen = size_t.sizeof * 8;
463     immutable len = lengths.lengthsProduct;
464     immutable dlen = (len / elen + (len % elen != 0));
465     return new size_t[dlen].sliced.bitwise[0 .. len].sliced(lengths);
466 }
467 
468 /// 1D
469 @safe pure version(mir_test) unittest
470 {
471     auto bitarray = bitSlice(100); // allocates 16 bytes total
472     assert(bitarray.shape == [100]);
473     assert(bitarray[72] == false);
474     bitarray[72] = true;
475     assert(bitarray[72] == true);
476 }
477 
478 /// 2D
479 @safe pure version(mir_test) unittest
480 {
481     auto bitmatrix = bitSlice(20, 6); // allocates 16 bytes total
482     assert(bitmatrix.shape == [20, 6]);
483     assert(bitmatrix[3, 4] == false);
484     bitmatrix[3, 4] = true;
485     assert(bitmatrix[3, 4] == true);
486 }
487 
488 /++
489 GC-Allocates an uninitialized n-dimensional slice.
490 Params:
491     lengths = list of lengths for each dimension
492 Returns:
493     contiguous uninitialized n-dimensional slice
494 +/
495 auto uninitSlice(T, size_t N)(size_t[N] lengths...)
496 {
497     immutable len = lengths.lengthsProduct;
498     import std.array : uninitializedArray;
499     auto arr = uninitializedArray!(T[])(len);
500     version (mir_secure_memory)
501     {()@trusted{
502         (cast(ubyte[])arr)[] = 0;
503     }();}
504     return arr.sliced(lengths);
505 }
506 
507 ///
508 version(mir_test)
509 @safe pure nothrow unittest
510 {
511     import mir.ndslice.slice: Slice;
512     auto tensor = uninitSlice!int(5, 6, 7);
513     assert(tensor.length == 5);
514     assert(tensor.elementCount == 5 * 6 * 7);
515     static assert(is(typeof(tensor) == Slice!(int*, 3)));
516 }
517 
518 /++
519 GC-Allocates an uninitialized aligned an n-dimensional slice.
520 Params:
521     lengths = list of lengths for each dimension
522     alignment = memory alignment (bytes)
523 Returns:
524     contiguous uninitialized n-dimensional slice
525 +/
526 auto uninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @system
527 {
528     immutable len = lengths.lengthsProduct;
529     import std.array : uninitializedArray;
530     assert((alignment != 0) && ((alignment & (alignment - 1)) == 0), "'alignment' must be a power of two");
531     size_t offset = alignment <= 16 ? 0 : alignment - 1;
532     void* basePtr = uninitializedArray!(byte[])(len * T.sizeof + offset).ptr;
533     T* alignedPtr = cast(T*)((cast(size_t)(basePtr) + offset) & ~offset);
534     return alignedPtr.sliced(lengths);
535 }
536 
537 ///
538 version(mir_test)
539 @system pure nothrow unittest
540 {
541     import mir.ndslice.slice: Slice;
542     auto tensor = uninitAlignedSlice!double([5, 6, 7], 64);
543     tensor[] = 0;
544     assert(tensor.length == 5);
545     assert(tensor.elementCount == 5 * 6 * 7);
546     assert(cast(size_t)(tensor.ptr) % 64 == 0);
547     static assert(is(typeof(tensor) == Slice!(double*, 3)));
548 }
549 
550 /++
551 Allocates an array through a specified allocator and creates an n-dimensional slice over it.
552 See also $(MREF std, experimental, allocator).
553 Params:
554     alloc = allocator
555     lengths = list of lengths for each dimension
556     init = default value for array initialization
557     slice = slice to copy shape and data from
558 Returns:
559     a structure with fields `array` and `slice`
560 Note:
561     `makeSlice` always returns slice with mutable elements
562 +/
563 auto makeSlice(Allocator, size_t N, Iterator)(auto ref Allocator alloc, Slice!(N, Iterator) slice)
564 {
565     alias T = Unqual!(slice.DeepElement);
566     return makeSlice!(T)(alloc, slice);
567 }
568 
569 /// ditto
570 Slice!(T*, N)
571 makeSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...)
572 {
573     import std.experimental.allocator : makeArray;
574     return alloc.makeArray!T(lengths.lengthsProduct).sliced(lengths);
575 }
576 
577 /// ditto
578 Slice!(T*, N)
579 makeSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths, T init)
580 {
581     import std.experimental.allocator : makeArray;
582     immutable len = lengths.lengthsProduct;
583     auto array = alloc.makeArray!T(len, init);
584     return array.sliced(lengths);
585 }
586 
587 /// ditto
588 auto makeSlice(Allocator, Iterator, size_t N, SliceKind kind)
589     (auto ref Allocator allocator, Slice!(Iterator, N, kind) slice)
590 {
591     import mir.conv: emplaceRef;
592     alias E = slice.DeepElement;
593 
594     auto result = allocator.makeUninitSlice!(Unqual!E)(slice.shape);
595 
596     import mir.algorithm.iteration: each;
597     each!(emplaceRef!E)(result, slice);
598 
599     return cast(Slice!(E*, N)) result;
600 }
601 
602 /// Initialization with default value
603 version(mir_test)
604 @nogc unittest
605 {
606     import std.experimental.allocator;
607     import std.experimental.allocator.mallocator;
608     import mir.algorithm.iteration: all;
609     import mir.ndslice.topology: map;
610 
611     auto sl = Mallocator.instance.makeSlice([2, 3, 4], 10);
612     auto ar = sl.field;
613     assert(sl.all!"a == 10");
614 
615     auto sl2 = Mallocator.instance.makeSlice(sl.map!"a * 2");
616     auto ar2 = sl2.field;
617     assert(sl2.all!"a == 20");
618 
619     Mallocator.instance.dispose(ar);
620     Mallocator.instance.dispose(ar2);
621 }
622 
623 version(mir_test)
624 @nogc unittest
625 {
626     import std.experimental.allocator;
627     import std.experimental.allocator.mallocator;
628 
629     // cast to your own type
630     auto sl = makeSlice!double(Mallocator.instance, [2, 3, 4], 10);
631     auto ar = sl.field;
632     assert(sl[1, 1, 1] == 10.0);
633     Mallocator.instance.dispose(ar);
634 }
635 
636 /++
637 Allocates an uninitialized array through a specified allocator and creates an n-dimensional slice over it.
638 See also $(MREF std, experimental, allocator).
639 Params:
640     alloc = allocator
641     lengths = list of lengths for each dimension
642 Returns:
643     a structure with fields `array` and `slice`
644 +/
645 Slice!(T*, N)
646 makeUninitSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...)
647     if (N)
648 {
649     if (immutable len = lengths.lengthsProduct)
650     {
651         auto mem = alloc.allocate(len * T.sizeof);
652         if (mem.length == 0) assert(0);
653         auto array = () @trusted { return cast(T[]) mem; }();
654         version (mir_secure_memory)
655         {() @trusted { 
656             (cast(ubyte[])array)[] = 0;
657         }();}
658         return array.sliced(lengths);
659     }
660     else
661     {
662         return T[].init.sliced(lengths);
663     }
664 }
665 
666 ///
667 version(mir_test)
668 @system @nogc unittest
669 {
670     import std.experimental.allocator;
671     import std.experimental.allocator.mallocator;
672 
673     auto sl = makeUninitSlice!int(Mallocator.instance, 2, 3, 4);
674     auto ar = sl.field;
675     assert(ar.ptr is sl.iterator);
676     assert(ar.length           == 24);
677     assert(sl.elementCount    == 24);
678 
679     Mallocator.instance.dispose(ar);
680 }
681 
682 /++
683 Allocates a common n-dimensional array from a slice.
684 Params:
685     slice = slice
686 Returns:
687     multidimensional D array
688 +/
689 auto ndarray(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
690 {
691     import  mir.array.allocation : array;
692     static if (slice.N == 1)
693     {
694         return slice.array;
695     }
696     else
697     {
698         import mir.ndslice.topology: ipack, map;
699         return slice.ipack!1.map!(.ndarray).array;
700     }
701 }
702 
703 ///
704 version(mir_test)
705 @safe pure nothrow unittest
706 {
707     import mir.ndslice.topology : iota;
708     auto slice = iota(3, 4);
709     auto m = slice.ndarray;
710     static assert(is(typeof(m) == sizediff_t[][])); // sizediff_t is long for 64 bit platforms
711     assert(m == [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]);
712 }
713 
714 /++
715 Allocates a common n-dimensional array using data from a slice.
716 Params:
717     alloc = allocator (optional)
718     slice = slice
719 Returns:
720     multidimensional D array
721 +/
722 auto makeNdarray(T, Allocator, Iterator, size_t N, SliceKind kind)(auto ref Allocator alloc, Slice!(Iterator, N, kind) slice)
723 {
724     import std.experimental.allocator : makeArray;
725     static if (slice.N == 1)
726     {
727         return makeArray!T(alloc, slice);
728     }
729     else
730     {
731         alias E = typeof(makeNdarray!T(alloc, slice[0]));
732         auto ret = makeArray!E(alloc, slice.length);
733         foreach (i, ref e; ret)
734             e = .makeNdarray!T(alloc, slice[i]);
735         return ret;
736     }
737 }
738 
739 ///
740 version(mir_test)
741 @nogc unittest
742 {
743     import std.experimental.allocator;
744     import std.experimental.allocator.mallocator;
745     import mir.ndslice.topology : iota;
746 
747     auto slice = iota(3, 4);
748     auto m = Mallocator.instance.makeNdarray!long(slice);
749 
750     static assert(is(typeof(m) == long[][]));
751 
752     static immutable ar = [[0L, 1, 2, 3], [4L, 5, 6, 7], [8L, 9, 10, 11]];
753     assert(m == ar);
754 
755     foreach (ref row; m)
756         Mallocator.instance.dispose(row);
757     Mallocator.instance.dispose(m);
758 }
759 
760 /++
761 Shape of a common n-dimensional array.
762 Params:
763     array = common n-dimensional array
764     err = error flag passed by reference
765 Returns:
766     static array of dimensions type of `size_t[n]`
767 +/
768 auto shape(T)(T[] array, ref int err)
769 {
770     static if (isDynamicArray!T)
771     {
772         size_t[1 + typeof(shape(T.init, err)).length] ret;
773 
774         if (array.length)
775         {
776             ret[0] = array.length;
777             ret[1..$] = shape(array[0], err);
778             if (err)
779                 goto L;
780             foreach (ar; array)
781             {
782                 if (shape(ar, err) != ret[1..$])
783                     err = 1;
784                 if (err)
785                     goto L;
786             }
787         }
788     }
789     else
790     {
791         size_t[1] ret;
792         ret[0] = array.length;
793     }
794     err = 0;
795 L:
796     return ret;
797 }
798 
799 ///
800 version(mir_test)
801 @safe pure unittest
802 {
803     int err;
804     size_t[2] shape = [[1, 2, 3], [4, 5, 6]].shape(err);
805     assert(err == 0);
806     assert(shape == [2, 3]);
807 
808     [[1, 2], [4, 5, 6]].shape(err);
809     assert(err == 1);
810 }
811 
812 /// Slice from ndarray
813 version(mir_test)
814 unittest
815 {
816     import mir.ndslice.allocation: slice, shape;
817     int err;
818     auto array = [[1, 2, 3], [4, 5, 6]];
819     auto s = array.shape(err).slice!int;
820     s[] = [[1, 2, 3], [4, 5, 6]];
821     assert(s == array);
822 }
823 
824 version(mir_test)
825 @safe pure unittest
826 {
827     int err;
828     size_t[2] shape = (int[][]).init.shape(err);
829     assert(shape[0] == 0);
830     assert(shape[1] == 0);
831 }
832 
833 version(mir_test)
834 nothrow unittest
835 {
836     import mir.ndslice.allocation;
837     import mir.ndslice.topology : iota;
838 
839     auto sl = iota([0, 0], 1);
840 
841     assert(sl.empty!0);
842     assert(sl.empty!1);
843 
844     auto gcsl1 = sl.slice;
845     auto gcsl2 = slice!double(0, 0);
846 
847     import std.experimental.allocator;
848     import std.experimental.allocator.mallocator;
849 
850     auto sl2 = makeSlice!double(Mallocator.instance, 0, 0);
851 
852     Mallocator.instance.dispose(sl2.field);
853 }
854 
855 /++
856 Allocates an uninitialized array using `core.stdc.stdlib.malloc` and creates an n-dimensional slice over it.
857 Params:
858     lengths = list of lengths for each dimension
859 Returns:
860     contiguous uninitialized n-dimensional slice
861 See_also:
862     $(LREF stdcSlice), $(LREF stdcFreeSlice)
863 +/
864 Slice!(T*, N) stdcUninitSlice(T, size_t N)(size_t[N] lengths...)
865 {
866     import core.stdc.stdlib: malloc;
867     immutable len = lengths.lengthsProduct;
868     auto p = malloc(len * T.sizeof);
869     if (p is null) assert(0);
870     version (mir_secure_memory)
871     {
872         (cast(ubyte*)p)[0 .. len * T.sizeof] = 0;
873     }
874     auto ptr = len ? cast(T*) p : null;
875     return ptr.sliced(lengths);
876 }
877 
878 /++
879 Allocates a copy of a slice using `core.stdc.stdlib.malloc`.
880 Params:
881     slice = n-dimensional slice
882 Returns:
883     contiguous n-dimensional slice
884 See_also:
885     $(LREF stdcUninitSlice), $(LREF stdcFreeSlice)
886 +/
887 auto stdcSlice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice)
888 {
889     alias E = slice.DeepElement;
890     alias T = Unqual!E;
891     static assert (!hasElaborateAssign!T, "stdcSlice is not miplemented for slices that have elaborate assign");
892     auto ret = stdcUninitSlice!T(slice.shape);
893 
894     import mir.conv: emplaceRef;
895     import mir.algorithm.iteration: each;
896     each!(emplaceRef!E)(ret, slice);
897     return ret;
898 }
899 
900 /++
901 Frees memory using `core.stdc.stdlib.free`.
902 Params:
903     slice = n-dimensional slice
904 See_also:
905     $(LREF stdcSlice), $(LREF stdcUninitSlice)
906 +/
907 void stdcFreeSlice(T, size_t N)(Slice!(T*, N) slice)
908 {
909     import core.stdc.stdlib: free;
910     version (mir_secure_memory)
911     {
912         (cast(ubyte[])slice.field)[] = 0;
913     }
914     slice._iterator.free;
915 }
916 
917 ///
918 version(mir_test)
919 unittest
920 {
921     import mir.ndslice.topology: iota;
922     
923     auto i = iota(3, 4);
924     auto s = i.stdcSlice;
925     auto t = s.shape.stdcUninitSlice!size_t;
926     
927     t[] = s;
928 
929     assert(t == i);
930     
931     s.stdcFreeSlice;
932     t.stdcFreeSlice;
933 }
934 
935 /++
936 Allocates an uninitialized aligned array using `core.stdc.stdlib.malloc` and creates an n-dimensional slice over it.
937 Params:
938     lengths = list of lengths for each dimension
939     alignment = memory alignment (bytes)
940 Returns:
941     contiguous uninitialized n-dimensional slice
942 +/
943 auto stdcUninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @system
944 {
945     immutable len = lengths.lengthsProduct;
946     import mir.internal.memory: alignedAllocate;
947     auto arr = (cast(T*)alignedAllocate(len * T.sizeof, alignment))[0 .. len];
948     version (mir_secure_memory)
949     {
950         (cast(ubyte[])arr)[] = 0;
951     }
952     return arr.sliced(lengths);
953 }
954 
955 ///
956 version(mir_test)
957 @system pure nothrow unittest
958 {
959     import mir.ndslice.slice: Slice;
960     auto tensor = stdcUninitAlignedSlice!double([5, 6, 7], 64);
961     assert(tensor.length == 5);
962     assert(tensor.elementCount == 5 * 6 * 7);
963     assert(cast(size_t)(tensor.ptr) % 64 == 0);
964     static assert(is(typeof(tensor) == Slice!(double*, 3)));
965     stdcFreeAlignedSlice(tensor);
966 }
967 
968 /++
969 Frees aligned memory allocaged by CRuntime.
970 Params:
971     slice = n-dimensional slice
972 See_also:
973     $(LREF stdcSlice), $(LREF stdcUninitSlice)
974 +/
975 void stdcFreeAlignedSlice(T, size_t N)(Slice!(T*, N) slice)
976 {
977     import mir.internal.memory: alignedFree;
978     version (mir_secure_memory)
979     {
980         (cast(ubyte[])slice.field)[] = 0;
981     }
982     slice._iterator.alignedFree;
983 }