structs/source/structs/package.d

441 lines
10 KiB
D
Raw Normal View History

2020-05-27 09:30:58 +00:00
module structs;
private
{
2020-06-14 20:15:34 +00:00
import core.exception;
2020-05-27 09:30:58 +00:00
import std.array;
import std.bitmanip;
2020-06-14 20:15:34 +00:00
import std.exception;
2020-05-27 09:30:58 +00:00
import std.system;
/++
+ Supported format types
+/
2020-05-27 09:43:08 +00:00
enum FORMAT_TYPE
2020-05-27 09:30:58 +00:00
{
INT_8, /// Signed integer 8 bit
INT_16, /// Signed integer 16 bit
INT_32, /// Signed integer 32 bit
INT_64, /// Signed integer 64 bit
UINT_8, /// Unsigned integer 8 bit
UINT_16, /// Unsigned integer 16 bit
UINT_32, /// Unsigned integer 32 bit
2020-05-27 09:43:08 +00:00
UINT_64, /// Unsigned integer 64 bit
STRING /// C-String
2020-05-27 09:30:58 +00:00
}
/++
+ A element of a package
+/
struct Element
{
Endian endian; /// The endian to use
2020-06-14 20:15:34 +00:00
bool is_array; /// If it's an array
size_t array_size; /// Array size
FORMAT_TYPE format_type; /// The formated type
2020-05-27 09:43:08 +00:00
/++
+ Calculate the size of the element
+ Returns: Size in bytes
+/
size_t packSize() const
{
// Base format type size
size_t base_size = 0;
2020-06-15 11:09:46 +00:00
final switch (this.format_type)
2020-05-27 09:43:08 +00:00
{
case FORMAT_TYPE.STRING:
2020-06-15 11:09:46 +00:00
case FORMAT_TYPE.INT_8:
case FORMAT_TYPE.UINT_8:
2020-05-27 09:43:08 +00:00
base_size = 1;
break;
case FORMAT_TYPE.INT_16:
2020-06-15 11:09:46 +00:00
case FORMAT_TYPE.UINT_16:
2020-05-27 09:43:08 +00:00
base_size = 2;
break;
case FORMAT_TYPE.INT_32:
2020-06-15 11:09:46 +00:00
case FORMAT_TYPE.UINT_32:
2020-05-27 09:43:08 +00:00
base_size = 4;
break;
case FORMAT_TYPE.INT_64:
2020-06-15 11:09:46 +00:00
case FORMAT_TYPE.UINT_64:
2020-05-27 09:43:08 +00:00
base_size = 8;
break;
}
// Add array size
2020-06-14 20:15:34 +00:00
return base_size * this.array_size;
}
static pure Element genFromString(string data)
2020-06-15 11:09:46 +00:00
in (data.length > 0, "Format can't be empty.")
2020-06-14 20:15:34 +00:00
{
// Pre checks
size_t current_pos = 0;
// Read endian
2020-06-14 20:44:10 +00:00
bool endian_explicit = false;
2020-06-14 20:15:34 +00:00
Endian endian_found = std.system.endian;
2020-06-15 11:09:46 +00:00
switch (data[0])
2020-06-14 20:15:34 +00:00
{
case '=':
endian_found = std.system.endian;
current_pos++;
2020-06-14 20:44:10 +00:00
endian_explicit = true;
2020-06-14 20:15:34 +00:00
break;
case '<':
endian_found = Endian.littleEndian;
current_pos++;
2020-06-14 20:44:10 +00:00
endian_explicit = true;
2020-06-14 20:15:34 +00:00
break;
case '!':
2020-06-15 11:09:46 +00:00
case '>':
2020-06-14 20:15:34 +00:00
endian_found = Endian.bigEndian;
current_pos++;
2020-06-14 20:44:10 +00:00
endian_explicit = true;
2020-06-14 20:15:34 +00:00
break;
default:
}
// Get size
bool set_size = false;
size_t size = 0;
2020-06-15 11:09:46 +00:00
while ((current_pos < data.length) && ('0' <= data[current_pos]) && (data[current_pos] <= '9'))
2020-06-14 20:15:34 +00:00
{
set_size = true;
size = (size * 10) + (data[current_pos] - '0');
current_pos++;
}
2020-06-15 11:09:46 +00:00
if (!set_size)
2020-06-14 20:15:34 +00:00
{
size = 1;
}
// Get format
2020-06-15 11:09:46 +00:00
assert(current_pos + 1 == data.length, "Format '" ~ data[current_pos .. $] ~ "' isn't a valid format type.");
2020-06-14 20:15:34 +00:00
FORMAT_TYPE format_type_found;
2020-06-15 11:09:46 +00:00
switch (data[current_pos])
2020-06-14 20:15:34 +00:00
{
case 'b':
format_type_found = FORMAT_TYPE.INT_8;
current_pos++;
break;
case 'B':
format_type_found = FORMAT_TYPE.UINT_8;
current_pos++;
break;
case 'h':
format_type_found = FORMAT_TYPE.INT_16;
current_pos++;
break;
case 'H':
format_type_found = FORMAT_TYPE.UINT_16;
current_pos++;
break;
case 'i':
format_type_found = FORMAT_TYPE.INT_32;
current_pos++;
break;
case 'I':
format_type_found = FORMAT_TYPE.UINT_32;
current_pos++;
break;
case 'q':
format_type_found = FORMAT_TYPE.INT_64;
current_pos++;
break;
case 'Q':
format_type_found = FORMAT_TYPE.UINT_64;
current_pos++;
break;
case 's':
assert(set_size, "Size have to be set for the string.");
format_type_found = FORMAT_TYPE.STRING;
current_pos++;
break;
default:
2020-06-15 11:09:46 +00:00
assert(false, "Unknown format string: '" ~ data[current_pos .. $] ~ "'");
2020-06-14 20:15:34 +00:00
}
2020-06-14 20:44:10 +00:00
// TODO: Warn if endian isn't set explicit
2020-06-14 20:15:34 +00:00
// Return new struct
assert(current_pos == data.length);
Element result = {endian: endian_found, is_array: set_size, array_size: size, format_type: format_type_found};
return result;
}
}
template GET_TYPE(Element ELEMENT)
{
2020-06-15 11:09:46 +00:00
static if (ELEEMENT.format_type == FORMAT_TYPE.STRING)
2020-06-14 20:15:34 +00:00
{
alias GET_TYPE = string;
}
else
{
static assert(false);
2020-05-27 09:43:08 +00:00
}
2020-05-27 09:30:58 +00:00
}
/++
+ Removes the whitespaces of the string.
+ Params:
+ source = The source string to format
+ Returns: String without whitespaces
+/
pure string remove_whitespaces(string source)
{
return source.replace(" ", "");
}
2020-06-14 20:15:34 +00:00
pure Element[] parse_string(string source)
{
// Remove whitespaces
source = remove_whitespaces(source);
// Split after char
Element[] elements = [];
size_t pos = 0;
size_t last = 0;
2020-06-15 11:09:46 +00:00
while (pos < source.length)
2020-06-14 20:15:34 +00:00
{
2020-06-15 11:09:46 +00:00
if ((source[pos] >= 'a' && source[pos] <= 'z') || (source[pos] >= 'A' && source[pos] <= 'Z'))
2020-06-14 20:15:34 +00:00
{
2020-06-15 11:09:46 +00:00
elements ~= [Element.genFromString(source[last .. pos + 1])];
2020-06-14 20:15:34 +00:00
last = pos + 1;
}
pos++;
}
assert(last == pos, "Format doesn't end correctly.");
return elements;
}
2020-05-27 09:30:58 +00:00
// Test remove whitespaces.
unittest
{
assert(remove_whitespaces(" a b c ") == "abc");
}
2020-06-14 20:15:34 +00:00
// Test endian and length
unittest
{
{
const auto tmp = Element.genFromString("i");
assert(tmp.endian == endian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString("=i");
assert(tmp.endian == endian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString(">i");
assert(tmp.endian == Endian.bigEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString("!i");
assert(tmp.endian == Endian.bigEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString("<i");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString("<1i");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == true);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString("<11i");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == true);
assert(tmp.array_size == 11);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString("<111i");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == true);
assert(tmp.array_size == 111);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
}
// Test types parse
unittest
{
{
const auto tmp = Element.genFromString("<b");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_8);
}
{
const auto tmp = Element.genFromString("<B");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.UINT_8);
}
{
const auto tmp = Element.genFromString("<h");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_16);
}
{
const auto tmp = Element.genFromString("<H");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.UINT_16);
}
{
const auto tmp = Element.genFromString("<i");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_32);
}
{
const auto tmp = Element.genFromString("<I");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.UINT_32);
}
{
const auto tmp = Element.genFromString("<q");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.INT_64);
}
{
const auto tmp = Element.genFromString("<Q");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == false);
assert(tmp.array_size == 1);
assert(tmp.format_type == FORMAT_TYPE.UINT_64);
}
{
const auto tmp = Element.genFromString("<10s");
assert(tmp.endian == Endian.littleEndian);
assert(tmp.is_array == true);
assert(tmp.array_size == 10);
assert(tmp.format_type == FORMAT_TYPE.STRING);
}
assertThrown!AssertError(Element.genFromString("s"));
assertThrown!AssertError(Element.genFromString("Z"));
}
// Test parse string
unittest
{
{
const auto tmp = parse_string("<16i>16i=16i!16i");
2020-06-15 11:09:46 +00:00
foreach (i; tmp)
2020-06-14 20:15:34 +00:00
{
assert(i.is_array == true);
assert(i.array_size == 16);
assert(i.format_type == FORMAT_TYPE.INT_32);
}
}
assertThrown!AssertError(parse_string("i16"));
assertThrown!AssertError(parse_string("i!"));
assertThrown!AssertError(parse_string("16!i"));
}
2020-05-27 09:30:58 +00:00
}
public
{
2020-06-14 20:15:34 +00:00
/++
+ Base format type
+/
struct BaseFormat(string CONFIG)
{
private
{
2020-06-14 20:27:57 +00:00
static const(Element[]) elements = parse_string(CONFIG);
static const(size_t) elements_size = {
size_t size = 0;
static foreach (i; elements)
{
size += i.packSize();
}
return size;
}();
2020-06-14 20:15:34 +00:00
}
2020-05-27 09:30:58 +00:00
2020-06-14 20:15:34 +00:00
public
{
/++
+ Calculate the size of the format.
+ Returns: Size of the format
+/
static size_t size()
{
2020-06-14 20:27:57 +00:00
return elements_size;
2020-06-14 20:15:34 +00:00
}
}
}
}
private
{
2020-06-14 20:35:27 +00:00
size_t test_size(string FORMAT)()
{
size_t result = BaseFormat!FORMAT.size();
{
auto tmp = parse_string(FORMAT);
size_t calced = 0;
2020-06-15 11:09:46 +00:00
foreach (i; tmp)
2020-06-14 20:35:27 +00:00
{
calced += i.packSize();
}
assert(result == calced);
}
return result;
}
2020-06-14 20:15:34 +00:00
unittest
{
2020-06-14 20:35:27 +00:00
assert(test_size!"10s"() == 10);
assert(test_size!"b"() == 1);
assert(test_size!"4b"() == 4);
assert(test_size!"B"() == 1);
assert(test_size!"4B"() == 4);
assert(test_size!"h"() == 2);
assert(test_size!"4h"() == 8);
assert(test_size!"H"() == 2);
assert(test_size!"4H"() == 8);
assert(test_size!"i"() == 4);
assert(test_size!"4i"() == 16);
assert(test_size!"I"() == 4);
assert(test_size!"4I"() == 16);
assert(test_size!"q"() == 8);
assert(test_size!"4q"() == 32);
assert(test_size!"Q"() == 8);
assert(test_size!"4Q"() == 32);
2020-06-14 20:15:34 +00:00
}
2020-05-27 09:30:58 +00:00
}