You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
6.0 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tools;
namespace napdump
{
[Flags]
public enum WritingOption : uint
{
/// <summary>
/// Serialize dump IBInfos
/// </summary>
Dump = 1 << 0,
/// <summary>
/// Serialize as binary IBInfos
/// </summary>
Binary = 1 << 1,
/// <summary>
/// Serialize as text IBInfos
/// </summary>
Text = 1<<2,
/// <summary>
/// (flag) GZ compress IBInfos
/// </summary>
Gz = 1 << 3,
/// <summary>
/// (flag) File system serialise format
/// </summary>
Fs = 1 << 4,
/// <summary>
/// Archive serialise format
/// </summary>
Ar = 1 << 5,
}
public static class BinWriter
{
public static WritingOption Options { get; private set; }
public static void Initialise(WritingOption opt)
{
Options = opt;
}
public static async Task SaveOptions(Stream to)
{
await to.WriteValueUnmanagedAsync<uint>((uint)Options);
}
public static async Task<WritingOption> LoadOptions(Stream from, CancellationToken token = default)
{
var opt = (WritingOption)await from.ReadValueUnmanagedAsync<uint>(token);
Initialise(opt);
return opt;
}
public static async Task WriteEntry<T>(Stream to, T toWrite, CancellationToken token=default) where T : ISerializable, IBinaryWritable, ITextWritable
{
var Options = BinWriter.Options;
if (Options.HasFlag(WritingOption.Gz))
to = new GZipStream(to, CompressionLevel.Optimal);
try
{
if (Options.HasFlag(WritingOption.Dump))
{
var binf = new BinaryFormatter();
await Task.Yield();
token.ThrowIfCancellationRequested();
binf.Serialize(to, toWrite);
}
else if (Options.HasFlag(WritingOption.Binary))
{
await to.WriteStringAsync(toWrite.GetType().AssemblyQualifiedName, token);
await toWrite.WriteBinaryAsync(to, token);
}
else if (Options.HasFlag(WritingOption.Text))
{
using (var textWriter = new StreamWriter(to))
{
await textWriter.WriteLineAsync($"Type={toWrite.GetType().AssemblyQualifiedName}".AsMemory(), token);
await textWriter.WriteLineAsync(new ReadOnlyMemory<char>(), token);
await toWrite.WriteTextAsync(textWriter, token);
}
}
}
finally
{
if (Options.HasFlag(WritingOption.Gz))
await to.DisposeAsync();
}
}
public static async Task<object> ReadEntry(Stream from, CancellationToken token = default)
{
var Options = BinWriter.Options;
if (Options.HasFlag(WritingOption.Gz))
from = new GZipStream(from, CompressionMode.Decompress);
try
{
object value = default;
if (Options.HasFlag(WritingOption.Dump))
{
var binf = new BinaryFormatter();
await Task.Yield();
token.ThrowIfCancellationRequested();
value= binf.Deserialize(from);
}
else if (Options.HasFlag(WritingOption.Binary))
{
string typeName = await from.ReadStringAsync(token);
Type type = Type.GetType(typeName);
value = Activator.CreateInstance(type);
await ((value as IBinaryWritable)?.ReadBinaryAsync(from, token) ?? throw new InvalidDataException("Bad type definition: " + type.FullName + " does not implement IBinaryWritable"));
}
else if (Options.HasFlag(WritingOption.Text))
{
using (var reader = new StreamReader(from))
{
var line0 = await reader.ReadLineAsync();
token.ThrowIfCancellationRequested();
var def = line0.Split(new char[] { '=' }, 2);
await reader.ReadLineAsync();
token.ThrowIfCancellationRequested();
if (def.Length != 2||def[0].Trim().ToLower()!="type") throw new InvalidDataException("Bad type definition");
Type type = Type.GetType(def[1].Trim());
value = Activator.CreateInstance(type);
await ((value as ITextWritable)?.ReadTextAsync(reader, token) ?? throw new InvalidDataException("Bad type definition: "+type.FullName+" does not implement ITextWritable"));
}
}
return value;
}
finally
{
if (Options.HasFlag(WritingOption.Gz))
await from.DisposeAsync();
}
}
}
public interface IBinaryWritable
{
ValueTask WriteBinaryAsync(Stream to, CancellationToken cancel);
ValueTask ReadBinaryAsync(Stream from, CancellationToken cancel);
}
public interface ITextWritable
{
ValueTask WriteTextAsync(StreamWriter to, CancellationToken cancel);
ValueTask ReadTextAsync(StreamReader from, CancellationToken cancel);
}
}