/// <summary> /// Compute a cache manifest from runtime data. /// </summary> /// <param name="version">The version of the cache</param> /// <param name="graphicsApi">The graphics api used by the cache</param> /// <param name="hashType">The hash type of the cache</param> /// <param name="entries">The entries in the cache</param> /// <returns>The cache manifest from runtime data</returns> public static byte[] ComputeManifest(ulong version, CacheGraphicsApi graphicsApi, CacheHashType hashType, HashSet <Hash128> entries) { if (hashType != CacheHashType.XxHash128) { throw new NotImplementedException($"{hashType}"); } CacheManifestHeader manifestHeader = new CacheManifestHeader(version, graphicsApi, hashType); byte[] data = new byte[Unsafe.SizeOf <CacheManifestHeader>() + entries.Count * Unsafe.SizeOf <Hash128>()]; // CacheManifestHeader has the same size as a Hash128. Span <Hash128> dataSpan = MemoryMarshal.Cast <byte, Hash128>(data.AsSpan()).Slice(1); int i = 0; foreach (Hash128 hash in entries) { dataSpan[i++] = hash; } manifestHeader.UpdateChecksum(data.AsSpan(Unsafe.SizeOf <CacheManifestHeader>())); MemoryMarshal.Write(data, ref manifestHeader); return(data); }
/// <summary> /// Save the manifest file. /// </summary> private void SaveManifest() { CacheManifestHeader manifestHeader = new CacheManifestHeader(_version, _graphicsApi, _hashType); byte[] data; lock (_hashTable) { data = new byte[Unsafe.SizeOf <CacheManifestHeader>() + _hashTable.Count * Unsafe.SizeOf <Hash128>()]; // CacheManifestHeader has the same size as a Hash128. Span <Hash128> dataSpan = MemoryMarshal.Cast <byte, Hash128>(data.AsSpan()).Slice(1); int i = 0; foreach (Hash128 hash in _hashTable) { dataSpan[i++] = hash; } } manifestHeader.UpdateChecksum(data.AsSpan().Slice(Unsafe.SizeOf <CacheManifestHeader>())); MemoryMarshal.Write(data, ref manifestHeader); File.WriteAllBytes(GetManifestPath(), data); }
/// <summary> /// Try to read the manifest header from a given file path. /// </summary> /// <param name="manifestPath">The path to the manifest file</param> /// <param name="header">The manifest header read</param> /// <returns>Return true if the manifest header was read</returns> public static bool TryReadManifestHeader(string manifestPath, out CacheManifestHeader header) { header = default; if (File.Exists(manifestPath)) { Memory <byte> rawManifest = File.ReadAllBytes(manifestPath); if (MemoryMarshal.TryRead(rawManifest.Span, out header)) { return(true); } } return(false); }
/// <summary> /// Try to read the manifest from a given file path. /// </summary> /// <param name="manifestPath">The path to the manifest file</param> /// <param name="graphicsApi">The graphics api used by the cache</param> /// <param name="hashType">The hash type of the cache</param> /// <param name="header">The manifest header read</param> /// <param name="entries">The entries read from the cache manifest</param> /// <returns>Return true if the manifest was read</returns> public static bool TryReadManifestFile(string manifestPath, CacheGraphicsApi graphicsApi, CacheHashType hashType, out CacheManifestHeader header, out HashSet <Hash128> entries) { header = default; entries = new HashSet <Hash128>(); if (File.Exists(manifestPath)) { Memory <byte> rawManifest = File.ReadAllBytes(manifestPath); if (MemoryMarshal.TryRead(rawManifest.Span, out header)) { Memory <byte> hashTableRaw = rawManifest.Slice(Unsafe.SizeOf <CacheManifestHeader>()); bool isValid = header.IsValid(graphicsApi, hashType, hashTableRaw.Span); if (isValid) { ReadOnlySpan <Hash128> hashTable = MemoryMarshal.Cast <byte, Hash128>(hashTableRaw.Span); foreach (Hash128 hash in hashTable) { entries.Add(hash); } } return(isValid); } } return(false); }