Migrate UseMetadata to inline format, remove metadata footer
Refactored AcBinarySerializer to write type property metadata inline after the ObjectWithMetadata marker, eliminating the need for a separate metadata footer section. Updated serialization, deserialization, and diagnostic test logic to support the new inline metadata format. Also updated settings.local.json to allow "Bash(git stash:*)" commands.
This commit is contained in:
parent
18370879ec
commit
1410ee71f0
|
|
@ -30,7 +30,8 @@
|
||||||
"Bash(dotnet new:*)",
|
"Bash(dotnet new:*)",
|
||||||
"Bash(Remove-Item \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Core\\\\AyCode.Core\\\\Serializers\\\\Toons\\\\AcToonSerializer.RelationshipDetection.cs\")",
|
"Bash(Remove-Item \"H:\\\\Applications\\\\Aycode\\\\Source\\\\AyCode.Core\\\\AyCode.Core\\\\Serializers\\\\Toons\\\\AcToonSerializer.RelationshipDetection.cs\")",
|
||||||
"Bash(find:*)",
|
"Bash(find:*)",
|
||||||
"Bash(dir:*)"
|
"Bash(dir:*)",
|
||||||
|
"Bash(git stash:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -262,20 +262,55 @@ public class AcBinarySerializerDiagnosticTests
|
||||||
// Skip any header data (strings interning, etc.)
|
// Skip any header data (strings interning, etc.)
|
||||||
// New format uses PropertyIndex directly - no metadata header with property names
|
// New format uses PropertyIndex directly - no metadata header with property names
|
||||||
|
|
||||||
// Find Object marker (0x19)
|
// Find Object marker (0x19) or ObjectWithMetadata marker (0x1F)
|
||||||
while (pos < binary.Length && binary[pos] != 0x19)
|
while (pos < binary.Length && binary[pos] != 0x19 && binary[pos] != 0x1F)
|
||||||
{
|
{
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"\n=== BODY (starts at position {pos}) ===");
|
Console.WriteLine($"\n=== BODY (starts at position {pos}) ===");
|
||||||
|
|
||||||
// The body should start with Object marker (0x19)
|
// The body should start with Object (0x19) or ObjectWithMetadata (0x1F) marker
|
||||||
var bodyStart = pos;
|
var bodyStart = pos;
|
||||||
var objectMarker = binary[pos++];
|
var objectMarker = binary[pos++];
|
||||||
Console.WriteLine($"Object marker: 0x{objectMarker:X2} (expected 0x19 for Object)");
|
Console.WriteLine($"Object marker: 0x{objectMarker:X2} (0x19=Object, 0x1F=ObjectWithMetadata)");
|
||||||
Assert.AreEqual(0x19, objectMarker, "Object marker should be 0x19");
|
Assert.IsTrue(objectMarker == 0x19 || objectMarker == 0x1F,
|
||||||
|
$"Object marker should be 0x19 or 0x1F, got 0x{objectMarker:X2}");
|
||||||
|
|
||||||
|
// If ObjectWithMetadata (0x1F), skip inline metadata
|
||||||
|
if (objectMarker == 0x1F)
|
||||||
|
{
|
||||||
|
// propNameHash (4 bytes)
|
||||||
|
var propNameHash = BitConverter.ToInt32(binary, pos);
|
||||||
|
pos += 4;
|
||||||
|
Console.WriteLine($"PropNameHash: 0x{propNameHash:X8}");
|
||||||
|
|
||||||
|
// First occurrence: propCount (VarUInt) + property hashes
|
||||||
|
// VarUInt: if top bit is set, continue reading
|
||||||
|
var propCountByte = binary[pos];
|
||||||
|
int inlinePropCount;
|
||||||
|
if ((propCountByte & 0x80) == 0)
|
||||||
|
{
|
||||||
|
inlinePropCount = propCountByte;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Multi-byte VarUInt - simplified 2-byte parsing
|
||||||
|
inlinePropCount = (propCountByte & 0x7F) | (binary[pos + 1] << 7);
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Inline metadata propCount: {inlinePropCount}");
|
||||||
|
|
||||||
|
// Skip property hashes (4 bytes each)
|
||||||
|
for (int h = 0; h < inlinePropCount; h++)
|
||||||
|
{
|
||||||
|
var hash = BitConverter.ToInt32(binary, pos);
|
||||||
|
Console.WriteLine($" Property hash [{h}]: 0x{hash:X8}");
|
||||||
|
pos += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read ref ID (if reference handling is enabled)
|
// Read ref ID (if reference handling is enabled)
|
||||||
// VarInt: if top bit is set, continue reading
|
// VarInt: if top bit is set, continue reading
|
||||||
var refIdByte = binary[pos];
|
var refIdByte = binary[pos];
|
||||||
|
|
@ -292,43 +327,50 @@ public class AcBinarySerializerDiagnosticTests
|
||||||
pos += 2; // Skip for now
|
pos += 2; // Skip for now
|
||||||
}
|
}
|
||||||
Console.WriteLine($"RefId: {refId}");
|
Console.WriteLine($"RefId: {refId}");
|
||||||
|
|
||||||
// Read property count in body
|
// Read property count in body
|
||||||
var bodyPropCount = binary[pos++];
|
var bodyPropCount = binary[pos++];
|
||||||
Console.WriteLine($"Property count in body: {bodyPropCount}");
|
Console.WriteLine($"Property count in body: {bodyPropCount}");
|
||||||
|
|
||||||
Console.WriteLine($"\n=== BODY PROPERTIES (PropertyIndex format) ===");
|
Console.WriteLine($"\n=== BODY PROPERTIES ===");
|
||||||
for (int i = 0; i < bodyPropCount && pos < binary.Length; i++)
|
for (int i = 0; i < bodyPropCount && pos < binary.Length; i++)
|
||||||
{
|
{
|
||||||
var propIndex = binary[pos++]; // This is now PropertyIndex (alphabetical order)
|
// Log the value (no PropertyIndex in inline metadata mode — properties are in hash order)
|
||||||
Console.WriteLine($" Body property [{i}]: PropertyIndex={propIndex}");
|
|
||||||
|
|
||||||
// Skip the value (simplified - just log)
|
|
||||||
var valueType = binary[pos];
|
var valueType = binary[pos];
|
||||||
if (valueType == 0x14) // DateTime
|
if (valueType == 0x14) // DateTime
|
||||||
{
|
{
|
||||||
Console.WriteLine($" -> DateTime (9 bytes)");
|
Console.WriteLine($" Property [{i}]: DateTime (9 bytes)");
|
||||||
pos += 10; // type + 9 bytes
|
pos += 10; // type + 9 bytes
|
||||||
}
|
}
|
||||||
else if (valueType >= 0xD0 && valueType <= 0xE7) // TinyInt
|
else if (valueType >= 0xC0 && valueType <= 0xFF) // TinyInt (192-255)
|
||||||
{
|
{
|
||||||
var tinyValue = valueType - 0xD0;
|
var tinyValue = valueType - 192 - 16;
|
||||||
Console.WriteLine($" -> TinyInt value: {tinyValue}");
|
Console.WriteLine($" Property [{i}]: TinyInt value: {tinyValue}");
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
else if (valueType == 0x03) // False
|
else if (valueType == 0x02) // False (BinaryTypeCode.False = 2)
|
||||||
{
|
{
|
||||||
Console.WriteLine($" -> Boolean: false");
|
Console.WriteLine($" Property [{i}]: Boolean: false");
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
else if (valueType == 0x02) // True
|
else if (valueType == 0x01) // True (BinaryTypeCode.True = 1)
|
||||||
{
|
{
|
||||||
Console.WriteLine($" -> Boolean: true");
|
Console.WriteLine($" Property [{i}]: Boolean: true");
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
else if (valueType == 0x00) // Null
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Property [{i}]: Null");
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
else if (valueType == 0xBF) // PropertySkip
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Property [{i}]: PropertySkip (default/null)");
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($" -> Unknown type: 0x{valueType:X2}");
|
Console.WriteLine($" Property [{i}]: Unknown type: 0x{valueType:X2}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -195,44 +195,13 @@ public static partial class AcBinaryDeserializer
|
||||||
_nextDupPosition = _dupData[0];
|
_nextDupPosition = _dupData[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read UseMetadata footer section (per-type property hashes)
|
// Metadata is now inline in the body (not in footer).
|
||||||
if (HasMetadata && _position < _buffer.Length)
|
// No ReadMetadataFooter() call needed.
|
||||||
{
|
|
||||||
ReadMetadataFooter();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seek back to data position
|
// Seek back to data position
|
||||||
_position = dataPosition;
|
_position = dataPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UseMetadata footer olvasása: entry-k flat array-be.
|
|
||||||
/// Formátum: [entryCount (VarUInt)]
|
|
||||||
/// entry-nként: [propNameHash (4b)][propCount (VarUInt)][hash0 (4b)][hash1 (4b)]...
|
|
||||||
/// Entry 0 = root, 1+ = nested object-ek.
|
|
||||||
/// </summary>
|
|
||||||
private void ReadMetadataFooter()
|
|
||||||
{
|
|
||||||
var entryCount = (int)ReadVarUInt();
|
|
||||||
for (var i = 0; i < entryCount; i++)
|
|
||||||
{
|
|
||||||
EnsureAvailable(4);
|
|
||||||
var propNameHash = Unsafe.ReadUnaligned<int>(ref Unsafe.AsRef(in _buffer[_position]));
|
|
||||||
_position += 4;
|
|
||||||
|
|
||||||
var propCount = (int)ReadVarUInt();
|
|
||||||
var hashes = new int[propCount];
|
|
||||||
for (var p = 0; p < propCount; p++)
|
|
||||||
{
|
|
||||||
EnsureAvailable(4);
|
|
||||||
hashes[p] = Unsafe.ReadUnaligned<int>(ref Unsafe.AsRef(in _buffer[_position]));
|
|
||||||
_position += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextClass.RegisterFooterEntry(i, propNameHash, hashes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public byte ReadByte() => ReadByteInternal();
|
public byte ReadByte() => ReadByteInternal();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,14 +74,6 @@ public static partial class AcBinarySerializer
|
||||||
private const int PropertyIndexBufferMaxCache = 512;
|
private const int PropertyIndexBufferMaxCache = 512;
|
||||||
private const int PropertyStateBufferMaxCache = 512;
|
private const int PropertyStateBufferMaxCache = 512;
|
||||||
private const int InitialInternCapacity = 32;
|
private const int InitialInternCapacity = 32;
|
||||||
/// <summary>
|
|
||||||
/// UseMetadata footer bejegyzések sorrendben.
|
|
||||||
/// Minden bejegyzés egy (Type, propertyHashes) pár.
|
|
||||||
/// A 0. elem mindig a root object.
|
|
||||||
/// A body-ban a nested object-eknél az index (VarUInt) kerül kiírásra.
|
|
||||||
/// </summary>
|
|
||||||
private List<MetadataFooterEntry>? _metadataEntries;
|
|
||||||
|
|
||||||
private byte[] _buffer;
|
private byte[] _buffer;
|
||||||
private int _position;
|
private int _position;
|
||||||
private int _initialBufferSize;
|
private int _initialBufferSize;
|
||||||
|
|
@ -129,7 +121,11 @@ public static partial class AcBinarySerializer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if we need footer position in header (string interning OR reference handling OR metadata).
|
/// True if we need footer position in header (string interning OR reference handling OR metadata).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasFooter => UseStringInterning || ReferenceHandling != ReferenceHandlingMode.None || UseMetadata;
|
/// <summary>
|
||||||
|
/// True if we need footer position in header (string interning OR reference handling).
|
||||||
|
/// UseMetadata no longer uses footer — metadata is inline in the body.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFooter => UseStringInterning || ReferenceHandling != ReferenceHandlingMode.None;
|
||||||
public bool UseMetadata => Options.UseMetadata;
|
public bool UseMetadata => Options.UseMetadata;
|
||||||
public byte MinStringInternLength => Options.MinStringInternLength;
|
public byte MinStringInternLength => Options.MinStringInternLength;
|
||||||
public byte MaxStringInternLength => Options.MaxStringInternLength;
|
public byte MaxStringInternLength => Options.MaxStringInternLength;
|
||||||
|
|
@ -180,7 +176,6 @@ public static partial class AcBinarySerializer
|
||||||
|
|
||||||
//_refTracker.Reset();
|
//_refTracker.Reset();
|
||||||
_stringInternMap?.Reset();
|
_stringInternMap?.Reset();
|
||||||
_metadataEntries?.Clear();
|
|
||||||
_nextCacheIndex = 0;
|
_nextCacheIndex = 0;
|
||||||
|
|
||||||
if (_propertyIndexBuffer != null && _propertyIndexBuffer.Length > PropertyIndexBufferMaxCache)
|
if (_propertyIndexBuffer != null && _propertyIndexBuffer.Length > PropertyIndexBufferMaxCache)
|
||||||
|
|
@ -367,68 +362,37 @@ public static partial class AcBinarySerializer
|
||||||
#region UseMetadata Type Tracking
|
#region UseMetadata Type Tracking
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Egy footer bejegyzés: típus propNameHash + property hash-ek.
|
/// Regisztrálja a típust UseMetadata módban.
|
||||||
/// </summary>
|
/// Visszaadja true-t ha ez az első előfordulás (inline hash-eket kell írni),
|
||||||
internal readonly struct MetadataFooterEntry
|
/// false-t ha ismételt (csak propNameHash kell).
|
||||||
{
|
|
||||||
public readonly int PropNameHash; // FNV-1a hash a típus nevéből
|
|
||||||
public readonly int[] PropertyHashes; // property name hash-ek sorrendben
|
|
||||||
|
|
||||||
public MetadataFooterEntry(int propNameHash, int[] propertyHashes)
|
|
||||||
{
|
|
||||||
PropNameHash = propNameHash;
|
|
||||||
PropertyHashes = propertyHashes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Regisztrálja a típust a UseMetadata footer-be.
|
|
||||||
/// Visszaadja a footer index-et. Ha már regisztrálva van (wrapper.MetadataFooterIndex >= 0),
|
|
||||||
/// a meglévő index-et adja vissza. Nincs Dictionary lookup — a wrapper tárolja az indexet.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public int RegisterMetadataType(TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper)
|
public bool RegisterMetadataType(TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper)
|
||||||
{
|
{
|
||||||
if (wrapper.MetadataFooterIndex >= 0)
|
if (wrapper.MetadataFooterIndex >= 0)
|
||||||
return wrapper.MetadataFooterIndex;
|
return false; // ismételt
|
||||||
|
|
||||||
return RegisterMetadataTypeSlow(wrapper);
|
wrapper.MetadataFooterIndex = 0; // jelöljük hogy már regisztrálva
|
||||||
}
|
return true; // első előfordulás
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private int RegisterMetadataTypeSlow(TypeMetadataWrapper<BinarySerializeTypeMetadata> wrapper)
|
|
||||||
{
|
|
||||||
_metadataEntries ??= new List<MetadataFooterEntry>();
|
|
||||||
|
|
||||||
// PropNameHash and MetadataPropertyHashes are lazy-computed once per type in metadata.
|
|
||||||
// Duplicate hash validation also happens once (in MetadataPropertyHashes getter).
|
|
||||||
var index = _metadataEntries.Count;
|
|
||||||
_metadataEntries.Add(new MetadataFooterEntry(
|
|
||||||
wrapper.Metadata.PropNameHash,
|
|
||||||
wrapper.Metadata.MetadataPropertyHashes));
|
|
||||||
wrapper.MetadataFooterIndex = index;
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// UseMetadata footer kiírása.
|
/// Inline metadata kiírása az ObjectWithMetadata marker után.
|
||||||
/// Formátum: [entryCount (VarUInt)]
|
/// Első előfordulás: [propNameHash (4b)][propCount (VarUInt)][hash0 (4b)][hash1 (4b)]...
|
||||||
/// entry-nként: [propNameHash (4b)][propCount (VarUInt)][hash0 (4b)][hash1 (4b)]...
|
/// Ismételt: [propNameHash (4b)]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void WriteMetadataFooter()
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteInlineMetadata(BinarySerializeTypeMetadata metadata, bool isFirstOccurrence)
|
||||||
{
|
{
|
||||||
if (_metadataEntries == null || _metadataEntries.Count == 0) return;
|
WriteRaw(metadata.PropNameHash);
|
||||||
|
|
||||||
WriteVarUInt((uint)_metadataEntries.Count);
|
if (isFirstOccurrence)
|
||||||
|
|
||||||
for (var i = 0; i < _metadataEntries.Count; i++)
|
|
||||||
{
|
{
|
||||||
var entry = _metadataEntries[i];
|
var hashes = metadata.MetadataPropertyHashes;
|
||||||
WriteRaw(entry.PropNameHash);
|
WriteVarUInt((uint)hashes.Length);
|
||||||
WriteVarUInt((uint)entry.PropertyHashes.Length);
|
for (var i = 0; i < hashes.Length; i++)
|
||||||
for (var j = 0; j < entry.PropertyHashes.Length; j++)
|
|
||||||
{
|
{
|
||||||
WriteRaw(entry.PropertyHashes[j]);
|
WriteRaw(hashes[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1063,27 +1027,22 @@ public static partial class AcBinarySerializer
|
||||||
{
|
{
|
||||||
var dupCount = GetDupCount(); // Shared counter: string intern + ID tracking
|
var dupCount = GetDupCount(); // Shared counter: string intern + ID tracking
|
||||||
var hasInternTable = dupCount > 0;
|
var hasInternTable = dupCount > 0;
|
||||||
var hasMetadataFooter = UseMetadata && _metadataEntries is { Count: > 0 };
|
|
||||||
|
|
||||||
// Footer: write merged intern entries (string + ID)
|
// Footer: write merged intern entries (string + ID)
|
||||||
|
// Metadata footer is no longer written here — metadata is inline in the body.
|
||||||
var footerPosition = 0;
|
var footerPosition = 0;
|
||||||
if (hasInternTable || hasMetadataFooter)
|
if (hasInternTable)
|
||||||
{
|
{
|
||||||
footerPosition = _position;
|
footerPosition = _position;
|
||||||
|
|
||||||
// Intern footer
|
// Intern footer
|
||||||
WriteVarUInt((uint)dupCount);
|
WriteVarUInt((uint)dupCount);
|
||||||
if (hasInternTable)
|
WriteInternedFooter();
|
||||||
WriteInternedFooter();
|
|
||||||
|
|
||||||
// Metadata footer (per-type property hashes)
|
|
||||||
if (hasMetadataFooter)
|
|
||||||
WriteMetadataFooter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
var flags = BinaryTypeCode.HeaderFlagsBase;
|
var flags = BinaryTypeCode.HeaderFlagsBase;
|
||||||
if (hasMetadataFooter)
|
if (UseMetadata)
|
||||||
flags |= BinaryTypeCode.HeaderFlag_Metadata;
|
flags |= BinaryTypeCode.HeaderFlag_Metadata;
|
||||||
// Encode ReferenceHandlingMode using separate bits
|
// Encode ReferenceHandlingMode using separate bits
|
||||||
if (ReferenceHandling == ReferenceHandlingMode.OnlyId)
|
if (ReferenceHandling == ReferenceHandlingMode.OnlyId)
|
||||||
|
|
|
||||||
|
|
@ -775,22 +775,16 @@ public static partial class AcBinarySerializer
|
||||||
var metadata = wrapper.Metadata;
|
var metadata = wrapper.Metadata;
|
||||||
|
|
||||||
// Wire format:
|
// Wire format:
|
||||||
// - IId types: [Object/ObjectWithMetadata][props 0-tól...] - Id a props-ban, nincs extra
|
// - UseMetadata=false: [Object][props...]
|
||||||
// - Non-IId + All: [Object/ObjectWithMetadata][props 0-tól...] - no hashcode prefix
|
// - UseMetadata=true, első: [ObjectWithMetadata][propNameHash (4b)][propCount (VarUInt)][hash0..N][props...]
|
||||||
// - Ref=Off: [Object/ObjectWithMetadata][props 0-tól...] - no prefix
|
// - UseMetadata=true, ismételt: [ObjectWithMetadata][propNameHash (4b)][props...]
|
||||||
// ObjectRef format:
|
// ObjectRef: [ObjectRef][cacheIndex]
|
||||||
// - IId: [ObjectRef][cacheIndex]
|
|
||||||
// - Non-IId: [ObjectRef][cacheIndex]
|
|
||||||
//
|
|
||||||
// UseMetadata:
|
|
||||||
// - Root (depth==0): [Object] marker, footer entry 0
|
|
||||||
// - Nested: [ObjectWithMetadata][footerIndex (VarUInt)]
|
|
||||||
|
|
||||||
// UseMetadata: regisztráljuk a típust a footer-be (index kell a marker kiíráshoz)
|
// UseMetadata: típus regisztrálása (első vs ismételt előfordulás tracking)
|
||||||
var metadataFooterIndex = -1;
|
var isFirstMetadataOccurrence = false;
|
||||||
if (context.UseMetadata)
|
if (context.UseMetadata)
|
||||||
{
|
{
|
||||||
metadataFooterIndex = context.RegisterMetadataType(wrapper);
|
isFirstMetadataOccurrence = context.RegisterMetadataType(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.UseTypeReferenceHandling(metadata))
|
if (context.UseTypeReferenceHandling(metadata))
|
||||||
|
|
@ -840,11 +834,11 @@ public static partial class AcBinarySerializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marker kiírása: UseMetadata nested → ObjectWithMetadata + footer index, egyébként Object
|
// Marker kiírása: UseMetadata → ObjectWithMetadata + inline metadata, egyébként Object
|
||||||
if (context.UseMetadata && isNested)
|
if (context.UseMetadata)
|
||||||
{
|
{
|
||||||
context.WriteByte(BinaryTypeCode.ObjectWithMetadata);
|
context.WriteByte(BinaryTypeCode.ObjectWithMetadata);
|
||||||
context.WriteVarUInt((uint)metadataFooterIndex);
|
context.WriteInlineMetadata(wrapper.Metadata, isFirstMetadataOccurrence);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue