0031333: Data Exchange - RWGltf_TriangulationReader::readBuffer() fails to read inter...
[occt.git] / src / Standard / Standard_ReadBuffer.hxx
1 // Copyright (c) 2017-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_ReadBuffer_HeaderFile
15 #define _Standard_ReadBuffer_HeaderFile
16
17 #include <Standard_ProgramError.hxx>
18
19 #include <iostream>
20
21 //! Auxiliary tool for buffered reading from input stream within chunks of constant size.
22 class Standard_ReadBuffer
23 {
24 public:
25
26   //! Constructor with initialization.
27   Standard_ReadBuffer (int64_t theDataLen,
28                        size_t  theChunkLen,
29                        bool theIsPartialPayload = false)
30   : myBufferPtr(NULL),
31     myBufferEnd(NULL),
32     myDataLen  (0),
33     myDataRead (0),
34     myChunkLen (0),
35     myNbChunks (0),
36     myBufferLen(0)
37   {
38     Init (theDataLen, theChunkLen, theIsPartialPayload);
39   }
40
41   //! Initialize the buffer.
42   //! @param theDataLen  [in] the full length of input data to read from stream.
43   //! @param theChunkLen [in] the length of single chunk to read
44   //! @param theIsPartialPayload [in] when FALSE, theDataLen will be automatically aligned to the multiple of theChunkLen;
45   //!                                 when TRUE, last chunk will be read from stream exactly till theDataLen
46   //!                                 allowing portion of chunk to be uninitialized (useful for interleaved data)
47   void Init (int64_t theDataLen,
48              size_t  theChunkLen,
49              bool theIsPartialPayload = false)
50   {
51     myDataRead  = 0;
52     if (theIsPartialPayload)
53     {
54       myDataLen = theDataLen;
55     }
56     else
57     {
58       myDataLen = theDataLen - theDataLen % int64_t(theChunkLen);
59     }
60     myChunkLen  = theChunkLen;
61     myNbChunks  = sizeof(myBuffer) / theChunkLen;
62     myBufferLen = theChunkLen * myNbChunks;
63
64     myBufferEnd = myBuffer + sizeof(myBuffer);
65     myBufferPtr = myBuffer + sizeof(myBuffer);
66     memset (myBuffer, 0, sizeof(myBuffer));
67
68     if (theChunkLen > sizeof(myBuffer))
69     {
70       Standard_ProgramError::Raise ("Internal error - chunk size is greater then preallocated buffer");
71     }
72   }
73
74   //! Return TRUE if amount of read bytes is equal to requested length of entire data.
75   bool IsDone() const
76   {
77     return myDataRead == myDataLen;
78   }
79
80   //! Read next chunk.
81   //! @return pointer to the chunk or NULL on error / end of reading buffer
82   template<typename Chunk_T, typename Stream_T>
83   Chunk_T* ReadChunk (Stream_T& theStream)
84   {
85     return reinterpret_cast<Chunk_T*> (readRawDataChunk (theStream));
86   }
87
88   //! Read next chunk.
89   //! @return pointer to the chunk or NULL on error / end of reading buffer
90   template<typename Stream_T>
91   char* ReadDataChunk (Stream_T& theStream)
92   {
93     return readRawDataChunk (theStream);
94   }
95
96 private:
97
98   //! Read next chunk.
99   //! @return pointer to the chunk or NULL on error / end of reading buffer
100   template<typename Stream_T>
101   char* readRawDataChunk (Stream_T& theStream)
102   {
103     myBufferPtr += myChunkLen;
104     if (myBufferPtr < myBufferEnd)
105     {
106       return myBufferPtr;
107     }
108
109     const int64_t aDataLeft = myDataLen - myDataRead;
110     if (aDataLeft <= 0) // myDataLen is normally multiple of myChunkLen, but can be smaller in interleaved data
111     {
112       myBufferPtr = NULL;
113       return NULL;
114     }
115
116     const size_t aDataToRead = int64_t(myBufferLen) > aDataLeft ? size_t(aDataLeft) : myBufferLen;
117     if (!readStream (theStream, aDataToRead))
118     {
119       myBufferPtr = NULL;
120       return NULL;
121     }
122
123     myBufferPtr = myBuffer;
124     myBufferEnd = myBuffer + aDataToRead;
125     myDataRead += aDataToRead;
126     return myBufferPtr;
127   }
128
129   //! Read from stl stream.
130   bool readStream (std::istream& theStream,
131                    size_t theLen)
132   {
133     theStream.read (myBuffer, theLen);
134     return theStream.good();
135   }
136
137   //! Read from FILE stream.
138   bool readStream (FILE* theStream,
139                    size_t theLen)
140   {
141     return ::fread (myBuffer, 1, theLen, theStream) == theLen;
142   }
143
144 private:
145
146   char        myBuffer[4096]; //!< data cache
147   char*       myBufferPtr;    //!< current position within the buffer
148   const char* myBufferEnd;    //!< end of the buffer
149   int64_t     myDataLen;      //!< length of entire data to read
150   int64_t     myDataRead;     //!< amount of data already processed
151   size_t      myChunkLen;     //!< length of single chunk that caller would like to read (e.g. iterator increment)
152   size_t      myNbChunks;     //!< number of cached chunks
153   size_t      myBufferLen;    //!< effective length of the buffer to be read at once (multiple of chunk length)
154
155 };
156
157 #endif // _Standard_ReadBuffer_HeaderFile