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
42using namespace std;
43
44namespace WebCore {
45
46static const unsigned segmentSize = 0x1000;
47static const unsigned segmentPositionMask = 0x0FFF;
48
49static inline unsigned segmentIndex(unsigned position)
50{
51    return position / segmentSize;
52}
53
54static inline unsigned offsetInSegment(unsigned position)
55{
56    return position & segmentPositionMask;
57}
58
59static inline char* allocateSegment()
60{
61    return static_cast<char*>(fastMalloc(segmentSize));
62}
63
64static inline void freeSegment(char* p)
65{
66    fastFree(p);
67}
68
69#ifdef SHARED_BUFFER_STATS
70
71static Mutex& statsMutex()
72{
73    DEFINE_STATIC_LOCAL(Mutex, mutex, ());
74    return mutex;
75}
76
77static HashSet<SharedBuffer*>& liveBuffers()
78{
79    DEFINE_STATIC_LOCAL(HashSet<SharedBuffer*>, buffers, ());
80    return buffers;
81}
82
83static bool sizeComparator(SharedBuffer* a, SharedBuffer* b)
84{
85    return a->size() > b->size();
86}
87
88static 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
113static 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
128static void didCreateSharedBuffer(SharedBuffer* buffer)
129{
130    MutexLocker locker(statsMutex());
131    liveBuffers().add(buffer);
132
133    callOnMainThread(printStats, 0);
134}
135
136static void willDestroySharedBuffer(SharedBuffer* buffer)
137{
138    MutexLocker locker(statsMutex());
139    liveBuffers().remove(buffer);
140}
141
142#endif
143
144SharedBuffer::SharedBuffer()
145    : m_size(0)
146{
147#ifdef SHARED_BUFFER_STATS
148    didCreateSharedBuffer(this);
149#endif
150}
151
152SharedBuffer::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
161SharedBuffer::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
175SharedBuffer::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
189SharedBuffer::~SharedBuffer()
190{
191    clear();
192
193#ifdef SHARED_BUFFER_STATS
194    willDestroySharedBuffer(this);
195#endif
196}
197
198PassRefPtr<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
206PassRefPtr<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
214unsigned SharedBuffer::size() const
215{
216    if (m_purgeableBuffer)
217        return m_purgeableBuffer->size();
218
219    return m_size;
220}
221
222void SharedBuffer::createPurgeableBuffer() const
223{
224    if (m_purgeableBuffer)
225        return;
226
227    m_purgeableBuffer = PurgeableBuffer::create(buffer().data(), m_size);
228}
229
230const char* SharedBuffer::data() const
231{
232    if (m_purgeableBuffer)
233        return m_purgeableBuffer->data();
234
235    return this->buffer().data();
236}
237
238void 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
268void 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
278void 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
318void SharedBuffer::append(const Vector<char>& data)
319{
320    append(data.data(), data.size());
321}
322
323void 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
335PassRefPtr<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
358PassOwnPtr<PurgeableBuffer> SharedBuffer::releasePurgeableBuffer()
359{
360    ASSERT(hasOneRef());
361    return m_purgeableBuffer.release();
362}
363
364const 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
383unsigned 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
420PassRefPtr<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
440PassRefPtr<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