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 #ifndef _PDF_REF_COUNTED_BUFFER_H_
35 #define _PDF_REF_COUNTED_BUFFER_H_
36 
37 #include "PdfDefines.h"
38 
39 namespace PoDoFo {
40 
41 /**
42  * A reference counted buffer object
43  * which is deleted as soon as the last
44  * object having access to it is delteted.
45  *
46  * The attached memory object can be resized.
47  */
48 class PODOFO_API PdfRefCountedBuffer {
49  public:
50     /** Created an empty reference counted buffer
51      *  The buffer will be initialize to NULL
52      */
53     inline PdfRefCountedBuffer();
54 
55     /** Created an reference counted buffer and use an exiting buffer
56      *  The buffer will be owned by this object.
57      *
58      *  \param pBuffer a pointer to an allocated buffer
59      *  \param lSize   size of the allocated buffer
60      *
61      *  \see SetTakePossesion
62      */
63     PdfRefCountedBuffer( char* pBuffer, size_t lSize );
64 
65     /** Create a new PdfRefCountedBuffer.
66      *  \param lSize buffer size
67      */
68     inline PdfRefCountedBuffer( size_t lSize );
69 
70     /** Copy an existing PdfRefCountedBuffer and increase
71      *  the reference count
72      *  \param rhs the PdfRefCountedBuffer to copy
73      */
74     inline PdfRefCountedBuffer( const PdfRefCountedBuffer & rhs );
75 
76     /** Decrease the reference count and delete the buffer
77      *  if this is the last owner
78      */
79     inline ~PdfRefCountedBuffer();
80 
81     /* !Non-Doxygen comment because constructor is disabled!
82      *  Append to the current buffers contents.
83      *  If the buffer is referenced by another PdfRefCountedBuffer
84      *  this object detaches from this buffer and allocates an own
85      *  buffer. Otherwise the internal buffer is used and resized if
86      *  necessary.
87      *
88      *  \param pszString a buffer
89      *  \param lLen length of the buffer
90      */
91     //void Append( const char* pszString, long lLen );
92 
93     /** Get access to the buffer
94      *  \returns the buffer
95      */
96     inline char* GetBuffer() const;
97 
98     /** Return the buffer size.
99      *
100      *  \returns the buffer size
101      */
102     inline size_t GetSize() const;
103 
104     /** Resize the buffer to hold at least
105      *  lSize bytes.
106      *
107      *  \param lSize the size of bytes the buffer can at least hold
108      *
109      *  If the buffer is larger no operation is performed.
110      */
111     inline void Resize( size_t lSize );
112 
113     /** Copy an existing PdfRefCountedBuffer and increase
114      *  the reference count
115      *  \param rhs the PdfRefCountedBuffer to copy
116      *  \returns the copied object
117      */
118     const PdfRefCountedBuffer & operator=( const PdfRefCountedBuffer & rhs );
119 
120     /** If the PdfRefCountedBuffer has no possesion on its buffer,
121      *  it won't delete the buffer. By default the buffer is owned
122      *  and deleted by the PdfRefCountedBuffer object.
123      *
124      *  \param bTakePossession if false the buffer will not be deleted.
125      */
126     inline void SetTakePossesion( bool bTakePossession );
127 
128     /**
129      * \returns true if the buffer is owned by the PdfRefCountedBuffer
130      *               and is deleted along with it.
131      */
132     inline bool TakePossesion() const;
133 
134     /** Compare to buffers.
135      *  \param rhs compare to this buffer
136      *  \returns true if both buffers contain the same contents
137      */
138     bool operator==( const PdfRefCountedBuffer & rhs ) const;
139 
140     /** Compare to buffers.
141      *  \param rhs compare to this buffer
142      *  \returns true if this buffer is lexically littler than rhs
143      */
144     bool operator<( const PdfRefCountedBuffer & rhs ) const;
145 
146     /** Compare to buffers.
147      *  \param rhs compare to this buffer
148      *  \returns true if this buffer is lexically greater than rhs
149      */
150     bool operator>( const PdfRefCountedBuffer & rhs ) const;
151 
152  private:
153     /**
154      * Indicate that the buffer is no longer being used, freeing it if there
155      * are no further users. The buffer becomes inaccessible to this
156      * PdfRefCountedBuffer in either case.
157      */
158     inline void DerefBuffer();
159 
160     /**
161      * Free a buffer if the refcount is zero. Internal method used by DerefBuffer.
162      */
163     void FreeBuffer();
164 
165     /** Detach from a shared buffer or do nothing if we are the only
166      *  one referencing the buffer.
167      *
168      *  Call this function before any operation modifiying the buffer!
169      *
170      *  \param lLen an additional parameter specifiying extra bytes
171      *              to be allocated to optimize allocations of a new buffer.
172      */
173     inline void Detach( size_t lExtraLen = 0 );
174 
175     /**
176      * Called by Detach() to do the work if action is actually required.
177      * \see Detach
178      */
179     void ReallyDetach( size_t lExtraLen );
180 
181     /**
182      * Do the hard work of resizing the buffer if it turns out not to already be big enough.
183      * \see Resize
184      */
185     void ReallyResize( size_t lSize );
186 
187  private:
188     struct TRefCountedBuffer {
189         enum { INTERNAL_BUFSIZE = 32 };
190         // Convenience inline for buffer switching
GetRealBufferTRefCountedBuffer191         PODOFO_NOTHROW inline char * GetRealBuffer() {
192             return m_bOnHeap? m_pHeapBuffer : &(m_sInternalBuffer[0]);
193         }
194         // size in bytes of the buffer. If and only if this is strictly >INTERNAL_BUFSIZE,
195         // this buffer is on the heap in memory pointed to by m_pHeapBuffer . If it is <=INTERNAL_BUFSIZE,
196         // the buffer is in the in-object buffer m_sInternalBuffer.
197         size_t  m_lBufferSize;
198         // Size in bytes of m_pBuffer that should be reported to clients. We
199         // over-allocate on the heap for efficiency and have a minimum 32 byte
200         // size, but this extra should NEVER be visible to a client.
201         size_t  m_lVisibleSize;
202         long  m_lRefCount;
203         char* m_pHeapBuffer;
204         char  m_sInternalBuffer[INTERNAL_BUFSIZE];
205         bool  m_bPossesion;
206         // Are we using the heap-allocated buffer in place of our small internal one?
207         bool  m_bOnHeap;
208     };
209 
210     TRefCountedBuffer* m_pBuffer;
211 };
212 
213 // -----------------------------------------------------
214 //
215 // -----------------------------------------------------
PdfRefCountedBuffer()216 PdfRefCountedBuffer::PdfRefCountedBuffer()
217     : m_pBuffer( NULL )
218 {
219 }
220 
221 // -----------------------------------------------------
222 //
223 // -----------------------------------------------------
PdfRefCountedBuffer(size_t lSize)224 PdfRefCountedBuffer::PdfRefCountedBuffer( size_t lSize )
225     : m_pBuffer( NULL )
226 {
227     this->Resize( lSize );
228 }
229 
230 // -----------------------------------------------------
231 //
232 // -----------------------------------------------------
233 // We define the copy ctor separately to the assignment
234 // operator since it's a *LOT* faster this way.
PdfRefCountedBuffer(const PdfRefCountedBuffer & rhs)235 PdfRefCountedBuffer::PdfRefCountedBuffer( const PdfRefCountedBuffer & rhs )
236     : m_pBuffer( rhs.m_pBuffer )
237 {
238     if (m_pBuffer)
239         ++(m_pBuffer->m_lRefCount);
240 }
241 
242 // -----------------------------------------------------
243 //
244 // -----------------------------------------------------
~PdfRefCountedBuffer()245 PdfRefCountedBuffer::~PdfRefCountedBuffer()
246 {
247     DerefBuffer();
248 }
249 
250 // -----------------------------------------------------
251 //
252 // -----------------------------------------------------
GetBuffer()253 inline char* PdfRefCountedBuffer::GetBuffer() const
254 {
255     if (!m_pBuffer) return NULL;
256     return m_pBuffer->GetRealBuffer();
257 }
258 
259 // -----------------------------------------------------
260 //
261 // -----------------------------------------------------
GetSize()262 inline size_t PdfRefCountedBuffer::GetSize() const
263 {
264     return m_pBuffer ? m_pBuffer->m_lVisibleSize : 0;
265 }
266 
267 // -----------------------------------------------------
268 //
269 // -----------------------------------------------------
SetTakePossesion(bool bTakePossession)270 inline void PdfRefCountedBuffer::SetTakePossesion( bool bTakePossession )
271 {
272     if( m_pBuffer )
273         m_pBuffer->m_bPossesion = bTakePossession;
274 }
275 
276 // -----------------------------------------------------
277 //
278 // -----------------------------------------------------
TakePossesion()279 inline bool PdfRefCountedBuffer::TakePossesion() const
280 {
281     return m_pBuffer ? m_pBuffer->m_bPossesion : false;
282 }
283 
284 // -----------------------------------------------------
285 //
286 // -----------------------------------------------------
Detach(size_t lExtraLen)287 inline void PdfRefCountedBuffer::Detach( size_t lExtraLen )
288 {
289     if (m_pBuffer && m_pBuffer->m_lRefCount > 1L)
290         ReallyDetach(lExtraLen);
291 }
292 
293 // -----------------------------------------------------
294 //
295 // -----------------------------------------------------
Resize(size_t lSize)296 inline void PdfRefCountedBuffer::Resize( size_t lSize )
297 {
298     if (m_pBuffer && m_pBuffer->m_lRefCount == 1L  && static_cast<size_t>(m_pBuffer->m_lBufferSize) >= lSize)
299     {
300         // We have a solely owned buffer the right size already; no need to
301         // waste any time detaching or resizing it. Just let the client see
302         // more of it (or less if they're shrinking their view).
303         m_pBuffer->m_lVisibleSize = lSize;
304     }
305     else
306     {
307         ReallyResize( lSize );
308     }
309 }
310 
311 // -----------------------------------------------------
312 //
313 // -----------------------------------------------------
DerefBuffer()314 inline void PdfRefCountedBuffer::DerefBuffer()
315 {
316     if ( m_pBuffer && !(--m_pBuffer->m_lRefCount) )
317         FreeBuffer();
318     // Whether or not it still exists, we no longer have anything to do with
319     // the buffer we just released our claim on.
320     m_pBuffer = NULL;
321 }
322 
323 };
324 
325 #endif // _PDF_REF_COUNTED_BUFFER_H_
326 
327