cssmalloc.h   [plain text]


/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This 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.
 */


//
// cssmalloc - memory allocation in the CDSA world
//
#ifndef _H_CSSMALLOC
#define _H_CSSMALLOC

#include <Security/utilities.h>
#include <Security/cssm.h>
#include <cstring>

#ifdef _CPP_CSSMALLOC
# pragma export on
#endif

namespace Security
{

//
// An abstract allocator superclass, based on the simple malloc/realloc/free paradigm
// that CDSA loves so much. If you have an allocation strategy and want objects
// to be allocated through it, inherit from this.
//
class CssmAllocator {
public:
	virtual ~CssmAllocator();
	virtual void *malloc(size_t) throw(std::bad_alloc) = 0;
	virtual void free(void *) throw() = 0;
	virtual void *realloc(void *, size_t) throw(std::bad_alloc) = 0;

	//
	// Template versions for added expressiveness.
	// Note that the integers are element counts, not byte sizes.
	//
	template <class T> T *alloc() throw(std::bad_alloc)
	{ return reinterpret_cast<T *>(malloc(sizeof(T))); }

	template <class T> T *alloc(uint32 count) throw(std::bad_alloc)
	{ return reinterpret_cast<T *>(malloc(sizeof(T) * count)); }

	template <class T> T *alloc(T *old, uint32 count) throw(std::bad_alloc)
	{ return reinterpret_cast<T *>(realloc(old, sizeof(T) * count)); }
	
	template <class Data> CssmData alloc(const Data &source) throw(std::bad_alloc)
	{
		size_t length = source.length();
		return CssmData(memcpy(malloc(length), source.data(), length), length);
	}
	
	//
	// Happier malloc/realloc for any type. Note that these still have
	// the original (byte-sized) argument profile.
	//
	template <class T> T *malloc(size_t size) throw(std::bad_alloc)
	{ return reinterpret_cast<T *>(malloc(size)); }
	
	template <class T> T *realloc(void *addr, size_t size) throw(std::bad_alloc)
	{ return reinterpret_cast<T *>(realloc(addr, size)); }

	// All right, if you *really* have to have calloc...
	void *calloc(size_t size, unsigned int count) throw(std::bad_alloc)
	{
		void *addr = malloc(size * count);
		memset(addr, 0, size * count);
		return addr;
	}
	
	// compare CssmAllocators for identity
	virtual bool operator == (const CssmAllocator &alloc) const throw();

public:
	// allocator chooser options
	enum {
		normal = 0x0000,
		sensitive = 0x0001
	};

	static CssmAllocator &standard(uint32 request = normal);
};


//
// A POD wrapper for the memory functions structure passed around in CSSM.
//
class CssmMemoryFunctions : public PodWrapper<CssmMemoryFunctions, CSSM_MEMORY_FUNCS> {
public:
	CssmMemoryFunctions(const CSSM_MEMORY_FUNCS &funcs)
	{ *(CSSM_MEMORY_FUNCS *)this = funcs; }
	CssmMemoryFunctions() { }

	void *malloc(size_t size) const throw(std::bad_alloc);
	void free(void *mem) const throw() { free_func(mem, AllocRef); }
	void *realloc(void *mem, size_t size) const throw(std::bad_alloc);
	void *calloc(uint32 count, size_t size) const throw(std::bad_alloc);
	
	bool operator == (const CSSM_MEMORY_FUNCS &other) const throw()
	{ return !memcmp(this, &other, sizeof(*this)); }
};

inline void *CssmMemoryFunctions::malloc(size_t size) const throw(std::bad_alloc)
{
	if (void *addr = malloc_func(size, AllocRef))
		return addr;
	throw std::bad_alloc();
}

inline void *CssmMemoryFunctions::calloc(uint32 count, size_t size) const throw(std::bad_alloc)
{
	if (void *addr = calloc_func(count, size, AllocRef))
		return addr;
	throw std::bad_alloc();
}

inline void *CssmMemoryFunctions::realloc(void *mem, size_t size) const throw(std::bad_alloc)
{
	if (void *addr = realloc_func(mem, size, AllocRef))
		return addr;
	throw std::bad_alloc();
}


//
// A CssmAllocator based on CssmMemoryFunctions
//
class CssmMemoryFunctionsAllocator : public CssmAllocator {
public:
	CssmMemoryFunctionsAllocator(const CssmMemoryFunctions &memFuncs) : functions(memFuncs) { }
	
