Code
·
115 lines
·
3774 bytes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115using System;
using System.Threading;
using System.Threading.Tasks;
using Xtremegaida.DataStructures;
namespace MediaContainers;
public struct EBMLVInt
{
public static readonly EBMLVInt Empty = new();
public readonly byte WidthBytes;
public readonly ulong Value;
public ulong ValueMask => (1UL << ((WidthBytes << 3) - WidthBytes)) - 1;
public ulong ValueWithMarker => Value | ((0x100UL >> WidthBytes) << ((WidthBytes - 1) << 3));
public long SignedValue { get { var shift = 64 - ((WidthBytes << 3) - WidthBytes); return (long)(Value << shift) >> shift; } }
public bool IsUnknownValue => Value == ValueMask;
public bool IsMinWidth => WidthBytes == CalculateWidth(Value);
public bool IsValidValue => WidthBytes != 0;
public bool IsEmpty => WidthBytes == 0;
public EBMLVInt(byte width, ulong value)
{
WidthBytes = width;
Value = value;
if (width <= 0 || width > 8) { throw new ArgumentOutOfRangeException(nameof(width)); }
if ((value & ~ValueMask) != 0) { throw new ArgumentOutOfRangeException(nameof(value)); }
}
public EBMLVInt(ulong value) : this(CalculateWidth(value), value) { }
public static byte CalculateWidth(ulong value)
{
byte width = 1;
do
{
var mask = (1UL << ((width << 3) - width)) - 1;
if ((value & ~mask) == 0 && value != mask) { break; }
}
while (++width < 8);
return width;
}
public static EBMLVInt CreateUnknown(int width = 1)
{
if (width <= 0) { width = 1; }
if (width > 8) { width = 8; }
var mask = (1UL << ((width << 3) - width)) - 1;
return new EBMLVInt((byte)width, mask);
}
public static EBMLVInt CreateWithMarker(ulong value)
{
if (value == 0) { return Empty; }
byte width = 1;
do
{
var mask = (1UL << ((width << 3) + 1 - width)) - 1;
if ((value & ~mask) == 0) { break; }
}
while (++width < 8);
var widthMask = (ulong)(-1L << ((width << 3) + 1 - width));
if ((value & widthMask) != 0) { throw new ArgumentException(nameof(value)); }
var markerBit = ((0x100UL >> width) << ((width - 1) << 3));
if ((value & markerBit) != markerBit) { throw new ArgumentException(nameof(value)); }
return new EBMLVInt(width, value & ~markerBit);
}
public async ValueTask Write(IDataQueueWriter buffer, CancellationToken cancellationToken = default)
{
await buffer.WriteByteAsync((byte)((0x100 >> WidthBytes) | (byte)(Value >> ((WidthBytes - 1) << 3))), cancellationToken);
for (int i = 2; i <= WidthBytes; i++)
{
await buffer.WriteByteAsync((byte)((Value >> ((WidthBytes - i) << 3)) & 0xff), cancellationToken);
}
}
public void Write(DataBuffer buffer)
{
buffer.Buffer[buffer.WriteOffset++] = (byte)((0x100 >> WidthBytes) | (byte)(Value >> ((WidthBytes - 1) << 3)));
for (int i = 2; i <= WidthBytes; i++) { buffer.Buffer[buffer.WriteOffset++] = (byte)((Value >> ((WidthBytes - i) << 3)) & 0xff); }
}
public static async ValueTask<EBMLVInt> Read(IDataQueueReader buffer, CancellationToken cancellationToken = default)
{
var prefix = await buffer.ReadByteAsync(cancellationToken);
if (prefix <= 0) { return Empty; }
byte width = 1;
if ((prefix & 0x80) == 0)
{
width++;
if ((prefix & 0x40) == 0)
{
width++;
while (width < 8 && (prefix & (0x100 >> width)) == 0) { width++; }
}
}
if (width == 1) { return new EBMLVInt(1, (ulong)(prefix & 0x7F)); }
if (width == 2)
{
var next = await buffer.ReadByteAsync(cancellationToken);
if (next < 0) { return Empty; }
return new EBMLVInt(2, ((ulong)(prefix & 0x3F) << 8) | (byte)next);
}
ulong value = (ulong)(prefix & ((0x100 >> width) - 1));
for (int i = 1; i < width; i++)
{
var next = await buffer.ReadByteAsync(cancellationToken);
if (next < 0) { return Empty; }
value = (value << 8) | (byte)next;
}
return new EBMLVInt(width, value);
}
}