Coverage Report

Created: 2026-09-28 08:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh
Line
Count
Source
1
#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
2
#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
3
4
#include "PairSet.hh"
5
6
namespace OT {
7
namespace Layout {
8
namespace GPOS_impl {
9
10
11
template <typename Types>
12
struct PairPosFormat1_3
13
{
14
  using PairSet = GPOS_impl::PairSet<Types>;
15
  using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
16
17
  protected:
18
  HBUINT16      format;                 /* Format identifier--format = 1 */
19
  typename Types::template OffsetTo<Coverage>
20
                coverage;               /* Offset to Coverage table--from
21
                                         * beginning of subtable */
22
  ValueFormat   valueFormat[2];         /* [0] Defines the types of data in
23
                                         * ValueRecord1--for the first glyph
24
                                         * in the pair--may be zero (0) */
25
                                        /* [1] Defines the types of data in
26
                                         * ValueRecord2--for the second glyph
27
                                         * in the pair--may be zero (0) */
28
  Array16Of<typename Types::template OffsetTo<PairSet>>
29
                pairSet;                /* Array of PairSet tables
30
                                         * ordered by Coverage Index */
31
  public:
32
  DEFINE_SIZE_ARRAY (8 + Types::size, pairSet);
33
34
  bool sanitize (hb_sanitize_context_t *c) const
35
0
  {
36
0
    TRACE_SANITIZE (this);
37
38
0
    if (!c->check_struct (this)) return_trace (false);
39
0
    hb_barrier ();
40
41
0
    unsigned int len1 = valueFormat[0].get_len ();
42
0
    unsigned int len2 = valueFormat[1].get_len ();
43
0
    typename PairSet::sanitize_closure_t closure =
44
0
    {
45
0
      valueFormat,
46
0
      len1,
47
0
      PairSet::get_size (len1, len2)
48
0
    };
49
50
0
    return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
51
0
  }
52
53
  bool intersects (const hb_set_t *glyphs) const
54
0
  {
55
0
    auto &cov = this+coverage;
56
57
0
    if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len))
58
0
    {
59
0
      for (hb_codepoint_t g : glyphs->iter())
60
0
      {
61
0
  unsigned i = cov.get_coverage (g);
62
0
  if ((this+pairSet[i]).intersects (glyphs, valueFormat))
63
0
    return true;
64
0
      }
65
0
      return false;
66
0
    }
67
68
0
    return
69
0
    + hb_zip (cov, pairSet)
70
0
    | hb_filter (*glyphs, hb_first)
71
0
    | hb_map (hb_second)
72
0
    | hb_map ([glyphs, this] (const typename Types::template OffsetTo<PairSet> &_)
73
0
              { return (this+_).intersects (glyphs, valueFormat); })
74
0
    | hb_any
75
0
    ;
76
0
  }
77
78
0
  void closure_lookups (hb_closure_lookups_context_t *c) const {}
79
  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
80
0
  {
81
0
    if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return;
82
83
0
    auto it =
84
0
    + hb_zip (this+coverage, pairSet)
85
0
    | hb_filter (c->glyph_set, hb_first)
86
0
    | hb_map (hb_second)
87
0
    ;
88
89
0
    if (!it) return;
90
0
    + it
91
0
    | hb_map (hb_add (this))
92
0
    | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); })
93
0
    ;
94
0
  }
95
96
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
97
0
  {
98
0
    if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
99
0
    unsigned int count = pairSet.len;
100
0
    for (unsigned int i = 0; i < count; i++)
101
0
      (this+pairSet[i]).collect_glyphs (c, valueFormat);
102
0
  }
103
104
  void collect_second_glyphs (hb_set_digest_t *digest) const
105
0
  {
106
0
    unsigned count = pairSet.len;
107
0
    for (unsigned i = 0; i < count; i++)
108
0
      (this+pairSet[i]).collect_second_glyphs (digest, valueFormat);
109
0
  }
110
111
0
  const Coverage &get_coverage () const { return this+coverage; }
112
113
  struct external_cache_t
114
  {
115
    hb_ot_layout_mapping_cache_t coverage;
116
    hb_set_digest_t pair_sets[HB_VAR_ARRAY];
117
  };
118
  void *external_cache_create () const
119
0
  {
120
0
    unsigned count = pairSet.len;
121
0
    unsigned size = sizeof (external_cache_t) -
122
0
        HB_VAR_ARRAY * sizeof (hb_set_digest_t) +
123
0
        count * sizeof (hb_set_digest_t);
124
0
    external_cache_t *cache = (external_cache_t *) hb_malloc (size);
125
0
    if (likely (cache))
126
0
    {
127
0
      cache->coverage.clear ();
128
0
      for (unsigned i = 0; i < count; i++)
129
0
      {
130
0
  cache->pair_sets[i].init ();
131
0
  (this+pairSet[i]).collect_second_glyphs (&cache->pair_sets[i], valueFormat);
132
0
      }
133
0
    }
134
0
    return cache;
135
0
  }
