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), |
fa1710b5 |
30 | myBufferPos (0), |
31 | myBytesLastRead (0) |
32 | { |
33 | // allocate read buffer |
34 | myReadBuffer.resize (theMaxBufferSizeBytes); |
35 | } |
36 | |
37 | //! Destructor. |
38 | virtual ~Standard_ReadLineBuffer() {} |
39 | |
40 | //! Clear buffer and cached values. |
41 | void Clear() |
42 | { |
43 | myReadBufferLastStr.clear(); |
44 | myUseReadBufferLastStr = false; |
51ee6a7d |
45 | myIsMultilineMode = false; |
fa1710b5 |
46 | myBufferPos = 0; |
47 | myBytesLastRead = 0; |
48 | } |
49 | |
50 | //! Read next line from the stream. |
51 | //! @return pointer to the line or NULL on error / end of reading buffer |
52 | //! (in case of NULL result theStream should be checked externally to identify the presence of errors). |
53 | //! Empty lines will be returned also with zero length. |
54 | //! @param theLineLength [out] - output parameter defined length of returned line. |
55 | template<typename Stream_T> |
56 | const char* ReadLine (Stream_T& theStream, |
57 | size_t& theLineLength) |
58 | { |
59 | int64_t aReadData = 0; |
60 | return ReadLine (theStream, theLineLength, aReadData); |
61 | } |
62 | |
63 | //! Read next line from the stream. |
64 | //! @return pointer to the line or NULL on error / end of reading buffer |
65 | //! (in case of NULL result theStream should be checked externally to identify the presence of errors). |
66 | //! Empty lines will be returned also with zero length. |
67 | //! @param theLineLength [out] - output parameter defined length of returned line. |
68 | //! @param theReadData [out] - output parameter defined the number of elements successfully read from the stream during this call, |
69 | //! it can be zero if no data was read and the line is taken from the buffer. |
70 | template<typename Stream_T> |
71 | const char* ReadLine (Stream_T& theStream, |
72 | size_t& theLineLength, |
73 | int64_t& theReadData) |
74 | { |
75 | char* aResultLine = NULL; |
51ee6a7d |
76 | bool isMultiline = false; |
fa1710b5 |
77 | theLineLength = 0; |
78 | theReadData = 0; |
79 | |
80 | while (aResultLine == NULL) |
81 | { |
82 | if (myBufferPos == 0 || myBufferPos >= (myBytesLastRead)) |
83 | { |
84 | // read new chunk from the stream |
85 | if (!readStream (theStream, myReadBuffer.size(), myBytesLastRead)) |
86 | { |
87 | // error during file reading |
88 | break; |
89 | } |
90 | |
91 | theReadData = myBytesLastRead; |
92 | |
93 | if (myBytesLastRead > 0) |
94 | { |
95 | myBufferPos = 0; |
96 | } |
97 | else |
98 | { |
99 | // end of the stream |
100 | if (myUseReadBufferLastStr) |
101 | { |
102 | theLineLength = myReadBufferLastStr.size(); |
51ee6a7d |
103 | aResultLine = &myReadBufferLastStr.front(); |
fa1710b5 |
104 | myUseReadBufferLastStr = false; |
105 | } |
106 | break; |
107 | } |
108 | } |
109 | |
110 | size_t aStartLinePos = myBufferPos; |
111 | bool isEndLineFound = false; |
112 | |
113 | // read next line from myReadBuffer |
114 | while (myBufferPos < myBytesLastRead) |
115 | { |
51ee6a7d |
116 | if (myReadBuffer[myBufferPos] == '\\' && myIsMultilineMode) |
fa1710b5 |
117 | { |
51ee6a7d |
118 | // multi-line syntax |
119 | if (myBufferPos + 1 == myBytesLastRead |
120 | ||(myBufferPos + 2 == myBytesLastRead && myReadBuffer[myBufferPos + 1] == '\r')) |
121 | { |
122 | isMultiline = true; |
123 | } |
124 | else if (myReadBuffer[myBufferPos + 1] == '\n' |
125 | ||(myReadBuffer[myBufferPos + 1] == '\r' && myReadBuffer[myBufferPos + 2] == '\n')) |
126 | { |
127 | if (myUseReadBufferLastStr) |
128 | { |
129 | myReadBufferLastStr.insert (myReadBufferLastStr.end(), myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
130 | } |
131 | else |
132 | { |
133 | myReadBufferLastStr = std::vector<char>(myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
134 | myUseReadBufferLastStr = true; |
135 | } |
136 | |
137 | if (myReadBuffer[myBufferPos + 1] == '\r') |
138 | { |
139 | myBufferPos += 2; |
140 | } |
141 | else |
142 | { |
143 | myBufferPos += 1; |
144 | } |
145 | |
146 | aStartLinePos = myBufferPos + 1; |
147 | } |
148 | } |
149 | else if (myReadBuffer[myBufferPos] == '\n') |
150 | { |
151 | if (!isMultiline) |
152 | { |
153 | isEndLineFound = true; |
154 | } |
155 | else if (myBufferPos == 1 && myReadBuffer[0] == '\r') |
156 | { |
157 | myReadBufferLastStr.erase (myReadBufferLastStr.end() - 1); |
158 | aStartLinePos += 2; |
159 | isMultiline = false; |
160 | } |
161 | else if (myBufferPos == 0) |
162 | { |
163 | aStartLinePos += 1; |
164 | if (myReadBufferLastStr[myReadBufferLastStr.size() - 1] == '\\') |
165 | { |
166 | myReadBufferLastStr.erase (myReadBufferLastStr.end() - 1); |
167 | } |
168 | else |
169 | { |
170 | myReadBufferLastStr.erase (myReadBufferLastStr.end() - 2, myReadBufferLastStr.end()); |
171 | } |
172 | isMultiline = false; |
173 | } |
fa1710b5 |
174 | } |
175 | |
176 | ++myBufferPos; |
177 | |
178 | if (isEndLineFound) break; |
179 | } |
180 | |
181 | if (isEndLineFound) |
182 | { |
183 | if (myUseReadBufferLastStr) |
184 | { |
185 | // append current string to the last "unfinished" string of the previous chunk |
186 | myReadBufferLastStr.insert (myReadBufferLastStr.end(), myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
187 | myUseReadBufferLastStr = false; |
188 | theLineLength = myReadBufferLastStr.size(); |
51ee6a7d |
189 | aResultLine = &myReadBufferLastStr.front(); |
fa1710b5 |
190 | } |
191 | else |
192 | { |
193 | if (myReadBufferLastStr.size() > 0) |
194 | { |
195 | myReadBufferLastStr.clear(); |
196 | } |
197 | theLineLength = myBufferPos - aStartLinePos; |
51ee6a7d |
198 | aResultLine = &myReadBuffer.front() + aStartLinePos; |
fa1710b5 |
199 | } |
200 | // make string null terminated by replacing '\n' or '\r' (before '\n') symbol to null character. |
201 | if (theLineLength > 1 && aResultLine[theLineLength - 2] == '\r') |
202 | { |
203 | aResultLine[theLineLength - 2] = '\0'; |
204 | theLineLength -= 2; |
205 | } |
206 | else |
207 | { |
208 | aResultLine[theLineLength - 1] = '\0'; |
209 | theLineLength -= 1; |
210 | } |
211 | } |
212 | else |
213 | { |
214 | // save "unfinished" part of string to additional buffer |
215 | if (aStartLinePos != myBufferPos) |
216 | { |
51ee6a7d |
217 | if (myUseReadBufferLastStr) |
218 | { |
219 | myReadBufferLastStr.insert (myReadBufferLastStr.end(), myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
220 | } |
221 | else |
222 | { |
223 | myReadBufferLastStr = std::vector<char>(myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + myBufferPos); |
224 | myUseReadBufferLastStr = true; |
225 | } |
fa1710b5 |
226 | } |
227 | } |
228 | } |
229 | return aResultLine; |
230 | } |
231 | |
51ee6a7d |
232 | //! Returns TRUE when the Multiline Mode is on. |
233 | bool IsMultilineMode() const { return myIsMultilineMode; } |
234 | |
235 | //! Sets or unsets the multi-line mode. |
236 | void SetMultilineMode (bool theMultilineMode) { myIsMultilineMode = theMultilineMode; } |
237 | |
fa1710b5 |
238 | protected: |
239 | |
240 | //! Read from stl stream. |
241 | //! @return true if reading was finished without errors. |
242 | bool readStream (std::istream& theStream, |
243 | size_t theLen, |
244 | size_t& theReadLen) |
245 | { |
51ee6a7d |
246 | theReadLen = (size_t )theStream.read (&myReadBuffer.front(), theLen).gcount(); |
fa1710b5 |
247 | return !theStream.bad(); |
248 | } |
249 | |
250 | //! Read from FILE stream. |
251 | //! @return true if reading was finished without errors. |
252 | bool readStream (FILE* theStream, |
253 | size_t theLen, |
254 | size_t& theReadLen) |
255 | { |
51ee6a7d |
256 | theReadLen = ::fread (&myReadBuffer.front(), 1, theLen, theStream); |
fa1710b5 |
257 | return ::ferror (theStream) == 0; |
258 | } |
259 | |
260 | protected: |
261 | |
262 | std::vector<char> myReadBuffer; //!< Temp read buffer |
263 | std::vector<char> myReadBufferLastStr; //!< Part of last string of myReadBuffer |
264 | bool myUseReadBufferLastStr; //!< Flag to use myReadBufferLastStr during next line reading |
51ee6a7d |
265 | bool myIsMultilineMode; //!< Flag to process of the special multi-line case at the end of the line |
fa1710b5 |
266 | size_t myBufferPos; //!< Current position in myReadBuffer |
267 | size_t myBytesLastRead; //!< The number of characters that were read last time from myReadBuffer. |
268 | }; |
269 | |
270 | #endif // _Standard_ReadLineBuffer_HeaderFile |