Skip to content

Commit 5119ffc

Browse files
committed
feat: exotic cells support, fix issue #9
1 parent f4e2799 commit 5119ffc

File tree

3 files changed

+78
-20
lines changed

3 files changed

+78
-20
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace TonLibDotNet.Cells
2+
{
3+
public class ExoticCellTests
4+
{
5+
private readonly Xunit.Abstractions.ITestOutputHelper outputHelper;
6+
7+
public ExoticCellTests(Xunit.Abstractions.ITestOutputHelper testOutputHelper)
8+
{
9+
this.outputHelper = testOutputHelper;
10+
}
11+
12+
[Fact]
13+
public void Issue9()
14+
{
15+
var source = "te6ccgECMwEABGoAAQgN9gLWAQlGA0I0rXIU3k/T5YpIO61lji2qYaoATvD6VByERzLig6MRAB4CIgWBcAIDBChIAQGscZahZEm3AZ4UdJ/dPrM78V0dRavHQpL6eB97AT6ldgAdIgEgBQYiASAHCChIAQHKGp/PF1wER/+4CaUIDKX/eiapHWm68vYD67aG7oIwLwAcKEgBAeTKJ8h5EGoDWsQX5S59aBqKEgGcUuslml9kIesPIr/xABoiASAJCihIAQGWtVL44MWCN4nl/16k7mooWkIJjktOgRcEunDS3G4YTAAZIgEgCwwiASANDihIAQFDMibwmVAs8NhR0vm0G4EXGAegDpqMaAKdOZZ1PUGZJQAXIgEgDxAoSAEBFMTW8x4iujqXyxnw8M83KBMqkBeg9hSxxJlKaLkyEEIAFihIAQFqEyscibISEP49oA3DFI7Fcfi2zrgrOknT+xRCrBp/cgAVIgEgERIoSAEBu07mjLXgshhCe8gFAFgsHjeW6/NqkV6Fxa14vTiI7PEAFCIBIBMUIgEgFRYoSAEBOIvEqlpTU8HT73NGqNTIyUfRO/BaYBRGBpMxfBxiFeEAEyIBIBcYKEgBAUh34ZMzMw36QL8MM4rIbf21AnC2DmiMjE0+Eh65fyL6ABEiASAZGihIAQEGTGRyw+F92fB5yTy6s98UTgmH7DBtHZdF8VBsPRklXAARIgEgGxwoSAEB/BfsrcrzVzEDfmc08kBJD/7BxVRla10dUnORaYpz6n0ADyhIAQHvse+SWPH2lxdldL8f459f7z7KvGWFcBPSbrGpRaV5XAAOIgEgHR4iASAfIChIAQF79/6raH+JRIIJDxvgQdONMUIl8iQP61pgZpPuvMz93AAOIgEgISIoSAEBYuNlD9WWXdgwDYNmaX0yyyJIgl/mVFvQ4k/IGHsqaLoADChIAQEAjwF4BPkGST68ndJz8VuEXANhYkaqFUFfdMNQY2W4aQALIgEgIyQoSAEB7RZAAe+jhAHJO+s3Y6pjPBk/tngCueKrAq9mkimXsiUACCIBICUmIgEgJygoSAEByDxVfFYpwwCYDWc25QK6r714aHKw5RZ9rs82FXvsrYwAByhIAQFS4LvxJSS5mzuQn7cnCOQ3z6cheqohnRtThY0mHsX6KwAGIgEgKSoiASArLChIAQFu6fvcPFG8zl5yRNjWIbkY12TL37CPuXe+AEmdv11h5AAFKEgBAd+Jh9SZp1gUOfTYKKQ6wdwq0c+FtJXImheyS/8DsjwnAAIiASAtLihIAQHN5Y49qCxJWMDRh+OrDf/PYSYtSwYpLkyjRiFjJQNNwwACIgEgLzAoSAEBvANMZEHZMeuHcivPXq5suoJLSyKv0cfU+OUXylmOoh8AACIBIDEyAGG6O8UAZQlH0cmyoTAQ+MguYNfwWNZkYDD7AOZL4rCYBgYFpCc8AAGb1MKAAAGyYyyCKEgBAZUuZYA8cQmbA3gOMBwAGz/KEhKvaCe2L5hiQuFEibfbAAI=";
16+
17+
var boc = Boc.ParseFromBase64(source);
18+
outputHelper.WriteLine(boc.DumpCells());
19+
20+
var result = boc.SerializeToBase64(false);
21+
Assert.Equal(source, result);
22+
}
23+
}
24+
}

TonLibDotNet/Cells/Boc.cs

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ static void AppendRefs(Cell cell, List<Cell> list)
165165

166166
foreach(var cell in list)
167167
{
168-
ms.WriteByte((byte)cell.Refs.Count);
169-
ms.WriteByte((byte)(Math.Ceiling(cell.BitsCount / 8f) + Math.Floor(cell.BitsCount / 8f)));
168+
var (d1, d2) = cell.GetDescriptors();
169+
ms.WriteByte(d1);
170+
ms.WriteByte(d2);
170171
ms.Write(cell.Content);
171172
foreach(var r in cell.Refs)
172173
{
@@ -282,39 +283,31 @@ protected static (Boc?, string? error) TryParseImpl(ReadOnlySpan<byte> bytes)
282283
}
283284
}
284285

