hb-machinery.hh (11456B)
1 /* 2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc. 3 * Copyright © 2012,2018 Google, Inc. 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29 #ifndef HB_MACHINERY_HH 30 #define HB_MACHINERY_HH 31 32 #include "hb.hh" 33 #include "hb-blob.hh" 34 35 #include "hb-dispatch.hh" 36 #include "hb-sanitize.hh" 37 38 39 /* 40 * Casts 41 */ 42 43 /* StructAtOffset<T>(P,Ofs) returns the struct T& that is placed at memory 44 * location pointed to by P plus Ofs bytes. */ 45 template<typename Type> 46 static inline const Type& StructAtOffset(const void *P, unsigned int offset) 47 { return * reinterpret_cast<const Type*> ((const char *) P + offset); } 48 template<typename Type> 49 static inline Type& StructAtOffset(void *P, unsigned int offset) 50 { return * reinterpret_cast<Type*> ((char *) P + offset); } 51 template<typename Type> 52 static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int offset) 53 { 54 #pragma GCC diagnostic push 55 #pragma GCC diagnostic ignored "-Wcast-align" 56 return * reinterpret_cast<const Type*> ((const char *) P + offset); 57 #pragma GCC diagnostic pop 58 } 59 template<typename Type> 60 static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) 61 { 62 #pragma GCC diagnostic push 63 #pragma GCC diagnostic ignored "-Wcast-align" 64 return * reinterpret_cast<Type*> ((char *) P + offset); 65 #pragma GCC diagnostic pop 66 } 67 68 /* StructAfter<T>(X) returns the struct T& that is placed after X. 69 * Works with X of variable size also. X must implement get_size(). 70 * Any extra arguments are forwarded to get_size, so for example 71 * it can work with UnsizedArrayOf<> as well. */ 72 template <typename Type, typename TObject, typename ...Ts> 73 static inline auto StructAfter(const TObject &X, Ts... args) HB_AUTO_RETURN(( 74 StructAtOffset<Type>(&X, X.get_size(std::forward<Ts> (args)...)) 75 )) 76 /* The is_const shenanigans is to avoid ambiguous overload with gcc-8. 77 * It disables this path when TObject is const. 78 * See: https://github.com/harfbuzz/harfbuzz/issues/5429 */ 79 template <typename Type, typename TObject, typename ...Ts> 80 static inline auto StructAfter(TObject &X, Ts... args) HB_AUTO_RETURN(( 81 sizeof(int[std::is_const<TObject>::value ? -1 : +1]) > 0 ? 82 StructAtOffset<Type>(&X, X.get_size(std::forward<Ts> (args)...)) 83 : *reinterpret_cast<Type*> (0) 84 )) 85 86 87 /* 88 * Size checking 89 */ 90 91 /* Size signifying variable-sized array */ 92 #ifndef HB_VAR_ARRAY 93 #define HB_VAR_ARRAY 1 94 #endif 95 96 /* Check _assertion in a method environment */ 97 #define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ 98 void _instance_assertion_on_line_##_line () const \ 99 { static_assert ((_assertion), ""); } 100 # define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) 101 # define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) 102 103 /* Check that _code compiles in a method environment */ 104 #define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ 105 void _compiles_assertion_on_line_##_line () const \ 106 { _code; } 107 # define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) 108 # define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) 109 110 111 #define DEFINE_SIZE_STATIC(size) \ 112 DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)) \ 113 unsigned int get_size () const { return (size); } \ 114 static constexpr unsigned null_size = (size); \ 115 static constexpr unsigned min_size = (size); \ 116 static constexpr unsigned static_size = (size) 117 118 #define DEFINE_SIZE_UNION(size, _member) \ 119 DEFINE_COMPILES_ASSERTION ((void) this->u._member.static_size) \ 120 DEFINE_INSTANCE_ASSERTION (sizeof(this->u._member) == (size)) \ 121 static constexpr unsigned null_size = (size); \ 122 static constexpr unsigned min_size = (size) 123 124 #define DEFINE_SIZE_MIN(size) \ 125 DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ 126 static constexpr unsigned null_size = (size); \ 127 static constexpr unsigned min_size = (size) 128 129 #define DEFINE_SIZE_UNBOUNDED(size) \ 130 DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ 131 static constexpr unsigned min_size = (size) 132 133 #define DEFINE_SIZE_ARRAY(size, array) \ 134 DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ 135 DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \ 136 static constexpr unsigned null_size = (size); \ 137 static constexpr unsigned min_size = (size) 138 139 #define DEFINE_SIZE_ARRAY_SIZED(size, array) \ 140 unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \ 141 DEFINE_SIZE_ARRAY(size, array) 142 143 144 /* 145 * Lazy loaders. 146 * 147 * The lazy-loaders are thread-safe pointer-like objects that create their 148 * instead on-demand. They also support access to a "data" object that is 149 * necessary for creating their instance. The data object, if specified, 150 * is accessed via pointer math, located at a location before the position 151 * of the loader itself. This avoids having to store a pointer to data 152 * for every lazy-loader. Multiple lazy-loaders can access the same data. 153 */ 154 155 template <typename Data, unsigned int WheresData> 156 struct hb_data_wrapper_t 157 { 158 static_assert (WheresData > 0, ""); 159 160 Data * get_data () const 161 { return *(((Data **) (void *) this) - WheresData); } 162 163 bool is_inert () const { return !get_data (); } 164 165 template <typename Stored, typename Subclass> 166 Stored * call_create () const { return Subclass::create (get_data ()); } 167 }; 168 template <> 169 struct hb_data_wrapper_t<void, 0> 170 { 171 bool is_inert () const { return false; } 172 173 template <typename Stored, typename Funcs> 174 Stored * call_create () const { return Funcs::create (); } 175 }; 176 177 template <typename T1, typename T2> struct hb_non_void_t { typedef T1 value; }; 178 template <typename T2> struct hb_non_void_t<void, T2> { typedef T2 value; }; 179 180 template <typename Returned, 181 typename Subclass = void, 182 typename Data = void, 183 unsigned int WheresData = 0, 184 typename Stored = Returned> 185 struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData> 186 { 187 typedef typename hb_non_void_t<Subclass, 188 hb_lazy_loader_t<Returned,Subclass,Data,WheresData,Stored> 189 >::value Funcs; 190 191 hb_lazy_loader_t () = default; 192 hb_lazy_loader_t (const hb_lazy_loader_t &other) = delete; 193 194 void init0 () {} /* Init, when memory is already set to 0. No-op for us. */ 195 void init () { instance.set_relaxed (nullptr); } 196 void fini () { do_destroy (instance.get_acquire ()); init (); } 197 198 void free_instance () 199 { 200 retry: 201 Stored *p = instance.get_acquire (); 202 if (unlikely (p && !cmpexch (p, nullptr))) 203 goto retry; 204 do_destroy (p); 205 } 206 207 static void do_destroy (Stored *p) 208 { 209 if (p && p != const_cast<Stored *> (Funcs::get_null ())) 210 Funcs::destroy (p); 211 } 212 213 const Returned * operator -> () const { return get (); } 214 template <typename U = Returned, hb_enable_if (!hb_is_same (U, void))> 215 const U & operator * () const { return *get (); } 216 explicit operator bool () const 217 { return get_stored () != Funcs::get_null (); } 218 template <typename C> operator const C * () const { return get (); } 219 220 Stored * get_stored () const 221 { 222 retry: 223 Stored *p = this->instance.get_acquire (); 224 if (unlikely (!p)) 225 { 226 if (unlikely (this->is_inert ())) 227 return const_cast<Stored *> (Funcs::get_null ()); 228 229 p = this->template call_create<Stored, Funcs> (); 230 if (unlikely (!p)) 231 p = const_cast<Stored *> (Funcs::get_null ()); 232 233 if (unlikely (!cmpexch (nullptr, p))) 234 { 235 do_destroy (p); 236 goto retry; 237 } 238 } 239 return p; 240 } 241 Stored * get_stored_relaxed () const 242 { 243 return this->instance.get_relaxed (); 244 } 245 246 bool cmpexch (Stored *current, Stored *value) const 247 { 248 /* This function can only be safely called directly if no 249 * other thread is accessing. */ 250 return this->instance.cmpexch (current, value); 251 } 252 253 const Returned * get () const { return Funcs::convert (get_stored ()); } 254 const Returned * get_relaxed () const { return Funcs::convert (get_stored_relaxed ()); } 255 Returned * get_unconst () const { return const_cast<Returned *> (Funcs::convert (get_stored ())); } 256 257 /* To be possibly overloaded by subclasses. */ 258 static Returned* convert (Stored *p) { return p; } 259 260 /* By default null/init/fini the object. */ 261 static const Stored* get_null () { return &Null (Stored); } 262 static Stored *create (Data *data) 263 { 264 Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); 265 if (likely (p)) 266 p = new (p) Stored (data); 267 return p; 268 } 269 static Stored *create () 270 { 271 Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); 272 if (likely (p)) 273 p = new (p) Stored (); 274 return p; 275 } 276 static void destroy (Stored *p) 277 { 278 p->~Stored (); 279 hb_free (p); 280 } 281 282 private: 283 /* Must only have one pointer. */ 284 mutable hb_atomic_t<Stored *> instance; 285 }; 286 287 /* Specializations. */ 288 289 template <typename T, unsigned int WheresFace> 290 struct hb_face_lazy_loader_t : hb_lazy_loader_t<T, 291 hb_face_lazy_loader_t<T, WheresFace>, 292 hb_face_t, WheresFace> 293 { 294 // Hack; have them here for API parity with hb_table_lazy_loader_t 295 hb_blob_t *get_blob () { return this->get ()->get_blob (); } 296 }; 297 298 template <typename T, unsigned int WheresFace, bool core=false> 299 struct hb_table_lazy_loader_t : hb_lazy_loader_t<T, 300 hb_table_lazy_loader_t<T, WheresFace, core>, 301 hb_face_t, WheresFace, 302 hb_blob_t> 303 { 304 static hb_blob_t *create (hb_face_t *face) 305 { 306 hb_sanitize_context_t c; 307 if (core) 308 c.set_num_glyphs (0); // So we don't recurse ad infinitum, or doesn't need num_glyphs 309 return c.reference_table<T> (face); 310 } 311 static void destroy (hb_blob_t *p) { hb_blob_destroy (p); } 312 313 static const hb_blob_t *get_null () 314 { return hb_blob_get_empty (); } 315 316 static const T* convert (const hb_blob_t *blob) 317 { return blob->as<T> (); } 318 319 hb_blob_t* get_blob () const { return this->get_stored (); } 320 }; 321 322 #define HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T(Type) \ 323 template <typename Subclass> \ 324 struct hb_##Type##_funcs_lazy_loader_t : hb_lazy_loader_t<hb_##Type##_funcs_t, Subclass> \ 325 { \ 326 static void destroy (hb_##Type##_funcs_t *p) \ 327 { hb_##Type##_funcs_destroy (p); } \ 328 static const hb_##Type##_funcs_t *get_null () \ 329 { return hb_##Type##_funcs_get_empty (); } \ 330 } 331 332 HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (font); 333 HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (unicode); 334 HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (draw); 335 HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (paint); 336 337 #undef HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T 338 339 340 #endif /* HB_MACHINERY_HH */