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) Standard_ReadLineBuffer(size_t theMaxBufferSizeBytes)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. ~Standard_ReadLineBuffer()39 virtual ~Standard_ReadLineBuffer() {} 40 41 //! Clear buffer and cached values. Clear()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> ReadLine(Stream_T & theStream,size_t & theLineLength)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> ReadLine(Stream_T & theStream,size_t & theLineLength,int64_t & theReadData)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 IsMultilineMode() const253 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. ToPutGapInMultiline() const261 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) SetMultilineMode(bool theMultilineMode,bool theToPutGap=true)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. readStream(std::istream & theStream,size_t theLen,size_t & theReadLen)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. readStream(FILE * theStream,size_t theLen,size_t & theReadLen)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 307