1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef SkDescriptor_DEFINED
18#define SkDescriptor_DEFINED
19
20#include "SkTypes.h"
21
22class SkDescriptor : SkNoncopyable {
23public:
24    static size_t ComputeOverhead(int entryCount)
25    {
26        SkASSERT(entryCount >= 0);
27        return sizeof(SkDescriptor) + entryCount * sizeof(Entry);
28    }
29
30    static SkDescriptor* Alloc(size_t length)
31    {
32        SkASSERT(SkAlign4(length) == length);
33        SkDescriptor* desc = (SkDescriptor*)sk_malloc_throw(length);
34        return desc;
35    }
36
37    static void Free(SkDescriptor* desc)
38    {
39        sk_free(desc);
40    }
41
42    void init()
43    {
44        fLength = sizeof(SkDescriptor);
45        fCount  = 0;
46    }
47
48    uint32_t getLength() const { return fLength; }
49
50    void* addEntry(uint32_t tag, uint32_t length, const void* data = NULL)
51    {
52        SkASSERT(tag);
53        SkASSERT(SkAlign4(length) == length);
54        SkASSERT(this->findEntry(tag, NULL) == NULL);
55
56        Entry*  entry = (Entry*)((char*)this + fLength);
57        entry->fTag = tag;
58        entry->fLen = length;
59        if (data)
60            memcpy(entry + 1, data, length);
61
62        fCount += 1;
63        fLength += sizeof(Entry) + length;
64        return (entry + 1); // return its data
65    }
66
67    void computeChecksum()
68    {
69        fChecksum = SkDescriptor::ComputeChecksum(this);
70    }
71
72#ifdef SK_DEBUG
73    void assertChecksum() const
74    {
75        SkASSERT(fChecksum == SkDescriptor::ComputeChecksum(this));
76    }
77#endif
78
79    const void* findEntry(uint32_t tag, uint32_t* length) const
80    {
81        const Entry* entry = (const Entry*)(this + 1);
82        int          count = fCount;
83
84        while (--count >= 0)
85        {
86            if (entry->fTag == tag)
87            {
88                if (length)
89                    *length = entry->fLen;
90                return entry + 1;
91            }
92            entry = (const Entry*)((const char*)(entry + 1) + entry->fLen);
93        }
94        return NULL;
95    }
96
97    SkDescriptor* copy() const
98    {
99        SkDescriptor* desc = SkDescriptor::Alloc(fLength);
100        memcpy(desc, this, fLength);
101        return desc;
102    }
103
104    bool equals(const SkDescriptor& other) const
105    {
106        // probe to see if we have a good checksum algo
107//        SkASSERT(a.fChecksum != b.fChecksum || memcmp(&a, &b, a.fLength) == 0);
108
109        // the first value we should look at is the checksum, so this loop
110        // should terminate early if they descriptors are different.
111        // NOTE: if we wrote a sentinel value at the end of each, we chould
112        //       remove the aa < stop test in the loop...
113        const uint32_t* aa = (const uint32_t*)this;
114        const uint32_t* bb = (const uint32_t*)&other;
115        const uint32_t* stop = (const uint32_t*)((const char*)aa + fLength);
116        do {
117            if (*aa++ != *bb++)
118                return false;
119        } while (aa < stop);
120        return true;
121    }
122
123    uint32_t getChecksum() const { return fChecksum; }
124
125    struct Entry {
126        uint32_t fTag;
127        uint32_t fLen;
128    };
129
130#ifdef SK_DEBUG
131    uint32_t getCount() const { return fCount; }
132#endif
133
134private:
135    uint32_t fChecksum;  // must be first
136    uint32_t fLength;    // must be second
137    uint32_t fCount;
138
139    static uint32_t ComputeChecksum(const SkDescriptor* desc)
140    {
141        const uint32_t*  ptr = (const uint32_t*)desc + 1; // skip the checksum field
142        const uint32_t*  stop = (const uint32_t*)((const char*)desc + desc->fLength);
143        uint32_t         sum = 0;
144
145        SkASSERT(ptr < stop);
146        do {
147            sum = (sum << 1) | (sum >> 31);
148            sum ^= *ptr++;
149        } while (ptr < stop);
150
151        return sum;
152    }
153
154    // private so no one can create one except our factories
155    SkDescriptor() {}
156};
157
158#include "SkScalerContext.h"
159
160class SkAutoDescriptor : SkNoncopyable {
161public:
162    SkAutoDescriptor(size_t size)
163    {
164        if (size <= sizeof(fStorage))
165            fDesc = (SkDescriptor*)(void*)fStorage;
166        else
167            fDesc = SkDescriptor::Alloc(size);
168    }
169    ~SkAutoDescriptor()
170    {
171        if (fDesc != (SkDescriptor*)(void*)fStorage)
172            SkDescriptor::Free(fDesc);
173    }
174    SkDescriptor* getDesc() const { return fDesc; }
175private:
176    enum {
177        kStorageSize =  sizeof(SkDescriptor)
178                        + sizeof(SkDescriptor::Entry) + sizeof(SkScalerContext::Rec)    // for rec
179                        + sizeof(SkDescriptor::Entry) + sizeof(void*)                   // for typeface
180                        + 32   // slop for occational small extras
181    };
182    SkDescriptor*   fDesc;
183    uint32_t        fStorage[(kStorageSize + 3) >> 2];
184};
185
186
187#endif
188
189