Skip to content

Commit 156d42d

Browse files
committed
Cache custom JSON enum names in JsonParser using Lazy.
Custom JSON enum names are defined in descriptor options and looked up when parsing JSON strings. Instead of performing a linear scan over Values on each lookup, this change adds a ConcurrentDictionary with Lazy<Dictionary> to JsonParser to lazily construct and cache the JSON name to EnumValueDescriptor lookup table per EnumDescriptor on first access. Lazy provides thread-safe, lock-free reads on all subsequent accesses. PiperOrigin-RevId: 963583475
1 parent f11aac4 commit 156d42d

1 file changed

Lines changed: 35 additions & 11 deletions

File tree

‎csharp/src/Google.Protobuf/JsonParser.cs‎

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
using Google.Protobuf.WellKnownTypes;
1212
using System;
1313
using System.Collections;
14+
using System.Collections.Concurrent;
1415
using System.Collections.Generic;
1516
using System.Globalization;
1617
using System.IO;
1718
using System.Linq;
1819
using System.Text;
1920
using System.Text.RegularExpressions;
21+
using System.Threading;
2022

2123
namespace Google.Protobuf
2224
{
@@ -756,23 +758,45 @@ private static object ParseSingleStringValue(FieldDescriptor field, string text)
756758
}
757759
}
758760

761+
private static readonly
762+
ConcurrentDictionary<EnumDescriptor,
763+
Lazy<Dictionary<string, EnumValueDescriptor>>>
764+
enumCustomJsonNameCache =
765+
new ConcurrentDictionary<
766+
EnumDescriptor,
767+
Lazy<Dictionary<string, EnumValueDescriptor>>>();
768+
769+
private static Dictionary<string, EnumValueDescriptor>
770+
CreateCustomJsonNameMap(EnumDescriptor enumDescriptor)
771+
{
772+
var map = new Dictionary<string, EnumValueDescriptor>();
773+
foreach (var value in enumDescriptor.Values)
774+
{
775+
var jsonOptions = value.GetOptions()?.GetExtension(
776+
Pb.Enumvalue.JsonEnumvalueOptionsExtensions.Json);
777+
if (jsonOptions != null && jsonOptions.HasString &&
778+
!map.ContainsKey(jsonOptions.String))
779+
{
780+
map[jsonOptions.String] = value;
781+
}
782+
}
783+
return map;
784+
}
785+
759786
private bool TryParseEnumStringValue(
760787
FieldDescriptor field, string text, out object value)
761788
{
762789
var enumValue = field.EnumType.FindValueByName(text);
763790
if (enumValue == null)
764791
{
765-
foreach (var valueDesc in field.EnumType.Values)
766-
{
767-
var jsonOptions = valueDesc.GetOptions()?.GetExtension(
768-
Pb.Enumvalue.JsonEnumvalueOptionsExtensions.Json);
769-
if (jsonOptions != null && jsonOptions.HasString &&
770-
jsonOptions.String == text)
771-
{
772-
enumValue = valueDesc;
773-
break;
774-
}
775-
}
792+
var map = enumCustomJsonNameCache.GetOrAdd(
793+
field.EnumType,
794+
static desc =>
795+
new Lazy<Dictionary<string, EnumValueDescriptor>>(
796+
() => CreateCustomJsonNameMap(desc),
797+
LazyThreadSafetyMode
798+
.ExecutionAndPublication)).Value;
799+
map.TryGetValue(text, out enumValue);
776800
}
777801

778802
if (enumValue == null)

0 commit comments

Comments
 (0)