Support basic unpacking
parent
d6f7aefc30
commit
820cf9df35
|
@ -30,26 +30,26 @@ public
|
||||||
{
|
{
|
||||||
destination.write!(TYPE, Endian.littleEndian)(value, 0);
|
destination.write!(TYPE, Endian.littleEndian)(value, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
+ Dumps the value in the system's endian.
|
+ Loads the value with little endian.
|
||||||
+ Params:
|
+ Params:
|
||||||
+ value = Input value to dump
|
+ source = Source buffer
|
||||||
+ destination = Target output buffer
|
|
||||||
+/
|
+/
|
||||||
@nogc pure nothrow void dumpNative(TYPE)(immutable TYPE value, ubyte[] destination)
|
@nogc pure nothrow TYPE loadBigEndian(TYPE)(immutable(ubyte)[] source)
|
||||||
|
in (source.length == TYPE.sizeof)
|
||||||
{
|
{
|
||||||
static if (endian == Endian.bigEndian)
|
return source.read!(TYPE, Endian.bigEndian)();
|
||||||
{
|
}
|
||||||
dumpBigEndian(value, destination);
|
/++
|
||||||
}
|
+ Dumps the value in big endian.
|
||||||
else static if (endian == Endian.littleEndian)
|
+ Params:
|
||||||
{
|
+ source = Source buffer
|
||||||
dumpLittleEndian(value, destination);
|
+/
|
||||||
}
|
@nogc pure nothrow TYPE loadLittleEndian(TYPE)(immutable(ubyte)[] source)
|
||||||
else
|
in (source.length == TYPE.sizeof)
|
||||||
{
|
{
|
||||||
static assert(false);
|
return source.read!(TYPE, Endian.littleEndian)();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,23 +5,30 @@ private
|
||||||
import core.exception;
|
import core.exception;
|
||||||
import std.array;
|
import std.array;
|
||||||
import std.bitmanip;
|
import std.bitmanip;
|
||||||
|
import std.conv;
|
||||||
import std.exception;
|
import std.exception;
|
||||||
|
import std.format;
|
||||||
|
import std.meta;
|
||||||
|
import std.range;
|
||||||
|
import std.string;
|
||||||
import std.system;
|
import std.system;
|
||||||
|
import std.typecons;
|
||||||
|
import structs.endian;
|
||||||
|
|
||||||
/++
|
/++
|
||||||
+ Supported format types
|
+ Supported format types
|
||||||
+/
|
+/
|
||||||
enum FORMAT_TYPE
|
enum FormatType
|
||||||
{
|
{
|
||||||
INT_8, /// Signed integer 8 bit
|
int8, /// Signed integer 8 bit
|
||||||
INT_16, /// Signed integer 16 bit
|
int16, /// Signed integer 16 bit
|
||||||
INT_32, /// Signed integer 32 bit
|
int32, /// Signed integer 32 bit
|
||||||
INT_64, /// Signed integer 64 bit
|
int64, /// Signed integer 64 bit
|
||||||
UINT_8, /// Unsigned integer 8 bit
|
uint8, /// Unsigned integer 8 bit
|
||||||
UINT_16, /// Unsigned integer 16 bit
|
uint16, /// Unsigned integer 16 bit
|
||||||
UINT_32, /// Unsigned integer 32 bit
|
uint32, /// Unsigned integer 32 bit
|
||||||
UINT_64, /// Unsigned integer 64 bit
|
uint64, /// Unsigned integer 64 bit
|
||||||
STRING /// C-String
|
str /// C-String
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -30,151 +37,216 @@ private
|
||||||
struct Element
|
struct Element
|
||||||
{
|
{
|
||||||
Endian endian; /// The endian to use
|
Endian endian; /// The endian to use
|
||||||
bool is_array; /// If it's an array
|
bool isArray; /// If it's an array
|
||||||
size_t array_size; /// Array size
|
size_t arraySize; /// Array size
|
||||||
FORMAT_TYPE format_type; /// The formated type
|
FormatType formatType; /// The formated type
|
||||||
|
|
||||||
|
pure nothrow @nogc size_t baseSize() const
|
||||||
|
{
|
||||||
|
final switch (this.formatType)
|
||||||
|
{
|
||||||
|
case FormatType.str:
|
||||||
|
case FormatType.int8:
|
||||||
|
case FormatType.uint8:
|
||||||
|
return 1;
|
||||||
|
case FormatType.int16:
|
||||||
|
case FormatType.uint16:
|
||||||
|
return 2;
|
||||||
|
case FormatType.int32:
|
||||||
|
case FormatType.uint32:
|
||||||
|
return 4;
|
||||||
|
case FormatType.int64:
|
||||||
|
case FormatType.uint64:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
/++
|
/++
|
||||||
+ Calculate the size of the element
|
+ Calculate the size of the element
|
||||||
+ Returns: Size in bytes
|
+ Returns: Size in bytes
|
||||||
+/
|
+/
|
||||||
size_t packSize() const
|
pure nothrow @nogc size_t packSize() const
|
||||||
{
|
{
|
||||||
// Base format type size
|
// Base format type size
|
||||||
size_t base_size = 0;
|
size_t baseSize = this.baseSize();
|
||||||
final switch (this.format_type)
|
|
||||||
{
|
|
||||||
case FORMAT_TYPE.STRING:
|
|
||||||
case FORMAT_TYPE.INT_8:
|
|
||||||
case FORMAT_TYPE.UINT_8:
|
|
||||||
base_size = 1;
|
|
||||||
break;
|
|
||||||
case FORMAT_TYPE.INT_16:
|
|
||||||
case FORMAT_TYPE.UINT_16:
|
|
||||||
base_size = 2;
|
|
||||||
break;
|
|
||||||
case FORMAT_TYPE.INT_32:
|
|
||||||
case FORMAT_TYPE.UINT_32:
|
|
||||||
base_size = 4;
|
|
||||||
break;
|
|
||||||
case FORMAT_TYPE.INT_64:
|
|
||||||
case FORMAT_TYPE.UINT_64:
|
|
||||||
base_size = 8;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add array size
|
// Add array size
|
||||||
return base_size * this.array_size;
|
return baseSize * this.arraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pure Element genBase() const
|
||||||
|
{
|
||||||
|
Element result = {};
|
||||||
|
result.endian = this.endian;
|
||||||
|
result.isArray = false;
|
||||||
|
result.arraySize = 1;
|
||||||
|
result.formatType = this.formatType;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pure Element genFromString(string data)
|
static pure Element genFromString(string data)
|
||||||
in (data.length > 0, "Format can't be empty.")
|
in (data.length > 0, "Format can't be empty.")
|
||||||
{
|
{
|
||||||
// Pre checks
|
// Pre checks
|
||||||
size_t current_pos = 0;
|
size_t currentPos = 0;
|
||||||
|
|
||||||
// Read endian
|
// Read endian
|
||||||
bool endian_explicit = false;
|
bool endianExplicit = false;
|
||||||
Endian endian_found = std.system.endian;
|
Endian endianFound = std.system.endian;
|
||||||
switch (data[0])
|
switch (data[0])
|
||||||
{
|
{
|
||||||
case '=':
|
case '=':
|
||||||
endian_found = std.system.endian;
|
endianFound = std.system.endian;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
endian_explicit = true;
|
endianExplicit = true;
|
||||||
break;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
endian_found = Endian.littleEndian;
|
endianFound = Endian.littleEndian;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
endian_explicit = true;
|
endianExplicit = true;
|
||||||
break;
|
break;
|
||||||
case '!':
|
case '!':
|
||||||
case '>':
|
case '>':
|
||||||
endian_found = Endian.bigEndian;
|
endianFound = Endian.bigEndian;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
endian_explicit = true;
|
endianExplicit = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get size
|
// Get size
|
||||||
bool set_size = false;
|
bool setSize = false;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
while ((current_pos < data.length) && ('0' <= data[current_pos]) && (data[current_pos] <= '9'))
|
while ((currentPos < data.length) && ('0' <= data[currentPos]) && (data[currentPos] <= '9'))
|
||||||
{
|
{
|
||||||
set_size = true;
|
setSize = true;
|
||||||
size = (size * 10) + (data[current_pos] - '0');
|
size = (size * 10) + (data[currentPos] - '0');
|
||||||
current_pos++;
|
currentPos++;
|
||||||
}
|
}
|
||||||
if (!set_size)
|
if (!setSize)
|
||||||
{
|
{
|
||||||
size = 1;
|
size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get format
|
// Get format
|
||||||
assert(current_pos + 1 == data.length, "Format '" ~ data[current_pos .. $] ~ "' isn't a valid format type.");
|
assert(currentPos + 1 == data.length, "Format '" ~ data[currentPos .. $] ~ "' isn't a valid format type.");
|
||||||
FORMAT_TYPE format_type_found;
|
FormatType formatTypeFound;
|
||||||
switch (data[current_pos])
|
switch (data[currentPos])
|
||||||
{
|
{
|
||||||
case 'b':
|
case 'b':
|
||||||
format_type_found = FORMAT_TYPE.INT_8;
|
formatTypeFound = FormatType.int8;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
format_type_found = FORMAT_TYPE.UINT_8;
|
formatTypeFound = FormatType.uint8;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
format_type_found = FORMAT_TYPE.INT_16;
|
formatTypeFound = FormatType.int16;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 'H':
|
case 'H':
|
||||||
format_type_found = FORMAT_TYPE.UINT_16;
|
formatTypeFound = FormatType.uint16;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
format_type_found = FORMAT_TYPE.INT_32;
|
formatTypeFound = FormatType.int32;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
format_type_found = FORMAT_TYPE.UINT_32;
|
formatTypeFound = FormatType.uint32;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
format_type_found = FORMAT_TYPE.INT_64;
|
formatTypeFound = FormatType.int64;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
format_type_found = FORMAT_TYPE.UINT_64;
|
formatTypeFound = FormatType.uint64;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
assert(set_size, "Size have to be set for the string.");
|
assert(setSize, "Size have to be set for the string.");
|
||||||
format_type_found = FORMAT_TYPE.STRING;
|
formatTypeFound = FormatType.str;
|
||||||
current_pos++;
|
currentPos++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false, "Unknown format string: '" ~ data[current_pos .. $] ~ "'");
|
assert(false, "Unknown format string: '" ~ data[currentPos .. $] ~ "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Warn if endian isn't set explicit
|
// Check endians
|
||||||
|
assert((formatTypeFound != FormatType.str) || !endianExplicit, "Endian isn't allowed for strings."); // Is string ==> not endian set
|
||||||
|
assert(((formatTypeFound != FormatType.int8) && (formatTypeFound != FormatType.uint8)
|
||||||
|
&& (formatTypeFound != FormatType.str))
|
||||||
|
|| !endianExplicit, "Endian isn't allowed for (unsigned) bytes or strings."); // Is byte ==> not endian set
|
||||||
|
assert((formatTypeFound == FormatType.str) || (formatTypeFound == FormatType.int8)
|
||||||
|
|| (formatTypeFound == FormatType.uint8)
|
||||||
|
|| endianExplicit, "Endian is required for non string or (unsigned) char."); // Is not string, int8 or uint8 ==> endian set
|
||||||
|
|
||||||
// Return new struct
|
// Return new struct
|
||||||
assert(current_pos == data.length);
|
assert(currentPos == data.length);
|
||||||
Element result = {endian: endian_found, is_array: set_size, array_size: size, format_type: format_type_found};
|
Element result = {endian: endianFound, isArray: setSize, arraySize: size, formatType: formatTypeFound};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template GET_TYPE(Element ELEMENT)
|
/++
|
||||||
|
+ Generate type of an element
|
||||||
|
+/
|
||||||
|
template GetType(Element ELEMENT)
|
||||||
{
|
{
|
||||||
static if (ELEEMENT.format_type == FORMAT_TYPE.STRING)
|
static if (ELEMENT.formatType == FormatType.str)
|
||||||
{
|
{
|
||||||
alias GET_TYPE = string;
|
alias GetType = string;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static assert(false);
|
// Get type
|
||||||
|
static if (ELEMENT.formatType == FormatType.int8)
|
||||||
|
{
|
||||||
|
alias TMP = byte;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.formatType == FormatType.int16)
|
||||||
|
{
|
||||||
|
alias TMP = short;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.formatType == FormatType.int32)
|
||||||
|
{
|
||||||
|
alias TMP = int;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.formatType == FormatType.int64)
|
||||||
|
{
|
||||||
|
alias TMP = long;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.formatType == FormatType.uint8)
|
||||||
|
{
|
||||||
|
alias TMP = ubyte;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.formatType == FormatType.uint16)
|
||||||
|
{
|
||||||
|
alias TMP = ushort;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.formatType == FormatType.uint32)
|
||||||
|
{
|
||||||
|
alias TMP = uint;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.formatType == FormatType.uint64)
|
||||||
|
{
|
||||||
|
alias TMP = ulong;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is array
|
||||||
|
static if (ELEMENT.isArray)
|
||||||
|
{
|
||||||
|
alias GetType = TMP[ELEMENT.arraySize];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alias GetType = TMP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,15 +256,15 @@ private
|
||||||
+ source = The source string to format
|
+ source = The source string to format
|
||||||
+ Returns: String without whitespaces
|
+ Returns: String without whitespaces
|
||||||
+/
|
+/
|
||||||
pure string remove_whitespaces(string source)
|
pure string removeWhitespaces(string source)
|
||||||
{
|
{
|
||||||
return source.replace(" ", "");
|
return source.replace(" ", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
pure Element[] parse_string(string source)
|
pure Element[] parseString(string source)
|
||||||
{
|
{
|
||||||
// Remove whitespaces
|
// Remove whitespaces
|
||||||
source = remove_whitespaces(source);
|
source = removeWhitespaces(source);
|
||||||
|
|
||||||
// Split after char
|
// Split after char
|
||||||
Element[] elements = [];
|
Element[] elements = [];
|
||||||
|
@ -214,67 +286,61 @@ private
|
||||||
// Test remove whitespaces.
|
// Test remove whitespaces.
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
assert(remove_whitespaces(" a b c ") == "abc");
|
assert(removeWhitespaces(" a b c ") == "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test endian and length
|
// Test endian and length
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
{
|
assertThrown!AssertError(Element.genFromString("i"));
|
||||||
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");
|
const auto tmp = Element.genFromString("=i");
|
||||||
assert(tmp.endian == endian);
|
assert(tmp.endian == endian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString(">i");
|
const auto tmp = Element.genFromString(">i");
|
||||||
assert(tmp.endian == Endian.bigEndian);
|
assert(tmp.endian == Endian.bigEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("!i");
|
const auto tmp = Element.genFromString("!i");
|
||||||
assert(tmp.endian == Endian.bigEndian);
|
assert(tmp.endian == Endian.bigEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<i");
|
const auto tmp = Element.genFromString("<i");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<1i");
|
const auto tmp = Element.genFromString("<1i");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == true);
|
assert(tmp.isArray == true);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<11i");
|
const auto tmp = Element.genFromString("<11i");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == true);
|
assert(tmp.isArray == true);
|
||||||
assert(tmp.array_size == 11);
|
assert(tmp.arraySize == 11);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<111i");
|
const auto tmp = Element.genFromString("<111i");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == true);
|
assert(tmp.isArray == true);
|
||||||
assert(tmp.array_size == 111);
|
assert(tmp.arraySize == 111);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,67 +348,67 @@ private
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<b");
|
const auto tmp = Element.genFromString("b");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_8);
|
assert(tmp.formatType == FormatType.int8);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<B");
|
const auto tmp = Element.genFromString("B");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.UINT_8);
|
assert(tmp.formatType == FormatType.uint8);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<h");
|
const auto tmp = Element.genFromString("<h");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_16);
|
assert(tmp.formatType == FormatType.int16);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<H");
|
const auto tmp = Element.genFromString("<H");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.UINT_16);
|
assert(tmp.formatType == FormatType.uint16);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<i");
|
const auto tmp = Element.genFromString("<i");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_32);
|
assert(tmp.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<I");
|
const auto tmp = Element.genFromString("<I");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.UINT_32);
|
assert(tmp.formatType == FormatType.uint32);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<q");
|
const auto tmp = Element.genFromString("<q");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.INT_64);
|
assert(tmp.formatType == FormatType.int64);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<Q");
|
const auto tmp = Element.genFromString("<Q");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == false);
|
assert(tmp.isArray == false);
|
||||||
assert(tmp.array_size == 1);
|
assert(tmp.arraySize == 1);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.UINT_64);
|
assert(tmp.formatType == FormatType.uint64);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const auto tmp = Element.genFromString("<10s");
|
const auto tmp = Element.genFromString("10s");
|
||||||
assert(tmp.endian == Endian.littleEndian);
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
assert(tmp.is_array == true);
|
assert(tmp.isArray == true);
|
||||||
assert(tmp.array_size == 10);
|
assert(tmp.arraySize == 10);
|
||||||
assert(tmp.format_type == FORMAT_TYPE.STRING);
|
assert(tmp.formatType == FormatType.str);
|
||||||
}
|
}
|
||||||
assertThrown!AssertError(Element.genFromString("s"));
|
assertThrown!AssertError(Element.genFromString("s"));
|
||||||
assertThrown!AssertError(Element.genFromString("Z"));
|
assertThrown!AssertError(Element.genFromString("Z"));
|
||||||
|
@ -352,31 +418,91 @@ private
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
const auto tmp = parse_string("<16i>16i=16i!16i");
|
const auto tmp = parseString("<16i>16i=16i!16i");
|
||||||
foreach (i; tmp)
|
foreach (i; tmp)
|
||||||
{
|
{
|
||||||
assert(i.is_array == true);
|
assert(i.isArray == true);
|
||||||
assert(i.array_size == 16);
|
assert(i.arraySize == 16);
|
||||||
assert(i.format_type == FORMAT_TYPE.INT_32);
|
assert(i.formatType == FormatType.int32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertThrown!AssertError(parse_string("i16"));
|
assertThrown!AssertError(parseString("i16"));
|
||||||
assertThrown!AssertError(parse_string("i!"));
|
assertThrown!AssertError(parseString("i!"));
|
||||||
assertThrown!AssertError(parse_string("16!i"));
|
assertThrown!AssertError(parseString("16!i"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test helper functions
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
{
|
||||||
|
Element tmp = {};
|
||||||
|
tmp.arraySize = 10;
|
||||||
|
tmp.endian = Endian.littleEndian;
|
||||||
|
tmp.formatType = FormatType.int8;
|
||||||
|
tmp.isArray = true;
|
||||||
|
const Element test = tmp.genBase();
|
||||||
|
assert(test.arraySize == 1);
|
||||||
|
assert(test.endian == Endian.littleEndian);
|
||||||
|
assert(test.formatType == FormatType.int8);
|
||||||
|
assert(test.isArray == false);
|
||||||
|
assert(tmp.arraySize == 10);
|
||||||
|
assert(tmp.endian == Endian.littleEndian);
|
||||||
|
assert(tmp.formatType == FormatType.int8);
|
||||||
|
assert(tmp.isArray == true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
/++
|
/++
|
||||||
+ Base format type
|
+ A format error when formating wasn't possible.
|
||||||
|
+/
|
||||||
|
class FormatError : Exception
|
||||||
|
{
|
||||||
|
/++
|
||||||
|
+ Constructor of an format error.
|
||||||
|
+ Params:
|
||||||
|
+ msg = Error message.
|
||||||
|
+ nextInChain = Next error in the chain.
|
||||||
|
+/
|
||||||
|
this(string msg, Throwable nextInChain = null) pure nothrow @nogc @safe
|
||||||
|
{
|
||||||
|
super(msg, nextInChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
+ Base format type.
|
||||||
|
+
|
||||||
|
+ A format string item contains of free options: "[Endian][Array size][Type]"
|
||||||
|
+
|
||||||
|
+ Endian can be "<" for little endian; ">", "!" for big endian; and "=" for native (highly not recommanded).
|
||||||
|
+
|
||||||
|
+ Array size is optional except for c-strings.
|
||||||
|
+
|
||||||
|
+ The type can be:
|
||||||
|
+ - "b"/"B" for a signed 8 bit integer. Setting an endian isn't allowed.
|
||||||
|
+ - "h"/"H" for a signed 16 bit integer.
|
||||||
|
+ - "i"/"I" for an 32 bit integer.
|
||||||
|
+ - "q"/"Q" for a signed 64 bit integer.
|
||||||
|
+ - "s" for a c-string. The array size descripes the maximal length of the c-string including the terminating
|
||||||
|
+ zero byte. Should the string be smaller then the given length will be expected (and gerated) additnal zero
|
||||||
|
+ bytes after the end of the string. Should the string be to long a exception will be thrown. Should a c-string
|
||||||
|
+ doesn't end with a valid zero byte during unpacking an exception will be thrown. Should during unpacking of
|
||||||
|
+ a c-string after the first zero byte follow additinal non zero-bytes inside the reservated space will be the
|
||||||
|
+ additional data be ignored. During unpacking a c-string will be the tailing zero byte removed and during
|
||||||
|
+ be added. Setting an endian isn't allowed.
|
||||||
|
+
|
||||||
|
+ Macros:
|
||||||
|
+ CONFIG = The config string of the byte format.
|
||||||
+/
|
+/
|
||||||
struct BaseFormat(string CONFIG)
|
struct BaseFormat(string CONFIG)
|
||||||
{
|
{
|
||||||
private
|
private
|
||||||
{
|
{
|
||||||
static const(Element[]) elements = parse_string(CONFIG);
|
static immutable(Element[]) elements = parseString(CONFIG);
|
||||||
static const(size_t) elements_size = {
|
static immutable(size_t) elementsSize = {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
static foreach (i; elements)
|
static foreach (i; elements)
|
||||||
{
|
{
|
||||||
|
@ -384,57 +510,244 @@ public
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
static pure void unpackEntry(Element ELEMENT)(ref GetType!ELEMENT target, immutable(ubyte)[] source)
|
||||||
|
in (source.length == ELEMENT.packSize())do
|
||||||
|
{
|
||||||
|
static if (ELEMENT.formatType == FormatType.str)
|
||||||
|
{
|
||||||
|
// Get length
|
||||||
|
size_t len = 0;
|
||||||
|
foreach (i; 0 .. source.length)
|
||||||
|
{
|
||||||
|
if (source[i] == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
if (len >= ELEMENT.arraySize)
|
||||||
|
{
|
||||||
|
throw new FormatError(format!"C-String of size %d had no terminating zero byte."(ELEMENT
|
||||||
|
.arraySize));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output string
|
||||||
|
target = (cast(immutable(char[])) source[0 .. len]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.isArray)
|
||||||
|
{
|
||||||
|
static if (ELEMENT.endian == Endian.littleEndian)
|
||||||
|
{
|
||||||
|
static foreach (i; 0 .. ELEMENT.arraySize)
|
||||||
|
{
|
||||||
|
target[i] = loadLittleEndian!(GetType!(ELEMENT.genBase()))(
|
||||||
|
source[(i * ELEMENT.baseSize()) .. ((i + 1) * ELEMENT.baseSize())]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.endian == Endian.bigEndian)
|
||||||
|
{
|
||||||
|
static foreach (i; 0 .. ELEMENT.arraySize)
|
||||||
|
{
|
||||||
|
target[i] = loadBigEndian!(GetType!(ELEMENT.genBase()))(
|
||||||
|
source[(i * ELEMENT.baseSize()) .. ((i + 1) * ELEMENT.baseSize())]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static assert(false); // Should never happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static if (ELEMENT.endian == Endian.littleEndian)
|
||||||
|
{
|
||||||
|
target = loadLittleEndian!(GetType!ELEMENT)(source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else static if (ELEMENT.endian == Endian.bigEndian)
|
||||||
|
{
|
||||||
|
target = loadBigEndian!(GetType!ELEMENT)(source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static assert(false); // Should never happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin({
|
||||||
|
string result = "alias ARGS = staticMap!(GetType";
|
||||||
|
foreach (i; 0 .. elements.length)
|
||||||
|
{
|
||||||
|
result ~= ", elements[" ~ to!string(i) ~ "]";
|
||||||
|
}
|
||||||
|
return result ~ ");";
|
||||||
|
}());
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
|
alias TUPLE = Tuple!(ARGS);
|
||||||
|
|
||||||
/++
|
/++
|
||||||
+ Calculate the size of the format.
|
+ Calculate the size of the format.
|
||||||
+ Returns: Size of the format
|
+ Returns: Size of the format
|
||||||
+/
|
+/
|
||||||
static size_t size()
|
static pure nothrow @nogc size_t size()
|
||||||
{
|
{
|
||||||
return elements_size;
|
return elementsSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
+
|
||||||
|
+/
|
||||||
|
static pure TUPLE unpack(immutable(ubyte)[] source)
|
||||||
|
{
|
||||||
|
// Check input
|
||||||
|
if (source.length != size())
|
||||||
|
{
|
||||||
|
throw new FormatError(format!"Source has size %d but %d is required."(source.length, size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate output
|
||||||
|
auto result = TUPLE();
|
||||||
|
size_t pos = 0;
|
||||||
|
static foreach (i; 0 .. elements.length)
|
||||||
|
{
|
||||||
|
unpackEntry!(elements[i])(result[i], source[pos .. pos + elements[i].packSize()]);
|
||||||
|
pos += elements[i].packSize();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
+ Is a integer of type size_t with the length of base format string.
|
||||||
|
+ For a detailed documentation of a format string look at [BaseFormat].
|
||||||
|
+/
|
||||||
|
template baseSize(string FORMAT)
|
||||||
|
{
|
||||||
|
static const size_t baseSize = BaseFormat!FORMAT.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
+ Unpacks a base format string.
|
||||||
|
+ For a detailed documentation of a format string look at [BaseFormat].
|
||||||
|
+/
|
||||||
|
pure BaseFormat!FORMAT.TUPLE baseUnpack(string FORMAT)(immutable(ubyte)[] source)
|
||||||
|
{
|
||||||
|
return BaseFormat!FORMAT.unpack(source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private
|
private
|
||||||
{
|
{
|
||||||
size_t test_size(string FORMAT)()
|
// Test size
|
||||||
{
|
|
||||||
size_t result = BaseFormat!FORMAT.size();
|
|
||||||
{
|
|
||||||
auto tmp = parse_string(FORMAT);
|
|
||||||
size_t calced = 0;
|
|
||||||
foreach (i; tmp)
|
|
||||||
{
|
|
||||||
calced += i.packSize();
|
|
||||||
}
|
|
||||||
assert(result == calced);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
assert(test_size!"10s"() == 10);
|
size_t testSize(string FORMAT)()
|
||||||
assert(test_size!"b"() == 1);
|
{
|
||||||
assert(test_size!"4b"() == 4);
|
size_t result = BaseFormat!FORMAT.size();
|
||||||
assert(test_size!"B"() == 1);
|
assert(baseSize!FORMAT == result);
|
||||||
assert(test_size!"4B"() == 4);
|
{
|
||||||
assert(test_size!"h"() == 2);
|
auto tmp = parseString(FORMAT);
|
||||||
assert(test_size!"4h"() == 8);
|
size_t calced = 0;
|
||||||
assert(test_size!"H"() == 2);
|
foreach (i; tmp)
|
||||||
assert(test_size!"4H"() == 8);
|
{
|
||||||
assert(test_size!"i"() == 4);
|
calced += i.packSize();
|
||||||
assert(test_size!"4i"() == 16);
|
}
|
||||||
assert(test_size!"I"() == 4);
|
assert(result == calced);
|
||||||
assert(test_size!"4I"() == 16);
|
}
|
||||||
assert(test_size!"q"() == 8);
|
return result;
|
||||||
assert(test_size!"4q"() == 32);
|
}
|
||||||
assert(test_size!"Q"() == 8);
|
|
||||||
assert(test_size!"4Q"() == 32);
|
assert(testSize!"10s"() == 10);
|
||||||
|
assert(testSize!"b"() == 1);
|
||||||
|
assert(testSize!"4b"() == 4);
|
||||||
|
assert(testSize!"B"() == 1);
|
||||||
|
assert(testSize!"4B"() == 4);
|
||||||
|
assert(testSize!">h"() == 2);
|
||||||
|
assert(testSize!">4h"() == 8);
|
||||||
|
assert(testSize!">H"() == 2);
|
||||||
|
assert(testSize!">4H"() == 8);
|
||||||
|
assert(testSize!">i"() == 4);
|
||||||
|
assert(testSize!">4i"() == 16);
|
||||||
|
assert(testSize!">I"() == 4);
|
||||||
|
assert(testSize!">4I"() == 16);
|
||||||
|
assert(testSize!">q"() == 8);
|
||||||
|
assert(testSize!">4q"() == 32);
|
||||||
|
assert(testSize!">Q"() == 8);
|
||||||
|
assert(testSize!">4Q"() == 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test pack and unpack
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
template testPacking(string FORMAT)
|
||||||
|
{
|
||||||
|
void testPacking(TUPLE)(TUPLE data, immutable(ubyte[]) packed)
|
||||||
|
{
|
||||||
|
assert(BaseFormat!FORMAT.unpack(packed) == data);
|
||||||
|
assert(baseUnpack!FORMAT(packed) == data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
testPacking!"3s"(tuple(""), [0, 0, 0]);
|
||||||
|
testPacking!"3s"(tuple("a"), ['a', 0, 0]);
|
||||||
|
testPacking!"3s"(tuple("ab"), ['a', 'b', 0]);
|
||||||
|
assertThrown!FormatError(BaseFormat!"3s".unpack(['a', 'b', 'c']));
|
||||||
|
|
||||||
|
// Bytes
|
||||||
|
testPacking!"b"(tuple(1), [1]);
|
||||||
|
testPacking!"b"(tuple(-1), [255]);
|
||||||
|
testPacking!"B"(tuple(255), [255]);
|
||||||
|
|
||||||
|
// Shorts
|
||||||
|
testPacking!"<h"(tuple(1), [1, 0]);
|
||||||
|
testPacking!"<h"(tuple(-1), [255, 255]);
|
||||||
|
testPacking!">h"(tuple(1), [0, 1]);
|
||||||
|
testPacking!">h"(tuple(-1), [255, 255]);
|
||||||
|
testPacking!"<H"(tuple(0xFFFE), [0xFE, 0xFF]);
|
||||||
|
testPacking!">H"(tuple(0xFFFE), [0xFF, 0xFE]);
|
||||||
|
|
||||||
|
// Integers
|
||||||
|
testPacking!"<i"(tuple(1), [1, 0, 0, 0]);
|
||||||
|
testPacking!"<i"(tuple(-1), [255, 255, 255, 255]);
|
||||||
|
testPacking!">i"(tuple(1), [0, 0, 0, 1]);
|
||||||
|
testPacking!">i"(tuple(-1), [255, 255, 255, 255]);
|
||||||
|
testPacking!"<I"(tuple(0xFFFFFFFE), [0xFE, 0xFF, 0xFF, 0xFF]);
|
||||||
|
testPacking!">I"(tuple(0xFFFFFFFE), [0xFF, 0xFF, 0xFF, 0xFE]);
|
||||||
|
|
||||||
|
// Longs
|
||||||
|
testPacking!"<q"(tuple(1), [1, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
testPacking!"<q"(tuple(-1), [255, 255, 255, 255, 255, 255, 255, 255]);
|
||||||
|
testPacking!">q"(tuple(1), [0, 0, 0, 0, 0, 0, 0, 1]);
|
||||||
|
testPacking!">q"(tuple(-1), [255, 255, 255, 255, 255, 255, 255, 255]);
|
||||||
|
testPacking!"<Q"(tuple(0xFFFFFFFFFFFFFFFE), [0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
|
||||||
|
testPacking!">Q"(tuple(0xFFFFFFFFFFFFFFFE), [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE]);
|
||||||
|
|
||||||
|
// Array
|
||||||
|
testPacking!"<1i"(tuple([1]), [1, 0, 0, 0]);
|
||||||
|
testPacking!">1i"(tuple([1]), [0, 0, 0, 1]);
|
||||||
|
testPacking!"<4i"(tuple([1, 2, 3, 4]), [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0]);
|
||||||
|
testPacking!">4i"(tuple([1, 2, 3, 4]), [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4]);
|
||||||
|
|
||||||
|
// Multiple types after each other
|
||||||
|
testPacking!"<4i>Q"(tuple([1, 2, 3, 4], 0xFFFFFFFFFFFFFFFE), [
|
||||||
|
1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE
|
||||||
|
]);
|
||||||
|
testPacking!"<Q>4i"(tuple(0xFFFFFFFFFFFFFFFE, [1, 2, 3, 4]), [
|
||||||
|
0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Except size error
|
||||||
|
assertThrown!FormatError(testPacking!">Q"(tuple(0xFFFFFFFFFFFFFFFE), [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue