1/*
2 * Copyright (C) 2008, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
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 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28#include "config.h"
29#include "SharedBuffer.h"
30
31#include "PurgeableBuffer.h"
32#include <wtf/cf/TypeCasts.h>
33
34#if ENABLE(DISK_IMAGE_CACHE)
35#include "DiskImageCacheIOS.h"
36#endif
37
38namespace WebCore {
39
40SharedBuffer::SharedBuffer(CFDataRef cfData)
41    : m_size(0)
42    , m_buffer(adoptRef(new DataBuffer))
43    , m_shouldUsePurgeableMemory(false)
44#if ENABLE(DISK_IMAGE_CACHE)
45    , m_isMemoryMapped(false)
46    , m_diskImageCacheId(DiskImageCache::invalidDiskCacheId)
47    , m_notifyMemoryMappedCallback(nullptr)
48    , m_notifyMemoryMappedCallbackData(nullptr)
49#endif
50    , m_cfData(cfData)
51{
52}
53
54// Using Foundation allows for an even more efficient implementation of this function,
55// so only use this version for non-Foundation.
56#if !USE(FOUNDATION)
57RetainPtr<CFDataRef> SharedBuffer::createCFData()
58{
59    if (m_cfData)
60        return m_cfData;
61
62    // Internal data in SharedBuffer can be segmented. We need to get the contiguous buffer.
63    const Vector<char>& contiguousBuffer = buffer();
64    return adoptCF(CFDataCreate(0, reinterpret_cast<const UInt8*>(contiguousBuffer.data()), contiguousBuffer.size()));
65}
66#endif
67
68PassRefPtr<SharedBuffer> SharedBuffer::wrapCFData(CFDataRef data)
69{
70    return adoptRef(new SharedBuffer(data));
71}
72
73bool SharedBuffer::hasPlatformData() const
74{
75    return m_cfData;
76}
77
78const char* SharedBuffer::platformData() const
79{
80    return reinterpret_cast<const char*>(CFDataGetBytePtr(m_cfData.get()));
81}
82
83unsigned SharedBuffer::platformDataSize() const
84{
85    return CFDataGetLength(m_cfData.get());
86}
87
88void SharedBuffer::maybeTransferPlatformData()
89{
90    if (!m_cfData)
91        return;
92
93    ASSERT(!m_size);
94
95    // Hang on to the m_cfData pointer in a local pointer as append() will re-enter maybeTransferPlatformData()
96    // and we need to make sure to early return when it does.
97    RetainPtr<CFDataRef> cfData = adoptCF(m_cfData.leakRef());
98
99    append(reinterpret_cast<const char*>(CFDataGetBytePtr(cfData.get())), CFDataGetLength(cfData.get()));
100}
101
102void SharedBuffer::clearPlatformData()
103{
104    m_cfData = 0;
105}
106
107void SharedBuffer::tryReplaceContentsWithPlatformBuffer(SharedBuffer* newContents)
108{
109    if (!newContents->m_cfData)
110        return;
111
112    clear();
113    m_cfData = newContents->m_cfData;
114}
115
116bool SharedBuffer::maybeAppendPlatformData(SharedBuffer* newContents)
117{
118    if (size() || !newContents->m_cfData)
119        return false;
120    m_cfData = newContents->m_cfData;
121    return true;
122}
123
124#if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
125PassRefPtr<SharedBuffer> SharedBuffer::wrapCFDataArray(CFArrayRef cfDataArray)
126{
127    return adoptRef(new SharedBuffer(cfDataArray));
128}
129
130SharedBuffer::SharedBuffer(CFArrayRef cfDataArray)
131    : m_size(0)
132    , m_buffer(adoptRef(new DataBuffer))
133    , m_shouldUsePurgeableMemory(false)
134#if ENABLE(DISK_IMAGE_CACHE)
135    , m_isMemoryMapped(false)
136    , m_diskImageCacheId(DiskImageCache::invalidDiskCacheId)
137    , m_notifyMemoryMappedCallback(nullptr)
138    , m_notifyMemoryMappedCallbackData(nullptr)
139#endif
140    , m_cfData(nullptr)
141{
142    CFIndex dataArrayCount = CFArrayGetCount(cfDataArray);
143    for (CFIndex index = 0; index < dataArrayCount; ++index)
144        append(checked_cf_cast<CFDataRef>(CFArrayGetValueAtIndex(cfDataArray, index)));
145}
146
147void SharedBuffer::append(CFDataRef data)
148{
149    ASSERT(data);
150    m_dataArray.append(data);
151    m_size += CFDataGetLength(data);
152}
153
154void SharedBuffer::copyBufferAndClear(char* destination, unsigned bytesToCopy) const
155{
156    if (m_dataArray.isEmpty())
157        return;
158
159    CFIndex bytesLeft = bytesToCopy;
160    Vector<RetainPtr<CFDataRef>>::const_iterator end = m_dataArray.end();
161    for (Vector<RetainPtr<CFDataRef>>::const_iterator it = m_dataArray.begin(); it != end; ++it) {
162        CFIndex dataLen = CFDataGetLength(it->get());
163        ASSERT(bytesLeft >= dataLen);
164        memcpy(destination, CFDataGetBytePtr(it->get()), dataLen);
165        destination += dataLen;
166        bytesLeft -= dataLen;
167    }
168    m_dataArray.clear();
169}
170
171unsigned SharedBuffer::copySomeDataFromDataArray(const char*& someData, unsigned position) const
172{
173    Vector<RetainPtr<CFDataRef>>::const_iterator end = m_dataArray.end();
174    unsigned totalOffset = 0;
175    for (Vector<RetainPtr<CFDataRef>>::const_iterator it = m_dataArray.begin(); it != end; ++it) {
176        unsigned dataLen = static_cast<unsigned>(CFDataGetLength(it->get()));
177        ASSERT(totalOffset <= position);
178        unsigned localOffset = position - totalOffset;
179        if (localOffset < dataLen) {
180            someData = reinterpret_cast<const char *>(CFDataGetBytePtr(it->get())) + localOffset;
181            return dataLen - localOffset;
182        }
183        totalOffset += dataLen;
184    }
185    return 0;
186}
187
188const char *SharedBuffer::singleDataArrayBuffer() const
189{
190    // If we had previously copied data into m_buffer in copyDataArrayAndClear() or some other
191    // function, then we can't return a pointer to the CFDataRef buffer.
192    if (m_buffer->data.size())
193        return 0;
194
195    if (m_dataArray.size() != 1)
196        return 0;
197
198    return reinterpret_cast<const char*>(CFDataGetBytePtr(m_dataArray.at(0).get()));
199}
200
201bool SharedBuffer::maybeAppendDataArray(SharedBuffer* data)
202{
203    if (m_buffer->data.size() || m_cfData || !data->m_dataArray.size())
204        return false;
205#if !ASSERT_DISABLED
206    unsigned originalSize = size();
207#endif
208    for (auto cfData : data->m_dataArray)
209        append(cfData.get());
210    ASSERT(size() == originalSize + data->size());
211    return true;
212}
213#endif
214
215}
216