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 | |
34 | namespace WebCore { |
35 | |
36 | SharedBuffer::SharedBuffer(const char* data, size_t size) |
37 | { |
38 | append(data, size); |
39 | } |
40 | |
41 | SharedBuffer::SharedBuffer(const unsigned char* data, size_t size) |
42 | { |
43 | append(reinterpret_cast<const char*>(data), size); |
44 | } |
45 | |
46 | SharedBuffer::SharedBuffer(FileSystem::MappedFileData&& fileData) |
47 | : m_size(fileData.size()) |
48 | { |
49 | m_segments.append({0, DataSegment::create(WTFMove(fileData))}); |
50 | } |
51 | |
52 | SharedBuffer::SharedBuffer(Vector<char>&& data) |
53 | { |
54 | append(WTFMove(data)); |
55 | } |
56 | |
57 | #if USE(GSTREAMER) |
58 | Ref<SharedBuffer> SharedBuffer::create(GstMappedBuffer& mappedBuffer) |
59 | { |
60 | ASSERT(mappedBuffer.isSharable()); |
61 | return adoptRef(*new SharedBuffer(mappedBuffer)); |
62 | } |
63 | |
64 | SharedBuffer::SharedBuffer(GstMappedBuffer& mappedBuffer) |
65 | : m_size(mappedBuffer.size()) |
66 | { |
67 | m_segments.append({0, DataSegment::create(&mappedBuffer)}); |
68 | } |
69 | #endif |
70 | |
71 | RefPtr<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 | |
82 | Ref<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. |
88 | Ref<SharedBuffer> SharedBuffer::create(Vector<uint8_t>&& vector) |
89 | { |
90 | return adoptRef(*new SharedBuffer { vector.data(), vector.size() }); |
91 | } |
92 | |
93 | void 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 | |
114 | const 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 | |
123 | SharedBufferDataView 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 | |
134 | RefPtr<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 | |
153 | void 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 | |
164 | void 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 | |
174 | void 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 | |
183 | void SharedBuffer::clear() |
184 | { |
185 | m_size = 0; |
186 | m_segments.clear(); |
187 | ASSERT(internallyConsistent()); |
188 | } |
189 | |
190 | Ref<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 |
203 | bool 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 | |
215 | const 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) |
237 | void SharedBuffer::hintMemoryNotNeededSoon() const |
238 | { |
239 | } |
240 | #endif |
241 | |
242 | bool 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 | |
291 | size_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 | |
312 | SharedBufferDataView::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 | |
319 | size_t SharedBufferDataView::size() const |
320 | { |
321 | return m_segment->size() - m_positionWithinSegment; |
322 | } |
323 | |
324 | const char* SharedBufferDataView::data() const |
325 | { |
326 | return m_segment->data() + m_positionWithinSegment; |
327 | } |
328 | |
329 | RefPtr<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 | |