1/*
2 * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#ifndef ArrayBuffer_h
27#define ArrayBuffer_h
28
29#include "GCIncomingRefCounted.h"
30#include "Weak.h"
31#include <wtf/PassRefPtr.h>
32#include <wtf/StdLibExtras.h>
33#include <wtf/Vector.h>
34
35namespace JSC {
36
37class ArrayBuffer;
38class ArrayBufferView;
39class JSArrayBuffer;
40
41class ArrayBufferContents {
42 WTF_MAKE_NONCOPYABLE(ArrayBufferContents);
43public:
44 ArrayBufferContents()
45 : m_data(0)
46 , m_sizeInBytes(0)
47 { }
48
49 inline ~ArrayBufferContents();
50
51 void* data() { return m_data; }
52 unsigned sizeInBytes() { return m_sizeInBytes; }
53
54private:
55 ArrayBufferContents(void* data, unsigned sizeInBytes)
56 : m_data(data)
57 , m_sizeInBytes(sizeInBytes)
58 { }
59
60 friend class ArrayBuffer;
61
62 enum InitializationPolicy {
63 ZeroInitialize,
64 DontInitialize
65 };
66
67 static inline void tryAllocate(unsigned numElements, unsigned elementByteSize, InitializationPolicy, ArrayBufferContents&);
68 void transfer(ArrayBufferContents& other)
69 {
70 ASSERT(!other.m_data);
71 other.m_data = m_data;
72 other.m_sizeInBytes = m_sizeInBytes;
73 m_data = 0;
74 m_sizeInBytes = 0;
75 }
76
77 void copyTo(ArrayBufferContents& other)
78 {
79 ASSERT(!other.m_data);
80 ArrayBufferContents::tryAllocate(m_sizeInBytes, sizeof(char), ArrayBufferContents::DontInitialize, other);
81 if (!other.m_data)
82 return;
83 memcpy(other.m_data, m_data, m_sizeInBytes);
84 other.m_sizeInBytes = m_sizeInBytes;
85 }
86
87 void* m_data;
88 unsigned m_sizeInBytes;
89};
90
91class ArrayBuffer : public GCIncomingRefCounted<ArrayBuffer> {
92public:
93 static inline PassRefPtr<ArrayBuffer> create(unsigned numElements, unsigned elementByteSize);
94 static inline PassRefPtr<ArrayBuffer> create(ArrayBuffer*);
95 static inline PassRefPtr<ArrayBuffer> create(const void* source, unsigned byteLength);
96 static inline PassRefPtr<ArrayBuffer> create(ArrayBufferContents&);
97 static inline PassRefPtr<ArrayBuffer> createAdopted(const void* data, unsigned byteLength);
98
99 // Only for use by Uint8ClampedArray::createUninitialized and SharedBuffer::createArrayBuffer.
100 static inline PassRefPtr<ArrayBuffer> createUninitialized(unsigned numElements, unsigned elementByteSize);
101
102 inline void* data();
103 inline const void* data() const;
104 inline unsigned byteLength() const;
105
106 inline size_t gcSizeEstimateInBytes() const;
107
108 inline PassRefPtr<ArrayBuffer> slice(int begin, int end) const;
109 inline PassRefPtr<ArrayBuffer> slice(int begin) const;
110
111 inline void pin();
112 inline void unpin();
113
114 JS_EXPORT_PRIVATE bool transfer(ArrayBufferContents&);
115 bool isNeutered() { return !m_contents.m_data; }
116
117 static ptrdiff_t offsetOfData() { return OBJECT_OFFSETOF(ArrayBuffer, m_contents) + OBJECT_OFFSETOF(ArrayBufferContents, m_data); }
118
119 ~ArrayBuffer() { }
120
121private:
122 static inline PassRefPtr<ArrayBuffer> create(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy);
123
124 inline ArrayBuffer(ArrayBufferContents&);
125 inline PassRefPtr<ArrayBuffer> sliceImpl(unsigned begin, unsigned end) const;
126 inline unsigned clampIndex(int index) const;
127 static inline int clampValue(int x, int left, int right);
128
129 unsigned m_pinCount;
130 ArrayBufferContents m_contents;
131
132public:
133 Weak<JSArrayBuffer> m_wrapper;
134};
135
136int ArrayBuffer::clampValue(int x, int left, int right)
137{
138 ASSERT(left <= right);
139 if (x < left)
140 x = left;
141 if (right < x)
142 x = right;
143 return x;
144}
145
146PassRefPtr<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize)
147{
148 return create(numElements, elementByteSize, ArrayBufferContents::ZeroInitialize);
149}
150
151PassRefPtr<ArrayBuffer> ArrayBuffer::create(ArrayBuffer* other)
152{
153 return ArrayBuffer::create(other->data(), other->byteLength());
154}
155
156PassRefPtr<ArrayBuffer> ArrayBuffer::create(const void* source, unsigned byteLength)
157{
158 ArrayBufferContents contents;
159 ArrayBufferContents::tryAllocate(byteLength, 1, ArrayBufferContents::ZeroInitialize, contents);
160 if (!contents.m_data)
161 return 0;
162 RefPtr<ArrayBuffer> buffer = adoptRef(new ArrayBuffer(contents));
163 ASSERT(!byteLength || source);
164 memcpy(buffer->data(), source, byteLength);
165 return buffer.release();
166}
167
168PassRefPtr<ArrayBuffer> ArrayBuffer::create(ArrayBufferContents& contents)
169{
170 return adoptRef(new ArrayBuffer(contents));
171}
172
173PassRefPtr<ArrayBuffer> ArrayBuffer::createAdopted(const void* data, unsigned byteLength)
174{
175 ArrayBufferContents contents(const_cast<void*>(data), byteLength);
176 return create(contents);
177}
178
179PassRefPtr<ArrayBuffer> ArrayBuffer::createUninitialized(unsigned numElements, unsigned elementByteSize)
180{
181 return create(numElements, elementByteSize, ArrayBufferContents::DontInitialize);
182}
183
184PassRefPtr<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy)
185{
186 ArrayBufferContents contents;
187 ArrayBufferContents::tryAllocate(numElements, elementByteSize, policy, contents);
188 if (!contents.m_data)
189 return 0;
190 return adoptRef(new ArrayBuffer(contents));
191}
192
193ArrayBuffer::ArrayBuffer(ArrayBufferContents& contents)
194 : m_pinCount(0)
195{
196 contents.transfer(m_contents);
197}
198
199void* ArrayBuffer::data()
200{
201 return m_contents.m_data;
202}
203
204const void* ArrayBuffer::data() const
205{
206 return m_contents.m_data;
207}
208
209unsigned ArrayBuffer::byteLength() const
210{
211 return m_contents.m_sizeInBytes;
212}
213
214size_t ArrayBuffer::gcSizeEstimateInBytes() const
215{
216 return sizeof(ArrayBuffer) + static_cast<size_t>(byteLength());
217}
218
219PassRefPtr<ArrayBuffer> ArrayBuffer::slice(int begin, int end) const
220{
221 return sliceImpl(clampIndex(begin), clampIndex(end));
222}
223
224PassRefPtr<ArrayBuffer> ArrayBuffer::slice(int begin) const
225{
226 return sliceImpl(clampIndex(begin), byteLength());
227}
228
229PassRefPtr<ArrayBuffer> ArrayBuffer::sliceImpl(unsigned begin, unsigned end) const
230{
231 unsigned size = begin <= end ? end - begin : 0;
232 return ArrayBuffer::create(static_cast<const char*>(data()) + begin, size);
233}
234
235unsigned ArrayBuffer::clampIndex(int index) const
236{
237 unsigned currentLength = byteLength();
238 if (index < 0)
239 index = currentLength + index;
240 return clampValue(index, 0, currentLength);
241}
242
243void ArrayBuffer::pin()
244{
245 m_pinCount++;
246}
247
248void ArrayBuffer::unpin()
249{
250 m_pinCount--;
251}
252
253void ArrayBufferContents::tryAllocate(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy, ArrayBufferContents& result)
254{
255 // Do not allow 31-bit overflow of the total size.
256 if (numElements) {
257 unsigned totalSize = numElements * elementByteSize;
258 if (totalSize / numElements != elementByteSize
259 || totalSize > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) {
260 result.m_data = 0;
261 return;
262 }
263 }
264 bool allocationSucceeded = false;
265 if (policy == ZeroInitialize)
266 allocationSucceeded = WTF::tryFastCalloc(numElements, elementByteSize).getValue(result.m_data);
267 else {
268 ASSERT(policy == DontInitialize);
269 allocationSucceeded = WTF::tryFastMalloc(numElements * elementByteSize).getValue(result.m_data);
270 }
271
272 if (allocationSucceeded) {
273 result.m_sizeInBytes = numElements * elementByteSize;
274 return;
275 }
276 result.m_data = 0;
277}
278
279ArrayBufferContents::~ArrayBufferContents()
280{
281 WTF::fastFree(m_data);
282}
283
284} // namespace JSC
285
286using JSC::ArrayBuffer;
287
288#endif // ArrayBuffer_h
289
290