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 }