Coverage Report

Created: 2026-10-17 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
Line
Count
Source
1
#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH
2
#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH
3
4
#include "Common.hh"
5
6
namespace OT {
7
namespace Layout {
8
namespace GSUB_impl {
9
10
template <typename Types>
11
struct ReverseChainSingleSubstFormat1_2
12
{
13
  protected:
14
  HBUINT16      format;                 /* Format identifier--format = 1 */
15
  typename Types::template LOffsetTo<Coverage>
16
                coverage;               /* Offset to Coverage table--from
17
                                         * beginning of table */
18
  Array16Of<typename Types::template OffsetTo<Coverage>>
19
                backtrack;              /* Array of coverage tables
20
                                         * in backtracking sequence, in glyph
21
                                         * sequence order */
22
  /* Format 2 widens the backtrack and lookahead coverage offsets but
23
   * keeps their counts 16-bit. */
24
  Array16Of<typename Types::template OffsetTo<Coverage>>
25
                lookaheadX;             /* Array of coverage tables
26
                                         * in lookahead sequence, in glyph
27
                                         * sequence order */
28
  typename Types::template ArrayOf<typename Types::HBGlyphID>
29
                substituteX;            /* Array of substitute
30
                                         * GlyphIDs--ordered by Coverage Index */
31
  public:
32
  DEFINE_SIZE_MIN (6 + Types::LOffset::static_size + Types::HBUINT::static_size);
33
34
  bool sanitize (hb_sanitize_context_t *c) const
35
0
  {
36
0
    TRACE_SANITIZE (this);
37
0
    if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
38
0
      return_trace (false);
39
0
    hb_barrier ();
40
0
    const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
41
0
    if (!lookahead.sanitize (c, this))
42
0
      return_trace (false);
43
0
    hb_barrier ();
44
0
    const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
45
0
    return_trace (substitute.sanitize (c));
46
0
  }
47
48
  bool intersects (const hb_set_t *glyphs) const
49
0
  {
50
0
    if (!(this+coverage).intersects (glyphs))
51
0
      return false;
52
53
0
    const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
54
55
0
    unsigned int count;
56
57
0
    count = backtrack.len;
58
0
    for (unsigned int i = 0; i < count; i++)
59
0
      if (!(this+backtrack[i]).intersects (glyphs))
60
0
        return false;
61
62
0
    count = lookahead.len;
63
0
    for (unsigned int i = 0; i < count; i++)
64
0
      if (!(this+lookahead[i]).intersects (glyphs))
65
0
        return false;
66
67
0
    return true;
68
0
  }
69
70
  bool may_have_non_1to1 () const
71
0
  { return false; }
72
73
0
  void depend (hb_depend_context_t *c) const {
74
0
    // Filter by intersects and parent_active_glyphs like closure does
75
0
    if (!intersects (c->glyphs)) return;
76
0
77
0
    const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
78
0
    const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
79
0
80
0
    /* Build context set from backtrack and lookahead coverages.
81
0
     * ReverseChainSingleSubst has contextual requirements - the substitution
82
0
     * only fires when backtrack and lookahead glyphs are present. */
83
0
    hb_set_t context_glyphs;
84
0
85
0
    /* Add glyphs from backtrack coverages */
86
0
    unsigned int count = backtrack.len;
87
0
    for (unsigned int i = 0; i < count; i++)
88
0
    {
89
0
      hb_set_t back_glyphs;
90
0
      (this+backtrack[i]).intersect_set (*c->glyphs, back_glyphs);
91
0
      if (back_glyphs.get_population () == 1)
92
0
        context_glyphs.add (back_glyphs.get_min ());
93
0
      else if (back_glyphs.get_population () > 1)
94
0
      {
95
0
        hb_codepoint_t set_idx = c->depend_data->find_or_create_context_set (back_glyphs);
96
0
        if (unlikely (set_idx == HB_CODEPOINT_INVALID))
97
0
          return;
98
0
        context_glyphs.add (HB_DEPEND_CONTEXT_SET_FLAG | set_idx);
99
0
      }
100
0
    }
101
0
102
0
    /* Add glyphs from lookahead coverages */
103
0
    count = lookahead.len;
104
0
    for (unsigned int i = 0; i < count; i++)
105
0
    {
106
0
      hb_set_t look_glyphs;
107
0
      (this+lookahead[i]).intersect_set (*c->glyphs, look_glyphs);
108
0
      if (look_glyphs.get_population () == 1)
109
0
        context_glyphs.add (look_glyphs.get_min ());
110
0
      else if (look_glyphs.get_population () > 1)
111
0
      {
112
0
        hb_codepoint_t set_idx = c->depend_data->find_or_create_context_set (look_glyphs);
113
0
        if (unlikely (set_idx == HB_CODEPOINT_INVALID))
114
0
          return;
115
0
        context_glyphs.add (HB_DEPEND_CONTEXT_SET_FLAG | set_idx);
116
0
      }
117
0
    }
118
0
119
0
    /* Allocate context set and save/restore around edge creation */
120
0
    if (unlikely (context_glyphs.in_error ()))
121
0
    {
122
0
      c->depend_data->fail ();
123
0
      return;
124
0
    }
125
0
    hb_codepoint_t context_set_idx = context_glyphs.is_empty ()
126
0
      ? HB_CODEPOINT_INVALID
127
0
      : c->depend_data->find_or_create_context_set (context_glyphs);
128
0
    if (unlikely (!context_glyphs.is_empty () &&
129
0
                  context_set_idx == HB_CODEPOINT_INVALID))
130
0
      return;
131
0
132
0
    hb_codepoint_t saved_context = c->depend_data->current_context_set_index;
133
0
    c->depend_data->current_context_set_index = context_set_idx;
134
0
135
0
    + hb_zip (this+coverage, substitute)
136
0
    | hb_filter (c->parent_active_glyphs (), hb_first)
137
0
    | hb_apply ([&] (const hb_codepoint_pair_t &_) { c->depend_data->add_gsub_lookup (_.first, c->lookup_index, _.second); })
138
0
    ;
139
0
140
0
    c->depend_data->current_context_set_index = saved_context;
141
0
  }
142
143
  void closure (hb_closure_context_t *c) const
144
0
  {
145
0
    if (!intersects (c->glyphs)) return;
146
147
0
    const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
148
0
    const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
149
150
0
    + hb_zip (this+coverage, substitute)
151
0
    | hb_filter (c->parent_active_glyphs (), hb_first)
152
0
    | hb_map (hb_second)
153
0
    | hb_sink (c->output)
154
0
    ;
155
0
  }
156
157
0
  void closure_lookups (hb_closure_lookups_context_t *c) const {}
158
159
  void collect_glyphs (hb_collect_glyphs_context_t *c) const
160
0
  {
161
0
    if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
162
163
0
    unsigned int count;
164
165
0
    count = backtrack.len;
166
0
    for (unsigned int i = 0; i < count; i++)
167
0
      if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
168
169
0
    const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
170
0
    count = lookahead.len;
171
0
    for (unsigned int i = 0; i < count; i++)
172
0
      if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
173
174
0
    const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
175
0
    count = substitute.len;
176
0
    c->output->add_array (substitute.arrayZ, substitute.len);
177
0
  }
178
179
0
  const Coverage &get_coverage () const { return this+coverage; }
180
181
  bool would_apply (hb_would_apply_context_t *c) const
182
0
  { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
183
184
  bool apply (hb_ot_apply_context_t *c) const
185
0
  {
186
0
    TRACE_APPLY (this);
187
0
    unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
188
0
    if (index == NOT_COVERED) return_trace (false);
189
190
0
    if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
191
0
      return_trace (false); /* No chaining to this type */
192
193
0
    const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
194
0
    const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
195
196
0
    if (unlikely (index >= substitute.len)) return_trace (false);
197
198
0
    unsigned int start_index = 0, end_index = 0;
199
0
    if (match_backtrack (c,
200
0
                         backtrack.len, backtrack.arrayZ,
201
0
                         match_coverage, this,
202
0
                         &start_index) &&
203
0
        match_lookahead (c,
204
0
                         lookahead.len, lookahead.arrayZ,
205
0
                         match_coverage, this,
206
0
                         c->buffer->idx + 1, &end_index))
207
0
    {
208
0
      c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
209
210
0
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
211
0
      {
212
0
  c->buffer->message (c->font,
213
0
          "replacing glyph at %u (reverse chaining substitution)",
214
0
          c->buffer->idx);
215
0
      }
216
217
0
      c->replace_glyph_inplace (substitute[index]);
218
219
0
      if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
220
0
      {
221
0
  c->buffer->message (c->font,
222
0
          "replaced glyph at %u (reverse chaining substitution)",
223
0
          c->buffer->idx);
224
0
      }
225
226
      /* Note: We DON'T decrease buffer->idx.  The main loop does it
227
       * for us.  This is useful for preventing surprises if someone
228
       * calls us through a Context lookup. */
229
0
      return_trace (true);
230
0
    }
231
0
    else
232
0
    {
233
0
      c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
234
0
      return_trace (false);
235
0
    }
236
0
  }
237
238
  template<typename Iterator,
239
           hb_requires (hb_is_iterator (Iterator))>
240
  bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const
241
0
  {
242
0
    TRACE_SERIALIZE (this);
243
0
    auto *out = c->serializer->start_embed<Array16Of<typename Types::template OffsetTo<Coverage>>> ();
244
0
    if (unlikely (!out->serialize (c->serializer, 0u))) return_trace (false);
245
0
246
0
    for (auto& offset : it) {
247
0
      auto *o = out->serialize_append (c->serializer);
248
0
      if (unlikely (!o) || !o->serialize_subset (c, offset, this))
249
0
        return_trace (false);
250
0
    }
251
0
252
0
    return_trace (true);
253
0
  }
254
255
  template<typename Iterator, typename BacktrackIterator, typename LookaheadIterator,
256
           hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_pair_t)),
257
           hb_requires (hb_is_iterator (BacktrackIterator)),
258
           hb_requires (hb_is_iterator (LookaheadIterator))>
259
  bool serialize (hb_subset_context_t *c,
260
                  Iterator coverage_subst_iter,
261
                  BacktrackIterator backtrack_iter,
262
                  LookaheadIterator lookahead_iter) const
263
0
  {
264
0
    TRACE_SERIALIZE (this);
265
0
266
0
    auto *out = c->serializer->start_embed (this);
267
0
    if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
268
0
    if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
269
0
270
0
    if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false);
271
0
    if (!serialize_lookahead_coverage_offset_array (c, lookahead_iter)) return_trace (false);
272
0
273
0
    auto *substitute_out = c->serializer->start_embed<typename Types::template ArrayOf<typename Types::HBGlyphID>> ();
274
0
    auto substitutes =
275
0
    + coverage_subst_iter
276
0
    | hb_map (hb_second)
277
0
    ;
278
0
279
0
    auto glyphs =
280
0
    + coverage_subst_iter
281
0
    | hb_map_retains_sorting (hb_first)
282
0
    ;
283
0
    if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes))))
