Files
Matt Nadareski 8f49e190d8 Fix everything
2026-03-24 19:17:25 -04:00

178 lines
6.3 KiB
C#

using System;
using System.IO;
using SabreTools.IO;
using SabreTools.IO.Extensions;
namespace SabreTools.Wrappers
{
public abstract class WrapperBase : IWrapper
{
#region Descriptive Properties
/// <inheritdoc/>
public string Description() => DescriptionString;
/// <summary>
/// Description of the object
/// </summary>
public abstract string DescriptionString { get; }
#endregion
#region Properties
/// <inheritdoc cref="ReadOnlyViewStream.Filename"/>
public string? Filename => _dataSource.Filename;
/// <inheritdoc cref="ReadOnlyViewStream.Length"/>
public long Length => _dataSource.Length;
#endregion
#region Instance Variables
/// <summary>
/// Source of the original data
/// </summary>
protected readonly ReadOnlyViewStream _dataSource;
#if NETCOREAPP
#pragma warning disable IDE1006 // Naming Styles
/// <summary>
/// JSON serializer options for output printing
/// </summary>
protected static System.Text.Json.JsonSerializerOptions _jsonSerializerOptions
{
get
{
#if NETCOREAPP3_1
var serializer = new System.Text.Json.JsonSerializerOptions { WriteIndented = true };
#else
var serializer = new System.Text.Json.JsonSerializerOptions { IncludeFields = true, WriteIndented = true };
#endif
serializer.Converters.Add(new ConcreteAbstractSerializer());
serializer.Converters.Add(new ConcreteInterfaceSerializer());
serializer.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
return serializer;
}
}
#pragma warning restore IDE1006
#endif
/// <summary>
/// Lock for accessing <see cref="_dataSource"/>
/// </summary>
protected readonly object _dataSourceLock = new();
#endregion
#region Byte Array Constructors
/// <summary>
/// Construct a new instance of the wrapper from a byte array
/// </summary>
/// <param name="data">Underlying data for the wrapper</param>
protected WrapperBase(byte[] data)
: this(data, 0, data.Length)
{
}
/// <summary>
/// Construct a new instance of the wrapper from a byte array
/// </summary>
/// <param name="data">Underlying data for the wrapper</param>
/// <param name="offset">Offset into the data to use as the window start</param>
protected WrapperBase(byte[] data, int offset)
: this(data, offset, data.Length - offset)
{
}
/// <summary>
/// Construct a new instance of the wrapper from a byte array
/// </summary>
/// <param name="data">Underlying data for the wrapper</param>
/// <param name="offset">Offset into the data to use as the window start</param>
/// <param name="length">Length of the window into the data</param>
protected WrapperBase(byte[] data, int offset, int length)
{
if (offset < 0 || offset >= data.Length)
throw new ArgumentOutOfRangeException(nameof(offset));
if (length < 0 || offset + length > data.Length)
throw new ArgumentOutOfRangeException(nameof(length));
_dataSource = new ReadOnlyViewStream(data, offset, length);
}
#endregion
#region Stream Constructors
/// <summary>
/// Construct a new instance of the wrapper from a Stream
/// </summary>
/// <param name="data">Underlying data for the wrapper</param>
/// <remarks>Uses the current stream position as the offset</remarks>
protected WrapperBase(Stream data)
: this(data, data.Position, data.Length - data.Position)
{
}
/// <summary>
/// Construct a new instance of the wrapper from a Stream
/// </summary>
/// <param name="data">Underlying data for the wrapper</param>
/// <param name="offset">Offset into the data to use as the window start</param>
protected WrapperBase(Stream data, long offset)
: this(data, offset, data.Length - offset)
{
}
/// <summary>
/// Construct a new instance of the wrapper from a Stream
/// </summary>
/// <param name="data">Underlying data for the wrapper</param>
/// <param name="offset">Offset into the data to use as the window start</param>
/// <param name="length">Length of the window into the data</param>
protected WrapperBase(Stream data, long offset, long length)
{
if (!data.CanSeek || !data.CanRead)
throw new InvalidDataException(nameof(data));
if (offset < 0 || offset >= data.Length)
throw new ArgumentOutOfRangeException(nameof(offset));
if (length < 0 || offset + length > data.Length)
throw new ArgumentOutOfRangeException(nameof(length));
_dataSource = new ReadOnlyViewStream(data, offset, length);
}
#endregion
#region Data
/// <summary>
/// Read a number of bytes from an offset from the data source, if possible
/// </summary>
/// <param name="offset">Offset within the data source to start reading</param>
/// <param name="length">Number of bytes to read from the offset</param>
/// <returns>Filled byte array on success, null on error</returns>
/// <remarks>
/// This method locks the data source to avoid potential conflicts in reading
/// from the data source. This should be the preferred way of reading in cases
/// where there may be multiple threads accessing the wrapper.
///
/// This method will return an empty array if the length is greater than what is left
/// in the stream. This is different behavior than a normal stream read that would
/// attempt to read as much as possible, returning the amount of bytes read.
/// </remarks>
protected byte[] ReadRangeFromSource(long offset, int length)
{
lock (_dataSourceLock)
{
return _dataSource.ReadFrom(offset, length, retainPosition: true) ?? [];
}
}
#endregion
}
}