0030964: Data Exchange - use Standard_ReadLineBuffer within OBJ reader
[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     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;
45     myIsMultilineMode = false;
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;
76     bool isMultiline = false;
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();
103             aResultLine = &myReadBufferLastStr.front();
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       {
116         if (myReadBuffer[myBufferPos] == '\\' && myIsMultilineMode)
117         {
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           }
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();
189           aResultLine = &myReadBufferLastStr.front();
190         }
191         else
192         {
193           if (myReadBufferLastStr.size() > 0)
194           {
195             myReadBufferLastStr.clear();
196           }
197           theLineLength = myBufferPos - aStartLinePos;
198           aResultLine = &myReadBuffer.front() + aStartLinePos;
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         {
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           }
226         }
227       }
228     }
229     return aResultLine;
230   }
231
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
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   {
246     theReadLen = (size_t )theStream.read (&myReadBuffer.front(), theLen).gcount();
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   {
256     theReadLen = ::fread (&myReadBuffer.front(), 1, theLen, theStream);
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
265   bool              myIsMultilineMode;      //!< Flag to process of the special multi-line case at the end of the line
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