NdsForge.NET 1.0.1
Read, validate, edit, compare, and build Nintendo DS and DSi images from .NET
Loading...
Searching...
No Matches
StreamImageDataSource.cs
1namespace NdsForge;
2
4internal sealed class StreamImageDataSource : IImageDataSource
5{
7 private readonly Stream _stream;
9 private readonly bool _leaveOpen;
11 private readonly SemaphoreSlim _gate = new(1, 1);
13 private bool _disposed;
14
18 public StreamImageDataSource(Stream stream, bool leaveOpen)
19 {
20 ArgumentNullException.ThrowIfNull(stream);
21 if (!stream.CanRead || !stream.CanSeek)
22 {
23 throw new ArgumentException("An image stream must be readable and seekable.", nameof(stream));
24 }
25
26 _stream = stream;
27 _leaveOpen = leaveOpen;
28 Length = stream.Length;
29 }
30
32 public long Length { get; }
33
35 public int Read(long offset, Span<byte> destination)
36 {
37 ObjectDisposedException.ThrowIf(_disposed, this);
38 _gate.Wait();
39 try
40 {
41 _stream.Position = offset;
42 return _stream.Read(destination);
43 }
44 finally
45 {
46 _gate.Release();
47 }
48 }
49
51 public async ValueTask<int> ReadAsync(
52 long offset,
53 Memory<byte> destination,
54 CancellationToken cancellationToken)
55 {
56 ObjectDisposedException.ThrowIf(_disposed, this);
57 await _gate.WaitAsync(cancellationToken).ConfigureAwait(false);
58 try
59 {
60 _stream.Position = offset;
61 return await _stream.ReadAsync(destination, cancellationToken).ConfigureAwait(false);
62 }
63 finally
64 {
65 _gate.Release();
66 }
67 }
68
70 public void Dispose()
71 {
72 if (_disposed)
73 {
74 return;
75 }
76
77 if (!_leaveOpen)
78 {
79 _stream.Dispose();
80 }
81
82 _gate.Dispose();
83 _disposed = true;
84 }
85
87 public async ValueTask DisposeAsync()
88 {
89 if (_disposed)
90 {
91 return;
92 }
93
94 if (!_leaveOpen)
95 {
96 await _stream.DisposeAsync().ConfigureAwait(false);
97 }
98
99 _gate.Dispose();
100 _disposed = true;
101 }
102}