Loading files
3d097a032This file is pinned to the selected release. Download the original to open it in your design tool.
// Copyright (c) 2019 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _Standard_ReadLineBuffer_HeaderFile
#define _Standard_ReadLineBuffer_HeaderFile
#include <NCollection_LinearVector.hxx>
#include <cstring>
#include <iostream>
//! Auxiliary tool for buffered reading of lines from input stream.
class Standard_ReadLineBuffer
{
public:
//! Constructor with initialization.
//! @param theMaxBufferSizeBytes the length of buffer to read (in bytes)
Standard_ReadLineBuffer(size_t theMaxBufferSizeBytes)
: myUseReadBufferLastStr(false),
myIsMultilineMode(false),
myToPutGapInMultiline(true),
myBufferPos(0),
myBytesLastRead(0)
{
// allocate read buffer
myReadBuffer.Resize(theMaxBufferSizeBytes);
}
//! Destructor.
virtual ~Standard_ReadLineBuffer() = default;
//! Clear buffer and cached values.
void Clear()
{
myReadBufferLastStr.Clear();
myUseReadBufferLastStr = false;
myIsMultilineMode = false;
myToPutGapInMultiline = true;
myBufferPos = 0;
myBytesLastRead = 0;
}
//! Read next line from the stream.
//! @return pointer to the line or NULL on error / end of reading buffer
//! (in case of NULL result theStream should be checked externally to identify the
//! presence of errors).
//! Empty lines will be returned also with zero length.
//! @param theStream [inout] - the stream to read from.
//! @param[out] theLineLength - output parameter defined length of returned line.
template <typename Stream_T>
const char* ReadLine(Stream_T& theStream, size_t& theLineLength)
{
int64_t aReadData = 0;
return ReadLine(theStream, theLineLength, aReadData);
}
//! Read next line from the stream.
//! @return pointer to the line or NULL on error / end of reading buffer
//! (in case of NULL result theStream should be checked externally to identify the
//! presence of errors).
//! Empty lines will be returned also with zero length.
//! @param theStream [inout] - the stream to read from.
//! @param[out] theLineLength - output parameter defined length of returned line.
//! @param[out] theReadData - output parameter defined the number of elements successfully read
//! from the stream during this call,
//! it can be zero if no data was read and the line is taken from the
//! buffer.
template <typename Stream_T>
const char* ReadLine(Stream_T& theStream, size_t& theLineLength, int64_t& theReadData)
{
char* aResultLine = nullptr;
bool isMultiline = false;
theLineLength = 0;
theReadData = 0;
while (aResultLine == nullptr)
{
if (myBufferPos == 0 || myBufferPos >= (myBytesLastRead))
{
// read new chunk from the stream
if (!readStream(theStream, myReadBuffer.Size(), myBytesLastRead))
{
// error during file reading
break;
}
theReadData = myBytesLastRead;
if (myBytesLastRead > 0)
{
myBufferPos = 0;
}
else
{
// end of the stream
if (myUseReadBufferLastStr)
{
theLineLength = myReadBufferLastStr.Size();
aResultLine = myReadBufferLastStr.Data();
myUseReadBufferLastStr = false;
}
break;
}
}
size_t aStartLinePos = myBufferPos;
bool isEndLineFound = false;
// read next line from myReadBuffer
while (myBufferPos < myBytesLastRead)
{
if (myIsMultilineMode && myReadBuffer[myBufferPos] == '\\')
{
// multi-line syntax
if (myBufferPos + 1 == myBytesLastRead
|| (myBufferPos + 2 == myBytesLastRead && myReadBuffer[myBufferPos + 1] == '\r'))
{
isMultiline = true;
if (myToPutGapInMultiline)
{
myReadBuffer[myBufferPos] = ' ';
if (myBufferPos + 1 != myBytesLastRead)
{
myReadBuffer[myBufferPos + 1] = ' ';
}
}
}
else if (myReadBuffer[myBufferPos + 1] == '\n'
|| (myReadBuffer[myBufferPos + 1] == '\r'
&& myReadBuffer[myBufferPos + 2] == '\n'))
{
size_t aBufferPos = myBufferPos;
myBufferPos = aBufferPos + (myReadBuffer[aBufferPos + 1] == '\r' ? 2 : 1);
if (myToPutGapInMultiline)
{
myReadBuffer[aBufferPos] = ' ';
++aBufferPos;
}
if (myUseReadBufferLastStr)
{
const size_t aIns = myReadBufferLastStr.Size();
const size_t aCnt = aBufferPos - aStartLinePos;
myReadBufferLastStr.Resize(aIns + aCnt);
std::memcpy(myReadBufferLastStr.Data() + aIns,
myReadBuffer.Data() + aStartLinePos,
aCnt);
}
else
{
const size_t aCnt = aBufferPos - aStartLinePos;
myReadBufferLastStr.Resize(aCnt);
std::memcpy(myReadBufferLastStr.Data(), myReadBuffer.Data() + aStartLinePos, aCnt);
myUseReadBufferLastStr = true;
}
aStartLinePos = myBufferPos + 1;
}
}
else if (myReadBuffer[myBufferPos] == '\n')
{
if (!isMultiline)
{
isEndLineFound = true;
}
else if (myBufferPos == 1 && myReadBuffer[0] == '\r')
{
myReadBufferLastStr.EraseLast();
aStartLinePos += 2;
isMultiline = false;
}
else if (myBufferPos == 0)
{
aStartLinePos += 1;
if (myReadBufferLastStr[myReadBufferLastStr.Size() - 1] == '\\')
{
myReadBufferLastStr.EraseLast();
}
else
{
myReadBufferLastStr.EraseLast();
myReadBufferLastStr.EraseLast();
}
isMultiline = false;
}
}
++myBufferPos;
if (isEndLineFound)
break;
}
if (isEndLineFound)
{
if (myUseReadBufferLastStr)
{
// append current string to the last "unfinished" string of the previous chunk
{
const size_t aIns = myReadBufferLastStr.Size();
const size_t aCnt = myBufferPos - aStartLinePos;
myReadBufferLastStr.Resize(aIns + aCnt);
std::memcpy(myReadBufferLastStr.Data() + aIns,
myReadBuffer.Data() + aStartLinePos,
aCnt);
}
myUseReadBufferLastStr = false;
theLineLength = myReadBufferLastStr.Size();
aResultLine = myReadBufferLastStr.Data();
}
else
{
if (!myReadBufferLastStr.IsEmpty())
{
myReadBufferLastStr.Clear();
}
theLineLength = myBufferPos - aStartLinePos;
aResultLine = myReadBuffer.Data() + aStartLinePos;
}
// make string null terminated by replacing '\n' or '\r' (before '\n') symbol to null
// character.
if (theLineLength > 1 && aResultLine[theLineLength - 2] == '\r')
{
aResultLine[theLineLength - 2] = '\0';
theLineLength -= 2;
}
else
{
aResultLine[theLineLength - 1] = '\0';
theLineLength -= 1;
}
}
else
{
// save "unfinished" part of string to additional buffer
if (aStartLinePos != myBufferPos)
{
if (myUseReadBufferLastStr)
{
const size_t aIns = myReadBufferLastStr.Size();
const size_t aCnt = myBufferPos - aStartLinePos;
myReadBufferLastStr.Resize(aIns + aCnt);
std::memcpy(myReadBufferLastStr.Data() + aIns,
myReadBuffer.Data() + aStartLinePos,
aCnt);
}
else
{
const size_t aCnt = myBufferPos - aStartLinePos;
myReadBufferLastStr.Resize(aCnt);
std::memcpy(myReadBufferLastStr.Data(), myReadBuffer.Data() + aStartLinePos, aCnt);
myUseReadBufferLastStr = true;
}
}
}
}
return aResultLine;
}
//! Returns TRUE when the Multiline Mode is on; FALSE by default.
//! Multiline modes joins several lines in file having \ at the end of line:
//! @code
//! Line starts here, \ // line continuation character without this comment
//! continues \ // line continuation character without this comment
//! and ends.
//! @endcode
bool IsMultilineMode() const { return myIsMultilineMode; }
//! Put gap space while merging lines within multiline syntax, so that the following sample:
//! @code
//! 1/2/3\ // line continuation character without this comment
//! 4/5/6
//! @endcode
//! Will become "1/2/3 4/5/6" when flag is TRUE, and "1/2/35/5/6" otherwise.
bool ToPutGapInMultiline() const { return myToPutGapInMultiline; }
//! Sets or unsets the multi-line mode.
//! @param[in] theMultilineMode multiline mode flag
//! @param[in] theToPutGap put gap space while connecting lines (no gap otherwise)
void SetMultilineMode(bool theMultilineMode, bool theToPutGap = true)
{
myIsMultilineMode = theMultilineMode;
myToPutGapInMultiline = theToPutGap;
}
protected:
//! Read from stl stream.
//! @return true if reading was finished without errors.
bool readStream(std::istream& theStream, size_t theLen, size_t& theReadLen)
{
theReadLen = (size_t)theStream.read(myReadBuffer.Data(), theLen).gcount();
return !theStream.bad();
}
//! Read from FILE stream.
//! @return true if reading was finished without errors.
bool readStream(FILE* theStream, size_t theLen, size_t& theReadLen)
{
theReadLen = ::fread(myReadBuffer.Data(), 1, theLen, theStream);
return ::ferror(theStream) == 0;
}
protected:
NCollection_LinearVector<char> myReadBuffer; //!< Temp read buffer
NCollection_LinearVector<char> myReadBufferLastStr; //!< Part of last string of myReadBuffer
// clang-format off
bool myUseReadBufferLastStr; //!< Flag to use myReadBufferLastStr during next line reading
bool myIsMultilineMode; //!< Flag to process of the special multi-line case at the end of the line
bool myToPutGapInMultiline; //!< Flag to put gap space while joining lines in multi-line syntax
size_t myBufferPos; //!< Current position in myReadBuffer
size_t myBytesLastRead; //!< The number of characters that were read last time from myReadBuffer.
// clang-format on
};
#endif // _Standard_ReadLineBuffer_HeaderFile