1 /**
2 Provides value conversion functionality, supplemental to [std.conv].
3 
4 Authors: Tony J. Hudgins
5 Copyright: Copyright © 2019, Tony J. Hudgins
6 License: MIT
7 */
8 module dext.conv;
9 
10 import std.traits : isIntegral, isStaticArray, isDynamicArray;
11 import std.range  : ElementType;
12 
13 version( unittest ) import std.system : Endian, endian;
14 
15 private template isByteArray( N, A )
16 {
17     static if( isStaticArray!A )
18         enum isByteArray = A.length == N.sizeof && is( ElementType!A : ubyte );
19     else static if( isDynamicArray!A )
20         enum isByteArray = is( ElementType!A : ubyte );
21     else
22         enum isByteArray = false;
23 }
24 
25 /++
26 Converts an integral number to a static or dynamic array of [ubyte].
27 
28 Authors: Tony J. Hudgins
29 Copyright: Copyright © 2019, Tony J. Hudgins
30 License: MIT
31 
32 Examples:
33 ---------
34 int num = 10;
35 ubyte[4] bytes = num.to!( ubyte[4] );
36 ---------
37 +/
38 A to( A, N )( N x ) if( isByteArray!( N, A ) && isIntegral!N )
39 {
40     static union Numeric
41     {
42         N num;
43         ubyte[N.sizeof] bytes;
44     }
45 
46     static if( isDynamicArray!A )
47         return Numeric( x ).bytes.dup;
48     else static if( isStaticArray!A )
49     {
50         auto num = Numeric( x );
51         A copy;
52         copy[] = num.bytes[];
53 
54         return copy;
55     } else static assert( false );
56 }
57 
58 @system unittest
59 {
60     // dynamic array
61     enum int five = 5;
62 
63     static if( endian == Endian.littleEndian )
64     {
65         enum ubyte[] expectedDynamic = [ 5, 0, 0, 0 ];
66         enum ubyte[int.sizeof] expectedStatic = [ 5, 0, 0, 0 ];
67     }
68     else static if( endian == Endian.bigEndian )
69     {
70         enum ubyte[] expectedDynamic = [ 0, 0, 0, 5 ];
71         enum ubyte[int.sizeof] expectedStatic = [ 0, 0, 0, 5 ];
72     }
73 
74     auto x = five.to!( ubyte[] );
75     assert( x == expectedDynamic, "num -> ubyte[]" );
76 
77     auto y = five.to!( ubyte[4] );
78     assert( x == expectedStatic, "num -> ubyte[4]" );
79 }
80 
81 /++
82 Converts an array (either static or dynamic, but not associative) of [ubyte] to an integral number.
83 
84 Authors: Tony J. Hudgins
85 Copyright: Copyright © 2019, Tony J. Hudgins
86 License: MIT
87 
88 Examples:
89 ---------
90 import std.system : Endian, endian;
91 
92 static if( endian == Endian.littleEndian )
93     auto bytes = [ 10, 0, 0, 0 ];
94 else
95     auto bytes = [ 0, 0, 0, 10 ];
96 
97 int num = bytes.to!int; // 10
98 ---------
99 +/
100 N to( N, A )( A arr ) if( isByteArray!( N, A ) && isIntegral!N )
101 {
102     static union Numeric
103     {
104         ubyte[N.sizeof] bytes;
105         N num;
106     }
107 
108     static if( isStaticArray!A )
109         return Numeric( arr ).num;
110     else
111     {
112         if( arr.length != N.sizeof )
113         {
114             import std.format : format;
115             import std.conv   : ConvException;
116 
117             throw new ConvException(
118                 "byte array must be equal to %s.sizeof (%s). actual length: %s"
119                 .format(
120                     N.stringof,
121                     N.sizeof,
122                     arr.length
123                 )
124             );
125         }
126 
127         Numeric num;
128         num.bytes[] = arr[];
129 
130         return num.num;
131     }
132 }
133 
134 @system unittest
135 {
136     static if( endian == Endian.littleEndian )
137     {
138         enum ubyte[] dynamicArray = [ 5, 0, 0, 0 ];
139         enum ubyte[int.sizeof] staticArray = [ 5, 0, 0, 0 ];
140     }
141     else static if( endian == Endian.bigEndian )
142     {
143         enum ubyte[] dynamicArray = [ 0, 0, 0, 5 ];
144         enum ubyte[int.sizeof] staticArray = [ 0, 0, 0, 5 ];
145     }
146 
147     enum int expected = 5;
148 
149     auto x = dynamicArray.to!int;
150     assert( x == expected, "ubyte[] -> int" );
151 
152     auto y = staticArray.to!int;
153     assert( x == expected, "ubyte[4] -> int" );
154 }