1/*
2 * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
4 * Copyright (C) 2015 Canon Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "SharedBuffer.h"
30
31#include <algorithm>
32#include <wtf/unicode/UTF8Conversion.h>
33
34namespace WebCore {
35
36SharedBuffer::SharedBuffer(const char* data, size_t size)
37{
38 append(data, size);
39}
40
41SharedBuffer::SharedBuffer(const unsigned char* data, size_t size)
42{
43 append(reinterpret_cast<const char*>(data), size);
44}
45
46SharedBuffer::SharedBuffer(FileSystem::MappedFileData&& fileData)
47 : m_size(fileData.size())
48{
49 m_segments.append({0, DataSegment::create(WTFMove(fileData))});
50}
51
52SharedBuffer::SharedBuffer(Vector<char>&& data)
53{
54 append(WTFMove(data));
55}
56
57#if USE(GSTREAMER)
58Ref<SharedBuffer> SharedBuffer::create(GstMappedBuffer& mappedBuffer)
59{
60 ASSERT(mappedBuffer.isSharable());
61 return adoptRef(*new SharedBuffer(mappedBuffer));
62}
63
64SharedBuffer::SharedBuffer(GstMappedBuffer& mappedBuffer)
65 : m_size(mappedBuffer.size())
66{
67 m_segments.append({0, DataSegment::create(&mappedBuffer)});
68}
69#endif
70
71RefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String& filePath)
72{
73 bool mappingSuccess;
74 FileSystem::MappedFileData mappedFileData(filePath, mappingSuccess);
75
76 if (!mappingSuccess)
77 return SharedBuffer::createFromReadingFile(filePath);
78
79 return adoptRef(new SharedBuffer(WTFMove(mappedFileData)));
80}
81
82Ref<SharedBuffer> SharedBuffer::create(Vector<char>&& vector)
83{
84 return adoptRef(*new SharedBuffer(WTFMove(vector)));
85}
86
87// FIXME: Move the whole class from Vector<char> to Vector<uint8_t> and make this efficient, replacing the Vector<char> version above.
88Ref<SharedBuffer> SharedBuffer::create(Vector<uint8_t>&& vector)
89{
90 return adoptRef(*new SharedBuffer { vector.data(), vector.size() });
91}
92
93void SharedBuffer::combineIntoOneSegment() const
94{
95#if !ASSERT_DISABLED
96 // FIXME: We ought to be able to set this to true and have no assertions fire.
97 // Remove all instances of appending after calling this, because they are all O(n^2) algorithms since r215686.
98 // m_hasBeenCombinedIntoOneSegment = true;
99#endif
100 if (m_segments.size() <= 1)
101 return;
102
103 Vector<char> combinedData;
104 combinedData.reserveInitialCapacity(m_size);
105 for (const auto& segment : m_segments)
106 combinedData.append(segment.segment->data(), segment.segment->size());
107 ASSERT(combinedData.size() == m_size);
108 m_segments.clear();
109 m_segments.append({0, DataSegment::create(WTFMove(combinedData))});
110 ASSERT(m_segments.size() == 1);
111 ASSERT(internallyConsistent());
112}
113
114const char* SharedBuffer::data() const
115{
116 if (!m_segments.size())
117 return nullptr;
118 combineIntoOneSegment();
119 ASSERT(internallyConsistent());
120 return m_segments[0].segment->data();
121}
122
123SharedBufferDataView SharedBuffer::getSomeData(size_t position) const
124{
125 RELEASE_ASSERT(position < m_size);
126 auto comparator = [](const size_t& position, const DataSegmentVectorEntry& entry) {
127 return position < entry.beginPosition;
128 };
129 const DataSegmentVectorEntry* element = std::upper_bound(m_segments.begin(), m_segments.end(), position, comparator);
130 element--; // std::upper_bound gives a pointer to the element that is greater than position. We want the element just before that.
131 return { element->segment.copyRef(), position - element->beginPosition };
132}
133
134RefPtr<ArrayBuffer> SharedBuffer::tryCreateArrayBuffer() const
135{
136 auto arrayBuffer = ArrayBuffer::tryCreateUninitialized(static_cast<unsigned>(size()), sizeof(char));
137 if (!arrayBuffer) {
138 WTFLogAlways("SharedBuffer::tryCreateArrayBuffer Unable to create buffer. Requested size was %zu\n", size());
139 return nullptr;
140 }
141
142 size_t position = 0;
143 for (const auto& segment : m_segments) {
144 memcpy(static_cast<char*>(arrayBuffer->data()) + position, segment.segment->data(), segment.segment->size());
145 position += segment.segment->size();
146 }
147
148 ASSERT(position == m_size);
149 ASSERT(internallyConsistent());
150 return arrayBuffer;
151}
152
153void SharedBuffer::append(const SharedBuffer& data)
154{
155 ASSERT(!m_hasBeenCombinedIntoOneSegment);
156 m_segments.reserveCapacity(m_segments.size() + data.m_segments.size());
157 for (const auto& element : data.m_segments) {
158 m_segments.uncheckedAppend({m_size, element.segment.copyRef()});
159 m_size += element.segment->size();
160 }
161 ASSERT(internallyConsistent());
162}
163
164void SharedBuffer::append(const char* data, size_t length)
165{
166 ASSERT(!m_hasBeenCombinedIntoOneSegment);
167 Vector<char> vector;
168 vector.append(data, length);
169 m_segments.append({m_size, DataSegment::create(WTFMove(vector))});
170 m_size += length;
171 ASSERT(internallyConsistent());
172}
173
174void SharedBuffer::append(Vector<char>&& data)
175{
176 ASSERT(!m_hasBeenCombinedIntoOneSegment);
177 auto dataSize = data.size();
178 m_segments.append({m_size, DataSegment::create(WTFMove(data))});
179 m_size += dataSize;
180 ASSERT(internallyConsistent());
181}
182
183void SharedBuffer::clear()
184{
185 m_size = 0;
186 m_segments.clear();
187 ASSERT(internallyConsistent());
188}
189
190Ref<SharedBuffer> SharedBuffer::copy() const
191{
192 Ref<SharedBuffer> clone = adoptRef(*new SharedBuffer);
193 clone->m_size = m_size;
194 clone->m_segments.reserveInitialCapacity(m_segments.size());
195 for (const auto& element : m_segments)
196 clone->m_segments.uncheckedAppend({element.beginPosition, element.segment.copyRef()});
197 ASSERT(clone->internallyConsistent());
198 ASSERT(internallyConsistent());
199 return clone;
200}
201
202#if !ASSERT_DISABLED
203bool SharedBuffer::internallyConsistent() const
204{
205 size_t position = 0;
206 for (const auto& element : m_segments) {
207 if (element.beginPosition != position)
208 return false;
209 position += element.segment->size();
210 }
211 return position == m_size;
212}
213#endif
214
215const char* SharedBuffer::DataSegment::data() const
216{
217 auto visitor = WTF::makeVisitor(
218 [](const Vector<char>& data) { return data.data(); },
219#if USE(CF)
220 [](const RetainPtr<CFDataRef>& data) { return reinterpret_cast<const char*>(CFDataGetBytePtr(data.get())); },
221#endif
222#if USE(SOUP)
223 [](const GUniquePtr<SoupBuffer>& data) { return data->data; },
224#endif
225#if USE(GLIB)
226 [](const GRefPtr<GBytes>& data) { return reinterpret_cast<const char*>(g_bytes_get_data(data.get(), nullptr)); },
227#endif
228#if USE(GSTREAMER)
229 [](const RefPtr<GstMappedBuffer>& data) { return reinterpret_cast<const char*>(data->data()); },
230#endif
231 [](const FileSystem::MappedFileData& data) { return reinterpret_cast<const char*>(data.data()); }
232 );
233 return WTF::visit(visitor, m_immutableData);
234}
235
236#if !USE(CF)
237void SharedBuffer::hintMemoryNotNeededSoon() const
238{
239}
240#endif
241
242bool SharedBuffer::operator==(const SharedBuffer& other) const
243{
244 if (this == &other)
245 return true;
246
247 if (m_size != other.m_size)
248 return false;
249
250 auto thisIterator = begin();
251 size_t thisOffset = 0;
252 auto otherIterator = other.begin();
253 size_t otherOffset = 0;
254
255 while (thisIterator != end() && otherIterator != other.end()) {
256 auto& thisSegment = thisIterator->segment.get();
257 auto& otherSegment = otherIterator->segment.get();
258
259 if (&thisSegment == &otherSegment && !thisOffset && !otherOffset) {
260 ++thisIterator;
261 ++otherIterator;
262 continue;
263 }
264
265 ASSERT(thisOffset < thisSegment.size());
266 ASSERT(otherOffset < otherSegment.size());
267
268 size_t thisRemaining = thisSegment.size() - thisOffset;
269 size_t otherRemaining = otherSegment.size() - otherOffset;
270 size_t remaining = std::min(thisRemaining, otherRemaining);
271
272 if (memcmp(thisSegment.data() + thisOffset, otherSegment.data() + otherOffset, remaining))
273 return false;
274
275 thisOffset += remaining;
276 otherOffset += remaining;
277
278 if (thisOffset == thisSegment.size()) {
279 ++thisIterator;
280 thisOffset = 0;
281 }
282
283 if (otherOffset == otherSegment.size()) {
284 ++otherIterator;
285 otherOffset = 0;
286 }
287 }
288 return true;
289}
290
291size_t SharedBuffer::DataSegment::size() const
292{
293 auto visitor = WTF::makeVisitor(
294 [](const Vector<char>& data) { return data.size(); },
295#if USE(CF)
296 [](const RetainPtr<CFDataRef>& data) { return CFDataGetLength(data.get()); },
297#endif
298#if USE(SOUP)
299 [](const GUniquePtr<SoupBuffer>& data) { return static_cast<size_t>(data->length); },
300#endif
301#if USE(GLIB)
302 [](const GRefPtr<GBytes>& data) { return g_bytes_get_size(data.get()); },
303#endif
304#if USE(GSTREAMER)
305 [](const RefPtr<GstMappedBuffer>& data) { return data->size(); },
306#endif
307 [](const FileSystem::MappedFileData& data) { return data.size(); }
308 );
309 return WTF::visit(visitor, m_immutableData);
310}
311
312SharedBufferDataView::SharedBufferDataView(Ref<SharedBuffer::DataSegment>&& segment, size_t positionWithinSegment)
313 : m_positionWithinSegment(positionWithinSegment)
314 , m_segment(WTFMove(segment))
315{
316 ASSERT(positionWithinSegment < m_segment->size());
317}
318
319size_t SharedBufferDataView::size() const
320{
321 return m_segment->size() - m_positionWithinSegment;
322}
323
324const char* SharedBufferDataView::data() const
325{
326 return m_segment->data() + m_positionWithinSegment;
327}
328
329RefPtr<SharedBuffer> utf8Buffer(const String& string)
330{
331 // Allocate a buffer big enough to hold all the characters.
332 const int length = string.length();
333 Vector<char> buffer(length * 3);
334
335 // Convert to runs of 8-bit characters.
336 char* p = buffer.data();
337 if (length) {
338 if (string.is8Bit()) {
339 const LChar* d = string.characters8();
340 if (!WTF::Unicode::convertLatin1ToUTF8(&d, d + length, &p, p + buffer.size()))
341 return nullptr;
342 } else {
343 const UChar* d = string.characters16();
344 if (WTF::Unicode::convertUTF16ToUTF8(&d, d + length, &p, p + buffer.size()) != WTF::Unicode::ConversionOK)
345 return nullptr;
346 }
347 }
348
349 buffer.shrink(p - buffer.data());
350 return SharedBuffer::create(WTFMove(buffer));
351}
352
353} // namespace WebCore
354