Skip to content

Commit

Permalink
C# endianness fixes (#1964)
Browse files Browse the repository at this point in the history
  • Loading branch information
kazimuth authored Nov 20, 2024
1 parent 3f0e152 commit dee297a
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<RootNamespace>SpacetimeDB</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsCheck" Version="4.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../BSATN.Runtime/BSATN.Runtime.csproj" />
</ItemGroup>

</Project>
142 changes: 142 additions & 0 deletions crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
namespace SpacetimeDB;

using CsCheck;
using Xunit;

public static class BSATNRuntimeTests
{
[Fact]
public static void AddressRoundtrips()
{
var str = "00112233445566778899AABBCCDDEEFF";
var addr = Address.FromHexString(str);

Assert.NotNull(addr);
Assert.Equal(addr.ToString(), str);

var bytes = Convert.FromHexString(str);

var addr2 = Address.FromBigEndian(bytes);
Assert.Equal(addr2, addr);

Array.Reverse(bytes);
var addr3 = Address.From(bytes);
Assert.Equal(addr3, addr);

var memoryStream = new MemoryStream();
var bsatn = new Address.BSATN();
using (var writer = new BinaryWriter(memoryStream))
{
if (addr is { } addrNotNull)
{
bsatn.Write(writer, addrNotNull);
}
else
{
Assert.Fail("Impossible");
}
}

var littleEndianBytes = memoryStream.ToArray();
var reader = new BinaryReader(new MemoryStream(littleEndianBytes));
var addr4 = bsatn.Read(reader);
Assert.Equal(addr4, addr);

// Note: From = FromLittleEndian
var addr5 = Address.From(littleEndianBytes);
Assert.Equal(addr5, addr);
}

static readonly Gen<string> genHex = Gen.String[Gen.Char["0123456789abcdef"], 0, 128];

[Fact]
public static void AddressLengthCheck()
{
genHex.Sample(s =>
{
if (s.Length == 32)
{
return;
}
Assert.ThrowsAny<Exception>(() => Address.FromHexString(s));
});
Gen.Byte.Array[0, 64]
.Sample(arr =>
{
if (arr.Length == 16)
{
return;
}
Assert.ThrowsAny<Exception>(() => Address.FromBigEndian(arr));
Assert.ThrowsAny<Exception>(() => Address.From(arr));
});
}

[Fact]
public static void IdentityRoundtrips()
{
var str = "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF";
var ident = Identity.FromHexString(str);

Assert.Equal(ident.ToString(), str);

// We can't use this in the implementation because it isn't available
// in Unity's .NET. But we can use it in tests.
var bytes = Convert.FromHexString(str);

var ident2 = Identity.FromBigEndian(bytes);
Assert.Equal(ident2, ident);

Array.Reverse(bytes);
var ident3 = Identity.From(bytes);
Assert.Equal(ident3, ident);

var memoryStream = new MemoryStream();
var bsatn = new Identity.BSATN();
using (var writer = new BinaryWriter(memoryStream))
{
bsatn.Write(writer, ident);
}

var littleEndianBytes = memoryStream.ToArray();
var reader = new BinaryReader(new MemoryStream(littleEndianBytes));
var ident4 = bsatn.Read(reader);
Assert.Equal(ident4, ident);

// Note: From = FromLittleEndian
var ident5 = Identity.From(littleEndianBytes);
Assert.Equal(ident5, ident);
}

[Fact]
public static void IdentityLengthCheck()
{
genHex.Sample(s =>
{
if (s.Length == 64)
{
return;
}
Assert.ThrowsAny<Exception>(() => Identity.FromHexString(s));
});
Gen.Byte.Array[0, 64]
.Sample(arr =>
{
if (arr.Length == 32)
{
return;
}
Assert.ThrowsAny<Exception>(() => Identity.FromBigEndian(arr));
Assert.ThrowsAny<Exception>(() => Identity.From(arr));
});
}

[Fact]
public static void NonHexStrings()
{
// n.b. 32 chars long
Assert.ThrowsAny<Exception>(
() => Address.FromHexString("these are not hex characters....")
);
}
}
Loading

2 comments on commit dee297a

@github-actions
Copy link

@github-actions github-actions bot commented on dee297a Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

@github-actions
Copy link

@github-actions github-actions bot commented on dee297a Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callgrind benchmark results

Callgrind Benchmark Report

These benchmarks were run using callgrind,
an instruction-level profiler. They allow comparisons between sqlite (sqlite), SpacetimeDB running through a module (stdb_module), and the underlying SpacetimeDB data storage engine (stdb_raw). Callgrind emulates a CPU to collect the below estimates.

Measurement changes larger than five percent are in bold.

