1 /***************************************************************************
2 * Copyright (C) 2006 by Dominik Seichter *
3 * domseichter@web.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU Library General Public License as *
7 * published by the Free Software Foundation; either version 2 of the *
8 * License, or (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU Library General Public *
16 * License along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 * *
20 * In addition, as a special exception, the copyright holders give *
21 * permission to link the code of portions of this program with the *
22 * OpenSSL library under certain conditions as described in each *
23 * individual source file, and distribute linked combinations *
24 * including the two. *
25 * You must obey the GNU General Public License in all respects *
26 * for all of the code used other than OpenSSL. If you modify *
27 * file(s) with this exception, you may extend this exception to your *
28 * version of the file(s), but you are not obligated to do so. If you *
29 * do not wish to do so, delete this exception statement from your *
30 * version. If you delete this exception statement from all source *
31 * files in the program, then also delete it here. *
32 ***************************************************************************/
33
34 #include "PdfRefCountedBuffer.h"
35 #include "PdfDefinesPrivate.h"
36
37 #include <stdlib.h>
38 #include <string.h>
39
40 namespace PoDoFo {
41
PdfRefCountedBuffer(char * pBuffer,size_t lSize)42 PdfRefCountedBuffer::PdfRefCountedBuffer( char* pBuffer, size_t lSize )
43 : m_pBuffer( NULL )
44 {
45 if( pBuffer && lSize )
46 {
47 m_pBuffer = new TRefCountedBuffer();
48 m_pBuffer->m_lRefCount = 1;
49 m_pBuffer->m_pHeapBuffer = pBuffer;
50 m_pBuffer->m_bOnHeap = true;
51 m_pBuffer->m_lBufferSize = lSize;
52 m_pBuffer->m_lVisibleSize = lSize;
53 m_pBuffer->m_bPossesion = true;
54 }
55 }
56
FreeBuffer()57 void PdfRefCountedBuffer::FreeBuffer()
58 {
59 PODOFO_RAISE_LOGIC_IF( !m_pBuffer || m_pBuffer->m_lRefCount, "Tried to free in-use buffer" );
60
61 // last owner of the file!
62 if( m_pBuffer->m_bOnHeap && m_pBuffer->m_bPossesion )
63 podofo_free( m_pBuffer->m_pHeapBuffer );
64 delete m_pBuffer;
65 }
66
ReallyDetach(size_t lExtraLen)67 void PdfRefCountedBuffer::ReallyDetach( size_t lExtraLen )
68 {
69 PODOFO_RAISE_LOGIC_IF( m_pBuffer && m_pBuffer->m_lRefCount == 1, "Use Detach() rather than calling ReallyDetach() directly." )
70
71 if( !m_pBuffer )
72 {
73 // throw error rather than de-referencing NULL
74 PODOFO_RAISE_ERROR( ePdfError_InternalLogic );
75 }
76
77 size_t lSize = m_pBuffer->m_lBufferSize + lExtraLen;
78 TRefCountedBuffer* pBuffer = new TRefCountedBuffer();
79 pBuffer->m_lRefCount = 1;
80
81 pBuffer->m_bOnHeap = (lSize > TRefCountedBuffer::INTERNAL_BUFSIZE);
82 if ( pBuffer->m_bOnHeap )
83 pBuffer->m_pHeapBuffer = static_cast<char*>(podofo_calloc( lSize, sizeof(char) ));
84 else
85 pBuffer->m_pHeapBuffer = 0;
86 pBuffer->m_lBufferSize = PDF_MAX( lSize, static_cast<size_t>(+TRefCountedBuffer::INTERNAL_BUFSIZE) );
87 pBuffer->m_bPossesion = true;
88
89 if( pBuffer->m_bOnHeap && !pBuffer->m_pHeapBuffer )
90 {
91 delete pBuffer;
92 pBuffer = NULL;
93
94 PODOFO_RAISE_ERROR( ePdfError_OutOfMemory );
95 }
96
97 memcpy( pBuffer->GetRealBuffer(), this->GetBuffer(), this->GetSize() );
98 // Detaching the buffer should have NO visible effect to clients, so the
99 // visible size must not change.
100 pBuffer->m_lVisibleSize = m_pBuffer->m_lVisibleSize;
101
102 // Now that we've copied the data, release our claim on the old buffer,
103 // deleting it if needed, and link up the new one.
104 DerefBuffer();
105 m_pBuffer = pBuffer;
106 }
107
ReallyResize(const size_t lSize)108 void PdfRefCountedBuffer::ReallyResize( const size_t lSize )
109 {
110 if( m_pBuffer )
111 {
112 // Resizing the buffer counts as altering it, so detach as per copy on write behaviour. If the detach
113 // actually has to do anything it'll reallocate the buffer at the new desired size.
114 this->Detach( static_cast<size_t>(m_pBuffer->m_lBufferSize) < lSize ? lSize - static_cast<size_t>(m_pBuffer->m_lBufferSize) : 0 );
115 // We might have pre-allocated enough to service the request already
116 if( static_cast<size_t>(m_pBuffer->m_lBufferSize) < lSize )
117 {
118 // Allocate more space, since we need it. We over-allocate so that clients can efficiently
119 // request lots of small resizes if they want, but these over allocations are not visible
120 // to clients.
121 //
122 const size_t lAllocSize = PDF_MAX(lSize, m_pBuffer->m_lBufferSize) << 1;
123 if ( m_pBuffer->m_bPossesion && m_pBuffer->m_bOnHeap )
124 {
125 // We have an existing on-heap buffer that we own. Realloc()
126 // it, potentially saving us a memcpy and free().
127 void* temp = podofo_realloc( m_pBuffer->m_pHeapBuffer, lAllocSize );
128 if (!temp)
129 {
130 PODOFO_RAISE_ERROR_INFO( ePdfError_OutOfMemory, "PdfRefCountedBuffer::Resize failed!" );
131 }
132 m_pBuffer->m_pHeapBuffer = static_cast<char*>(temp);
133 m_pBuffer->m_lBufferSize = lAllocSize;
134 }
135 else
136 {
137 // Either we don't own the buffer or it's a local static buffer that's no longer big enough.
138 // Either way, it's time to move to a heap-allocated buffer we own.
139 char* pBuffer = static_cast<char*>(podofo_calloc( lAllocSize, sizeof(char) ));
140 if( !pBuffer )
141 {
142 PODOFO_RAISE_ERROR_INFO( ePdfError_OutOfMemory, "PdfRefCountedBuffer::Resize failed!" );
143 }
144 // Only bother copying the visible portion of the buffer. It's completely incorrect
145 // to rely on anything more than that, and not copying it will help catch those errors.
146 memcpy( pBuffer, m_pBuffer->GetRealBuffer(), m_pBuffer->m_lVisibleSize );
147 // Record the newly allocated buffer's details. The visible size gets updated later.
148 m_pBuffer->m_lBufferSize = lAllocSize;
149 m_pBuffer->m_pHeapBuffer = pBuffer;
150 m_pBuffer->m_bOnHeap = true;
151 }
152 }
153 else
154 {
155 // Allocated buffer is large enough, do nothing
156 }
157 }
158 else
159 {
160 // No buffer was allocated at all, so we need to make one.
161 m_pBuffer = new TRefCountedBuffer();
162 m_pBuffer->m_lRefCount = 1;
163 m_pBuffer->m_bOnHeap = (lSize > TRefCountedBuffer::INTERNAL_BUFSIZE);
164 if ( m_pBuffer->m_bOnHeap )
165 {
166 m_pBuffer->m_pHeapBuffer = static_cast<char*>(podofo_calloc( lSize, sizeof(char) ));
167 }
168 else
169 {
170 m_pBuffer->m_pHeapBuffer = 0;
171 }
172
173 m_pBuffer->m_lBufferSize = PDF_MAX( lSize, static_cast<size_t>(+TRefCountedBuffer::INTERNAL_BUFSIZE) );
174 m_pBuffer->m_bPossesion = true;
175
176 if( m_pBuffer->m_bOnHeap && !m_pBuffer->m_pHeapBuffer )
177 {
178 delete m_pBuffer;
179 m_pBuffer = NULL;
180
181 PODOFO_RAISE_ERROR( ePdfError_OutOfMemory );
182 }
183 }
184 m_pBuffer->m_lVisibleSize = lSize;
185
186 PODOFO_RAISE_LOGIC_IF ( m_pBuffer->m_lVisibleSize > m_pBuffer->m_lBufferSize, "Buffer improperly allocated/resized");
187 }
188
operator =(const PdfRefCountedBuffer & rhs)189 const PdfRefCountedBuffer & PdfRefCountedBuffer::operator=( const PdfRefCountedBuffer & rhs )
190 {
191 // Self assignment is a no-op
192 if (this == &rhs)
193 return rhs;
194
195 DerefBuffer();
196
197 m_pBuffer = rhs.m_pBuffer;
198 if( m_pBuffer )
199 m_pBuffer->m_lRefCount++;
200
201 return *this;
202 }
203
operator ==(const PdfRefCountedBuffer & rhs) const204 bool PdfRefCountedBuffer::operator==( const PdfRefCountedBuffer & rhs ) const
205 {
206 if( m_pBuffer != rhs.m_pBuffer )
207 {
208 if( m_pBuffer && rhs.m_pBuffer )
209 {
210 if ( m_pBuffer->m_lVisibleSize != rhs.m_pBuffer->m_lVisibleSize )
211 // Unequal buffer sizes cannot be equal buffers
212 return false;
213 // Test for byte-for-byte equality since lengths match
214 return (memcmp( m_pBuffer->GetRealBuffer(), rhs.m_pBuffer->GetRealBuffer(), m_pBuffer->m_lVisibleSize ) == 0 );
215 }
216 else
217 // Cannot be equal if only one object has a real data buffer
218 return false;
219 }
220
221 return true;
222 }
223
operator <(const PdfRefCountedBuffer & rhs) const224 bool PdfRefCountedBuffer::operator<( const PdfRefCountedBuffer & rhs ) const
225 {
226 // equal buffers are neither smaller nor greater
227 if( m_pBuffer == rhs.m_pBuffer )
228 return false;
229
230 if( !m_pBuffer && rhs.m_pBuffer )
231 return true;
232 else if( m_pBuffer && !rhs.m_pBuffer )
233 return false;
234 else
235 {
236 int cmp = memcmp( m_pBuffer->GetRealBuffer(), rhs.m_pBuffer->GetRealBuffer(), PDF_MIN( m_pBuffer->m_lVisibleSize, rhs.m_pBuffer->m_lVisibleSize ) );
237 if (cmp == 0)
238 // If one is a prefix of the other, ie they compare equal for the length of the shortest but one is longer,
239 // the longer buffer is the greater one.
240 return m_pBuffer->m_lVisibleSize < rhs.m_pBuffer->m_lVisibleSize;
241 else
242 return cmp < 0;
243 }
244 }
245
operator >(const PdfRefCountedBuffer & rhs) const246 bool PdfRefCountedBuffer::operator>( const PdfRefCountedBuffer & rhs ) const
247 {
248 // equal buffers are neither smaller nor greater
249 if( m_pBuffer == rhs.m_pBuffer )
250 return false;
251
252 if( !m_pBuffer && rhs.m_pBuffer )
253 return false;
254 else if( m_pBuffer && !rhs.m_pBuffer )
255 return true;
256 else
257 {
258 int cmp = memcmp( m_pBuffer->GetRealBuffer(), rhs.m_pBuffer->GetRealBuffer(), PDF_MIN( m_pBuffer->m_lVisibleSize, rhs.m_pBuffer->m_lVisibleSize ) );
259 if (cmp == 0)
260 // If one is a prefix of the other, ie they compare equal for the length of the shortest but one is longer,
261 // the longer buffer is the greater one.
262 return m_pBuffer->m_lVisibleSize > rhs.m_pBuffer->m_lVisibleSize;
263 else
264 return cmp > 0;
265 }
266 }
267
268
269 };
270