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