1 /// module for serialisation/deserialisation for files or other uses 2 module ydlib.serialise; 3 4 import std.string; 5 import std.bitmanip; 6 7 /// type of a value (integer/string/structure) 8 enum ValueType { 9 Integer = 0x00, 10 String = 0x01, 11 Structure = 0x02 12 } 13 14 /// flags used for different variations of a value 15 enum Flags { 16 Array = 0b00000001 17 } 18 19 /// contents of a value 20 union ValueContents { 21 int _int; 22 string _string; 23 24 this(int pint) { 25 _int = pint; 26 } 27 28 this(string pstring) { 29 _string = pstring; 30 } 31 } 32 33 /// main value structure 34 struct Value { 35 ValueType type; 36 bool isArray; 37 38 union { 39 ValueContents single; 40 ValueContents[] array; 41 Structure structure; 42 } 43 44 /// makes a value from an integer 45 this(int from) { 46 type = ValueType.Integer; 47 isArray = false; 48 single = ValueContents(from); 49 } 50 51 /// makes a value from a string 52 this(string from) { 53 type = ValueType.String; 54 isArray = false; 55 single = ValueContents(from); 56 } 57 58 /// makes a value from an integer array 59 this(int[] from) { 60 type = ValueType.Integer; 61 isArray = true; 62 63 foreach (ref val ; from) { 64 array ~= ValueContents(val); 65 } 66 } 67 68 /// makes a value from a string array 69 this(string[] from) { 70 type = ValueType.String; 71 isArray = true; 72 73 foreach (ref val ; from) { 74 array ~= ValueContents(val); 75 } 76 } 77 78 /// makes a value from a structure 79 this(Structure from) { 80 type = ValueType.Structure; 81 isArray = false; 82 structure = from; 83 } 84 85 /// makes a value from an integer 86 static Value Int(int from = 0) { 87 return Value(from); 88 } 89 90 /// makes a value from a string 91 static Value String(string from = "") { 92 return Value(from); 93 } 94 95 /// makes a value from an integer array 96 static Value IntArray(int[] from = []) { 97 return Value(from); 98 } 99 100 /// makes a value from a string array 101 static Value StringArray(string[] from = []) { 102 return Value(from); 103 } 104 105 /// returns the value's contents as an integer 106 int GetInt() { 107 assert(type == ValueType.Integer); 108 assert(!array); 109 110 return single._int; 111 } 112 113 /// returns the value's contents as a string 114 string GetString() { 115 assert(type == ValueType.String); 116 assert(!array); 117 118 return single._string; 119 } 120 121 /// returns the value's contents as an integer array 122 int[] GetIntArray() { 123 assert(type == ValueType.Integer); 124 assert(array); 125 126 int[] ret; 127 128 foreach (ref val ; array) { 129 ret ~= val._int; 130 } 131 132 return ret; 133 } 134 135 /// returns the value's contents as a string array 136 string[] GetStringArray() { 137 assert(type == ValueType.String); 138 assert(array); 139 140 string[] ret; 141 142 foreach (ref val ; array) { 143 ret ~= val._string; 144 } 145 146 return ret; 147 } 148 } 149 150 /// structures are just arrays of values 151 alias Structure = Value[]; 152 153 /// exception for DataManager methods 154 class DataException : Exception { 155 this(string msg, string file = __FILE__, size_t line = __LINE__) { 156 super(msg, file, line); 157 } 158 } 159 160 /// main class for data from a file etc 161 class DataManager { 162 Structure[string] structures; 163 164 /// adds a structure type 165 void AddStructure(string name, Structure structure) { 166 structures[name] = structure; 167 } 168 169 /// checks if a structure is valid 170 bool ValidStructure(string name, Structure structure) { 171 Structure type = structures[name]; 172 173 if (structure.length != type.length) { 174 return false; 175 } 176 177 foreach (i, ref val ; structure) { 178 if (val.type != type[i].type) { 179 return false; 180 } 181 } 182 183 return true; 184 } 185 186 /// turns the data into an array of bytes 187 ubyte[] Serialise(Structure structure) { 188 ubyte[] ret; 189 190 ret ~= nativeToBigEndian(cast(ulong) structure.length); 191 192 foreach (ref val ; structure) { 193 ret ~= cast(ubyte) val.type; 194 195 ubyte flags; 196 197 if (val.isArray) { 198 flags |= cast(ubyte) Flags.Array; 199 } 200 201 ret ~= flags; 202 203 if (val.isArray) { 204 ret ~= nativeToBigEndian(cast(ulong) val.array.length); 205 } 206 207 switch (val.type) { 208 case ValueType.Integer: { 209 if (val.isArray) { 210 foreach (ref val2 ; val.array) { 211 ret ~= nativeToBigEndian(val2._int); 212 } 213 } 214 else { 215 ret ~= nativeToBigEndian(val.single._int); 216 } 217 break; 218 } 219 case ValueType.String: { 220 if (val.isArray) { 221 foreach (ref val2 ; val.array) { 222 ret ~= val2._string ~ 0; 223 } 224 } 225 else { 226 ret ~= val.single._string; 227 } 228 break; 229 } 230 case ValueType.Structure: { 231 ret ~= Serialise(val.structure); 232 break; 233 } 234 default: assert(0); 235 } 236 } 237 238 return ret; 239 } 240 241 /// creates data from an array of bytes 242 Structure Deserialise(ref ubyte[] data) { 243 Structure ret; 244 245 ulong length = bigEndianToNative!ulong(data[0 .. 8]); 246 data = data[8 .. $]; 247 248 while ((data.length > 0) && (ret.length < length)) { 249 switch (cast(ValueType) data[0]) { 250 case ValueType.Integer: { 251 if (data[1] & Flags.Array) { 252 ulong len = bigEndianToNative!ulong(data[2 .. 10]); 253 254 data = data[10 .. $]; 255 256 Value array; 257 array.type = ValueType.Integer; 258 array.isArray = true; 259 260 for (ulong i = 0; i < len; ++i) { 261 array.array ~= ValueContents( 262 bigEndianToNative!int(data[0 .. 4]) 263 ); 264 data = data[4 .. $]; 265 } 266 267 ret ~= array; 268 } 269 else { 270 ret ~= Value(bigEndianToNative!int(data[1 .. 5])); 271 data = data[5 .. $]; 272 } 273 break; 274 } 275 case ValueType.String: { 276 string str; 277 278 data = data[1 .. $]; 279 280 while (data[0] > 0) { 281 str ~= data[0]; 282 data = data[1 .. $]; 283 } 284 285 data = data[1 .. $]; // remove null terminator 286 287 ret ~= Value(str); 288 break; 289 } 290 case ValueType.Structure: { 291 ret ~= Value(Deserialise(data)); 292 break; 293 } 294 default: assert(0); 295 } 296 } 297 298 return ret; 299 } 300 }