using System;
using System.IO;
using SabreTools.IO;
using SabreTools.IO.Extensions;
namespace SabreTools.Wrappers
{
public abstract class WrapperBase : IWrapper
{
#region Descriptive Properties
///
public string Description() => DescriptionString;
///
/// Description of the object
///
public abstract string DescriptionString { get; }
#endregion
#region Properties
///
public string? Filename => _dataSource.Filename;
///
public long Length => _dataSource.Length;
#endregion
#region Instance Variables
///
/// Source of the original data
///
protected readonly ReadOnlyViewStream _dataSource;
#if NETCOREAPP
#pragma warning disable IDE1006 // Naming Styles
///
/// JSON serializer options for output printing
///
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
///
/// Lock for accessing
///
protected readonly object _dataSourceLock = new();
#endregion
#region Byte Array Constructors
///
/// Construct a new instance of the wrapper from a byte array
///
/// Underlying data for the wrapper
protected WrapperBase(byte[] data)
: this(data, 0, data.Length)
{
}
///
/// Construct a new instance of the wrapper from a byte array
///
/// Underlying data for the wrapper
/// Offset into the data to use as the window start
protected WrapperBase(byte[] data, int offset)
: this(data, offset, data.Length - offset)
{
}
///
/// Construct a new instance of the wrapper from a byte array
///
/// Underlying data for the wrapper
/// Offset into the data to use as the window start
/// Length of the window into the data
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
///
/// Construct a new instance of the wrapper from a Stream
///
/// Underlying data for the wrapper
/// Uses the current stream position as the offset
protected WrapperBase(Stream data)
: this(data, data.Position, data.Length - data.Position)
{
}
///
/// Construct a new instance of the wrapper from a Stream
///
/// Underlying data for the wrapper
/// Offset into the data to use as the window start
protected WrapperBase(Stream data, long offset)
: this(data, offset, data.Length - offset)
{
}
///
/// Construct a new instance of the wrapper from a Stream
///
/// Underlying data for the wrapper
/// Offset into the data to use as the window start
/// Length of the window into the data
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
///
/// Read a number of bytes from an offset from the data source, if possible
///
/// Offset within the data source to start reading
/// Number of bytes to read from the offset
/// Filled byte array on success, null on error
///
/// 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.
///
protected byte[] ReadRangeFromSource(long offset, int length)
{
lock (_dataSourceLock)
{
return _dataSource.ReadFrom(offset, length, retainPosition: true) ?? [];
}
}
#endregion
}
}