1/*
2 * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//
26// adornment - generic attached-storage facility
27//
28// Adornments are dynamic objects (subclasses of class Adornment) that can
29// be "attached" ("hung off") any object derived from Adornable. Any number
30// of Adornments can be attached to one object using different unique keys
31// (of type void *).
32//
33// Adornments can be used by a single caller to remember data "with" an Adornable
34// object. Multiple, cooperating callers can share an Adornment as long as they
35// agree on the Key.
36//
37// Memory management: All Adornments must be dynamically allocated, and will be
38// deleted when their Adornable dies. Once attached, their memory is owned by the
39// Adornable (NOT the caller). Do not get fancy with an Adornment's memory;
40// trying to share one Adornment instance between Adornables or slots is bad.
41// If you need shared storage, use a RefPointer attachment.
42//
43// Your Adornment's destructor will be called when its Adornable dies, or when
44// its slot is replaced (whichever happens sooner). So you CAN get notification
45// of an object's death by attaching an Adornment with a unique key and putting
46// code in its destructor.
47//
48// It is fairly popular for a subclass of Adornable to rename its getAdornment and
49// adornment methods as operator [], but we won't make that decision for you
50// at this level.
51//
52#ifndef _H_ADORNMENTS
53#define _H_ADORNMENTS
54
55#include <security_utilities/utilities.h>
56#include <security_utilities/threading.h>
57#include <map>
58
59
60namespace Security {
61
62class Adornable;
63
64
65//
66// An Adornment is data "hung" (stored with) an Adornable.
67//
68class Adornment {
69	friend class Adornable;
70public:
71	typedef const void *Key;
72
73	virtual ~Adornment() = 0;
74
75protected:
76	Adornment() { }
77};
78
79
80//
81// An Adornable can carry Adornments, potentially a different one for each
82// Key. We provide both a raw interface (dealing in Adornment subclasses),
83// and an attachment form that just pretends that the Adornable has extra,
84// dynamically allocated members filed under various keys.
85//
86class Adornable {
87public:
88	Adornable() : mAdornments(NULL) { }
89	~Adornable();
90
91	// adornment keys (slots)
92	typedef Adornment::Key Key;
93
94	// primitive access, raw form
95	Adornment *getAdornment(Key key) const;				// NULL if not present
96	void setAdornment(Key key, Adornment *ad);			// use NULL to delete
97	Adornment *swapAdornment(Key key, Adornment *ad);	// rotate in/out
98
99	// typed primitive access. Ad must be a unique subclass of Adornment
100	template <class Ad>
101	Ad *getAdornment(Key key) const
102	{ return safe_cast<Ad *>(getAdornment(key)); }
103
104	template <class Ad>
105	Ad *swapAdornment(Key key, Ad *ad)
106	{ return safe_cast<Ad *>(swapAdornment(key, ad)); }
107
108	// inquiries for the Adornable itself
109	bool empty() const				{ return !mAdornments || mAdornments->empty(); }
110	unsigned int size() const		{ return mAdornments ? (unsigned int)mAdornments->size() : 0; }
111	void clearAdornments();
112
113public:
114	// Adornment ref interface.  Will return an (optionally constructed) Adornment &.
115	template <class T> T &adornment(Key key);
116	template <class T, class Arg1> T &adornment(Key key, Arg1 &arg1);
117	template <class T, class Arg1, class Arg2> T &adornment(Key key, Arg1 &arg1, Arg2 &arg2);
118	template <class T, class Arg1, class Arg2, class Arg3> T &adornment(Key key, Arg1 &arg1, Arg2 &arg2, Arg3 &arg3);
119
120	// attached-value interface
121	template <class T> T &attachment(Key key);
122	template <class T, class Arg1> T &attachment(Key key, Arg1 arg1);
123
124private:
125	Adornment *&adornmentSlot(Key key);
126
127	template <class Type>
128	struct Attachment : public Adornment {
129		Attachment() { }
130		template <class Arg1> Attachment(Arg1 arg) : mValue(arg) { }
131		Type mValue;
132	};
133
134private:
135	typedef std::map<Key, Adornment *> AdornmentMap;
136	AdornmentMap *mAdornments;
137};
138
139
140//
141// Out-of-line implementations
142//
143template <class T> T &
144Adornable::adornment(Key key)
145{
146	Adornment *&slot = adornmentSlot(key);
147	if (!slot)
148		slot = new T;
149	return dynamic_cast<T &>(*slot);
150}
151
152template <class T, class Arg1> T &
153Adornable::adornment(Key key, Arg1 &arg1)
154{
155	Adornment *&slot = adornmentSlot(key);
156	if (!slot)
157		slot = new T(arg1);
158	return dynamic_cast<T &>(*slot);
159}
160
161template <class T, class Arg1, class Arg2> T &
162Adornable::adornment(Key key, Arg1 &arg1, Arg2 &arg2)
163{
164	Adornment *&slot = adornmentSlot(key);
165	if (!slot)
166		slot = new T(arg1, arg2);
167	return dynamic_cast<T &>(*slot);
168}
169
170template <class T, class Arg1, class Arg2, class Arg3> T &
171Adornable::adornment(Key key, Arg1 &arg1, Arg2 &arg2, Arg3 &arg3)
172{
173	Adornment *&slot = adornmentSlot(key);
174	if (!slot)
175		slot = new T(arg1, arg2, arg3);
176	return dynamic_cast<T &>(*slot);
177}
178
179template <class T>
180T &Adornable::attachment(Key key)
181{
182	typedef Attachment<T> Attach;
183	Adornment *&slot = adornmentSlot(key);
184	if (!slot)
185		slot = new Attach;
186	return safe_cast<Attach *>(slot)->mValue;
187}
188
189template <class T, class Arg1>
190T &Adornable::attachment(Key key, Arg1 arg1)
191{
192	typedef Attachment<T> Attach;
193	Adornment *&slot = adornmentSlot(key);
194	if (!slot)
195		slot = new Attach(arg1);
196	return safe_cast<Attach *>(slot)->mValue;
197}
198
199
200}	// end namespace Security
201
202#endif //_H_ADORNMENTS
203