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