module kissrpc.Endian;

import core.bitop : bswap;
import std.traits : isIntegral;

union IntBuf(T) {
	ubyte[T.sizeof] bytes;
	T value;
}

T byteSwap(T)(T t) pure nothrow @trusted if (isIntegral!T) {
	static if (T.sizeof == 2) {
		return cast(T)((t & 0xff) << 8) | cast(T)((t & 0xff00) >> 8);
	} else static if (T.sizeof == 4) {
		return cast(T)bswap(cast(uint)t);
	} else static if (T.sizeof == 8) {
		return cast(T)byteSwap(cast(uint)(t & 0xffffffff)) << 32 |
			cast(T)bswap(cast(uint)(t >> 32));
	} else static assert(false, "Type of size " ~ to!string(T.sizeof) ~ " not supported.");
}

T doNothing(T)(T val) { return val; }

version (BigEndian) {
	alias doNothing hostToNet;
	alias doNothing netToHost;
	alias byteSwap hostToLe;
	alias byteSwap leToHost;
} else {
	alias byteSwap hostToNet;
	alias byteSwap netToHost;
	alias doNothing hostToLe;
	alias doNothing leToHost;
}

unittest {
	import std.exception;
	
	IntBuf!short s;
	s.bytes = [1, 2];
	s.value = byteSwap(s.value);
	enforce(s.bytes == [2, 1]);
	
	IntBuf!int i;
	i.bytes = [1, 2, 3, 4];
	i.value = byteSwap(i.value);
	enforce(i.bytes == [4, 3, 2, 1]);
	
	IntBuf!long l;
	l.bytes = [1, 2, 3, 4, 5, 6, 7, 8];
	l.value = byteSwap(l.value);
	enforce(l.bytes == [8, 7, 6, 5, 4, 3, 2, 1]);
}