132 lines
3.9 KiB
C#
132 lines
3.9 KiB
C#
/*
|
|
* Copyright 2016 The Cartographer Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System.IO.Compression;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
|
|
namespace CartographerSharp.IO;
|
|
|
|
/// <summary>
|
|
/// A simple writer of a compressed sequence of protocol buffer messages to a file.
|
|
/// The format is not intended to be compatible with any other format used outside of Cartographer.
|
|
/// </summary>
|
|
public class ProtoStreamWriter : IProtoStreamWriter, IDisposable
|
|
{
|
|
// First eight bytes to identify our proto stream format.
|
|
private const ulong Magic = 0x7b1d1f7b5bf501db;
|
|
|
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
{
|
|
WriteIndented = false,
|
|
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.Never
|
|
};
|
|
|
|
private readonly FileStream _fileStream;
|
|
private bool _disposed;
|
|
private bool _magicWritten;
|
|
|
|
public ProtoStreamWriter(string filename)
|
|
{
|
|
if (string.IsNullOrEmpty(filename))
|
|
throw new ArgumentException("Filename cannot be null or empty", nameof(filename));
|
|
|
|
_fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read);
|
|
WriteMagic();
|
|
}
|
|
|
|
private void WriteMagic()
|
|
{
|
|
WriteSizeAsLittleEndian(Magic);
|
|
_magicWritten = true;
|
|
}
|
|
|
|
private void WriteSizeAsLittleEndian(ulong size)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
_fileStream.WriteByte((byte)(size & 0xff));
|
|
size >>= 8;
|
|
}
|
|
}
|
|
|
|
private void Write(string uncompressedData)
|
|
{
|
|
if (!_magicWritten)
|
|
{
|
|
WriteMagic();
|
|
}
|
|
|
|
// Compress using GZip
|
|
byte[] uncompressedBytes = Encoding.UTF8.GetBytes(uncompressedData);
|
|
byte[] compressedBytes;
|
|
|
|
using (var memoryStream = new MemoryStream())
|
|
{
|
|
using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, leaveOpen: true))
|
|
{
|
|
gzipStream.Write(uncompressedBytes, 0, uncompressedBytes.Length);
|
|
}
|
|
compressedBytes = memoryStream.ToArray();
|
|
}
|
|
|
|
// Write compressed size
|
|
WriteSizeAsLittleEndian((ulong)compressedBytes.Length);
|
|
|
|
// Write compressed data
|
|
_fileStream.Write(compressedBytes, 0, compressedBytes.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes, compresses and writes the proto to the file.
|
|
/// </summary>
|
|
public void WriteProto<T>(T proto)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(proto, nameof(proto));
|
|
|
|
// Serialize to JSON (we use System.Text.Json instead of Google.Protobuf)
|
|
string json = JsonSerializer.Serialize(proto, JsonOptions);
|
|
|
|
Write(json);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This should be called to check whether writing was successful.
|
|
/// </summary>
|
|
public bool Close()
|
|
{
|
|
if (_disposed)
|
|
return false;
|
|
|
|
_fileStream.Flush();
|
|
_fileStream.Close();
|
|
_disposed = true; // Prevent Dispose() from calling Close() again (double-close would throw on Flush)
|
|
return true;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
Close();
|
|
_fileStream?.Dispose();
|
|
_disposed = true;
|
|
}
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|