From 097c1e8efe87d55c749f2506b4156fe7b285c534 Mon Sep 17 00:00:00 2001 From: Loretta Date: Thu, 5 Feb 2026 07:12:08 +0100 Subject: [PATCH] Refactor deserialization property cache construction Move CacheMap building to dedicated method for efficiency. Remove incremental cache logic and SourceHashes field. Simplify property population and update documentation. Improves performance and code clarity. --- .../Binaries/AcBinaryDeserializer.Populate.cs | 53 ++----------------- .../Binaries/AcBinaryDeserializer.cs | 42 ++++++++++++--- .../Serializers/TypeMetadataWrapper.cs | 9 +--- 3 files changed, 39 insertions(+), 65 deletions(-) diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs index e47db4e..85db77a 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.Populate.cs @@ -57,10 +57,8 @@ public static partial class AcBinaryDeserializer /// /// Unified property populate for both UseMetadata and non-UseMetadata modes. - /// UseMetadata=false: properties[i] gives the setter directly (same-type, index-based). - /// UseMetadata=true: cacheMap[i] gives the setter. If null (first object of this type), - /// fast path checks props[index+1], fallback searches from props[index+2]. - /// index tracks last matched target property index (starts at -1). + /// UseMetadata=true: cacheMap[i] gives the setter (null → skip). + /// UseMetadata=false: properties[i] gives the setter directly. /// private static void PopulateObjectPropertiesIndexed( ref BinaryDeserializationContext context, @@ -78,51 +76,10 @@ public static partial class AcBinaryDeserializer // UseMetadata: cacheMap.Length a source property-k száma // Non-UseMetadata: properties.Length a target property-k száma (source == target) var propCount = cacheMap?.Length ?? properties.Length; - var sourceHashes = wrapper.SourceHashes; // only non-null when cacheMap != null - var index = -1; // last matched target property index (for incremental CacheMap building) for (int i = 0; i < propCount; i++) { - BinaryPropertySetterBase? propInfo; - - if (cacheMap != null) - { - // UseMetadata mode - propInfo = cacheMap[i]; - if (propInfo == null && sourceHashes != null) - { - // First object of this type — inkrementális CacheMap felépítés - var hash = sourceHashes[i]; - - // Fast path: props[index+1] (same-type → next property in order) - var nextIdx = index + 1; - if (nextIdx < properties.Length && properties[nextIdx].PropertyNameHash == hash) - { - propInfo = properties[nextIdx]; - index = nextIdx; - } - else - { - // Fallback: search from index+2 (cross-type, alphabetical order → forward only) - for (var j = nextIdx + 1; j < properties.Length; j++) - { - if (properties[j].PropertyNameHash == hash) - { - propInfo = properties[j]; - index = j; - break; - } - } - } - - cacheMap[i] = propInfo; // null marad ha nincs match → skip next time too - } - } - else - { - // Non-UseMetadata: direct index-based - propInfo = properties[i]; - } + var propInfo = cacheMap != null ? cacheMap[i] : properties[i]; var peekCode = context.PeekByte(); @@ -224,10 +181,6 @@ public static partial class AcBinaryDeserializer ex); } } - - // CacheMap kész — sourceHashes null-ra, hogy a következő objektumnál ne keressen újra - if (sourceHashes != null) - wrapper.SourceHashes = null; } /// diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs index 1a2a9fa..4b59cf1 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.cs @@ -1058,12 +1058,9 @@ public static partial class AcBinaryDeserializer context.RegisterInternedValue(instance, streamPosition); } - // CacheMap + SourceHashes a wrapper-en (per-context, futásonként változhat) + // CacheMap felépítése ha még nincs (1x per target type × source type kombináció) if (wrapper.CacheMap == null) - { - wrapper.CacheMap = new BinaryPropertySetterBase?[sourceHashes.Length]; - wrapper.SourceHashes = sourceHashes; - } + BuildCacheMap(wrapper, sourceHashes); PopulateObject(ref context, instance, wrapper, depth, skipDefaultWrite: true); @@ -1108,10 +1105,41 @@ public static partial class AcBinaryDeserializer var wrapper = context.ContextClass.GetWrapper(targetType); if (wrapper.CacheMap == null) + BuildCacheMap(wrapper, sourceHashes); + } + + /// + /// CacheMap felépítése: source property hash-ek → target PropertySetter mapping. + /// Fast path: same-type esetén props[index+1] egyezik. Cross-type: keresés index+1-től. + /// + private static void BuildCacheMap(TypeMetadataWrapper wrapper, int[] sourceHashes) + { + var properties = wrapper.Metadata.PropertiesArray; + var cacheMap = new BinaryPropertySetterBase?[sourceHashes.Length]; + var index = -1; + for (var i = 0; i < sourceHashes.Length; i++) { - wrapper.CacheMap = new BinaryPropertySetterBase?[sourceHashes.Length]; - wrapper.SourceHashes = sourceHashes; + var hash = sourceHashes[i]; + var nextIdx = index + 1; + if (nextIdx < properties.Length && properties[nextIdx].PropertyNameHash == hash) + { + cacheMap[i] = properties[nextIdx]; + index = nextIdx; + } + else + { + for (var j = nextIdx; j < properties.Length; j++) + { + if (properties[j].PropertyNameHash == hash) + { + cacheMap[i] = properties[j]; + index = j; + break; + } + } + } } + wrapper.CacheMap = cacheMap; } #endregion diff --git a/AyCode.Core/Serializers/TypeMetadataWrapper.cs b/AyCode.Core/Serializers/TypeMetadataWrapper.cs index b8b75b6..827e1c4 100644 --- a/AyCode.Core/Serializers/TypeMetadataWrapper.cs +++ b/AyCode.Core/Serializers/TypeMetadataWrapper.cs @@ -46,16 +46,10 @@ public sealed class TypeMetadataWrapper where TMetadata : TypeMetadat /// /// UseMetadata cachemap: source property index → target PropertySetter. /// Per-context (wrapper-szintű), mert futásonként eltérő source type-pal találkozhat. - /// Inkrementálisan épül a PopulateObjectPropertiesIndexed-ben. + /// Felépül a ReadObjectWithMetadata-ban, első előforduláskor. /// internal BinaryPropertySetterBase?[]? CacheMap; - /// - /// Source property hash-ek a CacheMap-hez — az inline metadata-ból olvasva. - /// Per-context, mert eltérő source type-pal más hash-ek jönnek. - /// - internal int[]? SourceHashes; - #region Typed IdentityMaps - No generic type checks in hot path! /// @@ -133,7 +127,6 @@ public sealed class TypeMetadataWrapper where TMetadata : TypeMetadat { MetadataFooterIndex = -1; CacheMap = null; - SourceHashes = null; if (SmallIdBitmap != null) Array.Clear(SmallIdBitmap);