Coverage Report

Created: 2026-10-30 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Layout/GPOS/PairSet.hh
Line
Count
Source
1
#ifndef OT_LAYOUT_GPOS_PAIRSET_HH
2
#define OT_LAYOUT_GPOS_PAIRSET_HH
3
4
#include "PairValueRecord.hh"
5
6
namespace OT {
7
namespace Layout {
8
namespace GPOS_impl {
9
10
11
/* Pair records vary in glyph width; their value traversal need not. */
12
struct pair_set_values_t
13
{
14
  const ValueBase *base;
15
  glyph_data_t glyphs;
16
  const Value *values;
17
  unsigned count;
18
  unsigned record_size;
19
20
  unsigned get_glyph (unsigned i) const
21
0
  {
22
0
    glyph_data_t glyph = glyphs;
23
0
    glyph.data = (const char *) glyph.data + i * record_size;
24
0
    return glyph[0];
25
0
  }
26
27
  const Value *get_values (unsigned i) const
28
0
  { return reinterpret_cast<const Value *> ((const char *) values + i * record_size); }
29
30
  hb_pair_t<unsigned, unsigned>
31
  compute_effective_value_formats (const hb_set_t &glyphset,
32
                                   const ValueFormat valueFormats[2],
33
                                   bool strip_hints, bool strip_empty,
34
                                   const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
35
0
  {
36
0
    unsigned len1 = valueFormats[0].get_len ();
37
0
    unsigned format1 = 0, format2 = 0;
38
0
    for (unsigned i = 0; i < count; i++)
39
0
    {
40
0
      if (!glyphset.has (get_glyph (i))) continue;
41
0
      const Value *v = get_values (i);
42
0
      format1 |= valueFormats[0].get_effective_format (v, strip_hints, strip_empty, base, varidx_delta_map);
43
0
      format2 |= valueFormats[1].get_effective_format (v + len1, strip_hints, strip_empty, base, varidx_delta_map);
44
0
    }
45
0
    return hb_pair (format1, format2);
46
0
  }
47
48
  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
49
                                  const ValueFormat valueFormats[2]) const
50
0
  {
51
0
    unsigned len1 = valueFormats[0].get_len ();
52
0
    unsigned len2 = valueFormats[1].get_len ();
53
0
    for (unsigned i = 0; i < count; i++)
54
0
    {
55
0
      if (!c->glyph_set->has (get_glyph (i))) continue;
56
0
      const hb_array_t<const Value> v = hb_array (get_values (i), len1 + len2);
57
0
      if (valueFormats[0].has_device ())
58
0
        valueFormats[0].collect_variation_indices (c, base, v.sub_array (0, len1));
59
0
      if (valueFormats[1].has_device ())
60
0
        valueFormats[1].collect_variation_indices (c, base, v.sub_array (len1, len2));
61
0
    }
62
0
  }
63
};
64
65
template <typename Types>
66
struct PairSet : ValueBase
67
{
68
  template <typename Types2>
69
  friend struct PairPosFormat1_3;
70
71
  using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
72
73
  protected:
74
  typename Types::HBUINT
75
                        len;    /* Number of PairValueRecords */
76
  PairValueRecord       firstPairValueRecord;
77
                                /* Array of PairValueRecords--ordered
78
                                 * by GlyphID of the second glyph */
79
  public:
80
  DEFINE_SIZE_MIN (Types::HBUINT::static_size);
81
82
  static size_t get_size (unsigned len1, unsigned len2)
83
0
  {
84
0
    return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2);
85
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::get_size(unsigned int, unsigned int)
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::get_size(unsigned int, unsigned int)
86
  static size_t get_size (const ValueFormat valueFormats[2])
87
0
  {
88
0
    unsigned len1 = valueFormats[0].get_len ();
89
0
    unsigned len2 = valueFormats[1].get_len ();
90
0
    return get_size (len1, len2);
91
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::get_size(OT::Layout::GPOS_impl::ValueFormat const*)
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::get_size(OT::Layout::GPOS_impl::ValueFormat const*)
92
93
  struct sanitize_closure_t
94
  {
95
    const ValueFormat *valueFormats;
96
    unsigned int len1; /* valueFormats[0].get_len() */
97
    size_t stride; /* bytes */
98
  };
99
100
  bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
101
0
  {
102
0
    TRACE_SANITIZE (this);
103
0
    if (!(c->check_struct (this) &&
104
0
    hb_barrier () &&
105
0
          c->check_range (&firstPairValueRecord,
106
0
                          len,
107
0
                          closure->stride))) return_trace (false);
108
0
    hb_barrier ();
109
110
0
    unsigned int count = len;
111
0
    const PairValueRecord *record = &firstPairValueRecord;
112
0
    return_trace (c->lazy_some_gpos ||
113
0
      (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
114
0
                   closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)));
115
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::sanitize(hb_sanitize_context_t*, OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::sanitize_closure_t const*) const
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::sanitize(hb_sanitize_context_t*, OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::sanitize_closure_t const*) const
116
117
  bool intersects (const hb_set_t *glyphs,
118
                   const ValueFormat *valueFormats) const
119
0
  {
120
0
    unsigned record_size = get_size (valueFormats);
121
0
122
0
    const PairValueRecord *record = &firstPairValueRecord;
123
0
    unsigned int count = len;
124
0
    for (unsigned int i = 0; i < count; i++)
125
0
    {
126
0
      if (glyphs->has (record->secondGlyph))
127
0
        return true;
128
0
      record = &StructAtOffset<const PairValueRecord> (record, record_size);
129
0
    }
130
0
    return false;
131
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::intersects(hb_set_t const*, OT::Layout::GPOS_impl::ValueFormat const*) const
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::intersects(hb_set_t const*, OT::Layout::GPOS_impl::ValueFormat const*) const
132
133
  void collect_glyphs (hb_collect_glyphs_context_t *c,
134
                       const ValueFormat *valueFormats) const
135
0
  {
136
0
    unsigned record_size = get_size (valueFormats);
137
138
0
    const PairValueRecord *record = &firstPairValueRecord;
139
0
    c->input->add_array (&record->secondGlyph, len, record_size);
140
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::collect_glyphs(OT::hb_collect_glyphs_context_t*, OT::Layout::GPOS_impl::ValueFormat const*) const
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::collect_glyphs(OT::hb_collect_glyphs_context_t*, OT::Layout::GPOS_impl::ValueFormat const*) const
141
142
  template <typename set_t>
143
  void collect_second_glyphs (set_t *glyphs,
144
            const ValueFormat *valueFormats) const
145
0
  {
146
0
    unsigned record_size = get_size (valueFormats);
147
0
    const PairValueRecord *record = &firstPairValueRecord;
148
0
    glyphs->add_array (&record->secondGlyph, len, record_size);
149
0
  }
Unexecuted instantiation: void OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::collect_second_glyphs<hb_set_digest_t>(hb_set_digest_t*, OT::Layout::GPOS_impl::ValueFormat const*) const
Unexecuted instantiation: void OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::collect_second_glyphs<hb_set_digest_t>(hb_set_digest_t*, OT::Layout::GPOS_impl::ValueFormat const*) const
150
151
  pair_set_values_t get_values_view (const ValueFormat valueFormats[2]) const
152
0
  {
153
0
    return {this, &firstPairValueRecord.secondGlyph, &firstPairValueRecord.values[0],
154
0
            (unsigned) len, (unsigned) get_size (valueFormats)};
155
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::get_values_view(OT::Layout::GPOS_impl::ValueFormat const*) const
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::get_values_view(OT::Layout::GPOS_impl::ValueFormat const*) const
156
157
  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
158
                                  const ValueFormat *valueFormats) const
159
0
  {
160
0
    get_values_view (valueFormats).collect_variation_indices (c, valueFormats);
161
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::collect_variation_indices(OT::hb_collect_variation_indices_context_t*, OT::Layout::GPOS_impl::ValueFormat const*) const
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::collect_variation_indices(OT::hb_collect_variation_indices_context_t*, OT::Layout::GPOS_impl::ValueFormat const*) const
162
163
  bool apply (hb_ot_apply_context_t *c,
164
              const ValueFormat *valueFormats,
165
              unsigned int pos) const
166
0
  {
167
0
    TRACE_APPLY (this);
168
0
    hb_buffer_t *buffer = c->buffer;
169
0
    unsigned int len1 = valueFormats[0].get_len ();
170
0
    unsigned int len2 = valueFormats[1].get_len ();
171
0
    unsigned record_size = get_size (len1, len2);
172
173
0
    const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
174
0
                                                &firstPairValueRecord,
175
0
                                                (unsigned) len,
176
0
                                                record_size);
177
0
    if (record)
178
0
    {
179
0
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
180
0
      {
181
0
  c->buffer->message (c->font,
182
0
          "try kerning glyphs at %u,%u",
183
0
          c->buffer->idx, pos);
184
0
      }
185
186
0
      bool applied_first = len1 && valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
187
0
      bool applied_second = len2 && valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
188
189
0
      if (applied_first || applied_second)
190
0
  if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
191
0
  {
192
0
    c->buffer->message (c->font,
193
0
            "kerned glyphs at %u,%u",
194
0
            c->buffer->idx, pos);
195
0
  }
196
197
0
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
198
0
      {
199
0
  c->buffer->message (c->font,
200
0
          "tried kerning glyphs at %u,%u",
201
0
          c->buffer->idx, pos);
202
0
      }
203
204
0
      if (applied_first || applied_second)
205
0
        buffer->unsafe_to_break (buffer->idx, pos + 1);
206
0
      else
207
        /* Even a zero-valued pair record is a concat hazard. */
208
0
        buffer->unsafe_to_concat (buffer->idx, pos + 1);
209
210
0
      if (len2)
211
0
      {
212
0
  pos++;
213
      // https://github.com/harfbuzz/harfbuzz/issues/3824
214
      // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116
215
0
      buffer->unsafe_to_break (buffer->idx, pos + 1);
216
0
      }
217
218
0
      buffer->idx = pos;
219
0
      return_trace (true);
220
0
    }
221
0
    buffer->unsafe_to_concat (buffer->idx, pos + 1);
222
0
    return_trace (false);
223
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::apply(OT::hb_ot_apply_context_t*, OT::Layout::GPOS_impl::ValueFormat const*, unsigned int) const
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::apply(OT::hb_ot_apply_context_t*, OT::Layout::GPOS_impl::ValueFormat const*, unsigned int) const
224
225
  bool subset (hb_subset_context_t *c,
226
               const ValueFormat valueFormats[2],
227
               const ValueFormat newFormats[2]) const
228
0
  {
229
0
    TRACE_SUBSET (this);
230
0
    auto snap = c->serializer->snapshot ();
231
0
232
0
    auto *out = c->serializer->start_embed (*this);
233
0
    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
234
0
    out->len = 0;
235
0
236
0
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
237
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
238
0
239
0
    unsigned len1 = valueFormats[0].get_len ();
240
0
    unsigned len2 = valueFormats[1].get_len ();
241
0
    unsigned record_size = get_size (len1, len2);
242
0
243
0
    pair_value_record_context_t context =
244
0
    {
245
0
      this,
246
0
      valueFormats,
247
0
      newFormats,
248
0
      len1,
249
0
      &glyph_map,
250
0
      &c->plan->layout_variation_idx_delta_map
251
0
    };
252
0
253
0
    const PairValueRecord *record = &firstPairValueRecord;
254
0
    unsigned count = len, num = 0;
255
0
    for (unsigned i = 0; i < count; i++)
256
0
    {
257
0
      if (glyphset.has (record->secondGlyph)
258
0
         && record->subset (c, &context)) num++;
259
0
      record = &StructAtOffset<const PairValueRecord> (record, record_size);
260
0
    }
261
0
262
0
    out->len = num;
263
0
    if (!num) c->serializer->revert (snap);
264
0
    return_trace (num);
265
0
  }
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::SmallTypes>::subset(hb_subset_context_t*, OT::Layout::GPOS_impl::ValueFormat const*, OT::Layout::GPOS_impl::ValueFormat const*) const
Unexecuted instantiation: OT::Layout::GPOS_impl::PairSet<OT::Layout::MediumTypes>::subset(hb_subset_context_t*, OT::Layout::GPOS_impl::ValueFormat const*, OT::Layout::GPOS_impl::ValueFormat const*) const
266
};
267
268
269
}
270
}
271
}
272
273
#endif  // OT_LAYOUT_GPOS_PAIRSET_HH