	void *malloc(size_t size) throw(std::bad_alloc);
	void free(void *addr) throw();
	void *realloc(void *addr, size_t size) throw(std::bad_alloc);
	
	operator const CssmMemoryFunctions & () const throw() { return functions; }

private:
	const CssmMemoryFunctions functions;
};

} // end namespace Security

//
// Global C++ allocation hooks to use CssmAllocators
//
inline void *operator new (size_t size, CssmAllocator &allocator) throw(std::bad_alloc)
{ return allocator.malloc(size); }

//
// You'd think that this is operator delete(const T *, CssmAllocator &), but you'd
// be wrong. Specialized operator delete is only called during constructor cleanup.
// Use this to cleanly destroy things.
//
template <class T>
inline void destroy(T *obj, CssmAllocator &alloc) throw()
{
	obj->~T();
	alloc.free(obj);
}

// untyped (release memory only, no destructor call)
inline void destroy(void *obj, CssmAllocator &alloc) throw()
{
	alloc.free(obj);
}

namespace Security
{

//
// A MemoryFunctions object based on a CssmAllocator.
// Note that we don't copy the CssmAllocator object. It needs to live (at least)
// as long as any CssmAllocatorMemoryFunctions object based on it.
//
class CssmAllocatorMemoryFunctions : public CssmMemoryFunctions {
public:
	CssmAllocatorMemoryFunctions(CssmAllocator &alloc);
	CssmAllocatorMemoryFunctions() { /*IFDEBUG(*/ AllocRef = NULL /*)*/ ; }	// later assignment req'd
	
private:
	static void *relayMalloc(size_t size, void *ref) throw(std::bad_alloc);
	static void relayFree(void *mem, void *ref) throw();
	static void *relayRealloc(void *mem, size_t size, void *ref) throw(std::bad_alloc);
	static void *relayCalloc(uint32 count, size_t size, void *ref) throw(std::bad_alloc);

	static CssmAllocator &allocator(void *ref) throw()
	{ return *reinterpret_cast<CssmAllocator *>(ref); }
};


//
// A mixin class to automagically manage your allocator.
// To allow allocation (of your object) from any instance of CssmAllocator,
// inherit from CssmHeap. Your users can then create heap instances of your thing by
//		new (an-allocator) YourClass(...)
// or (still)
//		new YourClass(...)
// for the default allocation source. The beauty is that when someone does a
//		delete pointer-to-your-instance
// then the magic fairies will find the allocator that created the object and ask it
// to free the memory (by calling its free() method).
// The price of all that glory is memory overhead - typically one pointer per object.
//
class CssmHeap {
public:    
	void *operator new (size_t size, CssmAllocator *alloc = NULL) throw(std::bad_alloc);
	void operator delete (void *addr, size_t size) throw();
	void operator delete (void *addr, size_t size, CssmAllocator *alloc) throw();
};


//
// Here is a version of auto_ptr that works with CssmAllocators. It is designed
// to be pretty much a drop-in replacement. It requires an allocator as a constructor
// argument, of course.
// Note that CssmAutoPtr<void> is perfectly valid, unlike its auto_ptr look-alike.
// You can't dereference it, naturally.
//
template <class T>
class CssmAutoPtr {
public:
	CssmAllocator &allocator;

	CssmAutoPtr(CssmAllocator &alloc = CssmAllocator::standard())
	: allocator(alloc), mine(NULL) { }
	CssmAutoPtr(CssmAllocator &alloc, T *p)
	: allocator(alloc), mine(p) { }
	CssmAutoPtr(T *p)
	: allocator(CssmAllocator::standard()), mine(p) { }
	template <class T1> CssmAutoPtr(CssmAutoPtr<T1> &src)
	: allocator(src.allocator), mine(src.release()) { }
	template <class T1> CssmAutoPtr(CssmAllocator &alloc, CssmAutoPtr<T1> &src)
	: allocator(alloc), mine(rc.release()) { assert(allocator == src.allocator); }
	
	~CssmAutoPtr()				{ allocator.free(mine); }
	
