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