0031682: Visualization - Prs3d_ShadingAspect::SetTransparency() has no effect with...
[occt.git] / src / Standard / Standard_ReadLineBuffer.hxx
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),
29     myIsMultilineMode     (false),
30     myToPutGapInMultiline (true),
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;
46     myIsMultilineMode = false;
47     myToPutGapInMultiline = true;
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.
56   //! @param theStream [inout] - the stream to read from.
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.
70   //! @param theStream [inout] - the stream to read from.
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;
80     bool isMultiline = false;
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();
107             aResultLine = &myReadBufferLastStr.front();
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       {
120         if (myIsMultilineMode
121          && myReadBuffer[myBufferPos] == '\\')
122         {
123           // multi-line syntax
124           if (myBufferPos + 1 == myBytesLastRead
125            || (myBufferPos + 2 == myBytesLastRead
126             && myReadBuffer[myBufferPos + 1] == '\r'))
127           {
128             isMultiline = true;
129             if (myToPutGapInMultiline)
130             {
131               myReadBuffer[myBufferPos] = ' ';
132               if (myBufferPos + 1 != myBytesLastRead)
133               {
134                 myReadBuffer[myBufferPos + 1] = ' ';
135               }
136             }
137           }
138           else if (myReadBuffer[myBufferPos + 1] == '\n'
139                  ||(myReadBuffer[myBufferPos + 1] == '\r'
140                  && myReadBuffer[myBufferPos + 2] == '\n'))
141           {
142             size_t aBufferPos = myBufferPos;
143             myBufferPos = aBufferPos + (myReadBuffer[aBufferPos + 1] == '\r' ? 2 : 1);
144             if (myToPutGapInMultiline)
145             {
146               myReadBuffer[aBufferPos] = ' ';
147               ++aBufferPos;
148             }
149
150             if (myUseReadBufferLastStr)
151             {
152               myReadBufferLastStr.insert (myReadBufferLastStr.end(), myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + aBufferPos);
153             }
154             else
155             {
156               myReadBufferLastStr = std::vector<char>(myReadBuffer.begin() + aStartLinePos, myReadBuffer.begin() + aBufferPos);
157               myUseReadBufferLastStr = true;
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           }
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();
203           aResultLine = &myReadBufferLastStr.front();
204         }
205         else
206         {
207           if (myReadBufferLastStr.size() > 0)
208           {
209             myReadBufferLastStr.clear();
210           }
211           theLineLength = myBufferPos - aStartLinePos;
212           aResultLine = &myReadBuffer.front() + aStartLinePos;
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         {
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           }
240         }
241       }
242     }
243     return aResultLine;
244   }
245
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
253   bool IsMultilineMode() const { return myIsMultilineMode; }
254
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
263   //! Sets or unsets the multi-line mode.
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   }
272
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   {
281     theReadLen = (size_t )theStream.read (&myReadBuffer.front(), theLen).gcount();
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   {
291     theReadLen = ::fread (&myReadBuffer.front(), 1, theLen, theStream);
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
300   bool              myIsMultilineMode;      //!< Flag to process of the special multi-line case at the end of the line
301   bool              myToPutGapInMultiline;  //!< Flag to put gap space while joining lines in multi-line syntax
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