285-
var data = new (bool isOrdinary, int start, int length, bool isAugmented, int[] links)[numberOfCells];
286+
var data = new (bool isExotic, byte level, int start, int length, bool isAugmented, int[] links)[numberOfCells];
286287
for (var i = 0; i < numberOfCells; i++)
287288
{
288289
var d1 = bytes[pos++];
289-
var numberOfLinks = d1 & 0b111;
290-
var isOrdinary = (d1 & 0b1000) == 0;
291290
var d2 = bytes[pos++];
292-
var isAugmented = (d2 % 2) != 0;
293-
var bytesOfData = d2 / 2 + (isAugmented ? 1 : 0);
291+
var (refCount, isExotic, level, dataLength, isAugmented) = Cell.ParseDescriptors(d1, d2);
294292
var dataStart = pos;
295-
pos += bytesOfData;
293+
pos += dataLength;
296294

297-
var refs = new int[numberOfLinks];
298-
for (var j = 0; j < numberOfLinks; j++)
295+
var refs = new int[refCount];
296+
for (var j = 0; j < refCount; j++)
299297
{
300298
refs[j] = ReadValue(bytes, ref pos, bytesForNumberOfCells);
301299
}
302300

303-
data[i] = (isOrdinary, dataStart, bytesOfData, isAugmented, refs);
301+
data[i] = (isExotic, level, dataStart, dataLength, isAugmented, refs);
304302
}
305303

306304
var cells = new Cell[numberOfCells];
307305
for (var i = numberOfCells - 1; i >= 0; i--)
308306
{
309-
var item = data[i];
310-
if (!item.isOrdinary)
311-
{
312-
continue;
313-
}
314-
315-
var content = bytes.Slice(item.start, item.length);
316-
var refs = item.links.Select(x => cells[x]).ToArray();
317-
cells[i] = new Cell(content, item.isAugmented, refs);
307+
var (isExotic, level, start, length, isAugmented, links) = data[i];
308+
var content = bytes.Slice(start, length);
309+
var refs = links.Select(x => cells[x]).ToArray();
310+
cells[i] = new Cell(isExotic, level, content, isAugmented, refs);
318311
}
319312

320313
var rootCells = rootCellIndexes.Select(x => cells[x]).ToArray();

TonLibDotNet/Cells/Cell.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ public class Cell
77
public const int MaxRefs = 4;
88

99
public Cell(ReadOnlySpan<byte> content, bool isAugmented, ICollection<Cell>? refs = null)
10+
: this (false, 0, content, isAugmented, refs)
11+
{
12+
// Nothing.
13+
}
14+
15+
public Cell(bool isExotic, byte level, ReadOnlySpan<byte> content, bool isAugmented, ICollection<Cell>? refs = null)
1016
{
1117
if (content.Length > MaxContentLength)
1218
{
@@ -23,6 +29,8 @@ public Cell(ReadOnlySpan<byte> content, bool isAugmented, ICollection<Cell>? ref
2329
throw new ArgumentOutOfRangeException(nameof(refs), $"Too many refs ({refs.Count}), only {MaxRefs} are allowed.");
2430
}
2531

32+
this.IsExotic = isExotic;
33+
this.Level = level;
2634
this.Content = content.ToArray();
2735
BitsCount = this.Content.Length * 8;
2836
if (isAugmented)
@@ -48,6 +56,10 @@ public Cell(ReadOnlySpan<byte> content, bool isAugmented, ICollection<Cell>? ref
4856
this.Refs = refs?.ToList().AsReadOnly() ?? new List<Cell>().AsReadOnly();
4957
}
5058

59+
public bool IsExotic { get; set; }
60+
61+
public byte Level { get; set; }
62+
5163
public byte[] Content { get; init; }
5264

5365
public bool IsAugmented { get; init; }
@@ -56,6 +68,35 @@ public Cell(ReadOnlySpan<byte> content, bool isAugmented, ICollection<Cell>? ref
5668

5769
public int BitsCount { get; protected set; }
5870

71+
public static (byte refCount, bool isExotic, byte level, int dataLength, bool isAugmented) ParseDescriptors(byte d1, byte d2)
72+
{
73+
var refCount = d1 & 0b111;
74+
var isExotic = (d1 & 0b1000) != 0;
75+
var level = d1 / 32;
76+
var isAugmented = (d2 % 2) != 0;
77+
var dataLength = d2 / 2 + (isAugmented ? 1 : 0);
78+
return ((byte)refCount, isExotic, (byte)level, dataLength, isAugmented);
79+
}
80+
81+
/// <summary>
82+
/// Builds cell d1 and d2 descriptors.
83+
/// </summary>
84+
/// <remarks>
85+
/// <para>tvm.pdf, 3.1.4. Standard cell representation:</para>
86+
/// <para>
87+
/// Byte d1 equals r + 8s + 32l, where 0 ≤ r ≤ 4 is the quantity of cell references contained
88+
/// in the cell, 0 ≤ l ≤ 3 is the level of the cell, and 0 ≤ s ≤ 1 is 1 for
89+
/// exotic cells and 0 for ordinary cells.
90+
/// </para>
91+
/// <para>Byte d2 equals ⌊b / 8⌋+⌈b / 8⌉, where 0 ≤ b ≤ 1023 is the quantity of data bits in c.</para>
92+
/// </remarks>
93+
public (byte d1, byte d2) GetDescriptors()
94+
{
95+
var d1 = 32 * Level + (IsExotic ? 8 : 0) + Refs.Count;
96+
var d2 = Math.Ceiling(BitsCount / 8f) + Math.Floor(BitsCount / 8f);
97+
return ((byte)d1, (byte)d2);
98+
}
99+
59100
public Slice BeginRead()
60101
{
61102
var bits = new bool[BitsCount];

0 commit comments

Comments
 (0)