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