blob.h   [plain text]


/*
 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
 
//
// blob - generic extensible binary blob frame
//
// To define a new type of binary blob:
//   class MyBlob : public Blob<MyBlob, magic number> { ... }
// Pick a unique magic number (32-bit). Blobs are understood to be a MyBlob
// header possibly followed by more data as a contiguous memory area. Length
// is overall (including the header), so a fixed-size blob would have length
// sizeof(MyBlob). Both length and magic are stored in NBO.
//
// You are highly encouraged to follow these rules:
//	Store all integers in NBO, including offsets.
//  Use internal offsets to "point to" dynamically-sized elements past the
//   header (using the at<Type>(offset) method for access).
//  Don't use pointers in your blob.
// If you follow those rules, your blobs will be fully relocatable, byte-order
// independent, and generally spreading happiness around your code.
//
#ifndef _H_BLOB
#define _H_BLOB

#include <security_utilities/endian.h>
#include <security_utilities/memutils.h>
#include <security_utilities/errors.h>

namespace Security {


//
// All blobs in memory have this form.
// You can have polymorphic memory blobs (C style) using different magics numbers.
//
class BlobCore {
public:
	typedef uint32_t Offset;
	typedef uint32_t Magic;

	Magic magic() const { return mMagic; }
	size_t length() const { return mLength; }
	
	bool validateBlob(Magic magic, size_t blobLength) const
	{
		return mMagic == magic && mLength >= blobLength;
	}

	template <class T, class Offset>
	T *at(Offset offset)
	{ return LowLevelMemoryUtilities::increment<T>(this, offset); }
	
	template <class T, class Offset>
	const T *at(Offset offset) const
	{ return LowLevelMemoryUtilities::increment<const T>(this, offset); }

	void *data()						{ return this; }
	const void *data() const			{ return this; }
	void length(size_t size)			{ mLength = size; }

	BlobCore *clone() const
	{
		if (BlobCore *copy = (BlobCore *)malloc(this->length())) {
			memcpy(copy, this, this->length());
			return copy;
		}
		UnixError::throwMe(ENOMEM);
	}
	
	template <class BlobType>
	bool is() const { return magic() == BlobType::typeMagic; }
	
	static BlobCore *readBlob(std::FILE *file)	{ return readBlob(file, 0, 0); }
	static BlobCore *readBlob(int fd)			{ return readBlob(fd, 0, 0); }
	
protected:
	static BlobCore *readBlob(std::FILE *file, uint32_t magic, size_t blobSize);
	static BlobCore *readBlob(int fd, uint32_t magic, size_t blobSize);

protected:
	Endian<uint32_t> mMagic;
	Endian<uint32_t> mLength;
};


//
// Typed Blobs (BlobCores that know their real type and magic)
//
template <class BlobType, uint32_t _magic>
class Blob: public BlobCore {
public:
	void initialize(size_t size = 0)	{ mMagic = _magic; mLength = size; }
	
	static const Magic typeMagic = _magic;
	
	bool validateBlob() const
	{
		return BlobCore::validateBlob(_magic, sizeof(BlobType));
	}
	
	bool validateBlob(size_t extLength) const
	{
		return validateBlob() && mLength == extLength;
	}
	
	static BlobType *specific(BlobCore *blob)
	{
		BlobType *p = static_cast<BlobType *>(blob);
		if (p)
			assert(p->validateBlob());
		return p;
	}
	
	static const BlobType *specific(const BlobCore *blob)
	{
		const BlobType *p = static_cast<const BlobType *>(blob);
		if (p)
			assert(p->validateBlob());
		return p;
	}
	
	BlobType *clone() const
	{ assert(validateBlob()); return specific(this->BlobCore::clone());	}

	static BlobType *readBlob(int fd)
	{
		return specific(BlobCore::readBlob(fd, _magic, sizeof(BlobType)));
	}

	static BlobType *readBlob(std::FILE *file)
	{
		return specific(BlobCore::readBlob(file, _magic, sizeof(BlobType)));
	}
};


//
// A generic blob wrapped around arbitrary (flat) binary data.
// This can be used to "regularize" plain binary data, so it can be handled
// as a genuine Blob (e.g. for insertion into a SuperBlob).
//
class BlobWrapper : public Blob<BlobWrapper, 0xfade0b01> {
public:
	static BlobWrapper *alloc(size_t length);
	static BlobWrapper *alloc(const void *data, size_t length);
	
	unsigned char dataArea[0];
	
	// override data/length to point to the payload (only)
	void *data() { return dataArea; }
	const void *data() const { return dataArea; }
	size_t length() const { return BlobCore::length() - sizeof(BlobCore); }
};


}	// Security

#endif //_H_BLOB