Skip to content

Commit

Permalink
Merge pull request #144 from Kozzi11/add_varbit
Browse files Browse the repository at this point in the history
add support for bit varying
  • Loading branch information
denizzzka authored Jan 2, 2020
2 parents e0e6218 + 7cbcce5 commit 3cfaf01
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 3 deletions.
76 changes: 75 additions & 1 deletion src/dpq2/conv/from_d_types.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import dpq2.conv.time : POSTGRES_EPOCH_DATE, TimeStamp, TimeStampUTC;
import dpq2.oids : detectOidTypeFromNative, oidConvTo, OidType;
import dpq2.value : Value, ValueFormat;

import std.bitmanip: nativeToBigEndian;
import std.bitmanip: nativeToBigEndian, BitArray, append;
import std.datetime.date: Date, DateTime, TimeOfDay;
import std.datetime.systime: SysTime;
import std.datetime.timezone: LocalTime, TimeZone, UTC;
Expand Down Expand Up @@ -71,6 +71,80 @@ unittest
assert(v.as!PGTestMoney == pgtm);
}

/// Convert std.bitmanip.BitArray to PG value
Value toValue(T)(T v) @trusted
if(is(Unqual!T == BitArray))
{
import std.array : appender;
import core.bitop : bitswap;

size_t len = v.length / 8 + (v.length % 8 ? 1 : 0);
auto data = cast(size_t[])v;
auto buffer = appender!(const ubyte[])();
buffer.append!uint(cast(uint)v.length);
foreach (d; data[0 .. v.dim])
{
// DMD Issue 19693
version(DigitalMars)
auto ntb = nativeToBigEndian(softBitswap(d));
else
auto ntb = nativeToBigEndian(bitswap(d));
foreach (b; ntb[0 .. len])
{
buffer.append!ubyte(b);
}

}
return Value(buffer.data.dup, detectOidTypeFromNative!T, false, ValueFormat.BINARY);
}

/// Reverses the order of bits - needed because of dmd Issue 19693
/// https://issues.dlang.org/show_bug.cgi?id=19693
package N softBitswap(N)(N x) pure
if (is(N == uint) || is(N == ulong))
{
import core.bitop : bswap;
// swap 1-bit pairs:
enum mask1 = cast(N) 0x5555_5555_5555_5555L;
x = ((x >> 1) & mask1) | ((x & mask1) << 1);
// swap 2-bit pairs:
enum mask2 = cast(N) 0x3333_3333_3333_3333L;
x = ((x >> 2) & mask2) | ((x & mask2) << 2);
// swap 4-bit pairs:
enum mask4 = cast(N) 0x0F0F_0F0F_0F0F_0F0FL;
x = ((x >> 4) & mask4) | ((x & mask4) << 4);

// reverse the order of all bytes:
x = bswap(x);

return x;
}

@trusted unittest
{
import std.bitmanip : BitArray;

auto varbit = BitArray([1,0,1,1,0]);

Value v = varbit.toValue;

assert(v.oidType == OidType.VariableBitString);
assert(v.as!BitArray == varbit);

// test softBitswap
assert (softBitswap!uint( 0x8000_0100 ) == 0x0080_0001);
foreach (i; 0 .. 32)
assert (softBitswap!uint(1 << i) == 1 << 32 - i - 1);

assert (softBitswap!ulong( 0b1000000000000000000000010000000000000000100000000000000000000001)
== 0b1000000000000000000000010000000000000000100000000000000000000001);
assert (softBitswap!ulong( 0b1110000000000000000000010000000000000000100000000000000000000001)
== 0b1000000000000000000000010000000000000000100000000000000000000111);
foreach (i; 0 .. 64)
assert (softBitswap!ulong(1UL << i) == 1UL << 64 - i - 1);

}

