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