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