Skip to content

Commit c7badca

Browse files
Add support for progress reporting
1 parent f31033e commit c7badca

File tree

7 files changed

+65
-27
lines changed

7 files changed

+65
-27
lines changed

ProtoBuf.Logic/MessageBinder.cs

+14-9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99
namespace ProtoBuf.Logic
1010
{
11+
internal class BindingContext
12+
{
13+
public ConcurrentDictionary<(Type_Context, string names), ParserRuleContext?> TypeBindingCache { get; } = new();
14+
public ConcurrentDictionary<(MessageDefContext? messageDef, int fieldIndex), FieldContext?> FieldCache { get; } = new();
15+
}
16+
1117
internal class MessageBinder : IMessage
1218
{
1319
enum FieldType
@@ -17,16 +23,14 @@ enum FieldType
1723
Packed
1824
}
1925

20-
public MessageBinder(ProtoContext protoContext, MessageDefContext? messageDef) : this(protoContext, messageDef, null) { }
21-
22-
private MessageBinder(ProtoContext protoContext, MessageDefContext? messageDef, ConcurrentDictionary<(Type_Context, string names), ParserRuleContext?>? cache)
26+
public MessageBinder(ProtoContext protoContext, MessageDefContext? messageDef, BindingContext bindingContext)
2327
{
2428
ProtoContext = protoContext;
2529
MessageDef = messageDef;
26-
BindingCache = cache ?? new();
30+
BindingContext = bindingContext ?? new();
2731
}
2832

29-
private ConcurrentDictionary<(Type_Context, string names), ParserRuleContext?> BindingCache { get; }
33+
private BindingContext BindingContext { get; }
3034
public ProtoContext ProtoContext { get; }
3135
public MessageDefContext? MessageDef { get; }
3236
public TypedMessage? Result { get; private set; }
@@ -39,7 +43,8 @@ public void MergeFrom(CodedInputStream input)
3943
while (input.Position < targetPosition && !input.IsAtEnd)
4044
{
4145
var (index, type) = input.ReadWireTag();
42-
var field = MessageDef?.messageBody().messageElement().Select(x => x.field()).FirstOrDefault(x => int.TryParse(x?.fieldNumber()?.GetText(), out var i) && i == index);
46+
var field = BindingContext.FieldCache.GetOrAdd((MessageDef, index),
47+
key => key.messageDef?.messageBody().messageElement().Select(x => x.field()).FirstOrDefault(x => int.TryParse(x?.fieldNumber()?.GetText(), out var i) && i == key.fieldIndex));
4348
var parsedFields = MessageDef != null && field != null && FitsFieldType(type, field.type_()) is var fieldType and not FieldType.Unknown
4449
? ParseField(input, fieldType, field)
4550
: ParseUnknownField(input, new WireTag(index, type));
@@ -184,15 +189,15 @@ static IEnumerable<string> DottedNames(EnumTypeContext message)
184189
{
185190
var namesString = string.Join(".", names);
186191
var key = (expectedType, namesString);
187-
if (BindingCache.TryGetValue(key, out var cached))
192+
if (BindingContext.TypeBindingCache.TryGetValue(key, out var cached))
188193
{
189194
return cached;
190195
}
191196
else
192197
{
193198
var result = BindType(names, expectedType, names => new MessageDefBinder(names)) as ParserRuleContext
194199
?? BindType(names, expectedType, names => new EnumDefBinder(names));
195-
BindingCache.TryAdd(key, result);
200+
BindingContext.TypeBindingCache.TryAdd(key, result);
196201
return result;
197202
}
198203
}
@@ -211,7 +216,7 @@ static IEnumerable<string> DottedNames(EnumTypeContext message)
211216

212217
private ProtoType? ParseMessage(CodedInputStream stream, MessageDefContext? messageDef)
213218
{
214-
var builder = new MessageBinder(ProtoContext, messageDef, BindingCache);
219+
var builder = new MessageBinder(ProtoContext, messageDef, BindingContext);
215220
stream.ReadRawMessage(builder);
216221
return builder.Result;
217222
}

ProtoBuf.Logic/TypedMessageDecoder.cs

+11-5
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,22 @@ public sealed record class TypedField(string Name, int Index, ParserRuleContext?
5959

6060
public class TypedMessageDecoder
6161
{
62-
public IReadOnlyList<TypedMessage> Parse(CodedInputStream stream, ProtoContext protoContext, MessageDefContext initialMessage)
62+
public async Task<IReadOnlyList<TypedMessage>> Parse(CodedInputStream stream, Func<double, Task> progress, ProtoContext protoContext, MessageDefContext initialMessage)
6363
{
6464
var result = new List<TypedMessage>();
65+
var context = new BindingContext();
6566
while (!stream.IsAtEnd)
6667
{
67-
var binder = new MessageBinder(protoContext, initialMessage);
68-
stream.ReadRawMessage(binder);
69-
if (binder.Result != null)
68+
var message = await Task.Run(async () =>
7069
{
71-
result.Add(binder.Result);
70+
await progress((double)(stream.Position * 100) / stream.SizeLimit);
71+
var binder = new MessageBinder(protoContext, initialMessage, context);
72+
stream.ReadRawMessage(binder);
73+
return binder.Result;
74+
});
75+
if (message != null)
76+
{
77+
result.Add(message);
7278
}
7379
}
7480
return result;

ProtoBufTests/TypedMessageDecoderTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,8 @@ private static IReadOnlyList<TypedMessage> ParseBinary(string pbFile, string pro
265265
var decoder = new TypedMessageDecoder();
266266
var proto = ProtoParser.ParseFile("AnalyzerReport.proto");
267267
var tokenTypeInfo = proto.topLevelDef().Where(x => x.messageDef()?.messageName().GetText() == protoDefinition).First().messageDef();
268-
var actual = decoder.Parse(stream, proto, tokenTypeInfo);
269-
return actual;
268+
var actual = decoder.Parse(stream, async _ => { }, proto, tokenTypeInfo);
269+
return actual.Result;
270270
}
271271
}
272272
}

ProtoBufViewer.Blazor/Components/VirtualTreeView.razor

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
@using MudBlazor.Utilities
22
@typeparam T
33

4-
<ul>
4+
<ul class="@(TreeViewClassname())">
55
<Virtualize Items="viewList" ItemSize="40" Context="viewListItem">
6-
<li @key="@(viewListItem.Key)" style="flex-shrink:0;margin-left:@(viewListItem.Level * 32)pt;height:30pt;" class="@(ListItemClassname())">
6+
<li @key="@(viewListItem.Key)" style="flex-shrink: 0; margin-left: @(viewListItem.Level * 32)pt; height: 30pt;" class="@(ListItemClassname())">
77
<div class="@(ContentClassname(false))">
88
<MudTreeViewItemToggleButton Visible="ChildrenSelector?.Invoke(viewListItem.Item)?.Count > 0" Expanded="@viewListItem.IsExpanded" ExpandedChanged="newIsExpanded => OnClick(newIsExpanded, viewListItem)" />
99
@if (ItemTemplate == null)
@@ -29,4 +29,8 @@
2929
.AddClass("cursor-pointer", false)
3030
.AddClass("mud-treeview-item-selected", isSelected)
3131
.Build();
32+
33+
protected string TreeViewClassname() =>
34+
new CssBuilder("mud-treeview")
35+
.Build();
3236
}

ProtoBufViewer.Blazor/Pages/Index.cs

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Google.Protobuf;
22
using Microsoft.AspNetCore.Components.Forms;
33
using ProtoBuf.Logic;
4+
using System.Diagnostics;
45
using static ProtoBuf.Antlr.Protobuf3Parser;
56

67
namespace ProtoBufViewer.Blazor.Pages
@@ -13,6 +14,7 @@ public partial class Index
1314
HashSet<MessageViewModel> Messages { get; } = new();
1415
MessageViewModel? SelectedMessage { get; set; }
1516
ProtoContext? ParseResult { get; set; }
17+
public double? Progress { get; set; }
1618

1719
List<ProtoType>? TypedMessages { get; set; }
1820

@@ -34,14 +36,27 @@ private async Task MessageFileChanged(IBrowserFile file)
3436

3537
private async Task BinFilesChanged(IBrowserFile file)
3638
{
39+
if (ParseResult == null || SelectedMessage == null) return;
3740
ProtoBinFile = file;
38-
using var ms = new MemoryStream();
39-
await file.OpenReadStream().CopyToAsync(ms);
41+
using var ms = new MemoryStream(capacity: (int)file.Size);
42+
Progress = 0;
43+
await file.OpenReadStream(maxAllowedSize: file.Size).CopyToAsync(ms);
4044
ms.Position = 0;
41-
using var coded = CodedInputStream.CreateWithLimits(ms, int.MaxValue, int.MaxValue);
45+
using var coded = CodedInputStream.CreateWithLimits(ms, (int)ms.Length, int.MaxValue);
4246
var decoder = new TypedMessageDecoder();
43-
var result = decoder.Parse(coded, ParseResult, SelectedMessage.MessageDefContext);
47+
var lastRender = Stopwatch.StartNew();
48+
var result = await decoder.Parse(coded, async progress =>
49+
{
50+
Progress = progress;
51+
if (lastRender.ElapsedMilliseconds > 1000)
52+
{
53+
lastRender = Stopwatch.StartNew();
54+
this.StateHasChanged();
55+
await Task.Delay(50);
56+
}
57+
}, ParseResult, SelectedMessage.MessageDefContext);
4458
TypedMessages = new(result);
59+
Progress = null;
4560
}
4661
}
4762
}

