1 /**
2  This module contains compiler support for casting dynamic arrays
3 
4   Copyright: Copyright Digital Mars 2000 - 2019.
5   License: Distributed under the
6        $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7      (See accompanying file LICENSE)
8   Source: $(DRUNTIMESRC core/internal/_array/_casting.d)
9 */
10 module core.internal.array.casting;
11 
12 /**
13 Used by `__ArrayCast` to emit a descriptive error message.
14 
15 It is a template so it can be used by `__ArrayCast` in -betterC
16 builds.  It is separate from `__ArrayCast` to minimize code
17 bloat.
18 
19 Params:
20     fromType = name of the type being cast from
21     fromSize = total size in bytes of the array being cast from
22     toType   = name of the type being cast o
23     toSize   = total size in bytes of the array being cast to
24  */
onArrayCastError()25 private void onArrayCastError()(string fromType, size_t fromSize, string toType, size_t toSize) @trusted
26 {
27     import core.internal.string : unsignedToTempString;
28     import core.memory : pureMalloc;
29 
30     // convert discontiguous `msgComponents` to contiguous string on the C heap
31     enum msgLength = 2048;
32     // note: never freed!
33     char* msg = cast(char *)pureMalloc(msgLength);
34 
35     size_t index = 0;
36     void add(const(char)[] m)
37     {
38         import core.stdc.string : memcpy;
39 
40         auto N = msgLength - 1 - index;
41         if (N > m.length)
42             N = m.length;
43         // prevent superfluous and betterC-unfriendly checks via direct memcpy
44         memcpy(msg + index, m.ptr, N);
45         index += N;
46     }
47 
48     add("An array of size ");
49     auto s = unsignedToTempString(fromSize);
50     add(s[]);
51     add(" does not align on an array of size ");
52     s = unsignedToTempString(toSize);
53     add(s[]);
54     add(", so `");
55     add(fromType);
56     add("` cannot be cast to `");
57     add(toType);
58     add("`");
59     msg[index] = '\0'; // null-termination
60 
61     // first argument must evaluate to `false` at compile-time to maintain memory safety in release builds
62     assert(false, msg[0 .. index]);
63 }
64 
65 /**
66 The compiler lowers expressions of `cast(TTo[])TFrom[]` to
67 this implementation.
68 
69 Params:
70     from = the array to reinterpret-cast
71 
72 Returns:
73     `from` reinterpreted as `TTo[]`
74  */
__ArrayCast(TFrom,TTo)75 TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted
76 {
77     const fromSize = from.length * TFrom.sizeof;
78     const toLength = fromSize / TTo.sizeof;
79 
80     if ((fromSize % TTo.sizeof) != 0)
81     {
82         onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof);
83     }
84 
85     struct Array
86     {
87         size_t length;
88         void* ptr;
89     }
90     auto a = cast(Array*)&from;
91     a.length = toLength; // jam new length
92     return *cast(TTo[]*)a;
93 }
94 
95 @safe @nogc pure nothrow unittest
96 {
97     byte[int.sizeof * 3] b = cast(byte) 0xab;
98     int[] i;
99     short[] s;
100 
101     i = __ArrayCast!(byte, int)(b);
102     assert(i.length == 3);
103     foreach (v; i)
104         assert(v == cast(int) 0xabab_abab);
105 
106     s = __ArrayCast!(byte, short)(b);
107     assert(s.length == 6);
108     foreach (v; s)
109         assert(v == cast(short) 0xabab);
110 
111     s = __ArrayCast!(int, short)(i);
112     assert(s.length == 6);
113     foreach (v; s)
114         assert(v == cast(short) 0xabab);
115 }
116