Refactor string interning to use enum and attribute
Replaces boolean UseStringInterning with StringInterningMode enum for more granular control (None, Attribute, All). Introduces AcStringInternAttribute for per-property interning configuration. Updates all usages and documentation to reflect the new approach, ensuring explicit and flexible string interning behavior in serialization. Default mode is now All to preserve legacy behavior.
This commit is contained in:
parent
1a77ee4bf9
commit
e73fd7ae4a
|
|
@ -535,7 +535,7 @@ public abstract class AcBinaryOptionsBenchmarkBase
|
|||
BinaryBenchmarkMode.FastMode => new AcBinarySerializerOptions
|
||||
{
|
||||
UseMetadata = false,
|
||||
UseStringInterning = false,
|
||||
UseStringInterning = StringInterningMode.None,
|
||||
ReferenceHandling = ReferenceHandlingMode.None,
|
||||
},
|
||||
_ => new AcBinarySerializerOptions()
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ public static class Program
|
|||
sharedUser: sharedUser);
|
||||
|
||||
var options = AcBinarySerializerOptions.WithoutReferenceHandling;
|
||||
options.UseStringInterning = false;
|
||||
options.UseStringInterning = StringInterningMode.None;
|
||||
|
||||
// Warmup (fills caches)
|
||||
System.Console.WriteLine("Warming up (10 iterations)...");
|
||||
|
|
@ -419,7 +419,7 @@ public static class Program
|
|||
new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.Default, SerializerAcBinaryDefault),
|
||||
new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.WithoutReferenceHandling, SerializerAcBinaryNoRef),
|
||||
new AcBinaryBenchmark(testData.Order, AcBinarySerializerOptions.FastMode, SerializerAcBinaryFastMode),
|
||||
new AcBinaryBenchmark(testData.Order, new AcBinarySerializerOptions { UseStringInterning = false }, SerializerAcBinaryNoIntern),
|
||||
new AcBinaryBenchmark(testData.Order, new AcBinarySerializerOptions { UseStringInterning = StringInterningMode.None }, SerializerAcBinaryNoIntern),
|
||||
|
||||
// AcJson
|
||||
new AcJsonBenchmark(testData.Order, AcJsonSerializerOptions.Default, SerializerAcJsonDefault),
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public class AcBinarySerializerBenchmarkTests
|
|||
var order = TestDataFactory.CreateBenchmarkOrder(itemCount: 5, palletsPerItem: 3, measurementsPerPallet: 2, pointsPerMeasurement: 5);
|
||||
|
||||
var binaryWithInterning = AcBinarySerializer.Serialize(order, AcBinarySerializerOptions.Default);
|
||||
var binaryWithoutInterning = AcBinarySerializer.Serialize(order, new AcBinarySerializerOptions { UseStringInterning = false });
|
||||
var binaryWithoutInterning = AcBinarySerializer.Serialize(order, new AcBinarySerializerOptions { UseStringInterning = StringInterningMode.None });
|
||||
|
||||
// Note: String interning may not always result in smaller size due to header overhead
|
||||
// The primary benefit is for larger datasets with many repeated strings
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class AcBinarySerializerStringInterningTests
|
|||
|
||||
var binaryWithInterning = AcBinarySerializer.Serialize(obj, AcBinarySerializerOptions.Default);
|
||||
var binaryWithoutInterning = AcBinarySerializer.Serialize(obj,
|
||||
new AcBinarySerializerOptions { UseStringInterning = false });
|
||||
new AcBinarySerializerOptions { UseStringInterning = StringInterningMode.None });
|
||||
|
||||
Assert.IsTrue(binaryWithInterning.Length < binaryWithoutInterning.Length,
|
||||
$"With interning: {binaryWithInterning.Length}, Without: {binaryWithoutInterning.Length}");
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ public class QuickBenchmark
|
|||
var withInterningMs = sw.Elapsed.TotalMilliseconds;
|
||||
|
||||
// Without interning
|
||||
var noInternOptions = new AcBinarySerializerOptions { UseStringInterning = false };
|
||||
var noInternOptions = new AcBinarySerializerOptions { UseStringInterning = StringInterningMode.None };
|
||||
sw.Restart();
|
||||
byte[] withoutInterning = null!;
|
||||
for (int i = 0; i < iterations; i++)
|
||||
|
|
@ -427,7 +427,7 @@ public class QuickBenchmark
|
|||
sharedMetadata: sharedMeta);
|
||||
|
||||
var singleOptions = AcBinarySerializerOptions.FastMode;
|
||||
singleOptions.UseStringInterning = false;
|
||||
singleOptions.UseStringInterning = StringInterningMode.None;
|
||||
|
||||
Console.WriteLine("=== MINIMAL WARMUP TEST ===");
|
||||
Console.WriteLine();
|
||||
|
|
@ -507,9 +507,9 @@ public class QuickBenchmark
|
|||
|
||||
// Options
|
||||
var withRefOptions = AcBinarySerializerOptions.Default;
|
||||
//withRefOptions.UseStringInterning = false;
|
||||
//withRefOptions.UseStringInterning = StringInterningMode.None;
|
||||
var noRefOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
|
||||
noRefOptions.UseStringInterning = false;
|
||||
noRefOptions.UseStringInterning = StringInterningMode.None;
|
||||
|
||||
// Pre-serialize
|
||||
var acBinaryWithRef = AcBinarySerializer.Serialize(testOrder, withRefOptions);
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ public static partial class AcBinarySerializer
|
|||
#endif
|
||||
|
||||
// These properties delegate to Options for convenience
|
||||
public bool UseStringInterning => Options.UseStringInterning;
|
||||
public bool UseStringInterning => Options.UseStringInterning != StringInterningMode.None;
|
||||
public bool UseMetadata => Options.UseMetadata;
|
||||
public byte MinStringInternLength => Options.MinStringInternLength;
|
||||
public byte MaxStringInternLength => Options.MaxStringInternLength;
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ public static partial class AcBinarySerializer
|
|||
options ??= AcBinarySerializerOptions.Default;
|
||||
|
||||
// For analysis, use the provided reference handling mode
|
||||
var analysisOptions = new AcBinarySerializerOptions
|
||||
{
|
||||
UseStringInterning = true,
|
||||
var analysisOptions = new AcBinarySerializerOptions
|
||||
{
|
||||
UseStringInterning = StringInterningMode.All,
|
||||
MinStringInternLength = options.MinStringInternLength,
|
||||
MaxStringInternLength = options.MaxStringInternLength,
|
||||
ReferenceHandling = options.ReferenceHandling
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
|
|||
/// </summary>
|
||||
public static AcBinarySerializerOptions FastMode => new()
|
||||
{
|
||||
UseStringInterning = false,
|
||||
UseStringInterning = StringInterningMode.None,
|
||||
ReferenceHandling = ReferenceHandlingMode.None
|
||||
};
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
|
|||
public static AcBinarySerializerOptions ShallowCopy => new()
|
||||
{
|
||||
MaxDepth = 0,
|
||||
UseStringInterning = false,
|
||||
UseStringInterning = StringInterningMode.None,
|
||||
ReferenceHandling = ReferenceHandlingMode.None
|
||||
};
|
||||
|
||||
|
|
@ -89,12 +89,13 @@ public sealed class AcBinarySerializerOptions : AcSerializerOptions
|
|||
public bool UseMetadata { get; init; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to intern repeated strings.
|
||||
/// When enabled, duplicate strings are stored once and referenced by index.
|
||||
/// Reduces size and memory for objects with many repeated string values.
|
||||
/// Default: true
|
||||
/// Controls how string interning is applied during serialization.
|
||||
/// None: No interning, all strings written inline.
|
||||
/// Attribute: Only properties with [AcStringIntern(true)] are interned.
|
||||
/// All: All strings within length limits are interned (legacy behavior).
|
||||
/// Default: All
|
||||
/// </summary>
|
||||
public bool UseStringInterning { get; set; } = true;
|
||||
public StringInterningMode UseStringInterning { get; set; } = StringInterningMode.All;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum string length to consider for interning.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
namespace AyCode.Core.Serializers.Binaries;
|
||||
|
||||
/// <summary>
|
||||
/// Controls string interning for a specific property during binary serialization.
|
||||
/// When StringInterningMode is Attribute: only properties with [AcStringIntern(true)] are interned.
|
||||
/// When StringInterningMode is All: properties with [AcStringIntern(false)] opt-out from interning.
|
||||
/// Attribute is inherited from base class properties if not explicitly specified.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
|
||||
public sealed class AcStringInternAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether string interning is enabled for this property.
|
||||
/// </summary>
|
||||
public bool Enabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new AcStringInternAttribute.
|
||||
/// </summary>
|
||||
/// <param name="enabled">True to enable interning, false to disable.</param>
|
||||
public AcStringInternAttribute(bool enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
namespace AyCode.Core.Serializers.Binaries;
|
||||
|
||||
/// <summary>
|
||||
/// Controls string interning behavior during binary serialization.
|
||||
/// </summary>
|
||||
public enum StringInterningMode
|
||||
{
|
||||
/// <summary>
|
||||
/// No string interning. All strings are written inline.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Only intern strings on properties marked with [AcStringIntern(true)].
|
||||
/// Properties without attribute or with [AcStringIntern(false)] are written inline.
|
||||
/// </summary>
|
||||
Attribute = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Intern all strings that meet the length criteria (MinStringInternLength to MaxStringInternLength).
|
||||
/// [AcStringIntern(false)] can opt-out specific properties.
|
||||
/// </summary>
|
||||
All = 2
|
||||
}
|
||||
Loading…
Reference in New Issue