michael@0: /* michael@0: * Copyright © 2012 Google, Inc. michael@0: * michael@0: * This is part of HarfBuzz, a text shaping library. michael@0: * michael@0: * Permission is hereby granted, without written agreement and without michael@0: * license or royalty fees, to use, copy, modify, and distribute this michael@0: * software and its documentation for any purpose, provided that the michael@0: * above copyright notice and the following two paragraphs appear in michael@0: * all copies of this software. michael@0: * michael@0: * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR michael@0: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES michael@0: * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN michael@0: * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH michael@0: * DAMAGE. michael@0: * michael@0: * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, michael@0: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND michael@0: * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS michael@0: * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO michael@0: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. michael@0: * michael@0: * Google Author(s): Behdad Esfahbod michael@0: */ michael@0: michael@0: #ifndef HB_SET_PRIVATE_HH michael@0: #define HB_SET_PRIVATE_HH michael@0: michael@0: #include "hb-private.hh" michael@0: #include "hb-object-private.hh" michael@0: michael@0: michael@0: /* michael@0: * The set digests here implement various "filters" that support michael@0: * "approximate member query". Conceptually these are like Bloom michael@0: * Filter and Quotient Filter, however, much smaller, faster, and michael@0: * designed to fit the requirements of our uses for glyph coverage michael@0: * queries. As a result, our filters have much higher. michael@0: */ michael@0: michael@0: template michael@0: struct hb_set_digest_lowest_bits_t michael@0: { michael@0: ASSERT_POD (); michael@0: michael@0: static const unsigned int mask_bytes = sizeof (mask_t); michael@0: static const unsigned int mask_bits = sizeof (mask_t) * 8; michael@0: static const unsigned int num_bits = 0 michael@0: + (mask_bytes >= 1 ? 3 : 0) michael@0: + (mask_bytes >= 2 ? 1 : 0) michael@0: + (mask_bytes >= 4 ? 1 : 0) michael@0: + (mask_bytes >= 8 ? 1 : 0) michael@0: + (mask_bytes >= 16? 1 : 0) michael@0: + 0; michael@0: michael@0: ASSERT_STATIC (shift < sizeof (hb_codepoint_t) * 8); michael@0: ASSERT_STATIC (shift + num_bits <= sizeof (hb_codepoint_t) * 8); michael@0: michael@0: inline void init (void) { michael@0: mask = 0; michael@0: } michael@0: michael@0: inline void add (hb_codepoint_t g) { michael@0: mask |= mask_for (g); michael@0: } michael@0: michael@0: inline void add_range (hb_codepoint_t a, hb_codepoint_t b) { michael@0: if ((b >> shift) - (a >> shift) >= mask_bits - 1) michael@0: mask = (mask_t) -1; michael@0: else { michael@0: mask_t ma = mask_for (a); michael@0: mask_t mb = mask_for (b); michael@0: mask |= mb + (mb - ma) - (mb < ma); michael@0: } michael@0: } michael@0: michael@0: inline bool may_have (hb_codepoint_t g) const { michael@0: return !!(mask & mask_for (g)); michael@0: } michael@0: michael@0: private: michael@0: michael@0: static inline mask_t mask_for (hb_codepoint_t g) { michael@0: return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); michael@0: } michael@0: mask_t mask; michael@0: }; michael@0: michael@0: template michael@0: struct hb_set_digest_combiner_t michael@0: { michael@0: ASSERT_POD (); michael@0: michael@0: inline void init (void) { michael@0: head.init (); michael@0: tail.init (); michael@0: } michael@0: michael@0: inline void add (hb_codepoint_t g) { michael@0: head.add (g); michael@0: tail.add (g); michael@0: } michael@0: michael@0: inline void add_range (hb_codepoint_t a, hb_codepoint_t b) { michael@0: head.add_range (a, b); michael@0: tail.add_range (a, b); michael@0: } michael@0: michael@0: inline bool may_have (hb_codepoint_t g) const { michael@0: return head.may_have (g) && tail.may_have (g); michael@0: } michael@0: michael@0: private: michael@0: head_t head; michael@0: tail_t tail; michael@0: }; michael@0: michael@0: michael@0: /* michael@0: * hb_set_digest_t michael@0: * michael@0: * This is a combination of digests that performs "best". michael@0: * There is not much science to this: it's a result of intuition michael@0: * and testing. michael@0: */ michael@0: typedef hb_set_digest_combiner_t michael@0: < michael@0: hb_set_digest_lowest_bits_t, michael@0: hb_set_digest_combiner_t michael@0: < michael@0: hb_set_digest_lowest_bits_t, michael@0: hb_set_digest_lowest_bits_t michael@0: > michael@0: > hb_set_digest_t; michael@0: michael@0: michael@0: michael@0: /* michael@0: * hb_set_t michael@0: */ michael@0: michael@0: michael@0: /* TODO Make this faster and memmory efficient. */ michael@0: michael@0: struct hb_set_t michael@0: { michael@0: hb_object_header_t header; michael@0: ASSERT_POD (); michael@0: bool in_error; michael@0: michael@0: inline void init (void) { michael@0: header.init (); michael@0: clear (); michael@0: } michael@0: inline void fini (void) { michael@0: } michael@0: inline void clear (void) { michael@0: if (unlikely (hb_object_is_inert (this))) michael@0: return; michael@0: in_error = false; michael@0: memset (elts, 0, sizeof elts); michael@0: } michael@0: inline bool is_empty (void) const { michael@0: for (unsigned int i = 0; i < ARRAY_LENGTH (elts); i++) michael@0: if (elts[i]) michael@0: return false; michael@0: return true; michael@0: } michael@0: inline void add (hb_codepoint_t g) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: if (unlikely (g == INVALID)) return; michael@0: if (unlikely (g > MAX_G)) return; michael@0: elt (g) |= mask (g); michael@0: } michael@0: inline void add_range (hb_codepoint_t a, hb_codepoint_t b) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: /* TODO Speedup */ michael@0: for (unsigned int i = a; i < b + 1; i++) michael@0: add (i); michael@0: } michael@0: inline void del (hb_codepoint_t g) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: if (unlikely (g > MAX_G)) return; michael@0: elt (g) &= ~mask (g); michael@0: } michael@0: inline void del_range (hb_codepoint_t a, hb_codepoint_t b) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: /* TODO Speedup */ michael@0: for (unsigned int i = a; i < b + 1; i++) michael@0: del (i); michael@0: } michael@0: inline bool has (hb_codepoint_t g) const michael@0: { michael@0: if (unlikely (g > MAX_G)) return false; michael@0: return !!(elt (g) & mask (g)); michael@0: } michael@0: inline bool intersects (hb_codepoint_t first, michael@0: hb_codepoint_t last) const michael@0: { michael@0: if (unlikely (first > MAX_G)) return false; michael@0: if (unlikely (last > MAX_G)) last = MAX_G; michael@0: unsigned int end = last + 1; michael@0: for (hb_codepoint_t i = first; i < end; i++) michael@0: if (has (i)) michael@0: return true; michael@0: return false; michael@0: } michael@0: inline bool is_equal (const hb_set_t *other) const michael@0: { michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: if (elts[i] != other->elts[i]) michael@0: return false; michael@0: return true; michael@0: } michael@0: inline void set (const hb_set_t *other) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: elts[i] = other->elts[i]; michael@0: } michael@0: inline void union_ (const hb_set_t *other) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: elts[i] |= other->elts[i]; michael@0: } michael@0: inline void intersect (const hb_set_t *other) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: elts[i] &= other->elts[i]; michael@0: } michael@0: inline void subtract (const hb_set_t *other) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: elts[i] &= ~other->elts[i]; michael@0: } michael@0: inline void symmetric_difference (const hb_set_t *other) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: elts[i] ^= other->elts[i]; michael@0: } michael@0: inline void invert (void) michael@0: { michael@0: if (unlikely (in_error)) return; michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: elts[i] = ~elts[i]; michael@0: } michael@0: inline bool next (hb_codepoint_t *codepoint) const michael@0: { michael@0: if (unlikely (*codepoint == INVALID)) { michael@0: hb_codepoint_t i = get_min (); michael@0: if (i != INVALID) { michael@0: *codepoint = i; michael@0: return true; michael@0: } else { michael@0: *codepoint = INVALID; michael@0: return false; michael@0: } michael@0: } michael@0: for (hb_codepoint_t i = *codepoint + 1; i < MAX_G + 1; i++) michael@0: if (has (i)) { michael@0: *codepoint = i; michael@0: return true; michael@0: } michael@0: *codepoint = INVALID; michael@0: return false; michael@0: } michael@0: inline bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const michael@0: { michael@0: hb_codepoint_t i; michael@0: michael@0: i = *last; michael@0: if (!next (&i)) michael@0: { michael@0: *last = *first = INVALID; michael@0: return false; michael@0: } michael@0: michael@0: *last = *first = i; michael@0: while (next (&i) && i == *last + 1) michael@0: (*last)++; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: inline unsigned int get_population (void) const michael@0: { michael@0: unsigned int count = 0; michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: count += _hb_popcount32 (elts[i]); michael@0: return count; michael@0: } michael@0: inline hb_codepoint_t get_min (void) const michael@0: { michael@0: for (unsigned int i = 0; i < ELTS; i++) michael@0: if (elts[i]) michael@0: for (unsigned int j = 0; j < BITS; j++) michael@0: if (elts[i] & (1 << j)) michael@0: return i * BITS + j; michael@0: return INVALID; michael@0: } michael@0: inline hb_codepoint_t get_max (void) const michael@0: { michael@0: for (unsigned int i = ELTS; i; i--) michael@0: if (elts[i - 1]) michael@0: for (unsigned int j = BITS; j; j--) michael@0: if (elts[i - 1] & (1 << (j - 1))) michael@0: return (i - 1) * BITS + (j - 1); michael@0: return INVALID; michael@0: } michael@0: michael@0: typedef uint32_t elt_t; michael@0: static const unsigned int MAX_G = 65536 - 1; /* XXX Fix this... */ michael@0: static const unsigned int SHIFT = 5; michael@0: static const unsigned int BITS = (1 << SHIFT); michael@0: static const unsigned int MASK = BITS - 1; michael@0: static const unsigned int ELTS = (MAX_G + 1 + (BITS - 1)) / BITS; michael@0: static const hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; michael@0: michael@0: elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; } michael@0: elt_t elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; } michael@0: elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); } michael@0: michael@0: elt_t elts[ELTS]; /* XXX 8kb */ michael@0: michael@0: ASSERT_STATIC (sizeof (elt_t) * 8 == BITS); michael@0: ASSERT_STATIC (sizeof (elt_t) * 8 * ELTS > MAX_G); michael@0: }; michael@0: michael@0: michael@0: michael@0: #endif /* HB_SET_PRIVATE_HH */