284
0
        return_trace (false);
285
0
286
0
    if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs)))
287
0
      return_trace (false);
288
0
    return_trace (true);
289
0
  }
Unexecuted instantiation: hb-ot-face.cc:_ZNK2OT6Layout9GSUB_impl32ReverseChainSingleSubstFormat1_2INS0_10SmallTypesEE9serializeI13hb_map_iter_tI16hb_filter_iter_tIS7_I13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS_11HBGlyphID16EEERK8hb_set_tRK3$_7LPv0EESJ_RK3$_6LSN_0EEZNKS4_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSE_EE_L24hb_function_sortedness_t1ELSN_0EESC_IKNS_8OffsetToISA_NS_7NumTypeILb1EtLj2EEEvLb1EEEES16_TnPN12hb_enable_ifIXaasr15hb_is_source_ofIT_SV_IjjEEE5valuesrS18_18is_sorted_iteratorEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT0_NS1D_6item_tEEE5valueEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT1_NS1I_6item_tEEE5valueEvE4typeELSN_0EEEbSU_S18_S1D_S1I_
Unexecuted instantiation: hb-aat-layout.cc:_ZNK2OT6Layout9GSUB_impl32ReverseChainSingleSubstFormat1_2INS0_10SmallTypesEE9serializeI13hb_map_iter_tI16hb_filter_iter_tIS7_I13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS_11HBGlyphID16EEERK8hb_set_tRK3$_7LPv0EESJ_RK3$_6LSN_0EEZNKS4_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSE_EE_L24hb_function_sortedness_t1ELSN_0EESC_IKNS_8OffsetToISA_NS_7NumTypeILb1EtLj2EEEvLb1EEEES16_TnPN12hb_enable_ifIXaasr15hb_is_source_ofIT_SV_IjjEEE5valuesrS18_18is_sorted_iteratorEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT0_NS1D_6item_tEEE5valueEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT1_NS1I_6item_tEEE5valueEvE4typeELSN_0EEEbSU_S18_S1D_S1I_
Unexecuted instantiation: hb-ot-layout.cc:_ZNK2OT6Layout9GSUB_impl32ReverseChainSingleSubstFormat1_2INS0_10SmallTypesEE9serializeI13hb_map_iter_tI16hb_filter_iter_tIS7_I13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS_11HBGlyphID16EEERK8hb_set_tRK3$_7LPv0EESJ_RK3$_6LSN_0EEZNKS4_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSE_EE_L24hb_function_sortedness_t1ELSN_0EESC_IKNS_8OffsetToISA_NS_7NumTypeILb1EtLj2EEEvLb1EEEES16_TnPN12hb_enable_ifIXaasr15hb_is_source_ofIT_SV_IjjEEE5valuesrS18_18is_sorted_iteratorEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT0_NS1D_6item_tEEE5valueEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT1_NS1I_6item_tEEE5valueEvE4typeELSN_0EEEbSU_S18_S1D_S1I_
Unexecuted instantiation: hb-ot-shaper-arabic.cc:_ZNK2OT6Layout9GSUB_impl32ReverseChainSingleSubstFormat1_2INS0_10SmallTypesEE9serializeI13hb_map_iter_tI16hb_filter_iter_tIS7_I13hb_zip_iter_tINS0_6Common8Coverage6iter_tE10hb_array_tIKNS_11HBGlyphID16EEERK8hb_set_tRK3$_7LPv0EESJ_RK3$_6LSN_0EEZNKS4_6subsetEP19hb_subset_context_tEUl9hb_pair_tIjRSE_EE_L24hb_function_sortedness_t1ELSN_0EESC_IKNS_8OffsetToISA_NS_7NumTypeILb1EtLj2EEEvLb1EEEES16_TnPN12hb_enable_ifIXaasr15hb_is_source_ofIT_SV_IjjEEE5valuesrS18_18is_sorted_iteratorEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT0_NS1D_6item_tEEE5valueEvE4typeELSN_0ETnPNS17_IXsr17hb_is_iterator_ofIT1_NS1I_6item_tEEE5valueEvE4typeELSN_0EEEbSU_S18_S1D_S1I_
290
291
  bool subset (hb_subset_context_t *c) const