ProtoBufViewer.Blazor/Pages/Index.razor

+11-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@
33
@using ProtoBuf.Logic
44
@using static ProtoBuf.Antlr.Protobuf3Parser
55

6+
<PageTitle>Protobuf Viewer</PageTitle>
67

78
<MudGrid>
8-
<MudItem xs="12">
9-
<PageTitle>Protobuf Viewer</PageTitle>
9+
<MudItem xxl="12" xs="12">
10+
@if (Progress.HasValue)
11+
{
12+
<MudProgressLinear Value="@Progress.Value" Color="Color.Primary" Class="mt-1"/>
13+
}
14+
else
15+
{
16+
<div class="mt-2" />
17+
}
1018
</MudItem>
1119
<MudItem xs="12" sm="4">
1220
<MudFileUpload T="IBrowserFile" Accept="*.proto" FilesChanged="MessageFileChanged" Hidden="@false" InputClass="absolute mud-width-full mud-height-full overflow-hidden z-20" InputStyle="opacity:0">
@@ -58,7 +66,7 @@
5866
}
5967
else
6068
{
61-
<MudFileUpload T="IBrowserFile" FilesChanged="BinFilesChanged" Hidden="@false" InputClass="absolute mud-width-full mud-height-full overflow-hidden z-20" InputStyle="opacity:0">
69+
<MudFileUpload T="IBrowserFile" FilesChanged="BinFilesChanged" Hidden="@false" InputClass="absolute mud-width-full mud-height-full overflow-hidden z-20" InputStyle="opacity:0" >
6270
<ButtonTemplate>
6371
@if (ProtoBinFile == null)
6472
{

ProtoBufViewer.WPF/MainWindowViewModel.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ private void OpenBinary()
137137
}
138138
}
139139

140-
private void ParseBinary()
140+
private async Task ParseBinary()
141141
{
142142
TypedMessages = null;
143143
try
@@ -149,7 +149,7 @@ private void ParseBinary()
149149
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read);
150150
using var coded = CodedInputStream.CreateWithLimits(fs, int.MaxValue, int.MaxValue);
151151
var decoder = new TypedMessageDecoder();
152-
TypedMessages = decoder.Parse(coded, protoContext, messageDefContext);
152+
TypedMessages = await decoder.Parse(coded, async _ => { }, protoContext, messageDefContext);
153153
}
154154
}
155155
catch (Exception ex)

0 commit comments

Comments
 (0)