• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #include "platform/SharedBuffer.h"
29 
30 #include "platform/PurgeableBuffer.h"
31 #include "wtf/PassOwnPtr.h"
32 #include "wtf/unicode/Unicode.h"
33 #include "wtf/unicode/UTF8.h"
34 
35 #undef SHARED_BUFFER_STATS
36 
37 #ifdef SHARED_BUFFER_STATS
38 #include "wtf/DataLog.h"
39 #include "wtf/MainThread.h"
40 #endif
41 
42 using namespace std;
43 
44 namespace WebCore {
45 
46 static const unsigned segmentSize = 0x1000;
47 static const unsigned segmentPositionMask = 0x0FFF;
48 
segmentIndex(unsigned position)49 static inline unsigned segmentIndex(unsigned position)
50 {
51     return position / segmentSize;
52 }
53 
offsetInSegment(unsigned position)54 static inline unsigned offsetInSegment(unsigned position)
55 {
56     return position & segmentPositionMask;
57 }
58 
allocateSegment()59 static inline char* allocateSegment()
60 {
61     return static_cast<char*>(fastMalloc(segmentSize));
62 }
63 
freeSegment(char * p)64 static inline void freeSegment(char* p)
65 {
66     fastFree(p);
67 }
68 
69 #ifdef SHARED_BUFFER_STATS
70 
statsMutex()71 static Mutex& statsMutex()
72 {
73     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
74     return mutex;
75 }
76 
liveBuffers()77 static HashSet<SharedBuffer*>& liveBuffers()
78 {
79     DEFINE_STATIC_LOCAL(HashSet<SharedBuffer*>, buffers, ());
80     return buffers;
81 }
82 
sizeComparator(SharedBuffer * a,SharedBuffer * b)83 static bool sizeComparator(SharedBuffer* a, SharedBuffer* b)
84 {
85     return a->size() > b->size();
86 }
87 
snippetForBuffer(SharedBuffer * sharedBuffer)88 static CString snippetForBuffer(SharedBuffer* sharedBuffer)
89 {
90     const unsigned kMaxSnippetLength = 64;
91     char* snippet = 0;
92     unsigned snippetLength = std::min(sharedBuffer->size(), kMaxSnippetLength);
93     CString result = CString::newUninitialized(snippetLength, snippet);
94 
95     const char* segment;
96     unsigned offset = 0;
97     while (unsigned segmentLength = sharedBuffer->getSomeData(segment, offset)) {
98         unsigned length = std::min(segmentLength, snippetLength - offset);
99         memcpy(snippet + offset, segment, length);
100         offset += segmentLength;
101         if (offset >= snippetLength)
102             break;
103     }
104 
105     for (unsigned i = 0; i < snippetLength; ++i) {
106         if (!isASCIIPrintable(snippet[i]))
107             snippet[i] = '?';
108     }
109 
110     return result;
111 }
112 
printStats(void *)113 static void printStats(void*)
114 {
115     MutexLocker locker(statsMutex());
116     Vector<SharedBuffer*> buffers;
117     for (HashSet<SharedBuffer*>::const_iterator iter = liveBuffers().begin(); iter != liveBuffers().end(); ++iter)
118         buffers.append(*iter);
119     std::sort(buffers.begin(), buffers.end(), sizeComparator);
120 
121     dataLogF("---- Shared Buffer Stats ----\n");
122     for (size_t i = 0; i < buffers.size() && i < 64; ++i) {
123         CString snippet = snippetForBuffer(buffers[i]);
124         dataLogF("Buffer size=%8u %s\n", buffers[i]->size(), snippet.data());
125     }
126 }
127 
didCreateSharedBuffer(SharedBuffer * buffer)128 static void didCreateSharedBuffer(SharedBuffer* buffer)
129 {
130     MutexLocker locker(statsMutex());
131     liveBuffers().add(buffer);
132 
133     callOnMainThread(printStats, 0);
134 }
135 
willDestroySharedBuffer(SharedBuffer * buffer)136 static void willDestroySharedBuffer(SharedBuffer* buffer)
137 {
138     MutexLocker locker(statsMutex());
139     liveBuffers().remove(buffer);
140 }
141 
142 #endif
143 
SharedBuffer()144 SharedBuffer::SharedBuffer()
145     : m_size(0)
146 {
147 #ifdef SHARED_BUFFER_STATS
148     didCreateSharedBuffer(this);
149 #endif
150 }
151 
SharedBuffer(size_t size)152 SharedBuffer::SharedBuffer(size_t size)
153     : m_size(size)
154     , m_buffer(size)
155 {
156 #ifdef SHARED_BUFFER_STATS
157     didCreateSharedBuffer(this);
158 #endif
159 }
160 
SharedBuffer(const char * data,int size)161 SharedBuffer::SharedBuffer(const char* data, int size)
162     : m_size(0)
163 {
164     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
165     if (size < 0)
166         CRASH();
167 
168     append(data, size);
169 
170 #ifdef SHARED_BUFFER_STATS
171     didCreateSharedBuffer(this);
172 #endif
173 }
174 
SharedBuffer(const unsigned char * data,int size)175 SharedBuffer::SharedBuffer(const unsigned char* data, int size)
176     : m_size(0)
177 {
178     // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
179     if (size < 0)
180         CRASH();
181 
182     append(reinterpret_cast<const char*>(data), size);
183 
184 #ifdef SHARED_BUFFER_STATS
185     didCreateSharedBuffer(this);
186 #endif
187 }
188 
~SharedBuffer()189 SharedBuffer::~SharedBuffer()
190 {
191     clear();
192 
193 #ifdef SHARED_BUFFER_STATS
194     willDestroySharedBuffer(this);
195 #endif
196 }
197 
adoptVector(Vector<char> & vector)198 PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector)
199 {
200     RefPtr<SharedBuffer> buffer = create();
201     buffer->m_buffer.swap(vector);
202     buffer->m_size = buffer->m_buffer.size();
203     return buffer.release();
204 }
205 
adoptPurgeableBuffer(PassOwnPtr<PurgeableBuffer> purgeableBuffer)206 PassRefPtr<SharedBuffer> SharedBuffer::adoptPurgeableBuffer(PassOwnPtr<PurgeableBuffer> purgeableBuffer)
207 {
208     ASSERT(!purgeableBuffer->isPurgeable());
209     RefPtr<SharedBuffer> buffer = create();
210     buffer->m_purgeableBuffer = purgeableBuffer;
211     return buffer.release();
212 }
213 
size() const214 unsigned SharedBuffer::size() const
215 {
216     if (m_purgeableBuffer)
217         return m_purgeableBuffer->size();
218 
219     return m_size;
220 }
221 
createPurgeableBuffer() const222 void SharedBuffer::createPurgeableBuffer() const
223 {
224     if (m_purgeableBuffer)
225         return;
226 
227     m_purgeableBuffer = PurgeableBuffer::create(buffer().data(), m_size);
228 }
229 
data() const230 const char* SharedBuffer::data() const
231 {
232     if (m_purgeableBuffer)
233         return m_purgeableBuffer->data();
234 
235     return this->buffer().data();
236 }
237 
moveTo(Vector<char> & result)238 void SharedBuffer::moveTo(Vector<char>& result)
239 {
240     ASSERT(result.isEmpty());
241     if (m_purgeableBuffer) {
242         result.reserveCapacity(m_purgeableBuffer->size());
243         result.append(m_purgeableBuffer->data(), m_purgeableBuffer->size());
244         clear();
245         return;
246     }
247 
248     unsigned bufferSize = m_buffer.size();
249     if (m_size == bufferSize) {
250         m_buffer.swap(result);
251         clear();
252         return;
253     }
254 
255     result.reserveCapacity(m_size);
256 
257     const char* segment = 0;
258     unsigned position = 0;
259     while (unsigned segmentSize = getSomeData(segment, position)) {
260         result.append(segment, segmentSize);
261         position += segmentSize;
262     }
263     ASSERT(result.size() == m_size);
264     clear();
265     return;
266 }
267 
append(SharedBuffer * data)268 void SharedBuffer::append(SharedBuffer* data)
269 {
270     const char* segment;
271     size_t position = 0;
272     while (size_t length = data->getSomeData(segment, position)) {
273         append(segment, length);
274         position += length;
275     }
276 }
277 
append(const char * data,unsigned length)278 void SharedBuffer::append(const char* data, unsigned length)
279 {
280     ASSERT(!m_purgeableBuffer);
281     if (!length)
282         return;
283 
284     unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size());
285     m_size += length;
286 
287     if (m_size <= segmentSize) {
288         // No need to use segments for small resource data
289         if (m_buffer.isEmpty())
290             m_buffer.reserveInitialCapacity(length);
291         m_buffer.append(data, length);
292         return;
293     }
294 
295     char* segment;
296     if (!positionInSegment) {
297         segment = allocateSegment();
298         m_segments.append(segment);
299     } else
300         segment = m_segments.last() + positionInSegment;
301 
302     unsigned segmentFreeSpace = segmentSize - positionInSegment;
303     unsigned bytesToCopy = min(length, segmentFreeSpace);
304 
305     for (;;) {
306         memcpy(segment, data, bytesToCopy);
307         if (static_cast<unsigned>(length) == bytesToCopy)
308             break;
309 
310         length -= bytesToCopy;
311         data += bytesToCopy;
312         segment = allocateSegment();
313         m_segments.append(segment);
314         bytesToCopy = min(length, segmentSize);
315     }
316 }
317 
append(const Vector<char> & data)318 void SharedBuffer::append(const Vector<char>& data)
319 {
320     append(data.data(), data.size());
321 }
322 
clear()323 void SharedBuffer::clear()
324 {
325     for (unsigned i = 0; i < m_segments.size(); ++i)
326         freeSegment(m_segments[i]);
327 
328     m_segments.clear();
329     m_size = 0;
330 
331     m_buffer.clear();
332     m_purgeableBuffer.clear();
333 }
334 
copy() const335 PassRefPtr<SharedBuffer> SharedBuffer::copy() const
336 {
337     RefPtr<SharedBuffer> clone(adoptRef(new SharedBuffer));
338     if (m_purgeableBuffer) {
339         clone->append(data(), size());
340         return clone.release();
341     }
342 
343     clone->m_size = m_size;
344     clone->m_buffer.reserveCapacity(m_size);
345     clone->m_buffer.append(m_buffer.data(), m_buffer.size());
346     if (!m_segments.isEmpty()) {
347         const char* segment = 0;
348         unsigned position = m_buffer.size();
349         while (unsigned segmentSize = getSomeData(segment, position)) {
350             clone->m_buffer.append(segment, segmentSize);
351             position += segmentSize;
352         }
353         ASSERT(position == clone->size());
354     }
355     return clone.release();
356 }
357 
releasePurgeableBuffer()358 PassOwnPtr<PurgeableBuffer> SharedBuffer::releasePurgeableBuffer()
359 {
360     ASSERT(hasOneRef());
361     return m_purgeableBuffer.release();
362 }
363 
buffer() const364 const Vector<char>& SharedBuffer::buffer() const
365 {
366     unsigned bufferSize = m_buffer.size();
367     if (m_size > bufferSize) {
368         m_buffer.resize(m_size);
369         char* destination = m_buffer.data() + bufferSize;
370         unsigned bytesLeft = m_size - bufferSize;
371         for (unsigned i = 0; i < m_segments.size(); ++i) {
372             unsigned bytesToCopy = min(bytesLeft, segmentSize);
373             memcpy(destination, m_segments[i], bytesToCopy);
374             destination += bytesToCopy;
375             bytesLeft -= bytesToCopy;
376             freeSegment(m_segments[i]);
377         }
378         m_segments.clear();
379     }
380     return m_buffer;
381 }
382 
getSomeData(const char * & someData,unsigned position) const383 unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const
384 {
385     unsigned totalSize = size();
386     if (position >= totalSize) {
387         someData = 0;
388         return 0;
389     }
390 
391     if (m_purgeableBuffer) {
392         ASSERT_WITH_SECURITY_IMPLICATION(position < size());
393         someData = data() + position;
394         return totalSize - position;
395     }
396 
397     ASSERT_WITH_SECURITY_IMPLICATION(position < m_size);
398     unsigned consecutiveSize = m_buffer.size();
399     if (position < consecutiveSize) {
400         someData = m_buffer.data() + position;
401         return consecutiveSize - position;
402     }
403 
404     position -= consecutiveSize;
405     unsigned segments = m_segments.size();
406     unsigned maxSegmentedSize = segments * segmentSize;
407     unsigned segment = segmentIndex(position);
408     if (segment < segments) {
409         unsigned bytesLeft = totalSize - consecutiveSize;
410         unsigned segmentedSize = min(maxSegmentedSize, bytesLeft);
411 
412         unsigned positionInSegment = offsetInSegment(position);
413         someData = m_segments[segment] + positionInSegment;
414         return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment;
415     }
416     ASSERT_NOT_REACHED();
417     return 0;
418 }
419 
getAsArrayBuffer() const420 PassRefPtr<ArrayBuffer> SharedBuffer::getAsArrayBuffer() const
421 {
422     RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::createUninitialized(static_cast<unsigned>(size()), 1);
423 
424     const char* segment = 0;
425     unsigned position = 0;
426     while (unsigned segmentSize = getSomeData(segment, position)) {
427         memcpy(static_cast<char*>(arrayBuffer->data()) + position, segment, segmentSize);
428         position += segmentSize;
429     }
430 
431     if (position != arrayBuffer->byteLength()) {
432         ASSERT_NOT_REACHED();
433         // Don't return the incomplete ArrayBuffer.
434         return 0;
435     }
436 
437     return arrayBuffer;
438 }
439 
getAsSkData() const440 PassRefPtr<SkData> SharedBuffer::getAsSkData() const
441 {
442     unsigned bufferLength = size();
443     char* buffer = static_cast<char*>(sk_malloc_throw(bufferLength));
444     const char* segment = 0;
445     unsigned position = 0;
446     while (unsigned segmentSize = getSomeData(segment, position)) {
447         memcpy(buffer + position, segment, segmentSize);
448         position += segmentSize;
449     }
450 
451     if (position != bufferLength) {
452         ASSERT_NOT_REACHED();
453         // Don't return the incomplete SkData.
454         return 0;
455     }
456     return adoptRef(SkData::NewFromMalloc(buffer, bufferLength));
457 }
458 
459 } // namespace WebCore
460