make @safe
, improve coverage and refresh the style
This commit is contained in:
parent
b77a3fb709
commit
edf4979207
|
@ -6,10 +6,6 @@
|
||||||
/// A minimal library providing functionality for changing the endianness of data.
|
/// A minimal library providing functionality for changing the endianness of data.
|
||||||
module tinyendian;
|
module tinyendian;
|
||||||
|
|
||||||
|
|
||||||
import core.stdc.string;
|
|
||||||
|
|
||||||
import std.algorithm;
|
|
||||||
import std.system;
|
import std.system;
|
||||||
import std.utf;
|
import std.utf;
|
||||||
|
|
||||||
|
@ -23,8 +19,8 @@ enum UTFEncoding : ubyte
|
||||||
UTF_16,
|
UTF_16,
|
||||||
UTF_32
|
UTF_32
|
||||||
}
|
}
|
||||||
|
///
|
||||||
unittest
|
@safe unittest
|
||||||
{
|
{
|
||||||
const ints = [314, -101];
|
const ints = [314, -101];
|
||||||
int[2] intsSwapBuffer = ints;
|
int[2] intsSwapBuffer = ints;
|
||||||
|
@ -39,8 +35,6 @@ unittest
|
||||||
assert(floats == floatsSwapBuffer, "Lost information when swapping byte order");
|
assert(floats == floatsSwapBuffer, "Lost information when swapping byte order");
|
||||||
}
|
}
|
||||||
|
|
||||||
@nogc @system pure nothrow:
|
|
||||||
|
|
||||||
/** Swap byte order of items in an array in place.
|
/** Swap byte order of items in an array in place.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
|
@ -48,19 +42,20 @@ unittest
|
||||||
* T = Item type. Must be either 2 or 4 bytes long.
|
* T = Item type. Must be either 2 or 4 bytes long.
|
||||||
* array = Buffer with values to fix byte order of.
|
* array = Buffer with values to fix byte order of.
|
||||||
*/
|
*/
|
||||||
void swapByteOrder(T)(T[] array)
|
void swapByteOrder(T)(T[] array) @trusted @nogc pure nothrow
|
||||||
if([2, 4].canFind(T.sizeof))
|
if (T.sizeof == 2 || T.sizeof == 4)
|
||||||
{
|
{
|
||||||
import core.bitop;
|
|
||||||
// Swap the byte order of all read characters.
|
// Swap the byte order of all read characters.
|
||||||
foreach(ref item; array)
|
foreach (ref item; array)
|
||||||
{
|
{
|
||||||
static if(T.sizeof == 2)
|
static if (T.sizeof == 2)
|
||||||
{
|
{
|
||||||
|
import std.algorithm.mutation : swap;
|
||||||
swap(*cast(ubyte*)&item, *(cast(ubyte*)&item + 1));
|
swap(*cast(ubyte*)&item, *(cast(ubyte*)&item + 1));
|
||||||
}
|
}
|
||||||
else static if(T.sizeof == 4)
|
else static if (T.sizeof == 4)
|
||||||
{
|
{
|
||||||
|
import core.bitop : bswap;
|
||||||
const swapped = bswap(*cast(uint*)&item);
|
const swapped = bswap(*cast(uint*)&item);
|
||||||
item = *cast(const(T)*)&swapped;
|
item = *cast(const(T)*)&swapped;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +63,15 @@ void swapByteOrder(T)(T[] array)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See fixUTFByteOrder.
|
||||||
|
struct FixUTFByteOrderResult
|
||||||
|
{
|
||||||
|
ubyte[] array;
|
||||||
|
UTFEncoding encoding;
|
||||||
|
Endian endian;
|
||||||
|
uint bytesStripped = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Convert byte order of an array encoded in UTF(8/16/32) to system endianness in place.
|
/** Convert byte order of an array encoded in UTF(8/16/32) to system endianness in place.
|
||||||
*
|
*
|
||||||
* Uses the UTF byte-order-mark (BOM) to determine UTF encoding. If there is no BOM
|
* Uses the UTF byte-order-mark (BOM) to determine UTF encoding. If there is no BOM
|
||||||
|
@ -100,7 +104,7 @@ void swapByteOrder(T)(T[] array)
|
||||||
*
|
*
|
||||||
* Complexity: (BIGOH array.length)
|
* Complexity: (BIGOH array.length)
|
||||||
*/
|
*/
|
||||||
auto fixUTFByteOrder(ubyte[] array)
|
auto fixUTFByteOrder(ubyte[] array) @safe @nogc pure nothrow
|
||||||
{
|
{
|
||||||
// Enumerates UTF BOMs, matching indices to byteOrderMarks/bomEndian.
|
// Enumerates UTF BOMs, matching indices to byteOrderMarks/bomEndian.
|
||||||
enum BOM: ubyte
|
enum BOM: ubyte
|
||||||
|
@ -126,23 +130,17 @@ auto fixUTFByteOrder(ubyte[] array)
|
||||||
Endian.bigEndian ];
|
Endian.bigEndian ];
|
||||||
|
|
||||||
// Documented in function ddoc.
|
// Documented in function ddoc.
|
||||||
struct Result
|
|
||||||
{
|
FixUTFByteOrderResult result;
|
||||||
ubyte[] array;
|
|
||||||
UTFEncoding encoding;
|
|
||||||
Endian endian;
|
|
||||||
uint bytesStripped = 0;
|
|
||||||
}
|
|
||||||
Result result;
|
|
||||||
|
|
||||||
// Detect BOM, if any, in the bytes we've read. -1 means no BOM.
|
// Detect BOM, if any, in the bytes we've read. -1 means no BOM.
|
||||||
// Need the last match: First 2 bytes of UTF-32LE BOM match the UTF-16LE BOM. If we
|
// Need the last match: First 2 bytes of UTF-32LE BOM match the UTF-16LE BOM. If we
|
||||||
// used the first match, UTF-16LE would be detected when we have a UTF-32LE BOM.
|
// used the first match, UTF-16LE would be detected when we have a UTF-32LE BOM.
|
||||||
|
import std.algorithm.searching : startsWith;
|
||||||
BOM bomId = BOM.None;
|
BOM bomId = BOM.None;
|
||||||
foreach(i, bom; byteOrderMarks) if(array.startsWith(bom))
|
foreach (i, bom; byteOrderMarks)
|
||||||
{
|
if (array.startsWith(bom))
|
||||||
bomId = cast(BOM)i;
|
bomId = cast(BOM)i;
|
||||||
}
|
|
||||||
|
|
||||||
result.endian = (bomId != BOM.None) ? bomEndian[bomId] : Endian.init;
|
result.endian = (bomId != BOM.None) ? bomEndian[bomId] : Endian.init;
|
||||||
|
|
||||||
|
@ -168,21 +166,52 @@ auto fixUTFByteOrder(ubyte[] array)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
array = array[0 .. $ - result.bytesStripped];
|
|
||||||
// If there's a BOM, we need to move data back to ensure it starts at array[0]
|
// If there's a BOM, we need to move data back to ensure it starts at array[0]
|
||||||
if(start != 0)
|
if (start != 0)
|
||||||
{
|
{
|
||||||
core.stdc.string.memmove(array.ptr, array.ptr + start, array.length - start);
|
array = array[start .. $ - result.bytesStripped];
|
||||||
array = array[0 .. $ - start];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We enforce above that array.length is divisible by 2/4 for UTF-16/32
|
// We enforce above that array.length is divisible by 2/4 for UTF-16/32
|
||||||
if(std.system.endian != result.endian)
|
if (std.system.endian != result.endian)
|
||||||
{
|
{
|
||||||
if(result.encoding == UTFEncoding.UTF_16) { swapByteOrder(cast(wchar[])array); }
|
if (result.encoding == UTFEncoding.UTF_16)
|
||||||
else if(result.encoding == UTFEncoding.UTF_32) { swapByteOrder(cast(dchar[])array); }
|
swapByteOrder(cast(wchar[])array);
|
||||||
|
else if (result.encoding == UTFEncoding.UTF_32)
|
||||||
|
swapByteOrder(cast(dchar[])array);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.array = array;
|
result.array = array;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
///
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
{
|
||||||
|
ubyte[] s = [0xEF, 0xBB, 0xBF, 'a'];
|
||||||
|
FixUTFByteOrderResult r = fixUTFByteOrder(s);
|
||||||
|
assert(r.encoding == UTFEncoding.UTF_8);
|
||||||
|
assert(r.array.length == 1);
|
||||||
|
assert(r.array == ['a']);
|
||||||
|
assert(r.endian == Endian.littleEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ubyte[] s = ['a'];
|
||||||
|
FixUTFByteOrderResult r = fixUTFByteOrder(s);
|
||||||
|
assert(r.encoding == UTFEncoding.UTF_8);
|
||||||
|
assert(r.array.length == 1);
|
||||||
|
assert(r.array == ['a']);
|
||||||
|
assert(r.endian == Endian.bigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// strip 'a' b/c not complete unit
|
||||||
|
ubyte[] s = [0xFE, 0xFF, 'a'];
|
||||||
|
FixUTFByteOrderResult r = fixUTFByteOrder(s);
|
||||||
|
assert(r.encoding == UTFEncoding.UTF_16);
|
||||||
|
assert(r.array.length == 0);
|
||||||
|
assert(r.endian == Endian.bigEndian);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue