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