1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 
  8 namespace System.IO
  9 {
 10     public class IOBuffer
 11     {
 12         public IOBuffer(long size_) : size(size_)mem(MemAlloc(size)) {}
 13         suppress IOBuffer(const IOBuffer&);
 14         suppress void operator=(const IOBuffer&);
 15         public IOBuffer(IOBuffer&& that) : size(that.size)mem(that.mem)
 16         {
 17             that.size = 0;
 18             that.mem = null;
 19         }
 20         public default void operator=(IOBuffer&& that);
 21         public ~IOBuffer()
 22         {
 23             if (mem != null)
 24             {
 25                 RtmMemFree(mem);
 26             }
 27         }
 28         public inline long Size() const
 29         {
 30             return size;
 31         }
 32         public inline void* Mem() const
 33         {
 34             return mem;
 35         }
 36         public inline byte operator[](long index) const
 37         {
 38             #assert(index >= 0 && index < size);
 39             return cast<byte*>(mem)[index];
 40         }
 41         public inline byte& operator[](long index)
 42         {
 43             #assert(index >= 0 && index < size);
 44             return cast<byte*>(mem)[index];
 45         }
 46         private long size;
 47         private void* mem;
 48     }
 49 
 50     public class BufferedStream : Stream
 51     {
 52         public BufferedStream() : base()baseStream(null)buffer()pos(buffer.Size())bytesAvailable(0)end(0)
 53         {
 54         }
 55         public explicit BufferedStream(Stream* baseStream_) : this(baseStream_4096)
 56         {
 57         }
 58         public BufferedStream(Stream* baseStream_long bufferSize_) : base()baseStream(baseStream_)buffer(bufferSize_)pos(buffer.Size())bytesAvailable(0)end(0)
 59         {
 60         }
 61         public Stream* BaseStream()
 62         {
 63             return baseStream;
 64         }
 65         suppress BufferedStream(const BufferedStream&);
 66         suppress void operator=(const BufferedStream&);
 67         public BufferedStream(BufferedStream&& that) : baseStream(that.baseStream)buffer(Rvalue(that.buffer))pos(that.pos)bytesAvailable(that.bytesAvailable)end(that.end)
 68         {
 69             that.baseStream = null;
 70         }
 71         public default void operator=(BufferedStream&&);
 72         public override ~BufferedStream()
 73         {
 74             if (baseStream != null)
 75             {
 76                 Flush();
 77             }
 78         }
 79         [nodiscard]
 80         public override Result<int> ReadByte()
 81         {
 82             auto flushResult = Flush();
 83             if (flushResult.Error())
 84             {
 85                 SetErrorId(flushResult.GetErrorId());
 86                 return Result<int>(ErrorId(flushResult.GetErrorId()));
 87             }
 88             if (bytesAvailable == 0)
 89             {
 90                 auto fillResult = FillBuf();
 91                 if (fillResult.Error())
 92                 {
 93                     SetErrorId(fillResult.GetErrorId());
 94                     return Result<int>(ErrorId(fillResult.GetErrorId()));
 95                 }
 96                 if (bytesAvailable == 0)
 97                 {
 98                     return Result<int>(-1);
 99                 }
100             }
101             byte value = buffer[pos++];
102             --bytesAvailable;
103             return Result<int>(value);
104         }
105         [nodiscard]
106         public override Result<long> Read(byte* buflong count)
107         {
108             if (count < 0)
109             {
110                 int errorId = RtmAllocateError("BufferedStream.Read: count less than zero");
111                 SetErrorId(errorId);
112                 return Result<long>(ErrorId(errorId));
113             }
114             auto flushResult = Flush();
115             if (flushResult.Error())
116             {
117                 SetErrorId(flushResult.GetErrorId());
118                 return Result<long>(ErrorId(flushResult.GetErrorId()));
119             }
120             if (bytesAvailable == 0)
121             {
122                 auto fillResult = FillBuf();
123                 if (fillResult.Error())
124                 {
125                     SetErrorId(fillResult.GetErrorId());
126                     return Result<long>(ErrorId(fillResult.GetErrorId()));
127                 }
128             }
129             long bytesRead = 0;
130             long n = Min(bytesAvailablecount);
131             for (long i = 0; i < n; ++i;)
132             {
133                 buf[i] = buffer[pos++];
134                 ++bytesRead;
135                 --bytesAvailable;
136             }
137             return Result<long>(bytesRead);
138         }
139         [nodiscard]
140         public override Result<bool> Write(byte x)
141         {
142             if (end >= buffer.Size())
143             {
144                 auto flushResult = Flush();
145                 if (flushResult.Error())
146                 {
147                     SetErrorId(flushResult.GetErrorId());
148                     return Result<bool>(ErrorId(flushResult.GetErrorId()));
149                 }
150             }
151             buffer[end++] = x;
152             return Result<bool>(true);
153         }
154         [nodiscard]
155         public override Result<bool> Write(byte* buflong count)
156         {
157             if (count < 0)
158             {
159                 int errorId = RtmAllocateError("BufferedStream.Write: count less than zero");
160                 SetErrorId(errorId);
161                 return Result<bool>(ErrorId(errorId));
162             }
163             for (long i = 0; i < count; ++i;)
164             {
165                 auto result = Write(buf[i]);
166                 if (result.Error())
167                 {
168                     SetErrorId(result.GetErrorId());
169                     return result;
170                 }
171             }
172             return Result<bool>(true);
173         }
174         public override Result<bool> Flush()
175         {
176             if (end != 0)
177             {
178                 if (baseStream == null)
179                 {
180                     int errorId = RtmAllocateError("BufferedStream.Flush: base stream is null");
181                     SetErrorId(errorId);
182                     return Result<bool>(ErrorId(errorId));
183                 }
184                 auto writeResult = baseStream->Write(cast<byte*>(buffer.Mem())end);
185                 if (writeResult.Error())
186                 {
187                     SetErrorId(writeResult.GetErrorId());
188                     return Result<bool>(ErrorId(writeResult.GetErrorId()));
189                 }
190                 auto flushResult = baseStream->Flush();
191                 if (flushResult.Error())
192                 {
193                     SetErrorId(flushResult.GetErrorId());
194                     return Result<bool>(ErrorId(flushResult.GetErrorId()));
195                 }
196                 end = 0;
197             }
198             return Result<bool>(true);
199         }
200         [nodiscard]
201         public override Result<bool> Seek(long posOrigin origin)
202         {
203             auto flushResult = Flush();
204             if (flushResult.Error())
205             {
206                 SetErrorId(flushResult.GetErrorId());
207                 return Result<bool>(ErrorId(flushResult.GetErrorId()));
208             }
209             bytesAvailable = 0;
210             if (baseStream == null)
211             {
212                 int errorId = RtmAllocateError("BufferedStream.Seek: base stream is null");
213                 SetErrorId(errorId);
214                 return Result<bool>(ErrorId(errorId));
215             }
216             auto seekResult = baseStream->Seek(posorigin);
217             if (seekResult.Error())
218             {
219                 SetErrorId(seekResult.GetErrorId());
220                 return seekResult;
221             }
222             return Result<bool>(true);
223         }
224         [nodiscard]
225         public override Result<long> Tell()
226         {
227             auto flushResult = Flush();
228             if (flushResult.Error())
229             {
230                 SetErrorId(flushResult.GetErrorId());
231                 return Result<long>(ErrorId(flushResult.GetErrorId()));
232             }
233             if (baseStream == null)
234             {
235                 int errorId = RtmAllocateError("BufferedStream.Tell: base stream is null");
236                 SetErrorId(errorId);
237                 return Result<long>(ErrorId(errorId));
238             }
239             Result<long> tellResult = baseStream->Tell();
240             if (tellResult.Error())
241             {
242                 SetErrorId(tellResult.GetErrorId());
243                 return tellResult;
244             }
245             return Result<long>(tellResult.Value() - bytesAvailable);
246         }
247         [nodiscard]
248         private Result<bool> FillBuf()
249         {
250             if (baseStream == null)
251             {
252                 int errorId = RtmAllocateError("BufferedStream.FillBuf: base stream is null");
253                 SetErrorId(errorId);
254                 return Result<bool>(ErrorId(errorId));
255             }
256             Result<long> readResult = baseStream->Read(cast<byte*>(buffer.Mem())buffer.Size());
257             if (readResult.Error())
258             {
259                 SetErrorId(readResult.GetErrorId());
260                 return Result<bool>(ErrorId(readResult.GetErrorId()));
261             }
262             bytesAvailable = readResult.Value();
263             pos = 0;
264             return Result<bool>(true);
265         }
266         private Stream* baseStream;
267         private IOBuffer buffer;
268         private long pos;
269         private long bytesAvailable;
270         private long end;
271     }