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 32 using namespace std; 33 34 namespace WebCore { 35 36 static const unsigned segmentSize = 0x1000; 37 static const unsigned segmentPositionMask = 0x0FFF; 38 39 static inline unsigned segmentIndex(unsigned position) 40 { 41 return position / segmentSize; 42 } 43 44 static inline unsigned offsetInSegment(unsigned position) 45 { 46 return position & segmentPositionMask; 47 } 48 49 static inline char* allocateSegment() 50 { 51 return static_cast<char*>(fastMalloc(segmentSize)); 52 } 53 54 static inline void freeSegment(char* p) 55 { 56 fastFree(p); 57 } 58 59 SharedBuffer::SharedBuffer() 60 : m_size(0) 61 { 62 } 63 64 SharedBuffer::SharedBuffer(const char* data, int size) 65 : m_size(0) 66 { 67 append(data, size); 68 } 69 70 SharedBuffer::SharedBuffer(const unsigned char* data, int size) 71 : m_size(0) 72 { 73 append(reinterpret_cast<const char*>(data), size); 74 } 75 76 SharedBuffer::~SharedBuffer() 77 { 78 clear(); 79 } 80 81 PassRefPtr<SharedBuffer> SharedBuffer::adoptVector(Vector<char>& vector) 82 { 83 RefPtr<SharedBuffer> buffer = create(); 84 buffer->m_buffer.swap(vector); 85 buffer->m_size = buffer->m_buffer.size(); 86 return buffer.release(); 87 } 88 89 PassRefPtr<SharedBuffer> SharedBuffer::adoptPurgeableBuffer(PurgeableBuffer* purgeableBuffer) 90 { 91 ASSERT(!purgeableBuffer->isPurgeable()); 92 RefPtr<SharedBuffer> buffer = create(); 93 buffer->m_purgeableBuffer.set(purgeableBuffer); 94 return buffer.release(); 95 } 96 97 unsigned SharedBuffer::size() const 98 { 99 if (hasPlatformData()) 100 return platformDataSize(); 101 102 if (m_purgeableBuffer) 103 return m_purgeableBuffer->size(); 104 105 return m_size; 106 } 107 108 const char* SharedBuffer::data() const 109 { 110 if (hasPlatformData()) 111 return platformData(); 112 113 if (m_purgeableBuffer) 114 return m_purgeableBuffer->data(); 115 116 return buffer().data(); 117 } 118 119 void SharedBuffer::append(const char* data, unsigned length) 120 { 121 ASSERT(!m_purgeableBuffer); 122 123 maybeTransferPlatformData(); 124 125 unsigned positionInSegment = offsetInSegment(m_size - m_buffer.size()); 126 m_size += length; 127 128 if (m_size <= segmentSize) { 129 // No need to use segments for small resource data 130 m_buffer.append(data, length); 131 return; 132 } 133 134 char* segment; 135 if (!positionInSegment) { 136 segment = allocateSegment(); 137 m_segments.append(segment); 138 } else 139 segment = m_segments.last() + positionInSegment; 140 141 unsigned segmentFreeSpace = segmentSize - positionInSegment; 142 unsigned bytesToCopy = min(length, segmentFreeSpace); 143 144 for (;;) { 145 memcpy(segment, data, bytesToCopy); 146 if (static_cast<unsigned>(length) == bytesToCopy) 147 break; 148 149 length -= bytesToCopy; 150 data += bytesToCopy; 151 segment = allocateSegment(); 152 m_segments.append(segment); 153 bytesToCopy = min(length, segmentSize); 154 } 155 } 156 157 void SharedBuffer::clear() 158 { 159 clearPlatformData(); 160 161 for (unsigned i = 0; i < m_segments.size(); ++i) 162 freeSegment(m_segments[i]); 163 164 m_segments.clear(); 165 m_size = 0; 166 167 m_buffer.clear(); 168 m_purgeableBuffer.clear(); 169 } 170 171 PassRefPtr<SharedBuffer> SharedBuffer::copy() const 172 { 173 RefPtr<SharedBuffer> clone(adoptRef(new SharedBuffer)); 174 if (m_purgeableBuffer || hasPlatformData()) { 175 clone->append(data(), size()); 176 return clone; 177 } 178 179 clone->m_size = m_size; 180 clone->m_buffer.reserveCapacity(m_size); 181 clone->m_buffer.append(m_buffer.data(), m_buffer.size()); 182 for (unsigned i = 0; i < m_segments.size(); ++i) 183 clone->m_buffer.append(m_segments[i], segmentSize); 184 return clone; 185 } 186 187 PurgeableBuffer* SharedBuffer::releasePurgeableBuffer() 188 { 189 ASSERT(hasOneRef()); 190 return m_purgeableBuffer.release(); 191 } 192 193 const Vector<char>& SharedBuffer::buffer() const 194 { 195 unsigned bufferSize = m_buffer.size(); 196 if (m_size > bufferSize) { 197 m_buffer.resize(m_size); 198 char* destination = m_buffer.data() + bufferSize; 199 unsigned bytesLeft = m_size - bufferSize; 200 for (unsigned i = 0; i < m_segments.size(); ++i) { 201 unsigned bytesToCopy = min(bytesLeft, segmentSize); 202 memcpy(destination, m_segments[i], bytesToCopy); 203 destination += bytesToCopy; 204 bytesLeft -= bytesToCopy; 205 freeSegment(m_segments[i]); 206 } 207 m_segments.clear(); 208 } 209 return m_buffer; 210 } 211 212 unsigned SharedBuffer::getSomeData(const char*& someData, unsigned position) const 213 { 214 if (hasPlatformData() || m_purgeableBuffer) { 215 someData = data() + position; 216 return size() - position; 217 } 218 219 if (position >= m_size) { 220 someData = 0; 221 return 0; 222 } 223 224 unsigned consecutiveSize = m_buffer.size(); 225 if (position < consecutiveSize) { 226 someData = m_buffer.data() + position; 227 return consecutiveSize - position; 228 } 229 230 position -= consecutiveSize; 231 unsigned segmentedSize = m_size - consecutiveSize; 232 unsigned segments = m_segments.size(); 233 unsigned segment = segmentIndex(position); 234 ASSERT(segment < segments); 235 236 unsigned positionInSegment = offsetInSegment(position); 237 someData = m_segments[segment] + positionInSegment; 238 return segment == segments - 1 ? segmentedSize - position : segmentSize - positionInSegment; 239 } 240 241 #if !PLATFORM(CF) 242 243 inline void SharedBuffer::clearPlatformData() 244 { 245 } 246 247 inline void SharedBuffer::maybeTransferPlatformData() 248 { 249 } 250 251 inline bool SharedBuffer::hasPlatformData() const 252 { 253 return false; 254 } 255 256 inline const char* SharedBuffer::platformData() const 257 { 258 ASSERT_NOT_REACHED(); 259 260 return 0; 261 } 262 263 inline unsigned SharedBuffer::platformDataSize() const 264 { 265 ASSERT_NOT_REACHED(); 266 267 return 0; 268 } 269 270 #endif 271 272 } 273