1+ /*
2+ Copyright 2012-2025 Marco De Salvo
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+ */
16+
17+ using System ;
18+ using System . IO ;
19+
20+ namespace RDFSharp . Model
21+ {
22+ /// <summary>
23+ /// Buffer scorrevole per la lettura efficiente di stream di grandi dimensioni durante il parsing Turtle
24+ /// </summary>
25+ internal class TurtleStreamBuffer : IDisposable
26+ {
27+ private readonly StreamReader _reader ;
28+ private readonly char [ ] _buffer ;
29+ private readonly int _bufferSize ;
30+
31+ private int _bufferStart ; // Posizione assoluta nel file del primo carattere nel buffer
32+ private int _bufferLength ; // Numero di caratteri validi nel buffer
33+ private bool _endOfStream ;
34+
35+ /// <summary>
36+ /// Posizione corrente nel file
37+ /// </summary>
38+ public int Position { get ; set ; }
39+
40+ /// <summary>
41+ /// Indica se abbiamo raggiunto la fine del file
42+ /// </summary>
43+ public bool IsEndOfFile => _endOfStream && Position >= _bufferStart + _bufferLength ;
44+
45+ public TurtleStreamBuffer ( StreamReader reader , int bufferSize = 8192 )
46+ {
47+ _reader = reader ?? throw new ArgumentNullException ( nameof ( reader ) ) ;
48+ _bufferSize = bufferSize ;
49+ _buffer = new char [ bufferSize ] ;
50+ _bufferStart = 0 ;
51+ _bufferLength = 0 ;
52+ Position = 0 ;
53+ _endOfStream = false ;
54+
55+ // Carica il primo blocco
56+ FillBuffer ( ) ;
57+ }
58+
59+ /// <summary>
60+ /// Legge il prossimo code point Unicode
61+ /// </summary>
62+ public int ReadCodePoint ( )
63+ {
64+ if ( IsEndOfFile )
65+ return - 1 ;
66+
67+ // Assicurati che il carattere corrente sia nel buffer
68+ EnsureBufferContainsPosition ( ) ;
69+
70+ if ( Position >= _bufferStart + _bufferLength )
71+ return - 1 ; // EOF
72+
73+ int bufferIndex = Position - _bufferStart ;
74+ char highSurrogate = _buffer [ bufferIndex ] ;
75+ Position ++ ;
76+
77+ // Gestione surrogate pairs per caratteri Unicode supplementari
78+ if ( char . IsHighSurrogate ( highSurrogate ) )
79+ {
80+ EnsureBufferContainsPosition ( ) ;
81+ if ( Position < _bufferStart + _bufferLength )
82+ {
83+ bufferIndex = Position - _bufferStart ;
84+ char lowSurrogate = _buffer [ bufferIndex ] ;
85+ if ( char . IsLowSurrogate ( lowSurrogate ) )
86+ {
87+ Position ++ ;
88+ return char . ConvertToUtf32 ( highSurrogate , lowSurrogate ) ;
89+ }
90+ }
91+ }
92+
93+ return highSurrogate ;
94+ }
95+
96+ /// <summary>
97+ /// Sbircia il prossimo code point senza avanzare la posizione
98+ /// </summary>
99+ public int PeekCodePoint ( )
100+ {
101+ int currentPos = Position ;
102+ int codePoint = ReadCodePoint ( ) ;
103+ Position = currentPos ; // Ripristina posizione
104+ return codePoint ;
105+ }
106+
107+ /// <summary>
108+ /// Torna indietro di un code point
109+ /// </summary>
110+ public void UnreadCodePoint ( int codePoint )
111+ {
112+ if ( codePoint == - 1 )
113+ return ;
114+
115+ if ( IsSupplementaryCodePoint ( codePoint ) )
116+ {
117+ // Carattere supplementare (surrogate pair) - torna indietro di 2 posizioni
118+ Position = Math . Max ( 0 , Position - 2 ) ;
119+ }
120+ else
121+ {
122+ // Carattere normale - torna indietro di 1 posizione
123+ Position = Math . Max ( 0 , Position - 1 ) ;
124+ }
125+ }
126+
127+ /// <summary>
128+ /// Torna indietro per una stringa di caratteri
129+ /// </summary>
130+ public void UnreadString ( string str )
131+ {
132+ if ( string . IsNullOrEmpty ( str ) )
133+ return ;
134+
135+ // Torna indietro carattere per carattere (dal fondo)
136+ for ( int i = str . Length - 1 ; i >= 0 ; i -- )
137+ {
138+ UnreadCodePoint ( str [ i ] ) ;
139+ }
140+ }
141+
142+ /// <summary>
143+ /// Assicura che il buffer contenga la posizione corrente
144+ /// </summary>
145+ private void EnsureBufferContainsPosition ( )
146+ {
147+ // Se la posizione è oltre la fine del buffer corrente, carica nuovo buffer
148+ if ( Position >= _bufferStart + _bufferLength && ! _endOfStream )
149+ {
150+ FillBuffer ( ) ;
151+ }
152+ // Se la posizione è prima dell'inizio del buffer, dobbiamo gestire backward seek
153+ else if ( Position < _bufferStart )
154+ {
155+ // Per semplicità, ricarica dall'inizio (può essere ottimizzato se necessario)
156+ ReloadFromPosition ( Position ) ;
157+ }
158+ }
159+
160+ /// <summary>
161+ /// Riempie il buffer dalla posizione corrente
162+ /// </summary>
163+ private void FillBuffer ( )
164+ {
165+ if ( _endOfStream )
166+ return ;
167+
168+ // Se siamo alla fine del buffer corrente, shift del buffer
169+ if ( Position >= _bufferStart + _bufferLength )
170+ {
171+ _bufferStart = Position ;
172+ }
173+
174+ // Leggi il prossimo blocco
175+ _bufferLength = _reader . Read ( _buffer , 0 , _bufferSize ) ;
176+
177+ if ( _bufferLength == 0 )
178+ {
179+ _endOfStream = true ;
180+ }
181+ }
182+
183+ /// <summary>
184+ /// Ricarica il buffer da una posizione specifica (per backward seeks)
185+ /// </summary>
186+ private void ReloadFromPosition ( int position )
187+ {
188+ // Questo è un caso limite - per semplicità resettiamo lo stream
189+ // In un'implementazione più sofisticata si potrebbe mantenere un buffer circolare
190+ if ( _reader . BaseStream . CanSeek )
191+ {
192+ _reader . BaseStream . Seek ( 0 , SeekOrigin . Begin ) ;
193+ _reader . DiscardBufferedData ( ) ;
194+ _bufferStart = 0 ;
195+ Position = 0 ;
196+ _endOfStream = false ;
197+
198+ // Avanza fino alla posizione desiderata
199+ while ( Position < position && ! IsEndOfFile )
200+ {
201+ ReadCodePoint ( ) ;
202+ }
203+ Position = position ;
204+ }
205+ else
206+ {
207+ throw new InvalidOperationException ( "Cannot seek backward on non-seekable stream" ) ;
208+ }
209+ }
210+
211+ /// <summary>
212+ /// Determina se il code point richiede surrogate pair
213+ /// </summary>
214+ private static bool IsSupplementaryCodePoint ( int codePoint )
215+ => ( codePoint & ~ char . MaxValue ) != 0 ;
216+
217+ public void Dispose ( )
218+ {
219+ _reader ? . Dispose ( ) ;
220+ }
221+ }
222+ }
0 commit comments