@@ -6,9 +6,8 @@ namespace LogExpert.Core.Classes.Log;
66
77public abstract class PositionAwareStreamReaderBase : LogStreamReaderBase
88{
9- #region Fields
109
11- private static readonly Encoding [ ] _preambleEncodings = [ Encoding . UTF8 , Encoding . Unicode , Encoding . BigEndianUnicode , Encoding . UTF32 ] ;
10+ #region Fields
1211
1312 private readonly BufferedStream _stream ;
1413 private readonly StreamReader _reader ;
@@ -18,19 +17,39 @@ public abstract class PositionAwareStreamReaderBase : LogStreamReaderBase
1817
1918 private long _position ;
2019
20+ private static readonly Encoding [ ] _preambleEncodings =
21+ [
22+ Encoding . UTF8 ,
23+ Encoding . Unicode ,
24+ Encoding . BigEndianUnicode ,
25+ Encoding . UTF32
26+ ] ;
27+
2128 #endregion
2229
2330 #region cTor
2431
2532 protected PositionAwareStreamReaderBase ( Stream stream , EncodingOptions encodingOptions , int maximumLineLength )
2633 {
34+ ArgumentNullException . ThrowIfNull ( stream ) ;
35+
36+ if ( ! stream . CanRead )
37+ {
38+ throw new ArgumentException ( "Stream must support reading." , nameof ( stream ) ) ;
39+ }
40+
41+ if ( ! stream . CanSeek )
42+ {
43+ throw new ArgumentException ( "Stream must support seeking." , nameof ( stream ) ) ;
44+ }
45+
2746 _stream = new BufferedStream ( stream ) ;
2847
2948 MaximumLineLength = maximumLineLength ;
3049
3150 _preambleLength = DetectPreambleLengthAndEncoding ( out var detectedEncoding ) ;
3251
33- var usedEncoding = GetUsedEncoding ( encodingOptions , detectedEncoding ) ;
52+ var usedEncoding = DetermineEncoding ( encodingOptions , detectedEncoding ) ;
3453 _posIncPrecomputed = GetPosIncPrecomputed ( usedEncoding ) ;
3554
3655 _reader = new StreamReader ( _stream , usedEncoding , true ) ;
@@ -59,8 +78,8 @@ public sealed override long Position
5978 * always delivers a fixed length (does not mater what kind of data)
6079 */
6180 _position = value ; // +Encoding.GetPreamble().Length; // 1
62- //stream.Seek(pos, SeekOrigin.Begin); // 2
63- //stream.Seek(pos + Encoding.GetPreamble().Length, SeekOrigin.Begin); // 3
81+ //stream.Seek(pos, SeekOrigin.Begin); // 2
82+ //stream.Seek(pos + Encoding.GetPreamble().Length, SeekOrigin.Begin); // 3
6483 _ = _stream . Seek ( _position + _preambleLength , SeekOrigin . Begin ) ; // 4
6584
6685 ResetReader ( ) ;
@@ -124,6 +143,8 @@ public override unsafe int ReadChar ()
124143 }
125144 }
126145
146+
147+
127148 protected virtual void ResetReader ( )
128149 {
129150 _reader . DiscardBufferedData ( ) ;
@@ -158,19 +179,42 @@ private int DetectPreambleLengthAndEncoding (out Encoding detectedEncoding)
158179 UTF-32-Little-Endian-Byteorder: FF FE 00 00
159180 */
160181
161- var readPreamble = new byte [ 4 ] ;
182+ var ( length , encoding ) = DetectPreambleLength ( _stream ) ;
183+ // not found or less than 2 byte read
184+ detectedEncoding = encoding ;
185+
186+ return length ;
187+ }
162188
163- var readLen = _stream . Read ( readPreamble , 0 , 4 ) ;
189+ public static Encoding DetermineEncoding ( EncodingOptions options , Encoding detectedEncoding )
190+ {
191+ return options ? . Encoding != null
192+ ? options . Encoding
193+ : detectedEncoding ?? options ? . DefaultEncoding ?? Encoding . Default ;
194+ }
164195
165- if ( readLen >= 2 )
196+ public static ( int length , Encoding ? detectedEncoding ) DetectPreambleLength ( Stream stream )
197+ {
198+ if ( ! stream . CanSeek )
199+ {
200+ return ( 0 , null ) ;
201+ }
202+
203+ var originalPos = stream . Position ;
204+ var buffer = new byte [ 4 ] ;
205+ _ = stream . Seek ( 0 , SeekOrigin . Begin ) ;
206+ var readBytes = stream . Read ( buffer , 0 , buffer . Length ) ;
207+ _ = stream . Seek ( originalPos , SeekOrigin . Begin ) ;
208+
209+ if ( readBytes >= 2 )
166210 {
167211 foreach ( var encoding in _preambleEncodings )
168212 {
169213 var preamble = encoding . GetPreamble ( ) ;
170214 var fail = false ;
171- for ( var i = 0 ; i < readLen && i < preamble . Length ; ++ i )
215+ for ( var i = 0 ; i < readBytes && i < preamble . Length ; ++ i )
172216 {
173- if ( readPreamble [ i ] != preamble [ i ] )
217+ if ( buffer [ i ] != preamble [ i ] )
174218 {
175219 fail = true ;
176220 break ;
@@ -179,26 +223,15 @@ private int DetectPreambleLengthAndEncoding (out Encoding detectedEncoding)
179223
180224 if ( ! fail )
181225 {
182- detectedEncoding = encoding ;
183- return preamble . Length ;
226+ return ( preamble . Length , encoding ) ;
184227 }
185228 }
186229 }
187230
188- // not found or less than 2 byte read
189- detectedEncoding = null ;
190-
191- return 0 ;
231+ return ( 0 , null ) ;
192232 }
193233
194- private static Encoding GetUsedEncoding ( EncodingOptions encodingOptions , Encoding detectedEncoding )
195- {
196- return encodingOptions . Encoding ??
197- detectedEncoding ??
198- encodingOptions . DefaultEncoding ??
199- Encoding . Default ;
200- }
201- private static int GetPosIncPrecomputed ( Encoding usedEncoding )
234+ public static int GetPosIncPrecomputed ( Encoding usedEncoding )
202235 {
203236 switch ( usedEncoding )
204237 {
0 commit comments