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 } }