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 "platform/SharedBuffer.h" 29 30 #include "platform/PurgeableBuffer.h" 31 #include "wtf/PassOwnPtr.h" 32 #include "wtf/unicode/Unicode.h" 33 #include "wtf/unicode/UTF8.h" 34 35 #undef SHARED_BUFFER_STATS 36 37 #ifdef SHARED_BUFFER_STATS 38 #include "wtf/DataLog.h" 39 #include "wtf/MainThread.h" 40 #endif 41 42 using namespace std; 43 44 namespace WebCore { 45 46 static const unsigned segmentSize = 0x1000; 47 static const unsigned segmentPositionMask = 0x0FFF; 48 49 static inline unsigned segmentIndex(unsigned position) 50 { 51 return position / segmentSize; 52 } 53 54 static inline unsigned offsetInSegment(unsigned position) 55 { 56 return position & segmentPositionMask; 57 } 58 59 static inline char* allocateSegment() 60 { 61 return static_cast<char*>(fastMalloc(segmentSize)); 62 } 63 64 static inline void freeSegment(char* p) 65 { 66 fastFree(p); 67 } 68 69 #ifdef SHARED_BUFFER_STATS 70 71 static Mutex& statsMutex() 72 { 73 DEFINE_STATIC_LOCAL(Mutex, mutex, ()); 74 return mutex; 75 } 76 77 static HashSet<SharedBuffer*>& liveBuffers() 78 { 79 DEFINE_STATIC_LOCAL(HashSet<SharedBuffer*>, buffers, ()); 80 return buffers; 81 } 82 83 static bool sizeComparator(SharedBuffer* a, SharedBuffer* b) 84 { 85 return a->size() > b->size(); 86 } 87 88 static CString snippetForBuffer(SharedBuffer* sharedBuffer) 89 { 90 const unsigned kMaxSnippetLength = 64; 91 char* snippet = 0; 92 unsigned snippetLength = std::min(sharedBuffer->size(), kMaxSnippetLength); 93 CString result = CString::newUninitialized(snippetLength, snippet); 94 95 const char* segment; 96 unsigned offset = 0; 97 while (unsigned segmentLength = sharedBuffer->getSomeData(segment, offset)) { 98 unsigned length = std::min(segmentLength, snippetLength - offset); 99 memcpy(snippet + offset, segment, length); 100 offset += segmentLength; 101 if (offset >= snippetLength) 102 break; 103 } 104 105 for (unsigned i = 0; i < snippetLength; ++i) { 106 if (!isASCIIPrintable(snippet[i])) 107 snippet[i] = '?'; 108 } 109 110 return result; 111 } 112 113 static void printStats(void*) 114 { 115 MutexLocker locker(statsMutex()); 116 Vector<SharedBuffer*> buffers; 117 for (HashSet<SharedBuffer*>::const_iterator iter = liveBuffers().begin(); iter != liveBuffers().end(); ++iter) 118 buffers.append(*iter); 119 std::sort(buffers.begin(), buffers.end(), sizeComparator); 120 121 dataLogF("---- Shared Buffer Stats ----\n"); 122 for (size_t i = 0; i < buffers.size() && i < 64; ++i) { 123 CString snippet = snippetForBuffer(buffers[i]); 124 dataLogF("Buffer size=%8u %s\n", buffers[i]->size(), snippet.data()); 125 } 126 } 127 128 static void didCreateSharedBuffer(SharedBuffer* buffer) 129 { 130 MutexLocker locker(statsMutex()); 131 liveBuffers().add(buffer); 132 133 callOnMainThread(printStats, 0); 134 } 135 136 static void willDestroySharedBuffer(SharedBuffer* buffer) 137 { 138 MutexLocker locker(statsMutex()); 139 liveBuffers().remove(buffer); 140 } 141 142 #endif 143 144 SharedBuffer::SharedBuffer() 145 : m_size(0) 146 { 147 #ifdef SHARED_BUFFER_STATS 148 didCreateSharedBuffer(this); 149 #endif 150 } 151 152 SharedBuffer::SharedBuffer(size_t size) 153 : m_size(size) 154 , m_buffer(size) 155 { 156 #ifdef SHARED_BUFFER_STATS 157 didCreateSharedBuffer(this); 158 #endif 159 } 160 161 SharedBuffer::SharedBuffer(const char* data, int size) 162 : m_size(0) 163 { 164 // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code. 165 if (size < 0) 166 CRASH(); 167 168 append(data, size); 169 170 #ifdef SHARED_BUFFER_STATS 171 didCreateSharedBuffer(this); 172 #endif 173 } 174 175 SharedBuffer::SharedBuffer(const unsigned char* data, int size) 176 : m_size(0) 177 { 178 // FIXME: Use unsigned consistently, and check for invalid casts when calling into SharedBuffer from other code. 179 if (size < 0) 180 CRASH(); 181 182 append(reinterpret_cast<const char*>(data), size); 183 184 #ifdef SHARED_BUFFER_STATS 185 didCreateSharedBuffer(this); 186 #endif 187 } 188 189 SharedBuffer::~SharedBuffer() 190 { 191 clear(); 192 193 #ifdef SHARED_BUFFER_STATS 194 willDestroySharedBuffer(this); 195 #endif 196 } 197 198 PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector) 199 { 200 RefPtr<SharedBuffer> buffer = create(); 201 buffer->m_buffer.swap(vector); 202 buffer->m_size = buffer->m_buffer.size(); 203 return buffer.release(); 204 } 205 206 PassRefPtr<SharedBuffer> SharedBuffer::adoptPurgeableBuffer(PassOwnPtr<PurgeableBuffer> purgeableBuffer) 207 { 208 ASSERT(!purgeableBuffer->isPurgeable()); 209 RefPtr<SharedBuffer> buffer = create(); 210 buffer->m_purgeableBuffer = purgeableBuffer; 211 return buffer.release(); 212 } 213 214 unsigned SharedBuffer::size() const 215 { 216 if (m_purgeableBuffer) 217 return m_purgeableBuffer->size(); 218 219 return m_size; 220 } 221 222 void SharedBuffer::createPurgeableBuffer() const 223 { 224 if (m_purgeableBuffer) 225 return; 226 227 m_purgeableBuffer = PurgeableBuffer::create(buffer().data(), m_size); 228 } 229 230 const char* SharedBuffer::data() const 231 { 232 if (m_purgeableBuffer) 233 return m_purgeableBuffer->data(); 234 235 return this->buffer().data(); 236 } 237 238 void SharedBuffer::moveTo(Vector<char>& result) 239 { 240 ASSERT(result.isEmpty()); 241 if (m_purgeableBuffer) { 242 result.reserveCapacity(m_purgeableBuffer->size()); 243 result.append(m_purgeableBuffer->data(), m_purgeableBuffer->size()); 244 clear(); 245 return; 246 } 247 248 unsigned bufferSize = m_buffer.size(); 249 if (m_size == bufferSize) { 250 m_buffer.swap(result); 251 clear(); 252 return; 253 } 254 255 result.reserveCapacity(m_size); 256 257 const char* segment = 0; 258 unsigned position = 0; 259 while (unsigned segmentSize = getSomeData(segment, position)) { 260 result.append(segment, segmentSize); 261 position += segmentSize; 262 } 263 ASSERT(result.size() == m_size); 264 clear(); 265 return; 266 } 267 268 void SharedBuffer::append(SharedBuffer* data) 269 { 270 const char* segment; 271 size_t position = 0; 272 while (size_t length = data->getSomeData(segment, position)) { 273 append(segment, length); 274 position += length; 275 } 276 } 277 278 void SharedBuffer::append(const char* data, unsigned length) 279 { 280 ASSERT(!m_purgeableBuffer); 281 if (!length) 282 return; 283 284 unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size()); 285 m_size += length; 286 287 if (m_size <= segmentSize) { 288 // No need to use segments for small resource data 289 if (m_buffer.isEmpty()) 290 m_buffer.reserveInitialCapacity(length); 291 m_buffer.append(data, length); 292 return; 293 } 294 295 char* segment; 296 if (!positionInSegment) { 297 segment = allocateSegment(); 298 m_segments.append(segment); 299 } else 300 segment = m_segments.last() + positionInSegment; 301 302 unsigned segmentFreeSpace = segmentSize - positionInSegment; 303 unsigned bytesToCopy = min(length, segmentFreeSpace); 304 305 for (;;) { 306 memcpy(segment, data, bytesToCopy); 307 if (static_cast<unsigned>(length) == bytesToCopy) 308 break; 309 310 length -= bytesToCopy; 311 data += bytesToCopy; 312 segment = allocateSegment(); 313 m_segments.append(segment); 314 bytesToCopy = min(length, segmentSize); 315 } 316 } 317 318 void SharedBuffer::append(const Vector<char>& data) 319 { 320 append(data.data(), data.size()); 321 } 322 323 void SharedBuffer::clear() 324 { 325 for (unsigned i = 0; i < m_segments.size(); ++i) 326 freeSegment(m_segments[i]); 327 328 m_segments.clear(); 329 m_size = 0; 330 331 m_buffer.clear(); 332 m_purgeableBuffer.clear(); 333 } 334 335 PassRefPtr<SharedBuffer> SharedBuffer::copy() const 336 { 337 RefPtr<SharedBuffer> clone(adoptRef(new SharedBuffer)); 338 if (m_purgeableBuffer) { 339 clone->append(data(), size()); 340 return clone.release(); 341 } 342 343 clone->m_size = m_size; 344 clone->m_buffer.reserveCapacity(m_size); 345 clone->m_buffer.append(m_buffer.data(), m_buffer.size()); 346 if (!m_segments.isEmpty()) { 347 const char* segment = 0; 348 unsigned position = m_buffer.size(); 349 while (unsigned segmentSize = getSomeData(segment, position)) { 350 clone->m_buffer.append(segment, segmentSize); 351 position += segmentSize; 352 } 353 ASSERT(position == clone->size()); 354 } 355 return clone.release(); 356 } 357 358 PassOwnPtr<PurgeableBuffer> SharedBuffer::releasePurgeableBuffer() 359 { 360 ASSERT(hasOneRef()); 361 return m_purgeableBuffer.release(); 362 } 363 364 const Vector<char>& SharedBuffer::buffer() const 365 { 366 unsigned bufferSize = m_buffer.size(); 367 if (m_size > bufferSize) { 368 m_buffer.resize(m_size); 369 char* destination = m_buffer.data() + bufferSize; 370 unsigned bytesLeft = m_size - bufferSize; 371 for (unsigned i = 0; i < m_segments.size(); ++i) { 372 unsigned bytesToCopy = min(bytesLeft, segmentSize); 373 memcpy(destination, m_segments[i], bytesToCopy); 374 destination += bytesToCopy; 375 bytesLeft -= bytesToCopy; 376 freeSegment(m_segments[i]); 377 } 378 m_segments.clear(); 379 } 380 return m_buffer; 381 } 382 383 unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const 384 { 385 unsigned totalSize = size(); 386 if (position >= totalSize) { 387 someData = 0; 388 return 0; 389 } 390 391 if (m_purgeableBuffer) { 392 ASSERT_WITH_SECURITY_IMPLICATION(position < size()); 393 someData = data() + position; 394 return totalSize - position; 395 } 396 397 ASSERT_WITH_SECURITY_IMPLICATION(position < m_size); 398 unsigned consecutiveSize = m_buffer.size(); 399 if (position < consecutiveSize) { 400 someData = m_buffer.data() + position; 401 return consecutiveSize - position; 402 } 403 404 position -= consecutiveSize; 405 unsigned segments = m_segments.size(); 406 unsigned maxSegmentedSize = segments * segmentSize; 407 unsigned segment = segmentIndex(position); 408 if (segment < segments) { 409 unsigned bytesLeft = totalSize - consecutiveSize; 410 unsigned segmentedSize = min(maxSegmentedSize, bytesLeft); 411 412 unsigned positionInSegment = offsetInSegment(position); 413 someData = m_segments[segment] + positionInSegment; 414 return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment; 415 } 416 ASSERT_NOT_REACHED(); 417 return 0; 418 } 419 420 PassRefPtr<ArrayBuffer> SharedBuffer::getAsArrayBuffer() const 421 { 422 RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::createUninitialized(static_cast<unsigned>(size()), 1); 423 424 const char* segment = 0; 425 unsigned position = 0; 426 while (unsigned segmentSize = getSomeData(segment, position)) { 427 memcpy(static_cast<char*>(arrayBuffer->data()) + position, segment, segmentSize); 428 position += segmentSize; 429 } 430 431 if (position != arrayBuffer->byteLength()) { 432 ASSERT_NOT_REACHED(); 433 // Don't return the incomplete ArrayBuffer. 434 return 0; 435 } 436 437 return arrayBuffer; 438 } 439 440 PassRefPtr<SkData> SharedBuffer::getAsSkData() const 441 { 442 unsigned bufferLength = size(); 443 char* buffer = static_cast<char*>(sk_malloc_throw(bufferLength)); 444 const char* segment = 0; 445 unsigned position = 0; 446 while (unsigned segmentSize = getSomeData(segment, position)) { 447 memcpy(buffer + position, segment, segmentSize); 448 position += segmentSize; 449 } 450 451 if (position != bufferLength) { 452 ASSERT_NOT_REACHED(); 453 // Don't return the incomplete SkData. 454 return 0; 455 } 456 return adoptRef(SkData::NewFromMalloc(buffer, bufferLength)); 457 } 458 459 } // namespace WebCore 460