]>
Commit | Line | Data |
---|---|---|
ebf6f434 A |
1 | // |
2 | // SuperBlob - a typed bag of Blobs | |
3 | // | |
4 | #ifndef _H_SUPERBLOB | |
5 | #define _H_SUPERBLOB | |
6 | ||
7 | #include "blob.h" | |
8 | #include <assert.h> | |
9 | #include <utility> | |
10 | #include <map> | |
11 | ||
12 | using namespace std; | |
13 | ||
14 | namespace Security { | |
15 | ||
16 | ||
17 | // | |
18 | // A SuperBlob is a Blob that contains multiple sub-Blobs of varying type. | |
19 | // The SuperBlob is contiguous and contains a directory of its sub-blobs. | |
20 | // A Maker is included. | |
21 | // | |
22 | // SuperBlobCore lets you define your own SuperBlob type. To just use a generic | |
23 | // SuperBlob, use SuperBlob<> below. | |
24 | // | |
25 | template <class _BlobType, uint32_t _magic, class _Type> | |
26 | class SuperBlobCore: public Blob<_BlobType, _magic> { | |
27 | public: | |
28 | class Maker; friend class Maker; | |
29 | ||
30 | typedef _Type Type; | |
31 | ||
32 | // echoes from parent BlobCore (the C++ type system is too restrictive here) | |
33 | typedef BlobCore::Offset Offset; | |
34 | template <class BlobType> BlobType *at(Offset offset) { return BlobCore::at<BlobType>(offset); } | |
35 | template <class BlobType> const BlobType *at(Offset offset) const { return BlobCore::at<BlobType>(offset); } | |
36 | ||
37 | void setup(size_t size, unsigned cnt) | |
38 | { this->initialize(size); this->mCount = cnt; } | |
39 | ||
40 | struct Index { | |
41 | Endian<Type> type; // type of sub-Blob | |
42 | Endian<Offset> offset; // starting offset | |
43 | }; | |
44 | ||
45 | bool validateBlob(size_t maxSize = 0) const; | |
46 | ||
47 | unsigned count() const { return mCount; } | |
48 | ||
49 | // access by index number | |
50 | Type type(unsigned n) const { assert(n < mCount); return mIndex[n].type; } | |
51 | const BlobCore *blob(unsigned n) const { assert(n < mCount); Offset off=mIndex[n].offset; return off ? at<const BlobCore>(off) : NULL; } | |
52 | ||
53 | template <class BlobType> | |
54 | const BlobType *blob(unsigned n) const { return BlobType::specific(blob(n)); } | |
55 | ||
56 | // access by index type (assumes unique types) | |
57 | const BlobCore *find(Type type) const; | |
58 | template <class BlobType> | |
59 | const BlobType *find(Type t) const { return BlobType::specific(find(t)); } | |
60 | ||
61 | private: | |
62 | Endian<uint32_t> mCount; // number of sub-Blobs following | |
63 | Index mIndex[0]; // <count> IndexSlot structures | |
64 | // followed by sub-Blobs, packed and ordered in an undefined way | |
65 | }; | |
66 | ||
67 | ||
68 | template <class _BlobType, uint32_t _magic, class _Type> | |
69 | inline bool SuperBlobCore<_BlobType, _magic, _Type>::validateBlob(size_t maxSize /* = 0 */) const | |
70 | { | |
71 | unsigned cnt = mCount; | |
72 | size_t ixLimit = sizeof(SuperBlobCore) + cnt * sizeof(Index); // end of index vector | |
73 | if (!BlobCore::validateBlob(_magic, ixLimit, maxSize)) | |
74 | return false; | |
75 | ||
76 | for (const Index *ix = mIndex + cnt - 1; ix >= mIndex; ix--) { | |
77 | Offset offset = ix->offset; | |
78 | if ( offset == 0 ) | |
79 | continue; // offset==0 means unused entry | |
80 | if (offset < ixLimit // offset not too small | |
81 | || offset + sizeof(BlobCore) > this->length() // fits Blob header (including length field) | |
82 | || offset + at<const BlobCore>(offset)->length() > this->length()) // fits entire blob | |
83 | return false; | |
84 | } | |
85 | return true; | |
86 | } | |
87 | ||
88 | ||
89 | // | |
90 | // A generic SuperBlob ready for use. You still need to specify a magic number. | |
91 | // | |
92 | template <uint32_t _magic, class _Type = uint32_t> | |
93 | class SuperBlob : public SuperBlobCore<SuperBlob<_magic, _Type>, _magic, _Type> { | |
94 | }; | |
95 | ||
96 | ||
97 | template <class _BlobType, uint32_t _magic, class _Type> | |
98 | const BlobCore *SuperBlobCore<_BlobType, _magic, _Type>::find(Type t) const | |
99 | { | |
100 | for (unsigned slot = 0; slot < mCount; slot++) { | |
101 | if (mIndex[slot].type == t) { | |
102 | uint32_t off = mIndex[slot].offset; | |
103 | if ( off == 0 ) | |
104 | return NULL; | |
105 | else | |
106 | return at<const BlobCore>(off); | |
107 | } | |
108 | } | |
109 | return NULL; // not found | |
110 | } | |
111 | ||
112 | ||
113 | // | |
114 | // A SuperBlob::Maker simply assembles multiple Blobs into a single, indexed | |
115 | // super-blob. Just add() sub-Blobs by type and call build() to get | |
116 | // the result, malloc'ed. A Maker is not reusable. | |
117 | // Maker can repeatedly make SuperBlobs from the same (cached) inputs. | |
118 | // It can also tell you how big its output will be, given established contents | |
119 | // plus (optional) additional sizes of blobs yet to come. | |
120 | // | |
121 | template <class _BlobType, uint32_t _magic, class _Type> | |
122 | class SuperBlobCore<_BlobType, _magic, _Type>::Maker { | |
123 | public: | |
124 | Maker() { } | |
125 | ||
126 | Maker(const Maker &src) | |
127 | { | |
128 | for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it) | |
129 | mPieces.insert(make_pair(it->first, it->second->clone())); | |
130 | } | |
131 | ||
132 | ~Maker() | |
133 | { | |
134 | for (typename BlobMap::iterator it = mPieces.begin(); it != mPieces.end(); ++it) | |
135 | ::free(it->second); | |
136 | } | |
137 | ||
138 | void add(Type type, BlobCore *blob); // takes ownership of blob | |
139 | void add(const _BlobType *blobs); // copies all blobs | |
140 | void add(const Maker &maker); // ditto | |
141 | ||
142 | size_t size(size_t size1 = 0, ...) const; // size with optional additional blob sizes | |
143 | _BlobType *make() const; // create (malloc) and return SuperBlob | |
144 | _BlobType *operator () () const { return make(); } | |
145 | ||
146 | private: | |
147 | typedef std::map<Type, BlobCore *> BlobMap; | |
148 | BlobMap mPieces; | |
149 | }; | |
150 | ||
151 | ||
152 | // | |
153 | // Add a Blob to a SuperBlob::Maker. | |
154 | // This takes ownership of the blob, which must have been malloc'ed. | |
155 | // Any previous value set for this Type will be freed immediately. | |
156 | // | |
157 | template <class _BlobType, uint32_t _magic, class _Type> | |
158 | void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(Type type, BlobCore *blob) | |
159 | { | |
160 | pair<typename BlobMap::iterator, bool> r = mPieces.insert(make_pair(type, blob)); | |
161 | if (!r.second) { // already there | |
162 | //secdebug("superblob", "Maker %p replaces type=%d", this, type); | |
163 | ::free(r.first->second); | |
164 | r.first->second = blob; | |
165 | } | |
166 | } | |
167 | ||
168 | template <class _BlobType, uint32_t _magic, class _Type> | |
169 | void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const _BlobType *blobs) | |
170 | { | |
171 | for (uint32_t ix = 0; ix < blobs->mCount; ix++) | |
172 | this->add(blobs->mIndex[ix].type, blobs->blob(ix)->clone()); | |
173 | } | |
174 | ||
175 | template <class _BlobType, uint32_t _magic, class _Type> | |
176 | void SuperBlobCore<_BlobType, _magic, _Type>::Maker::add(const Maker &maker) | |
177 | { | |
178 | for (typename BlobMap::const_iterator it = maker.mPieces.begin(); it != maker.mPieces.end(); ++it) | |
179 | this->add(it->first, it->second->clone()); | |
180 | } | |
181 | ||
182 | ||
183 | // | |
184 | // Calculate the size the new SuperBlob would have, given the contents of the Maker | |
185 | // so far, plus additional blobs with the sizes given. | |
186 | // | |
187 | template <class _BlobType, uint32_t _magic, class _Type> | |
188 | size_t SuperBlobCore<_BlobType, _magic, _Type>::Maker::size(size_t size1, ...) const | |
189 | { | |
190 | // count established blobs | |
191 | unsigned count = mPieces.size(); | |
192 | size_t total = 0; | |
193 | for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it) { | |
194 | if ( it->second != NULL ) | |
195 | total += (it->second->length() + 3) & (-4); // 4-byte align each element | |
196 | } | |
197 | ||
198 | // add preview blob sizes to calculation (if any) | |
199 | if (size1) { | |
200 | va_list args; | |
201 | va_start(args, size1); | |
202 | do { | |
203 | count++; | |
204 | total += size1; | |
205 | size1 = va_arg(args, size_t); | |
206 | } while (size1); | |
207 | va_end(args); | |
208 | } | |
209 | ||
210 | return sizeof(SuperBlobCore) + count * sizeof(Index) + total; | |
211 | } | |
212 | ||
213 | ||
214 | // | |
215 | // Finish SuperBlob construction and return the new, malloc'ed, SuperBlob. | |
216 | // This can be done repeatedly. | |
217 | // | |
218 | template <class _BlobType, uint32_t _magic, class _Type> | |
219 | _BlobType *SuperBlobCore<_BlobType, _magic, _Type>::Maker::make() const | |
220 | { | |
221 | Offset pc = sizeof(SuperBlobCore) + mPieces.size() * sizeof(Index); | |
222 | Offset total = size(); | |
223 | _BlobType *result = (_BlobType *)calloc(1, total); | |
224 | if (!result) | |
225 | throw ENOMEM; | |
226 | result->setup(total, mPieces.size()); | |
227 | unsigned n = 0; | |
228 | for (typename BlobMap::const_iterator it = mPieces.begin(); it != mPieces.end(); ++it) { | |
229 | result->mIndex[n].type = it->first; | |
230 | BlobCore* b = it->second; | |
231 | if ( b != NULL ) { | |
232 | result->mIndex[n].offset = pc; | |
233 | memcpy(result->template at<unsigned char>(pc), b, b->length()); | |
234 | pc += ((b->length() + 3) & (-4)); // 4-byte align each element | |
235 | } | |
236 | else { | |
237 | result->mIndex[n].offset = 0; | |
238 | } | |
239 | n++; | |
240 | } | |
241 | //secdebug("superblob", "Maker %p assembles %ld blob(s) into %p (size=%d)", | |
242 | // this, mPieces.size(), result, total); | |
243 | return result; | |
244 | } | |
245 | ||
246 | ||
247 | } // Security | |
248 | ||
249 | #endif //_H_SUPERBLOB |