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 "SkOncePtr.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. 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 //
SkData(size_t size)26 SkData::SkData(size_t size) {
27     fPtr = (char*)(this + 1);   // contents are immediately after this
28     fSize = size;
29     fReleaseProc = nullptr;
30     fReleaseProcContext = nullptr;
31 }
32 
~SkData()33 SkData::~SkData() {
34     if (fReleaseProc) {
35         fReleaseProc(fPtr, fReleaseProcContext);
36     }
37 }
38 
equals(const SkData * other) const39 bool SkData::equals(const SkData* other) const {
40     if (nullptr == other) {
41         return false;
42     }
43 
44     return fSize == other->fSize && !memcmp(fPtr, other->fPtr, fSize);
45 }
46 
copyRange(size_t offset,size_t length,void * buffer) const47 size_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 
PrivateNewWithCopy(const void * srcOrNull,size_t length)62 SkData* SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) {
63     if (0 == length) {
64         return SkData::NewEmpty();
65     }
66 
67     const size_t actualLength = length + sizeof(SkData);
68     if (actualLength < length) {
69         // we overflowed
70         sk_throw();
71     }
72 
73     char* storage = (char*)sk_malloc_throw(actualLength);
74     SkData* data = new (storage) SkData(length);
75     if (srcOrNull) {
76         memcpy(data->writable_data(), srcOrNull, length);
77     }
78     return data;
79 }
80 
81 ///////////////////////////////////////////////////////////////////////////////
82 
83 SK_DECLARE_STATIC_ONCE_PTR(SkData, gEmpty);
NewEmpty()84 SkData* SkData::NewEmpty() {
85     return SkRef(gEmpty.get([]{return new SkData(nullptr, 0, nullptr, nullptr); }));
86 }
87 
88 // assumes fPtr was allocated via sk_malloc
sk_free_releaseproc(const void * ptr,void *)89 static void sk_free_releaseproc(const void* ptr, void*) {
90     sk_free((void*)ptr);
91 }
92 
NewFromMalloc(const void * data,size_t length)93 SkData* SkData::NewFromMalloc(const void* data, size_t length) {
94     return new SkData(data, length, sk_free_releaseproc, nullptr);
95 }
96 
NewWithCopy(const void * src,size_t length)97 SkData* SkData::NewWithCopy(const void* src, size_t length) {
98     SkASSERT(src);
99     return PrivateNewWithCopy(src, length);
100 }
101 
NewUninitialized(size_t length)102 SkData* SkData::NewUninitialized(size_t length) {
103     return PrivateNewWithCopy(nullptr, length);
104 }
105 
NewWithProc(const void * ptr,size_t length,ReleaseProc proc,void * context)106 SkData* SkData::NewWithProc(const void* ptr, size_t length, ReleaseProc proc, void* context) {
107     return new SkData(ptr, length, proc, context);
108 }
109 
110 // assumes fPtr was allocated with sk_fmmap
sk_mmap_releaseproc(const void * addr,void * ctx)111 static void sk_mmap_releaseproc(const void* addr, void* ctx) {
112     size_t length = reinterpret_cast<size_t>(ctx);
113     sk_fmunmap(addr, length);
114 }
115 
NewFromFILE(FILE * f)116 SkData* SkData::NewFromFILE(FILE* f) {
117     size_t size;
118     void* addr = sk_fmmap(f, &size);
119     if (nullptr == addr) {
120         return nullptr;
121     }
122 
123     return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, reinterpret_cast<void*>(size));
124 }
125 
NewFromFileName(const char path[])126 SkData* SkData::NewFromFileName(const char path[]) {
127     FILE* f = path ? sk_fopen(path, kRead_SkFILE_Flag) : nullptr;
128     if (nullptr == f) {
129         return nullptr;
130     }
131     SkData* data = NewFromFILE(f);
132     sk_fclose(f);
133     return data;
134 }
135 
NewFromFD(int fd)136 SkData* SkData::NewFromFD(int fd) {
137     size_t size;
138     void* addr = sk_fdmmap(fd, &size);
139     if (nullptr == addr) {
140         return nullptr;
141     }
142 
143     return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, nullptr);
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 
NewSubset(const SkData * src,size_t offset,size_t length)152 SkData* SkData::NewSubset(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::NewEmpty();
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 new SkData(src->bytes() + offset, length, sk_dataref_releaseproc,
171                          const_cast<SkData*>(src));
172 }
173 
NewWithCString(const char cstr[])174 SkData* SkData::NewWithCString(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 NewWithCopy(cstr, size);
183 }
184 
185 ///////////////////////////////////////////////////////////////////////////////
186 
NewFromStream(SkStream * stream,size_t size)187 SkData* SkData::NewFromStream(SkStream* stream, size_t size) {
188     SkAutoDataUnref data(SkData::NewUninitialized(size));
189     if (stream->read(data->writable_data(), size) != size) {
190         return nullptr;
191     }
192     return data.detach();
193 }
194 
195