	T *get() const throw()		{ return mine; }
	T *release()				{ T *result = mine; mine = NULL; return result; }
	void reset()				{ allocator.free(mine); mine = NULL; }

	operator T * () const		{ return mine; }
	T *operator -> () const		{ return mine; }
	T &operator * () const		{ assert(mine); return *mine; }

private:
	T *mine;
};

// specialization for void (i.e. void *), omitting the troublesome dereferencing ops.
template <>
class CssmAutoPtr<void> {
public:
	CssmAllocator &allocator;

	CssmAutoPtr(CssmAllocator &alloc) : allocator(alloc), mine(NULL) { }
	CssmAutoPtr(CssmAllocator &alloc, void *p) : allocator(alloc), mine(p) { }
	template <class T1> CssmAutoPtr(CssmAutoPtr<T1> &src)
	: allocator(src.allocator), mine(src.release()) { }
	template <class T1> CssmAutoPtr(CssmAllocator &alloc, CssmAutoPtr<T1> &src)
	: allocator(alloc), mine(rc.release()) { assert(allocator == src.allocator); }
	
	~CssmAutoPtr()				{ destroy(mine, allocator); }
	
	void *get() throw()		{ return mine; }
	void *release()				{ void *result = mine; mine = NULL; return result; }
	void reset()				{ allocator.free(mine); mine = NULL; }

private:
	void *mine;
};


//
// Convenience forms of CssmAutoPtr that automatically make their (initial) object.
//
template <class T>
class CssmNewAutoPtr : public CssmAutoPtr<T> {
public:
	CssmNewAutoPtr(CssmAllocator &alloc = CssmAllocator::standard())
	: CssmAutoPtr<T>(alloc, new(alloc) T) { }
	
	template <class A1>
	CssmNewAutoPtr(CssmAllocator &alloc, A1 &arg1) : CssmAutoPtr<T>(alloc, new(alloc) T(arg1)) { }
	template <class A1>
	CssmNewAutoPtr(CssmAllocator &alloc, const A1 &arg1)
	: CssmAutoPtr<T>(alloc, new(alloc) T(arg1)) { }
	
	template <class A1, class A2>
	CssmNewAutoPtr(CssmAllocator &alloc, A1 &arg1, A2 &arg2)
	: CssmAutoPtr<T>(alloc, new(alloc) T(arg1, arg2)) { }
	template <class A1, class A2>
	CssmNewAutoPtr(CssmAllocator &alloc, const A1 &arg1, A2 &arg2)
	: CssmAutoPtr<T>(alloc, new(alloc) T(arg1, arg2)) { }
	template <class A1, class A2>
	CssmNewAutoPtr(CssmAllocator &alloc, A1 &arg1, const A2 &arg2)
	: CssmAutoPtr<T>(alloc, new(alloc) T(arg1, arg2)) { }
	template <class A1, class A2>
	CssmNewAutoPtr(CssmAllocator &alloc, const A1 &arg1, const A2 &arg2)
	: CssmAutoPtr<T>(alloc, new(alloc) T(arg1, arg2)) { }
};


//
// A generic helper for the unhappily ubiquitous CSSM-style
// (count, pointer-to-array) style of arrays.
//
template <class Base, class Wrapper = Base>
class CssmVector {
public:
    CssmVector(uint32 &cnt, Base * &vec, CssmAllocator &alloc = CssmAllocator::standard())
        : count(cnt), vector(reinterpret_cast<Wrapper * &>(vec)),
          allocator(alloc)
    {
        count = 0;
        vector = NULL;
    }
    
    ~CssmVector()	{ allocator.free(vector); }
        
    uint32 &count;
    Wrapper * &vector;
    CssmAllocator &allocator;

public:
    Wrapper &operator [] (uint32 ix)
    { assert(ix < count); return vector[ix]; }
    
    void operator += (const Wrapper &add)
    {
        vector = reinterpret_cast<Wrapper *>(allocator.realloc(vector, (count + 1) * sizeof(Wrapper)));
        //@@@???compiler bug??? vector = allocator.alloc<Wrapper>(vector, count + 1);
        vector[count++] = add;
    }
};


} // end namespace Security

#ifdef _CPP_CSSMALLOC
# pragma export off
#endif

#endif //_H_CSSMALLOC