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 "SkOSFile.h"
10 #include "SkOnce.h"
11 #include "SkReadBuffer.h"
12 #include "SkStream.h"
13 #include "SkWriteBuffer.h"
14 
SkData(const void * ptr,size_t size,ReleaseProc proc,void * context)15 SkData::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.
23  *  Thus we set fPtr to point right after this.
24  */
SkData(size_t size)25 SkData::SkData(size_t size) {
26     fPtr = (char*)(this + 1);   // contents are immediately after this
27     fSize = size;
28     fReleaseProc = nullptr;
29     fReleaseProcContext = nullptr;
30 }
31 
~SkData()32 SkData::~SkData() {
33     if (fReleaseProc) {
34         fReleaseProc(fPtr, fReleaseProcContext);
35     }
36 }
37 
equals(const SkData * other) const38 bool SkData::equals(const SkData* other) const {
39     if (nullptr == other) {
40         return false;
41     }
42 
43     return fSize == other->fSize && !memcmp(fPtr, other->fPtr, fSize);
44 }
45 
copyRange(size_t offset,size_t length,void * buffer) const46 size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const {
47     size_t available = fSize;
48     if (offset >= available || 0 == length) {
49         return 0;
50     }
51     available -= offset;
52     if (length > available) {
53         length = available;
54     }
55     SkASSERT(length > 0);
56 
57     memcpy(buffer, this->bytes() + offset, length);
58     return length;
59 }
60 
PrivateNewWithCopy(const void * srcOrNull,size_t length)61 sk_sp<SkData> SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) {
62     if (0 == length) {
63         return SkData::MakeEmpty();
64     }
65 
66     const size_t actualLength = length + sizeof(SkData);
67     SkASSERT_RELEASE(length < actualLength);  // Check for overflow.
68 
69     void* storage = ::operator new (actualLength);
70     sk_sp<SkData> data(new (storage) SkData(length));
71     if (srcOrNull) {
72         memcpy(data->writable_data(), srcOrNull, length);
73     }
74     return data;
75 }
76 
DummyReleaseProc(const void *,void *)77 void SkData::DummyReleaseProc(const void*, void*) {}
78 
79 ///////////////////////////////////////////////////////////////////////////////
80 
MakeEmpty()81 sk_sp<SkData> SkData::MakeEmpty() {
82     static SkOnce once;
83     static SkData* empty;
84 
85     once([]{ empty = new SkData(nullptr, 0, nullptr, nullptr); });
86     return sk_ref_sp(empty);
87 }
88 
89 // assumes fPtr was allocated via sk_malloc
sk_free_releaseproc(const void * ptr,void *)90 static void sk_free_releaseproc(const void* ptr, void*) {
91     sk_free((void*)ptr);
92 }
93 
MakeFromMalloc(const void * data,size_t length)94 sk_sp<SkData> SkData::MakeFromMalloc(const void* data, size_t length) {
95     return sk_sp<SkData>(new SkData(data, length, sk_free_releaseproc, nullptr));
96 }
97 
MakeWithCopy(const void * src,size_t length)98 sk_sp<SkData> SkData::MakeWithCopy(const void* src, size_t length) {
99     SkASSERT(src);
100     return PrivateNewWithCopy(src, length);
101 }
102 
MakeUninitialized(size_t length)103 sk_sp<SkData> SkData::MakeUninitialized(size_t length) {
104     return PrivateNewWithCopy(nullptr, length);
105 }
106 
MakeWithProc(const void * ptr,size_t length,ReleaseProc proc,void * ctx)107 sk_sp<SkData> SkData::MakeWithProc(const void* ptr, size_t length, ReleaseProc proc, void* ctx) {
108     return sk_sp<SkData>(new SkData(ptr, length, proc, ctx));
109 }
110 
111 // assumes fPtr was allocated with sk_fmmap
sk_mmap_releaseproc(const void * addr,void * ctx)112 static void sk_mmap_releaseproc(const void* addr, void* ctx) {
113     size_t length = reinterpret_cast<size_t>(ctx);
114     sk_fmunmap(addr, length);
115 }
116 
MakeFromFILE(FILE * f)117 sk_sp<SkData> SkData::MakeFromFILE(FILE* f) {
118     size_t size;
119     void* addr = sk_fmmap(f, &size);
120     if (nullptr == addr) {
121         return nullptr;
122     }
123 
124     return SkData::MakeWithProc(addr, size, sk_mmap_releaseproc, reinterpret_cast<void*>(size));
125 }
126 
MakeFromFileName(const char path[])127 sk_sp<SkData> SkData::MakeFromFileName(const char path[]) {
128     FILE* f = path ? sk_fopen(path, kRead_SkFILE_Flag) : nullptr;
129     if (nullptr == f) {
130         return nullptr;
131     }
132     auto data = MakeFromFILE(f);
133     sk_fclose(f);
134     return data;
135 }
136 
MakeFromFD(int fd)137 sk_sp<SkData> SkData::MakeFromFD(int fd) {
138     size_t size;
139     void* addr = sk_fdmmap(fd, &size);
140     if (nullptr == addr) {
141         return nullptr;
142     }
143     return SkData::MakeWithProc(addr, size, sk_mmap_releaseproc, reinterpret_cast<void*>(size));
144 }
145 
146 // assumes context is a SkData
sk_dataref_releaseproc(const void *,void * context)147 static void sk_dataref_releaseproc(const void*, void* context) {
148     SkData* src = reinterpret_cast<SkData*>(context);
149     src->unref();
150 }
151 
MakeSubset(const SkData * src,size_t offset,size_t length)152 sk_sp<SkData> SkData::MakeSubset(const SkData* src, size_t offset, size_t length) {
153     /*
154         We could, if we wanted/need to, just make a deep copy of src's data,
155         rather than referencing it. This would duplicate the storage (of the
156         subset amount) but would possibly allow src to go out of scope sooner.
157      */
158 
159     size_t available = src->size();
160     if (offset >= available || 0 == length) {
161         return SkData::MakeEmpty();
162     }
163     available -= offset;
164     if (length > available) {
165         length = available;
166     }
167     SkASSERT(length > 0);
168 
169     src->ref(); // this will be balanced in sk_dataref_releaseproc
170     return sk_sp<SkData>(new SkData(src->bytes() + offset, length, sk_dataref_releaseproc,
171                                     const_cast<SkData*>(src)));
172 }
173 
MakeWithCString(const char cstr[])174 sk_sp<SkData> SkData::MakeWithCString(const char cstr[]) {
175     size_t size;
176     if (nullptr == cstr) {
177         cstr = "";
178         size = 1;
179     } else {
180         size = strlen(cstr) + 1;
181     }
182     return MakeWithCopy(cstr, size);
183 }
184 
185 ///////////////////////////////////////////////////////////////////////////////
186 
MakeFromStream(SkStream * stream,size_t size)187 sk_sp<SkData> SkData::MakeFromStream(SkStream* stream, size_t size) {
188     sk_sp<SkData> data(SkData::MakeUninitialized(size));
189     if (stream->read(data->writable_data(), size) != size) {
190         return nullptr;
191     }
192     return data;
193 }
194