CultMesh: a raw write refuses a file whose header it cannot read
ExistingHeader read the whole snapshot and swallowed every failure into "no header": a truncated v3 file got a v1 raw write and an unknown v9 file was overwritten, where TypeScript, Python and Rust refuse. CultDocumentMessagePackSerialization.ReadStoreHeader reads the prefix header, refuses a file that is not one complete MessagePack value or whose format this runtime cannot read, and the raw write keeps a whole marked file marked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c4fbb53a3b
commit
aa6b11006f
3 changed files with 58 additions and 8 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
|
@ -240,6 +241,36 @@ public static class CultDocumentMessagePackSerialization
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The format header of a store file about to be replaced by a writer that does not read the store's records: null for
|
||||
/// an empty file or a legacy envelope array, the header string otherwise. The header is read from the prefix, but a file
|
||||
/// that is not one complete MessagePack value or whose header this runtime cannot read is refused, never taken for a
|
||||
/// legacy file, so a writer cannot overwrite what it cannot see.
|
||||
/// </summary>
|
||||
public static string? ReadStoreHeader(byte[] bytes)
|
||||
{
|
||||
if (bytes.Length == 0)
|
||||
return null;
|
||||
try
|
||||
{
|
||||
var whole = new MessagePackReader(bytes);
|
||||
whole.Skip();
|
||||
}
|
||||
catch (Exception ex) when (ex is EndOfStreamException or MessagePackSerializationException or InvalidOperationException)
|
||||
{
|
||||
throw new NotSupportedException("The store is not one complete MessagePack value; it is not rewritten.", ex);
|
||||
}
|
||||
|
||||
var reader = new MessagePackReader(bytes);
|
||||
if (reader.NextMessagePackType != MessagePackType.Array || reader.ReadArrayHeader() == 0 ||
|
||||
reader.NextMessagePackType != MessagePackType.String)
|
||||
return null;
|
||||
var header = reader.ReadString();
|
||||
if (header != null && header.StartsWith("cultcache.store.", StringComparison.Ordinal))
|
||||
RequireSingleFileFormat(new CultPersistedStoreSnapshot { FormatVersion = header });
|
||||
return header;
|
||||
}
|
||||
|
||||
private static void WritePersistedRecord(ref MessagePackWriter writer, CultPersistedRecord record)
|
||||
{
|
||||
writer.WriteArrayHeader(record.Variant == null ? PersistedRecordFieldCount : VariantRecordFieldCount);
|
||||
|
|
|
|||
Loading…
Reference in a new issue