1 // Written in the D programming language.
2 
3 /**
4 Module for manipulating bytes in constructing and deconstructing various byte orders. 
5 
6 Copyright: LightHouse Software, 2021
7 License:   $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0).
8 Authors:   Oleg Bakharev,
9 		   Ilya Pertsev
10 */
11 module styx2000.lowlevel.endianness;
12 
13 /// Endianess (i.e byte-order)
14 enum BYTE_ORDER
15 {
16 	/// Little-endian byte-order
17 	LITTLE_ENDIAN,
18 	/// Big-endian byte-order
19 	BIG_ENDIAN
20 }
21 
22 
23 /**
24 	A class that provides functionality for construct and deconstruct values. All methods is static.
25 */
26 class EndianSequence(BYTE_ORDER byteOrder)
27 {	
28 	/**
29 	Constructs (restores) a value from the passed in byte array.
30     Params:
31 	byteOrder = Byte order for value (little or big endian).
32     bytes = Array of unsigned bytes for reconstructing value.
33     
34     Typical usage:
35     ----
36     uint tmp = EndianSequence!(BYTE_ORDER.LITTLE_ENDIAN).unpack!uint([0xab, 0xcd, 0xef, 0x00]);
37     ----
38     */
39     private {
40 		import std.traits : isBasicType;
41 		
42 		static auto shiftOf(T)(T i) if (isBasicType!T)
43 		{
44 			static if (byteOrder == BYTE_ORDER.LITTLE_ENDIAN)
45 	            return i << 3;
46 	        else
47 	            return (T.sizeof - i - 1) << 3;
48 		}
49 	}
50     
51 	static T unpack(T)(ubyte[] bytes...)
52     {
53         T result;
54        
55         foreach (i, e; bytes)
56         {
57             result |= (e << shiftOf(i));
58         }
59 
60         return result;
61     }
62 
63 	/**
64 	Deconstructs (saves) a passed value to unsigned byte array.
65     Params:
66 	byteOrder = Byte order for value (little or big endian).
67     value = Value for deconstructing.
68     
69     Typical usage:
70     ----
71     ubyte[] tmp = EndianSequence!(BYTE_ORDER.LITTLE_ENDIAN).pack!uint(150_000);
72     ----
73     */
74     static ubyte[] pack(T)(T value)
75     {
76         ubyte[] data;
77         T mask = T(0xff);
78 
79         foreach (i; 0 .. T.sizeof)
80         {
81 			auto shift = shiftOf(i);
82             data ~= cast(ubyte) ((value & (mask << shift)) >> shift);
83         }
84 
85         return data;
86     }
87 }
88 
89 
90 /**
91 Parse a value into bytes in little-endian order. Helper for EndianSequence.pack with preset Little Endian byte order (LE).
92 Params:
93 value = Value for parsing.
94 Returns: An array of unsigned bytes representing the value passed as a parameter in little-endian order
95 
96 
97 Typical usage:
98 ----
99 ubyte[] tmp = toLEBytes!uint(150_000);
100 ----
101 */
102 alias toLEBytes = EndianSequence!(BYTE_ORDER.LITTLE_ENDIAN).pack;
103 
104 /**
105 Construct a value from bytes fed in little-endian order. Helper for EndianSequence.unpack with preset Little Endian byte order (LE).
106 Params:
107 bytes = An array of unsigned bytes (little-endian-order).
108 Returns: The value retrieved from the array
109 
110 
111 Typical usage:
112 ----
113 // value is 0x00efcdab in hex or 15715755 in dec  
114 uint tmp = fromLEBytes!uint([0xab, 0xcd, 0xef, 0x00]);
115 ----
116 */
117 alias fromLEBytes = EndianSequence!(BYTE_ORDER.LITTLE_ENDIAN).unpack;
118 
119 /**
120 Parse a value into bytes in big-endian order. Helper for EndianSequence.pack with preset Big Endian byte order (BE).
121 Params:
122 value = Value for parsing.
123 Returns: An array of unsigned bytes representing the value passed as a parameter in little-endian order
124 
125 
126 Typical usage:
127 ----
128 ubyte[] tmp = toBEBytes!uint(150_000);
129 ----
130 */
131 alias toBEBytes = EndianSequence!(BYTE_ORDER.BIG_ENDIAN).pack;
132 
133 /**
134 Construct a value from bytes fed in big-endian order. Helper for EndianSequence.unpack with preset Big Endian byte order (BE).
135 Params:
136 bytes = An array of unsigned bytes (big-endian-order).
137 Returns: The value retrieved from the array
138 
139 
140 Typical usage:
141 ----
142 // value is 0xabcdef00 in hex or 2882400000 in dec
143 uint tmp = fromBEBytes!uint([0xab, 0xcd, 0xef, 0x00]);
144 ----
145 */
146 alias fromLEBytes = EndianSequence!(BYTE_ORDER.LITTLE_ENDIAN).unpack;