1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkData.h"
9#include "SkLazyPtr.h"
10#include "SkOSFile.h"
11#include "SkReadBuffer.h"
12#include "SkStream.h"
13#include "SkWriteBuffer.h"
14
15SkData::SkData(const void* ptr, size_t size, ReleaseProc proc, void* context) {
16    fPtr = const_cast<void*>(ptr);
17    fSize = size;
18    fReleaseProc = proc;
19    fReleaseProcContext = context;
20}
21
22// This constructor means we are inline with our fPtr's contents. Thus we set fPtr
23// to point right after this. We also set our releaseproc to sk_inplace_sentinel_releaseproc,
24// since we need to handle "delete" ourselves. See internal_displose().
25//
26SkData::SkData(size_t size) {
27    fPtr = (char*)(this + 1);   // contents are immediately after this
28    fSize = size;
29    fReleaseProc = NULL;
30    fReleaseProcContext = NULL;
31}
32
33SkData::~SkData() {
34    if (fReleaseProc) {
35        fReleaseProc(fPtr, fSize, fReleaseProcContext);
36    }
37}
38
39bool SkData::equals(const SkData* other) const {
40    if (NULL == other) {
41        return false;
42    }
43
44    return fSize == other->fSize && !memcmp(fPtr, other->fPtr, fSize);
45}
46
47size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const {
48    size_t available = fSize;
49    if (offset >= available || 0 == length) {
50        return 0;
51    }
52    available -= offset;
53    if (length > available) {
54        length = available;
55    }
56    SkASSERT(length > 0);
57
58    memcpy(buffer, this->bytes() + offset, length);
59    return length;
60}
61
62SkData* SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) {
63    if (0 == length) {
64        return SkData::NewEmpty();
65    }
66    char* storage = (char*)sk_malloc_throw(sizeof(SkData) + length);
67    SkData* data = new (storage) SkData(length);
68    if (srcOrNull) {
69        memcpy(data->writable_data(), srcOrNull, length);
70    }
71    return data;
72}
73
74///////////////////////////////////////////////////////////////////////////////
75
76// As a template argument these must have external linkage.
77SkData* sk_new_empty_data() { return new SkData(NULL, 0, NULL, NULL); }
78namespace { void sk_unref_data(SkData* ptr) { return SkSafeUnref(ptr); } }
79
80SK_DECLARE_STATIC_LAZY_PTR(SkData, empty, sk_new_empty_data, sk_unref_data);
81
82SkData* SkData::NewEmpty() {
83    return SkRef(empty.get());
84}
85
86// assumes fPtr was allocated via sk_malloc
87static void sk_free_releaseproc(const void* ptr, size_t, void*) {
88    sk_free((void*)ptr);
89}
90
91SkData* SkData::NewFromMalloc(const void* data, size_t length) {
92    return new SkData(data, length, sk_free_releaseproc, NULL);
93}
94
95SkData* SkData::NewWithCopy(const void* src, size_t length) {
96    SkASSERT(src);
97    return PrivateNewWithCopy(src, length);
98}
99
100SkData* SkData::NewUninitialized(size_t length) {
101    return PrivateNewWithCopy(NULL, length);
102}
103
104SkData* SkData::NewWithProc(const void* data, size_t length,
105                            ReleaseProc proc, void* context) {
106    return new SkData(data, length, proc, context);
107}
108
109// assumes fPtr was allocated with sk_fmmap
110static void sk_mmap_releaseproc(const void* addr, size_t length, void*) {
111    sk_fmunmap(addr, length);
112}
113
114SkData* SkData::NewFromFILE(SkFILE* f) {
115    size_t size;
116    void* addr = sk_fmmap(f, &size);
117    if (NULL == addr) {
118        return NULL;
119    }
120
121    return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, NULL);
122}
123
124SkData* SkData::NewFromFileName(const char path[]) {
125    SkFILE* f = path ? sk_fopen(path, kRead_SkFILE_Flag) : NULL;
126    if (NULL == f) {
127        return NULL;
128    }
129    SkData* data = NewFromFILE(f);
130    sk_fclose(f);
131    return data;
132}
133
134SkData* SkData::NewFromFD(int fd) {
135    size_t size;
136    void* addr = sk_fdmmap(fd, &size);
137    if (NULL == addr) {
138        return NULL;
139    }
140
141    return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, NULL);
142}
143
144// assumes context is a SkData
145static void sk_dataref_releaseproc(const void*, size_t, void* context) {
146    SkData* src = reinterpret_cast<SkData*>(context);
147    src->unref();
148}
149
150SkData* SkData::NewSubset(const SkData* src, size_t offset, size_t length) {
151    /*
152        We could, if we wanted/need to, just make a deep copy of src's data,
153        rather than referencing it. This would duplicate the storage (of the
154        subset amount) but would possibly allow src to go out of scope sooner.
155     */
156
157    size_t available = src->size();
158    if (offset >= available || 0 == length) {
159        return SkData::NewEmpty();
160    }
161    available -= offset;
162    if (length > available) {
163        length = available;
164    }
165    SkASSERT(length > 0);
166
167    src->ref(); // this will be balanced in sk_dataref_releaseproc
168    return new SkData(src->bytes() + offset, length, sk_dataref_releaseproc,
169                         const_cast<SkData*>(src));
170}
171
172SkData* SkData::NewWithCString(const char cstr[]) {
173    size_t size;
174    if (NULL == cstr) {
175        cstr = "";
176        size = 1;
177    } else {
178        size = strlen(cstr) + 1;
179    }
180    return NewWithCopy(cstr, size);
181}
182
183///////////////////////////////////////////////////////////////////////////////
184
185SkData* SkData::NewFromStream(SkStream* stream, size_t size) {
186    SkAutoDataUnref data(SkData::NewUninitialized(size));
187    if (stream->read(data->writable_data(), size) != size) {
188        return NULL;
189    }
190    return data.detach();
191}
192
193