tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */