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