From f0923a07eb39fcfe23d6745814a6902a24022bc5 Mon Sep 17 00:00:00 2001 From: Apolixit Date: Thu, 26 Sep 2024 22:08:55 +0200 Subject: [PATCH] Adapt BaseBitSeq class to handle Lsb0 and Msb0.Msb0. Also add BitArray to display bits --- .../TypeConverters/BaseTypesTest.cs | 101 ++++++++++++++++-- .../Model/Types/Base/BaseBitSeq.cs | 94 ++++++++++++++-- 2 files changed, 173 insertions(+), 22 deletions(-) diff --git a/Substrate.NetApi.Test/TypeConverters/BaseTypesTest.cs b/Substrate.NetApi.Test/TypeConverters/BaseTypesTest.cs index a908e96..73298e6 100644 --- a/Substrate.NetApi.Test/TypeConverters/BaseTypesTest.cs +++ b/Substrate.NetApi.Test/TypeConverters/BaseTypesTest.cs @@ -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 + { + + /// + public override string TypeName() + { + return "Lsb0"; + } + + /// + public override byte[] Encode() + { + var result = new List(); + return result.ToArray(); + } + + /// + 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 + { + + /// + public override string TypeName() + { + return "Msb0"; + } + + /// + public override byte[] Encode() + { + var result = new List(); + return result.ToArray(); + } + + /// + 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] @@ -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(); + var baseBitSeqMsb0_1 = new BaseBitSeq(); + 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(); - baseBitSeq1.Create(testCase1); + var bits = "0b11010000_00000001_10100000_00000000_00000000"; + var bitSeqTest1 = FromBitString(bits); + var baseBitSeqLsb0_1 = new BaseBitSeq(); + var baseBitSeqMsb0_1 = new BaseBitSeq(); + 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(); + baseBitSeq1_bits.CreateFromBitString(bits); + Assert.That(testCase1, Is.EqualTo(Utils.Bytes2HexString(baseBitSeq1_bits.Encode()).ToLower())); + + var baseBitSeqMsb0_bits = new BaseBitSeq(); + 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(); - 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(); + 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(); + var baseBitSeq2 = new BaseBitSeq(); baseBitSeq2.Create("0xa04141140000"); for (int i = 0; i < bitSeqTest1.Length; i++) { diff --git a/Substrate.NetApi/Model/Types/Base/BaseBitSeq.cs b/Substrate.NetApi/Model/Types/Base/BaseBitSeq.cs index baad423..6ccfcb6 100644 --- a/Substrate.NetApi/Model/Types/Base/BaseBitSeq.cs +++ b/Substrate.NetApi/Model/Types/Base/BaseBitSeq.cs @@ -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 { @@ -31,19 +34,38 @@ public class BaseBitSeq : IType [JsonIgnore] public byte[] Bytes { get; internal set; } + private BitArray _bits { get; set; } + + /// + /// Return if it is LSB (Least Significant Bit first) or MSB (Most Significant Bit first) + /// + 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) + } + } + } + /// /// Encode to Bytes /// /// public byte[] Encode() { - var result = new List - { - Reverse((byte)Value.Length) - }; + var result = new List(); + 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(); } @@ -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; } @@ -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()); } /// @@ -104,6 +132,27 @@ public void Create(T1[] list) /// public void CreateFromJson(string str) => Create(Utils.HexToByteArray(str)); + /// + /// Create from string bits + /// Example : CreateFromBitString("0b11010000_00000001_10100000_00000000_00000000") + /// + /// + public void CreateFromBitString(string bits) + { + var s = bits.Replace("0b", "").Split('_'); + + var result = new List(); + + 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()); + } + /// /// Create from a byte array /// @@ -121,7 +170,30 @@ public void Create(byte[] byteArray) public IType New() => this; /// - public override string ToString() => JsonConvert.SerializeObject(this); + public override string ToString() => $"{{ bits = {_bits.Count}, capacity = {_bits.Count}}} => [{DisplayToBits()}]"; + + /// + /// Display this instance to bits + /// + /// + 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; /// /// Reverse