fa1710b5 |
1 | // Copyright (c) 2019 OPEN CASCADE SAS |
2 | // |
3 | // This file is part of Open CASCADE Technology software library. |
4 | // |
5 | // This library is free software; you can redistribute it and/or modify it under |
6 | // the terms of the GNU Lesser General Public License version 2.1 as published |
7 | // by the Free Software Foundation, with special exception defined in the file |
8 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT |
9 | // distribution for complete text of the license and disclaimer of any warranty. |
10 | // |
11 | // Alternatively, this file may be used under the terms of Open CASCADE |
12 | // commercial license or contractual agreement. |
13 | |
14 | #ifndef _Standard_ReadLineBuffer_HeaderFile |
15 | #define _Standard_ReadLineBuffer_HeaderFile |
16 | |
17 | #include <iostream> |
18 | #include <vector> |
19 | |
20 | //! Auxiliary tool for buffered reading of lines from input stream. |
21 | class Standard_ReadLineBuffer |
22 | { |
23 | public: |
24 | |
25 | //! Constructor with initialization. |
26 | //! @param theMaxBufferSizeBytes the length of buffer to read (in bytes) |
27 | Standard_ReadLineBuffer (size_t theMaxBufferSizeBytes) |
28 | : myUseReadBufferLastStr(false), |
51ee6a7d |
29 | myIsMultilineMode (false), |
3eba1c72 |
30 | myToPutGapInMultiline (true), |
fa1710b5 |
31 | myBufferPos (0), |
32 | myBytesLastRead (0) |
33 | { |
34 | // allocate read buffer |
35 | myReadBuffer.resize (theMaxBufferSizeBytes); |
36 | } |
37 | |
38 | //! Destructor. |
39 | virtual ~Standard_ReadLineBuffer() {} |
40 | |
41 | //! Clear buffer and cached values. |
42 | void Clear() |
43 | { |
44 | myReadBufferLastStr.clear(); |
45 | myUseReadBufferLastStr = false; |
51ee6a7d |
46 | myIsMultilineMode = false; |
3eba1c72 |
47 | myToPutGapInMultiline = true; |
fa1710b5 |
48 | myBufferPos = 0; |
49 | myBytesLastRead = 0; |
50 | } |
51 | |
52 | //! Read next line from the stream. |
53 | //! @return pointer to the line or NULL on error / end of reading buffer |
54 | //! (in case of NULL result theStream should be checked externally to identify the presence of errors). |
55 | //! Empty lines will be returned also with zero length. |
64e68ea6 |
56 | //! @param theStream [inout] - the stream to read from. |
fa1710b5 |
57 | //! @param theLineLength [out] - output parameter defined length of returned line. |
58 | template<typename Stream_T> |
59 | const char* ReadLine (Stream_T& theStream, |
60 | size_t& theLineLength) |
61 | { |
62 | int64_t aReadData = 0; |
63 | return ReadLine (theStream, theLineLength, aReadData); |
64 | } |
65 | |
66 | //! Read next line from the stream. |
67 | //! @return pointer to the line or NULL on error / end of reading buffer |
68 | //! (in case of NULL result theStream should be checked externally to identify the presence of errors). |
69 | //! Empty lines will be returned also with zero length. |
64e68ea6 |
70 | //! @param theStream [inout] - the stream to read from. |
fa1710b5 |
71 | //! @param theLineLength [out] - output parameter defined length of returned line. |
72 | //! @param theReadData [out] - output parameter defined the number of elements successfully read from the stream during this call, |
73 | //! it can be zero if no data was read and the line is taken from the buffer. |
74 | template<typename Stream_T> |
75 | const char* ReadLine (Stream_T& theStream, |
76 | size_t& theLineLength, |
77 | int64_t& theReadData) |
78 | { |
79 | char* aResultLine = NULL; |
51ee6a7d |
80 | bool isMultiline = false; |
fa1710b5 |
81 | theLineLength = 0; |
82 | theReadData = 0; |
83 | |
84 | while (aResultLine == NULL) |
85 | { |
86 | if (myBufferPos == 0 || myBufferPos >= (myBytesLastRead)) |
87 | { |
88 | // read new chunk from the stream |
89 | if (!readStream (theStream, myReadBuffer.size(), myBytesLastRead)) |
90 | { |
91 | // error during file reading |
92 | break; |
93 | } |
94 | |
95 | theReadData = myBytesLastRead; |
96 | |
97 | if (myBytesLastRead > 0) |
98 | { |
99 | myBufferPos = 0; |
100 | } |
101 | else |
102 | { |
103 | // end of the stream |
104 | if (myUseReadBufferLastStr) |
105 | { |
106 | theLineLength = myReadBufferLastStr.size(); |
51ee6a7d |
107 | aResultLine = &myReadBufferLastStr.front(); |
fa1710b5 |
108 | myUseReadBufferLastStr = false; |
109 | } |
110 | break; |
111 | } |
112 | } |
113 | |
114 | size_t aStartLinePos = myBufferPos; |
115 | bool isEndLineFound = false; |
116 | |
117 | // read next line from myReadBuffer |
118 | while (myBufferPos < myBytesLastRead) |
119 | { |
3eba1c72 |
120 | if (myIsMultilineMode |
121 | && myReadBuffer[myBufferPos] == '\\') |
fa1710b5 |
122 | { |
51ee6a7d |
123 | // multi-line syntax |
124 | if (myBufferPos + 1 == myBytesLastRead |
3eba1c72 |
125 | || (myBufferPos + 2 == myBytesLastRead |
126 | && myReadBuffer[myBufferPos + 1] == '\r')) |
51ee6a7d |
127 | { |
128 | isMultiline = true; |
3eba1c72 |
129 | if (myToPutGapInMultiline) |
130 | { |
131 | myReadBuffer[myBufferPos] = ' '; |
132 | if (myBufferPos + 1 != myBytesLastRead) |
133 | { |
134 | myReadBuffer[myBufferPos + 1] = ' '; |
135 | } |
136 | } |
51ee6a7d |
137 | } |
138 | else if (myReadBuffer[myBufferPos + 1] == '\n' |
3eba1c72 |
139 | ||(myReadBuffer[myBufferPos + 1] == '\r' |
140 | && myReadBuffer[myBufferPos + 2] == '\n')) |
51ee6a7d |
141 | { |
3eba1c72 |
142 | size_t aBufferPos = myBufferPos; |
143 | myBufferPos = aBufferPos + (myReadBuffer[aBufferPos + 1] == '\r' ? 2 : 1); |
144 | if (myToPutGapInMultiline) |
51ee6a7d |
145 | { |
3eba1c72 |
146 | myReadBuffer[aBufferPos] = ' '; |
147 | ++aBufferPos; |
51ee6a7d |
148 | } |
149 | |
3eba1c72 |
150 | if (myUseReadBufferLastStr) |
51ee6a7d |
151 | { |
3eba1c72 |
152 | myReadBufferLastStr.insert (myReadBufferLastStr.end(), myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + aBufferPos); |
51ee6a7d |
153 | } |
154 | else |
155 | { |
3eba1c72 |
156 | myReadBufferLastStr = std::vector<char>(myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + aBufferPos); |
157 | myUseReadBufferLastStr = true; |
51ee6a7d |
158 | } |
159 | |
160 | aStartLinePos = myBufferPos + 1; |
161 | } |
162 | } |
163 | else if (myReadBuffer[myBufferPos] == '\n') |
164 | { |
165 | if (!isMultiline) |
166 | { |
167 | isEndLineFound = true; |
168 | } |
169 | else if (myBufferPos == 1 && myReadBuffer[0] == '\r') |
170 | { |
171 | myReadBufferLastStr.erase (myReadBufferLastStr.end() - 1); |
172 | aStartLinePos += 2; |
173 | isMultiline = false; |
174 | } |
175 | else if (myBufferPos == 0) |
176 | { |
177 | aStartLinePos += 1; |
178 | if (myReadBufferLastStr[myReadBufferLastStr.size() - 1] == '\\') |
179 | { |
180 | myReadBufferLastStr.erase (myReadBufferLastStr.end() - 1); |
181 | } |
182 | else |
183 | { |
184 | myReadBufferLastStr.erase (myReadBufferLastStr.end() - 2, myReadBufferLastStr.end()); |
185 | } |
186 | isMultiline = false; |
187 | } |
fa1710b5 |
188 | } |
189 | |
190 | ++myBufferPos; |
191 | |
192 | if (isEndLineFound) break; |
193 | } |
194 | |
195 | if (isEndLineFound) |
196 | { |
197 | if (myUseReadBufferLastStr) |
198 | { |
199 | // append current string to the last "unfinished" string of the previous chunk |
200 | myReadBufferLastStr.insert (myReadBufferLastStr.end(), myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
201 | myUseReadBufferLastStr = false; |
202 | theLineLength = myReadBufferLastStr.size(); |
51ee6a7d |
203 | aResultLine = &myReadBufferLastStr.front(); |
fa1710b5 |
204 | } |
205 | else |
206 | { |
207 | if (myReadBufferLastStr.size() > 0) |
208 | { |
209 | myReadBufferLastStr.clear(); |
210 | } |
211 | theLineLength = myBufferPos - aStartLinePos; |
51ee6a7d |
212 | aResultLine = &myReadBuffer.front() + aStartLinePos; |
fa1710b5 |
213 | } |
214 | // make string null terminated by replacing '\n' or '\r' (before '\n') symbol to null character. |
215 | if (theLineLength > 1 && aResultLine[theLineLength - 2] == '\r') |
216 | { |
217 | aResultLine[theLineLength - 2] = '\0'; |
218 | theLineLength -= 2; |
219 | } |
220 | else |
221 | { |
222 | aResultLine[theLineLength - 1] = '\0'; |
223 | theLineLength -= 1; |
224 | } |
225 | } |
226 | else |
227 | { |
228 | // save "unfinished" part of string to additional buffer |
229 | if (aStartLinePos != myBufferPos) |
230 | { |
51ee6a7d |
231 | if (myUseReadBufferLastStr) |
232 | { |
233 | myReadBufferLastStr.insert (myReadBufferLastStr.end(), myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
234 | } |
235 | else |
236 | { |
237 | myReadBufferLastStr = std::vector<char>(myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
238 | myUseReadBufferLastStr = true; |
239 | } |
fa1710b5 |
240 | } |
241 | } |
242 | } |
243 | return aResultLine; |
244 | } |
245 | |
3eba1c72 |
246 | //! Returns TRUE when the Multiline Mode is on; FALSE by default. |
247 | //! Multiline modes joins several lines in file having \ at the end of line: |
248 | //! @code |
249 | //! Line starts here, \ // line continuation character without this comment |
250 | //! continues \ // line continuation character without this comment |
251 | //! and ends. |
252 | //! @endcode |
51ee6a7d |
253 | bool IsMultilineMode() const { return myIsMultilineMode; } |
254 | |
3eba1c72 |
255 | //! Put gap space while merging lines within multiline syntax, so that the following sample: |
256 | //! @code |
257 | //! 1/2/3\ // line continuation character without this comment |
258 | //! 4/5/6 |
259 | //! @endcode |
260 | //! Will become "1/2/3 4/5/6" when flag is TRUE, and "1/2/35/5/6" otherwise. |
261 | bool ToPutGapInMultiline() const { return myToPutGapInMultiline; } |
262 | |
51ee6a7d |
263 | //! Sets or unsets the multi-line mode. |
3eba1c72 |
264 | //! @param theMultilineMode [in] multiline mode flag |
265 | //! @param theToPutGap [in] put gap space while connecting lines (no gap otherwise) |
266 | void SetMultilineMode (bool theMultilineMode, |
267 | bool theToPutGap = true) |
268 | { |
269 | myIsMultilineMode = theMultilineMode; |
270 | myToPutGapInMultiline = theToPutGap; |
271 | } |
51ee6a7d |
272 | |
fa1710b5 |
273 | protected: |
274 | |
275 | //! Read from stl stream. |
276 | //! @return true if reading was finished without errors. |
277 | bool readStream (std::istream& theStream, |
278 | size_t theLen, |
279 | size_t& theReadLen) |
280 | { |
51ee6a7d |
281 | theReadLen = (size_t )theStream.read (&myReadBuffer.front(), theLen).gcount(); |
fa1710b5 |
282 | return !theStream.bad(); |
283 | } |
284 | |
285 | //! Read from FILE stream. |
286 | //! @return true if reading was finished without errors. |
287 | bool readStream (FILE* theStream, |
288 | size_t theLen, |
289 | size_t& theReadLen) |
290 | { |
51ee6a7d |
291 | theReadLen = ::fread (&myReadBuffer.front(), 1, theLen, theStream); |
fa1710b5 |
292 | return ::ferror (theStream) == 0; |
293 | } |
294 | |
295 | protected: |
296 | |
297 | std::vector<char> myReadBuffer; //!< Temp read buffer |
298 | std::vector<char> myReadBufferLastStr; //!< Part of last string of myReadBuffer |
299 | bool myUseReadBufferLastStr; //!< Flag to use myReadBufferLastStr during next line reading |
51ee6a7d |
300 | bool myIsMultilineMode; //!< Flag to process of the special multi-line case at the end of the line |
3eba1c72 |
301 | bool myToPutGapInMultiline; //!< Flag to put gap space while joining lines in multi-line syntax |
fa1710b5 |
302 | size_t myBufferPos; //!< Current position in myReadBuffer |
303 | size_t myBytesLastRead; //!< The number of characters that were read last time from myReadBuffer. |
304 | }; |
305 | |
306 | #endif // _Standard_ReadLineBuffer_HeaderFile |