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 "SkWriter32.h"
9
10SkWriter32::SkWriter32(size_t minSize, void* storage, size_t storageSize) {
11    fMinSize = minSize;
12    fSize = 0;
13    fWrittenBeforeLastBlock = 0;
14    fHead = fTail = NULL;
15
16    if (storageSize) {
17        this->reset(storage, storageSize);
18    }
19}
20
21SkWriter32::~SkWriter32() {
22    this->reset();
23}
24
25void SkWriter32::reset() {
26    Block* block = fHead;
27
28    if (this->isHeadExternallyAllocated()) {
29        SkASSERT(block);
30        // don't 'free' the first block, since it is owned by the caller
31        block = block->fNext;
32    }
33    while (block) {
34        Block* next = block->fNext;
35        sk_free(block);
36        block = next;
37    }
38
39    fSize = 0;
40    fWrittenBeforeLastBlock = 0;
41    fHead = fTail = NULL;
42}
43
44void SkWriter32::reset(void* storage, size_t storageSize) {
45    this->reset();
46
47    storageSize &= ~3;  // trunc down to multiple of 4
48    if (storageSize > 0 && SkIsAlign4((intptr_t)storage)) {
49        fHead = fTail = fExternalBlock.initFromStorage(storage, storageSize);
50    }
51}
52
53SkWriter32::Block* SkWriter32::doReserve(size_t size) {
54    SkASSERT(SkAlign4(size) == size);
55
56    Block* block = fTail;
57    SkASSERT(NULL == block || block->available() < size);
58
59    if (NULL == block) {
60        SkASSERT(NULL == fHead);
61        fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
62        SkASSERT(0 == fWrittenBeforeLastBlock);
63    } else {
64        SkASSERT(fSize > 0);
65        fWrittenBeforeLastBlock = fSize;
66
67        fTail = Block::Create(SkMax32(size, fMinSize));
68        block->fNext = fTail;
69        block = fTail;
70    }
71    return block;
72}
73
74uint32_t* SkWriter32::peek32(size_t offset) {
75    SkDEBUGCODE(this->validate();)
76
77    SkASSERT(SkAlign4(offset) == offset);
78    SkASSERT(offset <= fSize);
79
80    // try the fast case, where offset is within fTail
81    if (offset >= fWrittenBeforeLastBlock) {
82        return fTail->peek32(offset - fWrittenBeforeLastBlock);
83    }
84
85    Block* block = fHead;
86    SkASSERT(NULL != block);
87
88    while (offset >= block->fAllocatedSoFar) {
89        offset -= block->fAllocatedSoFar;
90        block = block->fNext;
91        SkASSERT(NULL != block);
92    }
93    return block->peek32(offset);
94}
95
96void SkWriter32::rewindToOffset(size_t offset) {
97    if (offset >= fSize) {
98        return;
99    }
100    if (0 == offset) {
101        this->reset();
102        return;
103    }
104
105    SkDEBUGCODE(this->validate();)
106
107    SkASSERT(SkAlign4(offset) == offset);
108    SkASSERT(offset <= fSize);
109    fSize = offset;
110
111    // Try the fast case, where offset is within fTail
112    if (offset >= fWrittenBeforeLastBlock) {
113        fTail->fAllocatedSoFar = offset - fWrittenBeforeLastBlock;
114    } else {
115        // Similar to peek32, except that we free up any following blocks.
116        // We have to re-compute fWrittenBeforeLastBlock as well.
117
118        size_t globalOffset = offset;
119        Block* block = fHead;
120        SkASSERT(NULL != block);
121        while (offset >= block->fAllocatedSoFar) {
122            offset -= block->fAllocatedSoFar;
123            block = block->fNext;
124            SkASSERT(NULL != block);
125        }
126
127        // this has to be recomputed, since we may free up fTail
128        fWrittenBeforeLastBlock = globalOffset - offset;
129
130        // update the size on the "last" block
131        block->fAllocatedSoFar = offset;
132        // end our list
133        fTail = block;
134        Block* next = block->fNext;
135        block->fNext = NULL;
136        // free up any trailing blocks
137        block = next;
138        while (block) {
139            Block* next = block->fNext;
140            sk_free(block);
141            block = next;
142        }
143    }
144    SkDEBUGCODE(this->validate();)
145}
146
147void SkWriter32::flatten(void* dst) const {
148    const Block* block = fHead;
149    SkDEBUGCODE(size_t total = 0;)
150
151    while (block) {
152        size_t allocated = block->fAllocatedSoFar;
153        memcpy(dst, block->base(), allocated);
154        dst = (char*)dst + allocated;
155        block = block->fNext;
156
157        SkDEBUGCODE(total += allocated;)
158        SkASSERT(total <= fSize);
159    }
160    SkASSERT(total == fSize);
161}
162
163uint32_t* SkWriter32::reservePad(size_t size) {
164    if (size > 0) {
165        size_t alignedSize = SkAlign4(size);
166        char* dst = (char*)this->reserve(alignedSize);
167        // Pad the last four bytes with zeroes in one step.
168        uint32_t* padding = (uint32_t*)(dst + (alignedSize - 4));
169        *padding = 0;
170        return (uint32_t*) dst;
171    }
172    return this->reserve(0);
173}
174
175void SkWriter32::writePad(const void* src, size_t size) {
176    if (size > 0) {
177        char* dst = (char*)this->reservePad(size);
178        // Copy the actual data.
179        memcpy(dst, src, size);
180    }
181}
182
183#include "SkStream.h"
184
185size_t SkWriter32::readFromStream(SkStream* stream, size_t length) {
186    char scratch[1024];
187    const size_t MAX = sizeof(scratch);
188    size_t remaining = length;
189
190    while (remaining != 0) {
191        size_t n = remaining;
192        if (n > MAX) {
193            n = MAX;
194        }
195        size_t bytes = stream->read(scratch, n);
196        this->writePad(scratch, bytes);
197        remaining -= bytes;
198        if (bytes != n) {
199            break;
200        }
201    }
202    return length - remaining;
203}
204
205bool SkWriter32::writeToStream(SkWStream* stream) {
206    const Block* block = fHead;
207    while (block) {
208        if (!stream->write(block->base(), block->fAllocatedSoFar)) {
209            return false;
210        }
211        block = block->fNext;
212    }
213    return true;
214}
215
216#ifdef SK_DEBUG
217void SkWriter32::validate() const {
218    SkASSERT(SkIsAlign4(fSize));
219
220    size_t accum = 0;
221    const Block* block = fHead;
222    while (block) {
223        SkASSERT(SkIsAlign4(block->fSizeOfBlock));
224        SkASSERT(SkIsAlign4(block->fAllocatedSoFar));
225        SkASSERT(block->fAllocatedSoFar <= block->fSizeOfBlock);
226        if (NULL == block->fNext) {
227            SkASSERT(fTail == block);
228            SkASSERT(fWrittenBeforeLastBlock == accum);
229        }
230        accum += block->fAllocatedSoFar;
231        SkASSERT(accum <= fSize);
232        block = block->fNext;
233    }
234    SkASSERT(accum == fSize);
235}
236#endif
237
238///////////////////////////////////////////////////////////////////////////////
239
240#include "SkReader32.h"
241#include "SkString.h"
242
243/*
244 *  Strings are stored as: length[4-bytes] + string_data + '\0' + pad_to_mul_4
245 */
246
247const char* SkReader32::readString(size_t* outLen) {
248    size_t len = this->readInt();
249    if (0xFFFF == len) {
250        if (outLen) {
251            *outLen = 0;
252        }
253        return NULL;
254    }
255    const void* ptr = this->peek();
256
257    // skip over teh string + '\0' and then pad to a multiple of 4
258    size_t alignedSize = SkAlign4(len + 1);
259    this->skip(alignedSize);
260
261    if (outLen) {
262        *outLen = len;
263    }
264    return (const char*)ptr;
265}
266
267size_t SkReader32::readIntoString(SkString* copy) {
268    size_t len;
269    const char* ptr = this->readString(&len);
270    if (copy) {
271        copy->set(ptr, len);
272    }
273    return len;
274}
275
276void SkWriter32::writeString(const char str[], size_t len) {
277    if (NULL == str) {
278        // We're already requiring len < 0xFFFF, so we can use that to mark NULL.
279        this->write32(0xFFFF);
280        return;
281    }
282    if ((long)len < 0) {
283        len = strlen(str);
284    }
285    this->write32(len);
286    // add 1 since we also write a terminating 0
287    size_t alignedLen = SkAlign4(len + 1);
288    char* ptr = (char*)this->reserve(alignedLen);
289    {
290        // Write the terminating 0 and fill in the rest with zeroes
291        uint32_t* padding = (uint32_t*)(ptr + (alignedLen - 4));
292        *padding = 0;
293    }
294    // Copy the string itself.
295    memcpy(ptr, str, len);
296}
297
298size_t SkWriter32::WriteStringSize(const char* str, size_t len) {
299    if ((long)len < 0) {
300        SkASSERT(str);
301        len = strlen(str);
302    }
303    const size_t lenBytes = 4;    // we use 4 bytes to record the length
304    // add 1 since we also write a terminating 0
305    return SkAlign4(lenBytes + len + 1);
306}
307