136
137
  bool apply (hb_ot_apply_context_t *c, void *external_cache) const
138
0
  {
139
0
    TRACE_APPLY (this);
140
141
0
    hb_buffer_t *buffer = c->buffer;
142
143
0
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
144
0
    external_cache_t *cache = (external_cache_t *) external_cache;
145
0
    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
146
#else
147
    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
148
#endif
149
0
    if (index == NOT_COVERED) return_trace (false);
150
151
0
    auto &skippy_iter = c->iter_input;
152
0
    skippy_iter.reset_fast (buffer->idx);
153
0
    unsigned unsafe_to;
154
0
    if (unlikely (!skippy_iter.next (&unsafe_to)))
155
0
    {
156
0
      buffer->unsafe_to_concat (buffer->idx, unsafe_to);
157
0
      return_trace (false);
158
0
    }
159
160
0
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
161
0
    if (cache &&
162
0
  index < pairSet.len &&
163
0
  !cache->pair_sets[index].may_have (buffer->info[skippy_iter.idx].codepoint))
164
0
    {
165
0
      buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
166
0
      return_trace (false);
167
0
    }
168
0
#endif
169
170
0
    return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
171
0
  }
172
173
  bool subset (hb_subset_context_t *c) const
174
0
  {
175
0
    TRACE_SUBSET (this);
176
177
0
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
178
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
179
180
0
    auto *out = c->serializer->start_embed (*this);
181
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
182
0
    out->format = format;
183
184
0
    hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat[0], valueFormat[1]);
185
186
0
    if (c->plan->normalized_coords)
187
0
    {
188
      /* all device flags will be dropped when full instancing, no need to strip
189
       * hints, also do not strip emtpy cause we don't compute the new default
190
       * value during stripping */
191
0
      newFormats = compute_effective_value_formats (glyphset, false, false, &c->plan->layout_variation_idx_delta_map);
192
0
    }
193
    /* do not strip hints for VF */
194
0
    else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
195
0
    {
196
0
      hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
197
0
      bool has_fvar = (blob != hb_blob_get_empty ());
198
0
      hb_blob_destroy (blob);
199
200
0
      bool strip = !has_fvar;
201
      /* special case: strip hints when a VF has no GDEF varstore after
202
       * subsetting*/
203
0
      if (has_fvar && !c->plan->has_gdef_varstore)
204
0
        strip = true;
205
0
      newFormats = compute_effective_value_formats (glyphset, strip, true);
206
0
    }
207
208
0
    out->valueFormat[0] = newFormats.first;
209
0
    out->valueFormat[1] = newFormats.second;
210
211
0
    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
212
213
0
    + hb_zip (this+coverage, pairSet)
214
0
    | hb_filter (glyphset, hb_first)
215
0
    | hb_filter ([this, c, out] (const typename Types::template OffsetTo<PairSet>& _)
216
0
                 {
217
0
                   auto snap = c->serializer->snapshot ();
218
0
                   auto *o = out->pairSet.serialize_append (c->serializer);
219
0
                   if (unlikely (!o)) return false;
220
0
                   bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat);
221
0
                   if (!ret)
222
0
                   {
223
0
                     out->pairSet.pop ();
224
0
                     c->serializer->revert (snap);
225
0
                   }
226
0
                   return ret;
227
0
                 },
228
0
                 hb_second)
229
0
    | hb_map (hb_first)
230
0
    | hb_map (glyph_map)
231
0
    | hb_sink (new_coverage)
232
0
    ;
233
234
0
    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
235
236
0
    return_trace (bool (new_coverage));
237
0
  }
238
239
240
  hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset,
241
                                                                 bool strip_hints, bool strip_empty,
242
                                                                 const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) const
243
0
  {
244
0
    unsigned record_size = PairSet::get_size (valueFormat);
245
246
0
    unsigned format1 = 0;
247
0
    unsigned format2 = 0;
248
0
    for (const auto & _ :
249
0
    + hb_zip (this+coverage, pairSet)
250
0
    | hb_filter (glyphset, hb_first)
251
0
    | hb_map (hb_second)
252
0
  )
253
0
    {
254
0
      const PairSet& set = (this + _);
255
0
      const PairValueRecord *record = &set.firstPairValueRecord;
256
257
0
      unsigned count = set.len;
258
0
      for (unsigned i = 0; i < count; i++)
259
0
      {
260
0
        if (record->intersects (glyphset))
261
0
        {
262
0
          format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 (), strip_hints, strip_empty, &set, varidx_delta_map);
263
0
          format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]), strip_hints, strip_empty, &set, varidx_delta_map);
264
0
        }
265
0
        record = &StructAtOffset<const PairValueRecord> (record, record_size);
266
0
      }
267
268
0
      if (format1 == valueFormat[0] && format2 == valueFormat[1])
269
0
        break;
270
0
    }
271
272
0
    return hb_pair (format1, format2);
273
0
  }
274
};
275
276
277
}
278
}
279
}
280
281
#endif  // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH