1 /* Copyright (C) 2010 Wildfire Games.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining
4 * a copy of this software and associated documentation files (the
5 * "Software"), to deal in the Software without restriction, including
6 * without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to
8 * permit persons to whom the Software is furnished to do so, subject to
9 * the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #include "precompiled.h"
24 #include "lib/file/archive/stream.h"
25
26 #include "lib/allocators/page_aligned.h"
27 #include "lib/allocators/shared_ptr.h"
28 #include "lib/file/archive/codec.h"
29 //#include "lib/timer.h"
30
31 //TIMER_ADD_CLIENT(tc_stream);
32
33
OutputBufferManager()34 OutputBufferManager::OutputBufferManager()
35 {
36 Reset();
37 }
38
Reset()39 void OutputBufferManager::Reset()
40 {
41 m_buffer = 0;
42 m_size = 0;
43 m_capacity = 0;
44 }
45
SetBuffer(u8 * buffer,size_t size)46 void OutputBufferManager::SetBuffer(u8* buffer, size_t size)
47 {
48 ENSURE(IsAllowableBuffer(buffer, size));
49
50 m_buffer = buffer;
51 m_size = size;
52 }
53
AllocateBuffer(size_t size)54 void OutputBufferManager::AllocateBuffer(size_t size)
55 {
56 // notes:
57 // - this implementation allows reusing previous buffers if they
58 // are big enough, which reduces the number of allocations.
59 // - no further attempts to reduce allocations (e.g. by doubling
60 // the current size) are made; this strategy is enough.
61 // - Pool etc. cannot be used because files may be huge (larger
62 // than the address space of 32-bit systems).
63
64 // no buffer or the previous one wasn't big enough: reallocate
65 if(!m_mem || m_capacity < size)
66 {
67 AllocateAligned(m_mem, size);
68 m_capacity = size;
69 }
70
71 SetBuffer(m_mem.get(), size);
72 }
73
IsAllowableBuffer(u8 * buffer,size_t size)74 bool OutputBufferManager::IsAllowableBuffer(u8* buffer, size_t size)
75 {
76 // none yet established
77 if(m_buffer == 0 && m_size == 0)
78 return true;
79
80 // same as last time (happens with temp buffers)
81 if(m_buffer == buffer && m_size == size)
82 return true;
83
84 // located after the last buffer (note: not necessarily after
85 // the entire buffer; a lack of input can cause the output buffer
86 // to only partially be used before the next call.)
87 if((unsigned)(buffer - m_buffer) <= m_size)
88 return true;
89
90 return false;
91 }
92
93
94 //-----------------------------------------------------------------------------
95
96
Stream(const PICodec & codec)97 Stream::Stream(const PICodec& codec)
98 : m_codec(codec)
99 , m_inConsumed(0), m_outProduced(0)
100 {
101 }
102
103
AllocateOutputBuffer(size_t outSizeMax)104 void Stream::AllocateOutputBuffer(size_t outSizeMax)
105 {
106 m_outputBufferManager.AllocateBuffer(outSizeMax);
107 }
108
109
SetOutputBuffer(u8 * out,size_t outSize)110 void Stream::SetOutputBuffer(u8* out, size_t outSize)
111 {
112 m_outputBufferManager.SetBuffer(out, outSize);
113 }
114
115
Feed(const u8 * in,size_t inSize)116 Status Stream::Feed(const u8* in, size_t inSize)
117 {
118 if(m_outProduced == m_outputBufferManager.Size()) // output buffer full; must not call Process
119 return INFO::ALL_COMPLETE;
120
121 size_t inConsumed, outProduced;
122 u8* const out = m_outputBufferManager.Buffer() + m_outProduced;
123 const size_t outSize = m_outputBufferManager.Size() - m_outProduced;
124 RETURN_STATUS_IF_ERR(m_codec->Process(in, inSize, out, outSize, inConsumed, outProduced));
125
126 m_inConsumed += inConsumed;
127 m_outProduced += outProduced;
128 return INFO::OK;
129 }
130
131
Finish()132 Status Stream::Finish()
133 {
134 size_t outProduced;
135 RETURN_STATUS_IF_ERR(m_codec->Finish(m_checksum, outProduced));
136 m_outProduced += outProduced;
137 return INFO::OK;
138 }
139