In-memory benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 6426 6426 0.00% 6594 6594 0.00%
sqlite 5579 5579 0.00% 6071 6071 0.00%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 1 u64 76579 76579 0.00% 77243 77243 0.00%
stdb_raw u32_u64_str no_index 64 128 2 string 118821 119910 -0.91% 119647 120860 -1.00%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25110 25112 -0.01% 25768 25770 -0.01%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24078 24078 0.00% 24666 24666 0.00%
sqlite u32_u64_str no_index 64 128 2 string 144695 144695 0.00% 146097 146097 0.00%
sqlite u32_u64_str no_index 64 128 1 u64 124044 124044 0.00% 125200 125200 0.00%
sqlite u32_u64_str btree_each_column 64 128 1 u64 131361 131361 0.00% 132629 132621 0.01%
sqlite u32_u64_str btree_each_column 64 128 2 string 134494 134494 0.00% 136072 136076 -0.00%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 877600 879002 -0.16% 893464 925610 -3.47%
stdb_raw u32_u64_str btree_each_column 64 128 1028500 1025700 0.27% 1054796 1052160 0.25%
sqlite u32_u64_str unique_0 64 128 398320 398320 0.00% 414836 414832 0.00%
sqlite u32_u64_str btree_each_column 64 128 983637 983643 -0.00% 1022747 1022749 -0.00%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 153810 153810 0.00% 153970 153970 0.00%
stdb_raw u32_u64_str unique_0 64 16835 16835 0.00% 16979 16979 0.00%
sqlite u32_u64_str unique_0 1024 1068299 1068281 0.00% 1071701 1071675 0.00%
sqlite u32_u64_str unique_0 64 76261 76261 0.00% 77231 77231 0.00%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47528 47528 0.00% 50214 50214 0.00%
64 bsatn 25509 25509 0.00% 27787 27787 0.00%
16 bsatn 8200 8200 0.00% 9594 9594 0.00%
16 json 12188 12188 0.00% 14126 14126 0.00%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 20318416 20547693 -1.12% 20769524 21041767 -1.29%
stdb_raw u32_u64_str unique_0 64 128 1287751 1288257 -0.04% 1352403 1321797 2.32%
sqlite u32_u64_str unique_0 1024 1024 1802182 1802182 0.00% 1811350 1811350 0.00%
sqlite u32_u64_str unique_0 64 128 128528 128528 0.00% 131370 131370 0.00%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 6431 6431 0.00% 6603 6603 0.00%
sqlite 5621 5621 0.00% 6165 6165 0.00%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 1 u64 76584 76584 0.00% 77228 77240 -0.02%
stdb_raw u32_u64_str no_index 64 128 2 string 118826 118826 0.00% 119756 119644 0.09%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25115 25118 -0.01% 25745 25748 -0.01%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24083 24083 0.00% 24651 24655 -0.02%
sqlite u32_u64_str no_index 64 128 1 u64 125965 125965 0.00% 127429 127429 0.00%
sqlite u32_u64_str no_index 64 128 2 string 146616 146616 0.00% 148330 148330 0.00%
sqlite u32_u64_str btree_each_column 64 128 2 string 136616 136616 0.00% 138644 138644 0.00%
sqlite u32_u64_str btree_each_column 64 128 1 u64 133475 133457 0.01% 135321 135283 0.03%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 828683 828207 0.06% 874417 843009 3.73%
stdb_raw u32_u64_str btree_each_column 64 128 978866 978552 0.03% 1035496 1034618 0.08%
sqlite u32_u64_str unique_0 64 128 415857 415857 0.00% 431639 431635 0.00%
sqlite u32_u64_str btree_each_column 64 128 1021898 1021898 0.00% 1059774 1059770 0.00%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 153815 153815 0.00% 153959 153959 0.00%
stdb_raw u32_u64_str unique_0 64 16840 16840 0.00% 16984 16984 0.00%
sqlite u32_u64_str unique_0 1024 1071343 1071343 0.00% 1075065 1075065 0.00%
sqlite u32_u64_str unique_0 64 78033 78033 0.00% 79343 79343 0.00%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47528 47528 0.00% 50214 50214 0.00%
64 bsatn 25509 25509 0.00% 27787 27787 0.00%
16 bsatn 8200 8200 0.00% 9594 9594 0.00%
16 json 12188 12188 0.00% 14126 14126 0.00%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 19055376 19055832 -0.00% 19571546 19571954 -0.00%
stdb_raw u32_u64_str unique_0 64 128 1241919 1241227 0.06% 1305053 1304039 0.08%
sqlite u32_u64_str unique_0 1024 1024 1809743 1809743 0.00% 1818407 1818407 0.00%
sqlite u32_u64_str unique_0 64 128 132654 132654 0.00% 135524 135524 0.00%

Please sign in to comment.