1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.IO;
9 using System.Linq;
10 
11 namespace Antmicro.Renode.Utilities
12 {
13     public static class BinaryReaderExtensions
14     {
ReadCString(this BinaryReader @this)15         public static string ReadCString(this BinaryReader @this)
16         {
17             var bytes = Misc.Iterate(@this.ReadByte).TakeWhile(b => b != 0x00).ToArray();
18             return System.Text.Encoding.Default.GetString(bytes);
19         }
20     }
21 
22     public class SafeBinaryReader : BinaryReader
23     {
SafeBinaryReader(Stream stream)24         public SafeBinaryReader(Stream stream) : base(stream)
25         {
26             Length = this.BaseStream.Length;
27         }
28 
WithLength(long newLength)29         public SafeBinaryReader WithLength(long newLength)
30         {
31             var newReader = new SafeBinaryReader(this.BaseStream, newLength);
32             return newReader;
33         }
34 
ReadBoolean()35         public override bool ReadBoolean()
36         {
37             return ExecuteAndHandleError(base.ReadBoolean);
38         }
39 
ReadByte()40         public override byte ReadByte()
41         {
42             return ExecuteAndHandleError(base.ReadByte);
43         }
44 
ReadBytes(int count)45         public override byte[] ReadBytes(int count)
46         {
47             return ExecuteAndHandleError(delegate { return base.ReadBytes(count); });
48         }
49 
ReadChar()50         public override char ReadChar()
51         {
52             return ExecuteAndHandleError(base.ReadChar);
53         }
54 
ReadChars(int count)55         public override char[] ReadChars(int count)
56         {
57             return ExecuteAndHandleError(delegate { return base.ReadChars(count); });
58         }
59 
ReadDecimal()60         public override decimal ReadDecimal()
61         {
62             return ExecuteAndHandleError(base.ReadDecimal);
63         }
64 
ReadDouble()65         public override double ReadDouble()
66         {
67             return ExecuteAndHandleError(base.ReadDouble);
68         }
69 
ReadInt16()70         public override short ReadInt16()
71         {
72             return ExecuteAndHandleError(base.ReadInt16);
73         }
74 
ReadInt32()75         public override int ReadInt32()
76         {
77             return ExecuteAndHandleError(base.ReadInt32);
78         }
79 
ReadInt64()80         public override long ReadInt64()
81         {
82             return ExecuteAndHandleError(base.ReadInt64);
83         }
84 
ReadSByte()85         public override sbyte ReadSByte()
86         {
87             return ExecuteAndHandleError(base.ReadSByte);
88         }
89 
ReadSingle()90         public override float ReadSingle()
91         {
92             return ExecuteAndHandleError(base.ReadSingle);
93         }
94 
ReadString()95         public override string ReadString()
96         {
97             return ExecuteAndHandleError(base.ReadString);
98         }
99 
ReadUInt16()100         public override ushort ReadUInt16()
101         {
102             return ExecuteAndHandleError(base.ReadUInt16);
103         }
104 
ReadUInt32()105         public override uint ReadUInt32()
106         {
107             return ExecuteAndHandleError(base.ReadUInt32);
108         }
109 
ReadUInt64()110         public override ulong ReadUInt64()
111         {
112             return ExecuteAndHandleError(base.ReadUInt64);
113         }
114 
ReadCString()115         public string ReadCString()
116         {
117             return ExecuteAndHandleError(() => ((BinaryReader)this).ReadCString());
118         }
119 
SkipBytes(long count)120         public bool SkipBytes(long count)
121         {
122             var previousPosition = this.BaseStream.Position;
123             if(previousPosition + count > Length)
124             {
125                 return false;
126             }
127 
128             var currentPosition = this.BaseStream.Seek(count, SeekOrigin.Current);
129             if(previousPosition + count > currentPosition)
130             {
131                 EndOfStreamEvent?.Invoke("Stream ended when skipping bytes");
132             }
133 
134             return (previousPosition + count) == currentPosition;
135         }
136 
SeekToEnd()137         public bool SeekToEnd()
138         {
139             if(this.BaseStream.Position == Length)
140             {
141                 return false;
142             }
143             this.BaseStream.Seek(Length, SeekOrigin.Begin);
144             return true;
145         }
146 
147         public long Length { get; }
148         public bool EOF => BaseStream.Position >= Length;
149         public IDisposable Checkpoint
150         {
151             get
152             {
153                 var currentPosition = this.BaseStream.Position;
154                 return DisposableWrapper.New(() => this.BaseStream.Seek(currentPosition, SeekOrigin.Begin));
155             }
156         }
157 
158         public Action<string> EndOfStreamEvent;
159 
SafeBinaryReader(Stream stream, long length)160         private SafeBinaryReader(Stream stream, long length) : this(stream)
161         {
162             Length = length;
163         }
164 
ExecuteAndHandleError(Func<T> func)165         private T ExecuteAndHandleError<T>(Func<T> func)
166         {
167             try
168             {
169                 return func();
170             }
171             catch(EndOfStreamException e)
172             {
173                 EndOfStreamEvent?.Invoke(e.Message);
174                 return default(T);
175             }
176         }
177     }
178 }
179