Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 90 additions & 11 deletions Substrate.NetApi.Test/TypeConverters/BaseTypesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,63 @@
using NUnit.Framework;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using System.Collections;

namespace Substrate.NetApi.Test
{
public sealed class Lsb0 : BaseType
{

/// <inheritdoc/>
public override string TypeName()
{
return "Lsb0";
}

/// <inheritdoc/>
public override byte[] Encode()
{
var result = new List<byte>();
return result.ToArray();
}

/// <inheritdoc/>
public override void Decode(byte[] byteArray, ref int p)
{
var start = p;
var bytesLength = p - start;
TypeSize = bytesLength;
Bytes = new byte[bytesLength];
global::System.Array.Copy(byteArray, start, Bytes, 0, bytesLength);
}
}

public sealed class Msb0 : BaseType
{

/// <inheritdoc/>
public override string TypeName()
{
return "Msb0";
}

/// <inheritdoc/>
public override byte[] Encode()
{
var result = new List<byte>();
return result.ToArray();
}

/// <inheritdoc/>
public override void Decode(byte[] byteArray, ref int p)
{
var start = p;
var bytesLength = p - start;
TypeSize = bytesLength;
Bytes = new byte[bytesLength];
global::System.Array.Copy(byteArray, start, Bytes, 0, bytesLength);
}
}
public class BaseTypesTest
{
[Test]
Expand Down Expand Up @@ -187,28 +241,53 @@ public void BaseOptTest()
Assert.IsTrue(exceptionThrown, "Exception not thrown for explicit cast with OptionFlag = false");
}

[Test]
public void BaseBitSeq_DisplayTest()
{
var testCase1 = "0xa00b80050000";
var baseBitSeqLsb0_1 = new BaseBitSeq<U8, Lsb0>();
var baseBitSeqMsb0_1 = new BaseBitSeq<U8, Msb0>();
baseBitSeqLsb0_1.Create(testCase1);
baseBitSeqMsb0_1.Create(testCase1);

Assert.That(baseBitSeqLsb0_1.Encode(), Is.EquivalentTo(baseBitSeqMsb0_1.Encode()));
Assert.That(baseBitSeqLsb0_1.DisplayToBits(), Is.EqualTo("0b11010000_00000001_10100000_00000000_00000000"));
Assert.That(baseBitSeqMsb0_1.DisplayToBits(), Is.EqualTo("0b00001011_10000000_00000101_00000000_00000000"));

}

[Test]
public void BaseBitSeqTest()
{
var testCase1 = "0xa00b80050000";
var bitSeqTest1 = FromBitString("0b11010000_00000001_10100000_00000000_00000000");
var baseBitSeq1 = new BaseBitSeq<U8, U8>();
baseBitSeq1.Create(testCase1);
var bits = "0b11010000_00000001_10100000_00000000_00000000";
var bitSeqTest1 = FromBitString(bits);
var baseBitSeqLsb0_1 = new BaseBitSeq<U8, Lsb0>();
var baseBitSeqMsb0_1 = new BaseBitSeq<U8, Msb0>();
baseBitSeqLsb0_1.Create(testCase1);
baseBitSeqMsb0_1.Create(testCase1);
for (int i = 0; i < bitSeqTest1.Length; i++)
{
Assert.AreEqual(bitSeqTest1[i], baseBitSeq1.Value[i].Value);
Assert.AreEqual(bitSeqTest1[i], baseBitSeqLsb0_1.Value[i].Value);
}
Assert.AreEqual(testCase1, Utils.Bytes2HexString(baseBitSeq1.Encode()).ToLower());
Assert.AreEqual(testCase1, Utils.Bytes2HexString(baseBitSeqLsb0_1.Encode()).ToLower());

var baseBitSeq1_bits = new BaseBitSeq<U8, Lsb0>();
baseBitSeq1_bits.CreateFromBitString(bits);
Assert.That(testCase1, Is.EqualTo(Utils.Bytes2HexString(baseBitSeq1_bits.Encode()).ToLower()));

var baseBitSeqMsb0_bits = new BaseBitSeq<U8, Msb0>();
baseBitSeqMsb0_bits.CreateFromBitString(bits);
Assert.That(bits, Is.EqualTo(baseBitSeqMsb0_bits.DisplayToBits()));

// Let's create the same object but with the other Create() method
var baseBitSeq1_1 = new BaseBitSeq<U8, U8>();
baseBitSeq1_1.Create(baseBitSeq1.Value);
Assert.AreEqual(baseBitSeq1.Bytes, baseBitSeq1_1.Bytes);
Assert.AreEqual(baseBitSeq1.Value, baseBitSeq1_1.Value);
Assert.AreEqual(baseBitSeq1.TypeSize, baseBitSeq1_1.TypeSize);
var baseBitSeq1_1 = new BaseBitSeq<U8, Lsb0>();
baseBitSeq1_1.Create(baseBitSeqLsb0_1.Encode());
Assert.AreEqual(baseBitSeqLsb0_1.Bytes, baseBitSeq1_1.Bytes);
Assert.AreEqual(baseBitSeqLsb0_1.TypeSize, baseBitSeq1_1.TypeSize);

var bitSeqTest2 = FromBitString("0b10000010_10000010_00101000_00000000_00000000");
var baseBitSeq2 = new BaseBitSeq<U8, U8>();
var baseBitSeq2 = new BaseBitSeq<U8, Lsb0>();
baseBitSeq2.Create("0xa04141140000");
for (int i = 0; i < bitSeqTest1.Length; i++)
{
Expand Down
94 changes: 83 additions & 11 deletions Substrate.NetApi/Model/Types/Base/BaseBitSeq.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Newtonsoft.Json;
using Substrate.NetApi.Model.Types.Primitive;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Substrate.NetApi.Model.Types.Base
{
Expand Down Expand Up @@ -31,19 +34,38 @@ public class BaseBitSeq<T1, T2> : IType
[JsonIgnore]
public byte[] Bytes { get; internal set; }

private BitArray _bits { get; set; }

/// <summary>
/// Return if it is LSB (Least Significant Bit first) or MSB (Most Significant Bit first)
/// </summary>
public bool IsLsb {
get
{
switch (typeof(T2).Name.ToLower())
{
case "lsb0":
return true;
case "msb0":
return false;
default:
return true; // not sure if I should throw an exception here or just assume lsb0 (which is the default)
}
}
}

/// <summary>
/// Encode to Bytes
/// </summary>
/// <returns></returns>
public byte[] Encode()
{
var result = new List<byte>
{
Reverse((byte)Value.Length)
};
var result = new List<byte>();
result.AddRange(new CompactInteger(Value.Length * 8).Encode());

for (int i = 0; i < Value.Length; i++)
{
result.AddRange(Reverse(Value[i].Encode()));
result.AddRange(IsLsb ? Reverse(Value[i].Encode()) : Value[i].Encode());
}
return result.ToArray();
}
Expand All @@ -56,16 +78,19 @@ public byte[] Encode()
public void Decode(byte[] byteArray, ref int p)
{
var start = p;

var length = Reverse(byteArray[0]);

p++;
int lengthInner = CompactInteger.Decode(byteArray, ref p);
var length = (int)Math.Ceiling((decimal)lengthInner / (decimal)8);
var tmpBytes = byteArray;
if (IsLsb)
{
tmpBytes = Reverse(byteArray);
}

var array = new T1[length];
for (var i = 0; i < length; i++)
{
var t = new T1();
t.Decode(Reverse(byteArray), ref p);
t.Decode(tmpBytes, ref p);
array[i] = t;
}

Expand All @@ -74,6 +99,9 @@ public void Decode(byte[] byteArray, ref int p)
Bytes = new byte[TypeSize];
Array.Copy(byteArray, start, Bytes, 0, TypeSize);
Value = array;

// By default, BitArray is using Lsb0. So we need to reverse the bits again
_bits = new BitArray(array.SelectMany(x => Reverse(x.Encode())).ToArray());
}

/// <summary>
Expand Down Expand Up @@ -104,6 +132,27 @@ public void Create(T1[] list)
/// <param name="str"></param>
public void CreateFromJson(string str) => Create(Utils.HexToByteArray(str));

/// <summary>
/// Create <see cref="BaseBitSeq{T1, T2}"/> from string bits
/// Example : CreateFromBitString("0b11010000_00000001_10100000_00000000_00000000")
/// </summary>
/// <param name="bits"></param>
public void CreateFromBitString(string bits)
{
var s = bits.Replace("0b", "").Split('_');

var result = new List<byte>();

for (int i = 0; i < s.Length; i++)
{
byte b = Convert.ToByte(s[i], 2);
result.Add(IsLsb ? Reverse(b) : b);
}

result.InsertRange(0, new CompactInteger(result.Count * 8).Encode());
Create(result.ToArray());
}

/// <summary>
/// Create from a byte array
/// </summary>
Expand All @@ -121,7 +170,30 @@ public void Create(byte[] byteArray)
public IType New() => this;

/// <inheritdoc/>
public override string ToString() => JsonConvert.SerializeObject(this);
public override string ToString() => $"{{ bits = {_bits.Count}, capacity = {_bits.Count}}} => [{DisplayToBits()}]";

/// <summary>
/// Display this instance to bits
/// </summary>
/// <returns></returns>
public string DisplayToBits()
{
var bitString = new System.Text.StringBuilder(_bits.Count);
bitString.Append("0b");
for(int i = 0; i < _bits.Length; i++)
{
bitString.Append(_bits[i] ? '1' : '0');

if ((i + 1) % 8 == 0 && i != _bits.Count - 1)
{
bitString.Append("_");
}
}

return bitString.ToString();
}

public int InternalLength => _bits.Length;

/// <summary>
/// Reverse
Expand Down