292
0
  {
293
0
    TRACE_SUBSET (this);
294
0
    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
295
0
    const hb_map_t &glyph_map = *c->plan->glyph_map;
296
0
297
0
    const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
298
0
    const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
299
0
300
0
    auto it =
301
0
    + hb_zip (this+coverage, substitute)
302
0
    | hb_filter (glyphset, hb_first)
303
0
    | hb_filter (glyphset, hb_second)
304
0
    | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const typename Types::HBGlyphID &> p) -> hb_codepoint_pair_t
305
0
                              { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
306
0
    ;
307
0
308
0
    return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ()));
309
0
  }
310
311
  template<typename Iterator,
312
           hb_requires (hb_is_iterator (Iterator))>
313
  bool serialize_lookahead_coverage_offset_array (hb_subset_context_t *c, Iterator it) const
314
0
  {
315
0
    TRACE_SERIALIZE (this);
316
0
    auto *out = c->serializer->start_embed<Array16Of<typename Types::template OffsetTo<Coverage>>> ();
317
0
    if (unlikely (!out->serialize (c->serializer, 0u))) return_trace (false);
318
0
319
0
    for (auto& offset : it) {
320
0
      auto *o = out->serialize_append (c->serializer);
321
0
      if (unlikely (!o) || !o->serialize_subset (c, offset, this))
322
0
        return_trace (false);
323
0
    }
324
0
325
0
    return_trace (true);
326
0
  }
327
};
328
329
}
330
}
331
}
332
333
#endif  /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH */