source: box/trunk/lib/common/ReadGatherStream.cpp @ 1586

Revision 1586, 7.0 KB checked in by chris, 5 years ago (diff)

Fix inability to handle streams over 2GB properly. (refs #3)

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    ReadGatherStream.cpp
5//              Purpose: Build a stream (for reading only) out of a number of other streams.
6//              Created: 10/12/03
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include "ReadGatherStream.h"
13#include "CommonException.h"
14
15#include "MemLeakFindOn.h"
16
17// --------------------------------------------------------------------------
18//
19// Function
20//              Name:    ReadGatherStream::ReadGatherStream(bool)
21//              Purpose: Constructor. Args says whether or not all the component streams will be deleted when this
22//                               object is deleted.
23//              Created: 10/12/03
24//
25// --------------------------------------------------------------------------
26ReadGatherStream::ReadGatherStream(bool DeleteComponentStreamsOnDestruction)
27        : mDeleteComponentStreamsOnDestruction(DeleteComponentStreamsOnDestruction),
28          mCurrentPosition(0),
29          mTotalSize(0),
30          mCurrentBlock(0),
31          mPositionInCurrentBlock(0),
32          mSeekDoneForCurrent(false)
33{
34}
35
36
37// --------------------------------------------------------------------------
38//
39// Function
40//              Name:    ReadGatherStream::~ReadGatherStream()
41//              Purpose: Destructor. Will delete all the stream objects, if required.
42//              Created: 10/12/03
43//
44// --------------------------------------------------------------------------
45ReadGatherStream::~ReadGatherStream()
46{
47        // Delete compoenent streams?
48        if(mDeleteComponentStreamsOnDestruction)
49        {
50                for(unsigned int l = 0; l < mComponents.size(); ++l)
51                {
52                        delete mComponents[l];
53                }
54        }
55}
56
57
58// --------------------------------------------------------------------------
59//
60// Function
61//              Name:    ReadGatherStream::AddComponent(IOStream *)
62//              Purpose: Add a component to this stream, returning the index
63//                       of this component in the internal list. Use this
64//                       with AddBlock()
65//              Created: 10/12/03
66//
67// --------------------------------------------------------------------------
68int ReadGatherStream::AddComponent(IOStream *pStream)
69{
70        ASSERT(pStream != 0);
71
72        // Just add the component to the list, returning it's index.
73        int index = mComponents.size();
74        mComponents.push_back(pStream);
75        return index;
76}
77
78
79// --------------------------------------------------------------------------
80//
81// Function
82//              Name:    ReadGatherStream::AddBlock(int, pos_type, bool, pos_type)
83//              Purpose: Add a block to the list of blocks being gathered into one stream.
84//                               Length is length of block to read from this component, Seek == true
85//                               if a seek is required, and if true, SeekTo is the position (absolute)
86//                               in the stream to be seeked to when this block is required.
87//              Created: 10/12/03
88//
89// --------------------------------------------------------------------------
90void ReadGatherStream::AddBlock(int Component, pos_type Length, bool Seek, pos_type SeekTo)
91{
92        // Check block
93        if(Component < 0 || Component >= (int)mComponents.size() || Length < 0 || SeekTo < 0)
94        {
95                THROW_EXCEPTION(CommonException, ReadGatherStreamAddingBadBlock);
96        }
97       
98        // Add to list
99        Block b;
100        b.mLength = Length;
101        b.mSeekTo = SeekTo;
102        b.mComponent = Component;
103        b.mSeek = Seek;
104       
105        mBlocks.push_back(b);
106       
107        // And update the total size
108        mTotalSize += Length;
109}
110
111
112// --------------------------------------------------------------------------
113//
114// Function
115//              Name:    ReadGatherStream::Read(void *, int, int)
116//              Purpose: As interface.
117//              Created: 10/12/03
118//
119// --------------------------------------------------------------------------
120int ReadGatherStream::Read(void *pBuffer, int NBytes, int Timeout)
121{
122        int bytesToRead = NBytes;
123        uint8_t *buffer = (uint8_t*)pBuffer;
124       
125        while(bytesToRead > 0)
126        {
127                // Done?
128                if(mCurrentBlock >= mBlocks.size())
129                {
130                        // Stop now, as have finished the last block
131                        return NBytes - bytesToRead;
132                }
133                       
134                // Seek?
135                if(mPositionInCurrentBlock == 0 && mBlocks[mCurrentBlock].mSeek && !mSeekDoneForCurrent)
136                {
137                        // Do seeks in this manner so that seeks are done regardless of whether the block
138                        // has length > 0, and it will only be done once, and at as late a stage as possible.
139                       
140                        mComponents[mBlocks[mCurrentBlock].mComponent]->Seek(mBlocks[mCurrentBlock].mSeekTo, IOStream::SeekType_Absolute);
141               
142                        mSeekDoneForCurrent = true;
143                }
144
145                // Anything in the current block?
146                if(mPositionInCurrentBlock < mBlocks[mCurrentBlock].mLength)
147                {
148                        // Read!
149                        pos_type s = mBlocks[mCurrentBlock].mLength - mPositionInCurrentBlock;
150                        if(s > bytesToRead) s = bytesToRead;
151                       
152                        pos_type r = mComponents[mBlocks[mCurrentBlock].mComponent]->Read(buffer, s, Timeout);
153                       
154                        // update variables
155                        mPositionInCurrentBlock += r;
156                        buffer += r;
157                        bytesToRead -= r;
158                        mCurrentPosition += r;
159                       
160                        if(r != s)
161                        {
162                                // Stream returned less than requested. To avoid blocking when not necessary,
163                                // return now.
164                                return NBytes - bytesToRead;
165                        }
166                }
167                else
168                {
169                        // Move to next block
170                        ++mCurrentBlock;
171                        mPositionInCurrentBlock = 0;
172                        mSeekDoneForCurrent = false;
173                }
174        }
175
176        return NBytes - bytesToRead;
177}
178
179
180// --------------------------------------------------------------------------
181//
182// Function
183//              Name:    ReadGatherStream::GetPosition()
184//              Purpose: As interface
185//              Created: 10/12/03
186//
187// --------------------------------------------------------------------------
188IOStream::pos_type ReadGatherStream::GetPosition() const
189{
190        return mCurrentPosition;
191}
192
193
194// --------------------------------------------------------------------------
195//
196// Function
197//              Name:    ReadGatherStream::BytesLeftToRead()
198//              Purpose: As interface
199//              Created: 10/12/03
200//
201// --------------------------------------------------------------------------
202IOStream::pos_type ReadGatherStream::BytesLeftToRead()
203{
204        return mTotalSize - mCurrentPosition;
205}
206
207
208// --------------------------------------------------------------------------
209//
210// Function
211//              Name:    ReadGatherStream::Write(const void *, int)
212//              Purpose: As interface.
213//              Created: 10/12/03
214//
215// --------------------------------------------------------------------------
216void ReadGatherStream::Write(const void *pBuffer, int NBytes)
217{
218        THROW_EXCEPTION(CommonException, CannotWriteToReadGatherStream);
219}
220
221
222// --------------------------------------------------------------------------
223//
224// Function
225//              Name:    ReadGatherStream::StreamDataLeft()
226//              Purpose: As interface.
227//              Created: 10/12/03
228//
229// --------------------------------------------------------------------------
230bool ReadGatherStream::StreamDataLeft()
231{
232        if(mCurrentBlock >= mBlocks.size())
233        {
234                // Done all the blocks
235                return false;
236        }
237       
238        if(mCurrentBlock == (mBlocks.size() - 1)
239                && mPositionInCurrentBlock >= mBlocks[mCurrentBlock].mLength)
240        {
241                // Are on the last block, and have got all the data from it.
242                return false;
243        }
244
245        // Otherwise, there's more data to be read
246        return true;
247}
248
249
250// --------------------------------------------------------------------------
251//
252// Function
253//              Name:    ReadGatherStream::StreamClosed()
254//              Purpose: As interface. But the stream is always closed.
255//              Created: 10/12/03
256//
257// --------------------------------------------------------------------------
258bool ReadGatherStream::StreamClosed()
259{
260        return true;
261}
262
263
Note: See TracBrowser for help on using the repository browser.