// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using EpicGames.Core;
using EpicGames.Serialization;
namespace EpicGames.Perforce.Managed
{
///
/// Stores the contents of a stream in memory
///
public class StreamSnapshotFromMemory : StreamSnapshot
{
///
/// The current signature for saved directory objects
///
static readonly byte[] s_currentSignature = { (byte)'W', (byte)'S', (byte)'D', 5 };
///
/// The root digest
///
public override StreamTreeRef Root { get; }
///
/// Map of digest to directory
///
public IReadOnlyDictionary HashToTree { get; }
///
/// Constructor
///
///
///
public StreamSnapshotFromMemory(StreamTreeRef root, Dictionary hashToTree)
{
Root = root;
HashToTree = hashToTree;
}
///
/// Constructor
///
///
public StreamSnapshotFromMemory(StreamTreeBuilder builder)
{
Dictionary hashToTree = new Dictionary();
Root = builder.EncodeRef(tree => EncodeObject(tree, hashToTree));
HashToTree = hashToTree;
}
///
/// Serialize to a compact binary object
///
///
///
///
static IoHash EncodeObject(StreamTree tree, Dictionary hashToTree)
{
CbObject @object = tree.ToCbObject();
IoHash hash = @object.GetHash();
hashToTree[hash] = @object;
return hash;
}
///
public override StreamTree Lookup(StreamTreeRef treeRef)
{
return new StreamTree(treeRef.Path, HashToTree[treeRef.Hash]);
}
///
/// Load a stream directory from a file on disk
///
/// File to read from
/// Base path to use if missing inside loaded file
/// Cancellation token
/// New StreamDirectoryInfo object
public static async Task TryLoadAsync(FileReference inputFile, Utf8String defaultBasePath, CancellationToken cancellationToken)
{
byte[] data = await FileReference.ReadAllBytesAsync(inputFile, cancellationToken);
if (!data.AsSpan().StartsWith(s_currentSignature))
{
return null;
}
CbObject rootObj = new CbObject(data.AsMemory(s_currentSignature.Length));
CbObject rootObj2 = rootObj["root"].AsObject();
Utf8String rootPath = rootObj2["path"].AsUtf8String(defaultBasePath);
StreamTreeRef root = new StreamTreeRef(rootPath, rootObj2);
CbArray array = rootObj["items"].AsArray();
Dictionary hashToTree = new Dictionary(array.Count);
foreach (CbField element in array)
{
CbObject objectElement = element.AsObject();
IoHash hash = objectElement["hash"].AsHash();
CbObject tree = objectElement["tree"].AsObject();
hashToTree[hash] = tree;
}
return new StreamSnapshotFromMemory(root, hashToTree);
}
///
/// Saves the contents of this object to disk
///
/// The output file to write to
///
public async Task SaveAsync(FileReference outputFile, Utf8String basePath)
{
CbWriter writer = new CbWriter();
writer.BeginObject();
writer.BeginObject("root");
if (Root.Path != basePath)
{
writer.WriteUtf8String("path", Root.Path);
}
Root.Write(writer);
writer.EndObject();
writer.BeginArray("items");
foreach ((IoHash hash, CbObject tree) in HashToTree)
{
writer.BeginObject();
writer.WriteHash("hash", hash);
writer.WriteObject("tree", tree);
writer.EndObject();
}
writer.EndArray();
writer.EndObject();
byte[] data = writer.ToByteArray();
using (FileStream outputStream = FileReference.Open(outputFile, FileMode.Create, FileAccess.Write, FileShare.Read))
{
await outputStream.WriteAsync(s_currentSignature, 0, s_currentSignature.Length);
await outputStream.WriteAsync(data, 0, data.Length);
}
}
}
}