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);