/**
Converts types implicitly convertible to string to PG Value.
Note that if string is null it is written as an empty string.
Expand Down
4 changes: 4 additions & 0 deletions src/dpq2/conv/native_tests.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module dpq2.conv.native_tests;
import dpq2;
import dpq2.conv.arrays : isArrayType;
import dpq2.conv.geometric: Line;
import std.bitmanip : BitArray;
import std.datetime;
import std.typecons: Nullable;
import std.uuid: UUID;
Expand Down Expand Up @@ -128,6 +129,9 @@ public void _integration_test( string connParam ) @system
"bytea", r"E'\\x44 20 72 75 6c 65 73 00 21'"); // "D rules\x00!" (ASCII)
C!PGuuid(UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640"), "uuid", "'8b9ab33a-96e9-499b-9c36-aad1fe86d640'");
C!(Nullable!PGuuid)(Nullable!UUID(UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640")), "uuid", "'8b9ab33a-96e9-499b-9c36-aad1fe86d640'");
C!PGvarbit(BitArray([1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]), "varbit", "'101011010110101'");
C!PGvarbit(BitArray([0, 0, 1, 0, 1]), "varbit", "'00101'");
C!PGvarbit(BitArray([1, 0, 1, 0, 0]), "varbit", "'10100'");

// numeric testing
C!PGnumeric("NaN", "numeric", "'NaN'");
Expand Down
2 changes: 1 addition & 1 deletion src/dpq2/conv/to_bson.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import dpq2.conv.numeric: rawValueToNumeric;
import vibe.data.bson;
import std.uuid;
import std.datetime: SysTime, dur, TimeZone, UTC;
import std.bitmanip: bigEndianToNative;
import std.bitmanip: bigEndianToNative, BitArray;
import std.conv: to;

///
Expand Down
46 changes: 45 additions & 1 deletion src/dpq2/conv/to_d_types.d
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import std.uuid;
import std.datetime;
import std.traits: isScalarType;
import std.typecons : Nullable;
import std.bitmanip: bigEndianToNative;
import std.bitmanip: bigEndianToNative, BitArray;
import std.conv: to;
version (unittest) import std.exception : assertThrown;

Expand All @@ -42,6 +42,7 @@ alias PGtimestamp = TimeStamp; /// Both date and time without time zone
alias PGtimestamptz = TimeStampUTC; /// Both date and time stored in UTC time zone
alias PGjson = Json; /// json or jsonb
alias PGline = Line; /// Line (geometric type)
alias PGvarbit = BitArray; /// BitArray

private alias VF = ValueFormat;
private alias AE = ValueConvException;
Expand Down Expand Up @@ -314,3 +315,46 @@ unittest
auto v = Value([1], OidType.Money);
assertThrown!ValueConvException(v.binaryValueAs!PGTestMoney);
}

T binaryValueAs(T)(in Value v) @trusted
if( is(T == BitArray) )
{
import core.bitop : bitswap;
import std.bitmanip;
import std.format: format;
import std.range : chunks;

if(v.data.length < int.sizeof)
throw new AE(
ET.SIZE_MISMATCH,
format(
"%s length (%d) is less than minimum int type size (%d)",
v.oidType.to!string,
v.data.length,
int.sizeof
)
);

auto data = v.data;
size_t len = data.read!int;
size_t[] newData;
foreach (ch; data.chunks(size_t.sizeof))
{
ubyte[size_t.sizeof] tmpData;
tmpData[0 .. ch.length] = ch[];

// DMD Issue 19693
version(DigitalMars)
auto re = softBitswap(bigEndianToNative!size_t(tmpData));
else
auto re = bitswap(bigEndianToNative!size_t(tmpData));
newData ~= re;
}
return T(newData, len);
}

unittest
{
auto v = Value([1], OidType.VariableBitString);
assertThrown!ValueConvException(v.binaryValueAs!BitArray);
}
2 changes: 2 additions & 0 deletions src/dpq2/oids.d
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ OidType detectOidTypeFromNative(T)()

private OidType detectOidTypeNotCareAboutNullable(T)()
{
import std.bitmanip : BitArray;
import std.datetime.date : StdDate = Date, TimeOfDay, DateTime;
import std.datetime.systime : SysTime;
import std.traits : Unqual;
Expand Down Expand Up @@ -194,6 +195,7 @@ private OidType detectOidTypeNotCareAboutNullable(T)()
static if(is(UT == dpq2.conv.time.TimeStampUTC)){ return TimeStampWithZone; } else
static if(is(UT == VibeJson)){ return Json; } else
static if(is(UT == StdUUID)){ return UUID; } else
static if(is(UT == BitArray)){ return VariableBitString; } else

static assert(false, "Unsupported D type: "~T.stringof);
}
Expand Down

0 comments on commit 3cfaf01

Please sign in to comment.