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