1 /** 2 This module implements functions for encoding various D types into ubyte arrays. Note that the encoding is not 3 cross platform and results will be different across platforms depending on CPU architecture. 4 5 ──────────────────────────────────────────────────────────────────────────────── 6 7 Copyright (C) 2021 pillager86.rf.gd 8 9 This program is free software: you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation, either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 16 PARTICULAR PURPOSE. See the GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 module mildew.util.encode; 22 23 import std.traits: isBasicType; 24 25 debug import std.stdio; 26 27 /** 28 * Encodes a value as an ubyte array. Note that only simple types, and arrays of simple types can be encoded. 29 * Params: 30 * value = The value to be encoded. 31 * Returns: 32 * The value stored as an ubyte[] 33 */ 34 ubyte[] encode(T)(T value) 35 { 36 ubyte[] encoding; 37 static if(isBasicType!T) 38 { 39 encoding ~= (cast(ubyte*)&value)[0..T.sizeof]; 40 } 41 else static if(is(T == E[], E)) 42 { 43 static assert(isBasicType!E, "Only arrays of basic types are supported"); 44 size_t size = value.length; 45 encoding ~= (cast(ubyte*)&size)[0..size.sizeof]; 46 foreach(item ; value) 47 encoding ~= encode(item); 48 } 49 else 50 { 51 static assert(false, "Unable to encode type " ~ T.stringof); 52 } 53 return encoding; 54 } 55 56 /** 57 * decode a value from an ubyte range. 58 * Params: 59 * data = An ubyte[] that should be large enough to contain the size of T otherwise an exception is thrown. 60 * Returns: 61 * The decoded piece of data described by type T. 62 */ 63 T decode(T)(const ubyte[] data) 64 { 65 static if(isBasicType!T) 66 { 67 if(data.length < T.sizeof) 68 throw new EncodeException("Data length is too short for type " ~ T.stringof); 69 return *cast(T*)(data.ptr); 70 } 71 else static if(is(T==E[], E)) 72 { 73 static assert(isBasicType!E, "Only arrays of basic types are supported"); 74 if(data.length < size_t.sizeof) 75 throw new EncodeException("Data length is shorter than size_t for " ~ T.stringof); 76 size_t size = *cast(size_t*)(data.ptr); 77 if(data.length < size_t.sizeof + E.sizeof * size) 78 throw new EncodeException("Data length is too short for array elements " ~ E.stringof); 79 T array = new T(size); 80 static if(is(E==ubyte)) 81 { 82 for(size_t i = 0; i < size; ++i) 83 array[i] = cast(E)data[size_t.sizeof + i]; 84 } 85 else 86 { 87 for(size_t i = 0; i < size; ++i) 88 array[i] = cast(E)decode!E(data[size_t.sizeof + i * E.sizeof..$]); 89 } 90 return array; 91 } 92 else static assert(false, "Unable to decode type " ~ T.stringof); 93 } 94 95 /// Thrown when decoding 96 class EncodeException : Exception 97 { 98 /// ctor 99 this(string msg, string file = __FILE__, size_t line = __LINE__) 100 { 101 super(msg, file, line); 102 } 103 } 104 105 unittest 106 { 107 import std.format: format; 108 auto testInts = [1, 5, 9]; 109 auto encoded = encode(testInts); 110 auto decoded = decode!(int[])(encoded[0..$]); 111 assert(decoded.length == 3); 112 assert(decoded[0] == 1, "Value is actually " ~ format("%x", decoded[0])); 113 assert(decoded[1] == 5); 114 assert(decoded[2] == 9); 115 }