hb-object-private.hh revision 29c67d3f70b081766a6c01353980f457f38aeb12
1/*
2 * Copyright © 2007  Chris Wilson
3 * Copyright © 2009,2010  Red Hat, Inc.
4 * Copyright © 2011  Google, Inc.
5 *
6 *  This is part of HarfBuzz, a text shaping library.
7 *
8 * Permission is hereby granted, without written agreement and without
9 * license or royalty fees, to use, copy, modify, and distribute this
10 * software and its documentation for any purpose, provided that the
11 * above copyright notice and the following two paragraphs appear in
12 * all copies of this software.
13 *
14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * DAMAGE.
19 *
20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 *
26 * Contributor(s):
27 *	Chris Wilson <chris@chris-wilson.co.uk>
28 * Red Hat Author(s): Behdad Esfahbod
29 * Google Author(s): Behdad Esfahbod
30 */
31
32#ifndef HB_OBJECT_PRIVATE_HH
33#define HB_OBJECT_PRIVATE_HH
34
35#include "hb-private.hh"
36
37HB_BEGIN_DECLS
38
39
40/* Debug */
41
42#ifndef HB_DEBUG_OBJECT
43#define HB_DEBUG_OBJECT (HB_DEBUG+0)
44#endif
45
46
47/* user_data */
48
49HB_END_DECLS
50
51
52template <typename Type, unsigned int StaticSize>
53struct hb_static_array_t {
54
55  unsigned int len;
56  unsigned int allocated;
57  Type *array;
58  Type static_array[StaticSize];
59
60  void finish (void) { for (unsigned i = 0; i < len; i++) array[i].finish (); }
61
62  inline Type& operator [] (unsigned int i)
63  {
64    return array[i];
65  }
66
67  inline Type *push (void)
68  {
69    if (!array) {
70      array = static_array;
71      allocated = ARRAY_LENGTH (static_array);
72    }
73    if (likely (len < allocated))
74      return &array[len++];
75    /* Need to reallocate */
76    unsigned int new_allocated = allocated + (allocated >> 1) + 8;
77    Type *new_array;
78    if (array == static_array) {
79      new_array = (Type *) calloc (new_allocated, sizeof (Type));
80      if (new_array) {
81        memcpy (new_array, array, len * sizeof (Type));
82	array = new_array;
83      }
84    } else {
85      bool overflows = new_allocated >= ((unsigned int) -1) / sizeof (Type);
86      if (unlikely (overflows))
87        new_array = NULL;
88      else
89	new_array = (Type *) realloc (array, new_allocated * sizeof (Type));
90      if (new_array) {
91        free (array);
92	array = new_array;
93      }
94    }
95    if ((len < allocated))
96      return &array[len++];
97    else
98      return NULL;
99  }
100
101  inline void pop (void)
102  {
103    len--;
104    /* TODO: shrink array if needed */
105  }
106};
107
108template <typename Type>
109struct hb_array_t : hb_static_array_t<Type, 2> {};
110
111
112template <typename Key, typename Value>
113struct hb_map_t
114{
115  struct item_t {
116    Key key;
117    /* unsigned int hash; */
118    Value value;
119
120    void finish (void) { value.finish (); }
121  };
122
123  hb_array_t <item_t> items;
124
125  private:
126
127  inline item_t *find (Key key) {
128    if (unlikely (!key)) return NULL;
129    for (unsigned int i = 0; i < items.len; i++)
130      if (key == items[i].key)
131	return &items[i];
132    return NULL;
133  }
134
135  public:
136
137  inline bool set (Key   key,
138		   Value &value)
139  {
140    if (unlikely (!key)) return NULL;
141    item_t *item;
142    item = find (key);
143    if (item)
144      item->finish ();
145    else
146      item = items.push ();
147    if (unlikely (!item)) return false;
148    item->key = key;
149    item->value = value;
150    return true;
151  }
152
153  inline void unset (Key &key)
154  {
155    item_t *item;
156    item = find (key);
157    if (!item) return;
158
159    item->finish ();
160    items[items.len - 1] = *item;
161    items.pop ();
162  }
163
164  inline Value *get (Key key)
165  {
166    item_t *item = find (key);
167    return item ? &item->value : NULL;
168  }
169
170  void finish (void) { items.finish (); }
171};
172
173
174HB_BEGIN_DECLS
175
176typedef struct {
177  void *data;
178  hb_destroy_func_t destroy;
179
180  void finish (void) { if (destroy) destroy (data); }
181} hb_user_data_t;
182
183struct hb_user_data_array_t {
184
185  hb_map_t<hb_user_data_key_t *, hb_user_data_t> map;
186
187  inline bool set (hb_user_data_key_t *key,
188		   void *              data,
189		   hb_destroy_func_t   destroy)
190  {
191    if (!data && !destroy) {
192      map.unset (key);
193      return true;
194    }
195    hb_user_data_t user_data = {data, destroy};
196    return map.set (key, user_data);
197  }
198
199  inline void *get (hb_user_data_key_t *key) {
200    return map.get (key);
201  }
202
203  void finish (void) { map.finish (); }
204};
205
206
207
208typedef struct _hb_object_header_t hb_object_header_t;
209
210struct _hb_object_header_t {
211  hb_reference_count_t ref_count;
212  hb_user_data_array_t user_data;
213
214#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID}
215
216  static inline void *create (unsigned int size) {
217    hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size);
218
219    if (likely (obj))
220      obj->init ();
221
222    return obj;
223  }
224
225  inline void init (void) {
226    ref_count.init (1);
227  }
228
229  inline bool is_inert (void) const {
230    return unlikely (ref_count.is_invalid ());
231  }
232
233  inline void reference (void) {
234    if (unlikely (!this || this->is_inert ()))
235      return;
236    ref_count.inc ();
237  }
238
239  inline bool destroy (void) {
240    if (unlikely (!this || this->is_inert ()))
241      return false;
242    if (ref_count.dec () != 1)
243      return false;
244
245    user_data.finish ();
246
247    return true;
248  }
249
250  inline bool set_user_data (hb_user_data_key_t *key,
251			     void *              data,
252			     hb_destroy_func_t   destroy) {
253    if (unlikely (!this || this->is_inert ()))
254      return false;
255
256    return user_data.set (key, data, destroy);
257  }
258
259  inline void *get_user_data (hb_user_data_key_t *key) {
260    return user_data.get (key);
261  }
262
263  inline void trace (const char *function) const {
264    (void) (HB_DEBUG_OBJECT &&
265	    fprintf (stderr, "OBJECT(%p) refcount=%d %s\n",
266		     this,
267		     this ? ref_count.get () : 0,
268		     function));
269  }
270
271};
272
273
274HB_END_DECLS
275
276template <typename Type>
277static inline void hb_object_trace (const Type *obj, const char *function)
278{
279  obj->header.trace (function);
280}
281template <typename Type>
282static inline Type *hb_object_create ()
283{
284  Type *obj = (Type *) hb_object_header_t::create (sizeof (Type));
285  hb_object_trace (obj, HB_FUNC);
286  return obj;
287}
288template <typename Type>
289static inline bool hb_object_is_inert (const Type *obj)
290{
291  return unlikely (obj->header.is_inert());
292}
293template <typename Type>
294static inline Type *hb_object_reference (Type *obj)
295{
296  hb_object_trace (obj, HB_FUNC);
297  obj->header.reference ();
298  return obj;
299}
300template <typename Type>
301static inline bool hb_object_destroy (Type *obj)
302{
303  hb_object_trace (obj, HB_FUNC);
304  return obj->header.destroy ();
305}
306
307
308HB_BEGIN_DECLS
309
310
311HB_END_DECLS
312
313#endif /* HB_OBJECT_PRIVATE_HH */
314