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 "SharedBuffer.h"
29
30#include "PurgeableBuffer.h"
31#include <wtf/PassOwnPtr.h>
32#include <wtf/unicode/UTF8.h>
33#include <wtf/unicode/Unicode.h>
34
35using namespace std;
36
37namespace WebCore {
38
39static const unsigned segmentSize = 0x1000;
40static const unsigned segmentPositionMask = 0x0FFF;
41
42static inline unsigned segmentIndex(unsigned position)
43{
44    return position / segmentSize;
45}
46
47static inline unsigned offsetInSegment(unsigned position)
48{
49    return position & segmentPositionMask;
50}
51
52static inline char* allocateSegment()
53{
54    return static_cast<char*>(fastMalloc(segmentSize));
55}
56
57static inline void freeSegment(char* p)
58{
59    fastFree(p);
60}
61
62SharedBuffer::SharedBuffer()
63    : m_size(0)
64{
65}
66
67SharedBuffer::SharedBuffer(size_t size)
68    : m_size(size)
69    , m_buffer(size)
70{
71}
72
73SharedBuffer::SharedBuffer(const char* data, int size)
74    : m_size(0)
75{
76    // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
77    if (size < 0)
78        CRASH();
79
80    append(data, size);
81}
82
83SharedBuffer::SharedBuffer(const unsigned char* data, int size)
84    : m_size(0)
85{
86    // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code.
87    if (size < 0)
88        CRASH();
89
90    append(reinterpret_cast<const char*>(data), size);
91}
92
93SharedBuffer::~SharedBuffer()
94{
95    clear();
96}
97
98PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector)
99{
100    RefPtr<SharedBuffer> buffer = create();
101    buffer->m_buffer.swap(vector);
102    buffer->m_size = buffer->m_buffer.size();
103    return buffer.release();
104}
105
106PassRefPtr<SharedBuffer> SharedBuffer::adoptPurgeableBuffer(PassOwnPtr<PurgeableBuffer> purgeableBuffer)
107{
108    ASSERT(!purgeableBuffer->isPurgeable());
109    RefPtr<SharedBuffer> buffer = create();
110    buffer->m_purgeableBuffer = purgeableBuffer;
111    return buffer.release();
112}
113
114unsigned SharedBuffer::size() const
115{
116    if (hasPlatformData())
117        return platformDataSize();
118
119    if (m_purgeableBuffer)
120        return m_purgeableBuffer->size();
121
122    return m_size;
123}
124
125void SharedBuffer::createPurgeableBuffer() const
126{
127    if (m_purgeableBuffer)
128        return;
129
130    if (hasPlatformData())
131        return;
132
133#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
134    if (singleDataArrayBuffer())
135        return;
136#endif
137
138    m_purgeableBuffer = PurgeableBuffer::create(buffer().data(), m_size);
139}
140
141const char* SharedBuffer::data() const
142{
143    if (hasPlatformData())
144        return platformData();
145
146#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
147    if (const char* buffer = singleDataArrayBuffer())
148        return buffer;
149#endif
150
151    if (m_purgeableBuffer)
152        return m_purgeableBuffer->data();
153
154    return this->buffer().data();
155}
156
157void SharedBuffer::append(SharedBuffer* data)
158{
159    const char* segment;
160    size_t position = 0;
161    while (size_t length = data->getSomeData(segment, position)) {
162        append(segment, length);
163        position += length;
164    }
165}
166
167void SharedBuffer::append(const char* data, unsigned length)
168{
169    ASSERT(!m_purgeableBuffer);
170    if (!length)
171        return;
172
173    maybeTransferPlatformData();
174
175    unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size());
176    m_size += length;
177
178    if (m_size <= segmentSize) {
179        // No need to use segments for small resource data
180        if (m_buffer.isEmpty())
181            m_buffer.reserveInitialCapacity(length);
182        m_buffer.append(data, length);
183        return;
184    }
185
186    char* segment;
187    if (!positionInSegment) {
188        segment = allocateSegment();
189        m_segments.append(segment);
190    } else
191        segment = m_segments.last() + positionInSegment;
192
193    unsigned segmentFreeSpace = segmentSize - positionInSegment;
194    unsigned bytesToCopy = min(length, segmentFreeSpace);
195
196    for (;;) {
197        memcpy(segment, data, bytesToCopy);
198        if (static_cast<unsigned>(length) == bytesToCopy)
199            break;
200
201        length -= bytesToCopy;
202        data += bytesToCopy;
203        segment = allocateSegment();
204        m_segments.append(segment);
205        bytesToCopy = min(length, segmentSize);
206    }
207}
208
209void SharedBuffer::append(const Vector<char>& data)
210{
211    append(data.data(), data.size());
212}
213
214void SharedBuffer::clear()
215{
216    clearPlatformData();
217
218    for (unsigned i = 0; i < m_segments.size(); ++i)
219        freeSegment(m_segments[i]);
220
221    m_segments.clear();
222    m_size = 0;
223
224    m_buffer.clear();
225    m_purgeableBuffer.clear();
226#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
227    m_dataArray.clear();
228#endif
229}
230
231PassRefPtr<SharedBuffer> SharedBuffer::copy() const
232{
233    RefPtr<SharedBuffer> clone(adoptRef(new SharedBuffer));
234    if (m_purgeableBuffer || hasPlatformData()) {
235        clone->append(data(), size());
236        return clone;
237    }
238
239    clone->m_size = m_size;
240    clone->m_buffer.reserveCapacity(m_size);
241    clone->m_buffer.append(m_buffer.data(), m_buffer.size());
242    for (unsigned i = 0; i < m_segments.size(); ++i)
243        clone->m_buffer.append(m_segments[i], segmentSize);
244    return clone;
245}
246
247PassOwnPtr<PurgeableBuffer> SharedBuffer::releasePurgeableBuffer()
248{
249    ASSERT(hasOneRef());
250    return m_purgeableBuffer.release();
251}
252
253const Vector<char>& SharedBuffer::buffer() const
254{
255    unsigned bufferSize = m_buffer.size();
256    if (m_size > bufferSize) {
257        m_buffer.resize(m_size);
258        char* destination = m_buffer.data() + bufferSize;
259        unsigned bytesLeft = m_size - bufferSize;
260        for (unsigned i = 0; i < m_segments.size(); ++i) {
261            unsigned bytesToCopy = min(bytesLeft, segmentSize);
262            memcpy(destination, m_segments[i], bytesToCopy);
263            destination += bytesToCopy;
264            bytesLeft -= bytesToCopy;
265            freeSegment(m_segments[i]);
266        }
267        m_segments.clear();
268#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
269        copyDataArrayAndClear(destination, bytesLeft);
270#endif
271    }
272    return m_buffer;
273}
274
275unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const
276{
277    unsigned totalSize = size();
278    if (position >= totalSize) {
279        someData = 0;
280        return 0;
281    }
282
283    if (hasPlatformData() || m_purgeableBuffer) {
284        ASSERT_WITH_SECURITY_IMPLICATION(position < size());
285        someData = data() + position;
286        return totalSize - position;
287    }
288
289    ASSERT_WITH_SECURITY_IMPLICATION(position < m_size);
290    unsigned consecutiveSize = m_buffer.size();
291    if (position < consecutiveSize) {
292        someData = m_buffer.data() + position;
293        return consecutiveSize - position;
294    }
295
296    position -= consecutiveSize;
297    unsigned segments = m_segments.size();
298    unsigned maxSegmentedSize = segments * segmentSize;
299    unsigned segment = segmentIndex(position);
300    if (segment < segments) {
301        unsigned bytesLeft = totalSize - consecutiveSize;
302        unsigned segmentedSize = min(maxSegmentedSize, bytesLeft);
303
304        unsigned positionInSegment = offsetInSegment(position);
305        someData = m_segments[segment] + positionInSegment;
306        return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment;
307    }
308#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
309    ASSERT(maxSegmentedSize <= position);
310    position -= maxSegmentedSize;
311    return copySomeDataFromDataArray(someData, position);
312#else
313    ASSERT_NOT_REACHED();
314    return 0;
315#endif
316}
317
318#if !USE(CF) || PLATFORM(QT)
319
320inline void SharedBuffer::clearPlatformData()
321{
322}
323
324inline void SharedBuffer::maybeTransferPlatformData()
325{
326}
327
328inline bool SharedBuffer::hasPlatformData() const
329{
330    return false;
331}
332
333inline const char* SharedBuffer::platformData() const
334{
335    ASSERT_NOT_REACHED();
336
337    return 0;
338}
339
340inline unsigned SharedBuffer::platformDataSize() const
341{
342    ASSERT_NOT_REACHED();
343
344    return 0;
345}
346
347#endif
348
349PassRefPtr<SharedBuffer> utf8Buffer(const String& string)
350{
351    // Allocate a buffer big enough to hold all the characters.
352    const int length = string.length();
353    Vector<char> buffer(length * 3);
354
355    // Convert to runs of 8-bit characters.
356    char* p = buffer.data();
357    const UChar* d = string.characters();
358    WTF::Unicode::ConversionResult result = WTF::Unicode::convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size(), true);
359    if (result != WTF::Unicode::conversionOK)
360        return 0;
361
362    buffer.shrink(p - buffer.data());
363    return SharedBuffer::adoptVector(buffer);
364}
365
366} // namespace WebCore
367