Merge remote-tracking branch 'origin/hands/reader-dup-keys-csharp' into reader-dup-keys
This commit is contained in:
commit
823ed52f53
2 changed files with 25 additions and 1 deletions
|
|
@ -310,7 +310,7 @@ public static class CultDocumentMessagePackSerialization
|
|||
/// <summary>
|
||||
/// Reads a store file: exactly one complete MessagePack array of three slots (header, schema catalog, records), or an empty
|
||||
/// array, which is an empty store. A truncated file, bytes after the array, a value that is not an array, a first slot that
|
||||
/// is not a header string, a store with fewer or more slots, a record whose schema the catalog does not publish, or a body
|
||||
/// is not a header string, a store with fewer or more slots, a record whose schema the catalog does not publish, two records under one key, or a body
|
||||
/// that does not decode are refused with
|
||||
/// <see cref="CultStoreUnreadableException"/>, so a reader never takes part of a file for the whole and never skips a slot
|
||||
/// it does not understand. The rewriting paths use this same reader as their verdict on whether a file may be replaced.
|
||||
|
|
@ -366,6 +366,7 @@ public static class CultDocumentMessagePackSerialization
|
|||
}
|
||||
|
||||
RequirePublishedSchemas(snapshot);
|
||||
RequireUniqueKeys(snapshot);
|
||||
return (snapshot, catalogBytes, recordBytes);
|
||||
}
|
||||
catch (CultStoreUnreadableException)
|
||||
|
|
@ -397,6 +398,17 @@ public static class CultDocumentMessagePackSerialization
|
|||
}
|
||||
}
|
||||
|
||||
// A key names one record: identity is the key string alone, compared by ordinal code units (no case folding, no normalisation).
|
||||
private static void RequireUniqueKeys(CultPersistedStoreSnapshot snapshot)
|
||||
{
|
||||
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var record in snapshot.Records)
|
||||
{
|
||||
if (!seen.Add(record.Key))
|
||||
throw new CultStoreUnreadableException($"Record '{record.Key}' appears more than once.");
|
||||
}
|
||||
}
|
||||
|
||||
// Slot 0 of a non-empty store, read before anything after it: a binary or nil stamp is refused as such, and a string stamp is
|
||||
// judged by the caller's rule when it passes one. Bytes that do not open with an array and a string are left to the full read.
|
||||
private static void JudgeStamp(byte[] payload, Action<string>? requireReadable)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,18 @@ namespace GameCult.Caching.Tests
|
|||
return path;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ADuplicateKeyIsRefusedNamingTheKeyAndTheFile()
|
||||
{
|
||||
var path = Path.Combine(_directory, "dup.cc");
|
||||
File.WriteAllBytes(path, File.ReadAllBytes(Path.Combine(VectorRoot(), "dup-key-same-schema-v3.bin")));
|
||||
|
||||
Assert.That(() => CultCacheMessagePack.Create(path, new CultCacheOpenOptions { Registry = Registry }),
|
||||
Throws.TypeOf<CultStoreUnreadableException>()
|
||||
.With.Property(nameof(CultStoreUnreadableException.Path)).EqualTo(path)
|
||||
.And.Message.Contains("more than once"));
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(Vectors))]
|
||||
public void OpenReadsExactlyTheFilesTheVectorsSayItReads(string vector, bool reads)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue