Enable property-level string interning via attribute
Introduce [AcStringIntern] for selective string interning on properties. Update serializer to use a per-property eligibility flag, improving efficiency and control over which string fields participate in interning during binary serialization. Update test models and internal context accordingly.
This commit is contained in:
parent
6f88306e54
commit
e30efff56c
|
|
@ -1,4 +1,5 @@
|
|||
using AyCode.Core.Interfaces;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
|
||||
namespace AyCode.Core.Tests.TestModels;
|
||||
|
||||
|
|
@ -30,7 +31,9 @@ public static class AcSerializerModels
|
|||
|
||||
public class TestClassWithRepeatedStrings
|
||||
{
|
||||
[AcStringIntern(true)]
|
||||
public string Field1 { get; set; } = "";
|
||||
[AcStringIntern(true)]
|
||||
public string Field2 { get; set; } = "";
|
||||
public string Field3 { get; set; } = "";
|
||||
public string Field4 { get; set; } = "";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using AyCode.Core.Extensions;
|
||||
using AyCode.Core.Interfaces;
|
||||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Binaries;
|
||||
using AyCode.Core.Serializers.Jsons;
|
||||
using MessagePack;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
|
@ -60,6 +61,7 @@ public class SharedTag : IId<int>
|
|||
public int Id { get; set; }
|
||||
[Key(1)]
|
||||
public string Name { get; set; } = "";
|
||||
[AcStringIntern(true)]
|
||||
[Key(2)]
|
||||
public string Color { get; set; } = "#000000";
|
||||
[Key(3)]
|
||||
|
|
@ -133,12 +135,15 @@ public class SharedUser : IId<int>
|
|||
[MessagePackObject]
|
||||
public class UserPreferences
|
||||
{
|
||||
[AcStringIntern(true)]
|
||||
[Key(0)]
|
||||
public string Theme { get; set; } = "light";
|
||||
[AcStringIntern(true)]
|
||||
[Key(1)]
|
||||
public string Language { get; set; } = "en-US";
|
||||
[Key(2)]
|
||||
public bool NotificationsEnabled { get; set; } = true;
|
||||
[AcStringIntern(true)]
|
||||
[Key(3)]
|
||||
public string? EmailDigestFrequency { get; set; }
|
||||
}
|
||||
|
|
@ -155,8 +160,10 @@ public class UserPreferences
|
|||
[MessagePackObject]
|
||||
public class MetadataInfo
|
||||
{
|
||||
[AcStringIntern(true)]
|
||||
[Key(0)]
|
||||
public string Key { get; set; } = "";
|
||||
[AcStringIntern(true)]
|
||||
[Key(1)]
|
||||
public string Value { get; set; } = "";
|
||||
[Key(2)]
|
||||
|
|
@ -240,6 +247,7 @@ public class TestOrderItem : IId<int>
|
|||
{
|
||||
[Key(0)]
|
||||
public int Id { get; set; }
|
||||
[AcStringIntern(true)]
|
||||
[Key(1)]
|
||||
public string ProductName { get; set; } = "";
|
||||
[Key(2)]
|
||||
|
|
|
|||
|
|
@ -146,6 +146,13 @@ public static partial class AcBinarySerializer
|
|||
/// <summary>Write pass visit counter. Mirrors ScanVisitIndex ordering.</summary>
|
||||
internal int WriteVisitIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Set per-property in WritePropertyOrSkip before calling WriteString.
|
||||
/// Controls whether the current string property participates in the cursor-based interning.
|
||||
/// Must mirror scan pass's prop.UseStringPropertyInterning() check.
|
||||
/// </summary>
|
||||
internal bool StringInternEligible;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to consume the next write plan entry at the current WriteVisitIndex.
|
||||
/// Returns true if the entry matches (duplicate exists at this visit point).
|
||||
|
|
@ -264,6 +271,7 @@ public static partial class AcBinarySerializer
|
|||
ScanVisitIndex = 0;
|
||||
WritePlanCursor = 0;
|
||||
WriteVisitIndex = 0;
|
||||
StringInternEligible = false;
|
||||
|
||||
// Clear write plan string references to avoid GC pinning, keep array if small enough
|
||||
if (_writePlan != null)
|
||||
|
|
|
|||
|
|
@ -912,8 +912,10 @@ public static partial class AcBinarySerializer
|
|||
return;
|
||||
}
|
||||
|
||||
if (context.UseStringInterning && context.IsValidForInterningString(value.Length))
|
||||
if (context.StringInternEligible && context.IsValidForInterningString(value.Length))
|
||||
{
|
||||
context.StringInternEligible = false;
|
||||
|
||||
if (context.TryConsumeWritePlanEntry(out var planEntry))
|
||||
{
|
||||
ValidateWritePlanString(in planEntry, value);
|
||||
|
|
@ -1383,6 +1385,7 @@ public static partial class AcBinarySerializer
|
|||
}
|
||||
else
|
||||
{
|
||||
context.StringInternEligible = prop.UseStringPropertyInterning(context.Options.UseStringInterning);
|
||||
WriteString(value, context);
|
||||
}
|
||||
return;
|
||||
|
|
@ -1391,6 +1394,8 @@ public static partial class AcBinarySerializer
|
|||
{
|
||||
// Object type (collection, complex object, byte[], dictionary)
|
||||
// Use pre-cached wrapper, fallback to GetWrapper on miss/polymorphism
|
||||
// Set interning eligibility for string collection elements
|
||||
context.StringInternEligible = prop.IsStringCollectionProperty && prop.UseStringPropertyInterning(context.Options.UseStringInterning);
|
||||
var value = prop.GetValue(obj);
|
||||
|
||||
// SKIP marker only for null (reference types)
|
||||
|
|
@ -1736,10 +1741,7 @@ public static partial class AcBinarySerializer
|
|||
/// Uses the typed Id getter to extract the Id and compares against the scan pass IdentityMap.
|
||||
/// </summary>
|
||||
[Conditional("DEBUG")]
|
||||
private static void ValidateWritePlanObject(
|
||||
in WriteDuplicateEntry planEntry,
|
||||
object value,
|
||||
TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper)
|
||||
private static void ValidateWritePlanObject(in WriteDuplicateEntry planEntry, object value, TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper)
|
||||
{
|
||||
var metadata = wrapper.Metadata;
|
||||
switch (metadata.IdAccessorType)
|
||||
|
|
|
|||
Loading…
Reference in New Issue