@@ -82,6 +82,7 @@ static StatusOr<GlyphSet> GetContextSet(
8282}
8383
8484// Tracks the details of an inprogress traversal.
85+ template <typename CallbackT>
8586class TraversalContext {
8687 public:
8788 hb_depend_t * depend = nullptr ;
@@ -113,15 +114,10 @@ class TraversalContext {
113114 // reached).
114115 bool enforce_context = false ;
115116
116- // Results of the traversal.
117- Traversal traversal;
118-
119- const flat_hash_set<Node>& Visited () const { return visited_; }
117+ // Informed of the results of traversal
118+ CallbackT callback;
120119
121120 private:
122- std::vector<Node> next_{};
123- flat_hash_set<Node> visited_{};
124-
125121 // These track information about things that are needed to
126122 // activate context which is not yet seen.
127123 std::vector<PendingEdge> pending_edges_;
@@ -147,18 +143,16 @@ class TraversalContext {
147143 feature_filter = other.feature_filter ;
148144 node_type_filter = other.node_type_filter ;
149145 enforce_context = other.enforce_context ;
150- traversal = other.traversal ;
146+ callback = other.callback ;
151147
152- next_ = other.next_ ;
153- visited_ = other.visited_ ;
154148 pending_edges_ = other.pending_edges_ ;
155149 reached_unicodes_ = other.reached_unicodes_ ;
156150 reached_glyphs_ = other.reached_glyphs_ ;
157151 reached_features_ = other.reached_features_ ;
158152 }
159153
160154 // Sets the nodes from which traversal starts.
161- void SetStartNodes (const btree_set<Node>& start) {
155+ void SetReached (const btree_set<Node>& start) {
162156 for (Node node : start) {
163157 Reached (node);
164158 }
@@ -178,14 +172,7 @@ class TraversalContext {
178172 }
179173
180174 // Returns the next node to be visited.
181- std::optional<Node> GetNext () {
182- if (next_.empty ()) {
183- return std::nullopt ;
184- }
185- Node node = next_.back ();
186- next_.pop_back ();
187- return node;
188- }
175+
189176
190177 // This checks all pending edges and if any have their constraints satisfied
191178 // then they are traversed. Returns true if there are now more nodes in the
@@ -206,17 +193,14 @@ class TraversalContext {
206193 // other than table tag
207194 void TraverseEdgeTo (Node dest,
208195 std::optional<hb_tag_t > table_tag = std::nullopt ) {
209- if (!ShouldFollow (dest, std::nullopt )) {
210- return ;
211- }
212- if (table_tag.has_value () && !table_filter.contains (*table_tag)) {
196+ if (!ShouldFollow (dest, table_tag, std::nullopt )) {
213197 return ;
214198 }
215199
216200 if (table_tag.has_value ()) {
217- traversal .Visit (dest, *table_tag);
201+ callback .Visit (dest, *table_tag);
218202 } else {
219- traversal .Visit (dest);
203+ callback .Visit (dest);
220204 }
221205 Reached (dest);
222206 }
@@ -280,13 +264,6 @@ class TraversalContext {
280264 }
281265
282266 void Reached (Node node) {
283- auto [_, did_insert] = visited_.insert (node);
284- if (!did_insert) {
285- return ;
286- }
287-
288- next_.push_back (node);
289-
290267 if (!enforce_context) {
291268 return ;
292269 }
@@ -386,11 +363,19 @@ class TraversalContext {
386363
387364 void Pending (PendingEdge edge) { pending_edges_.push_back (edge); }
388365
389- bool ShouldFollow (Node node, std::optional<hb_tag_t > layout_feature) const {
366+ bool ShouldFollow (
367+ Node node,
368+ std::optional<hb_tag_t > table_tag,
369+ std::optional<hb_tag_t > layout_feature) const {
370+
390371 if (!node.Matches (node_type_filter)) {
391372 return false ;
392373 }
393374
375+ if (table_tag.has_value () && !table_filter.contains (*table_tag)) {
376+ return false ;
377+ }
378+
394379 if (feature_filter != nullptr && layout_feature.has_value () &&
395380 !feature_filter->contains (*layout_feature)) {
396381 return false ;
@@ -412,26 +397,74 @@ class TraversalContext {
412397 }
413398};
414399
415- static Status DoTraversal (const PendingEdge& edge, TraversalContext& context) {
400+ void DependencyGraph::ClosureState::Visit (Node dest) {
401+ traversal.Visit (dest);
402+ Reached (dest);
403+ }
404+
405+ void DependencyGraph::ClosureState::Visit (Node dest, hb_tag_t table) {
406+ traversal.Visit (dest, table);
407+ Reached (dest);
408+ }
409+
410+ void DependencyGraph::ClosureState::VisitGsub (Node dest, hb_tag_t feature) {
411+ traversal.VisitGsub (dest, feature);
412+ Reached (dest);
413+ }
414+
415+ void DependencyGraph::ClosureState::VisitContextual (Node dest, hb_tag_t feature, GlyphSet context_glyphs) {
416+ traversal.VisitContextual (dest, feature, context_glyphs);
417+ Reached (dest);
418+ }
419+
420+ std::optional<Node> DependencyGraph::ClosureState::GetNext () {
421+ if (next.empty ()) {
422+ return std::nullopt ;
423+ }
424+ Node node = next.back ();
425+ next.pop_back ();
426+ return node;
427+ }
428+
429+ bool DependencyGraph::ClosureState::Reached (Node node) {
430+ auto [_, did_insert] = visited.insert (node);
431+ if (!did_insert) {
432+ return false ;
433+ }
434+
435+ next.push_back (node);
436+ return true ;
437+ }
438+
439+ void DependencyGraph::ClosureState::SetStartNodes (const absl::btree_set<Node>& start) {
440+ for (Node node : start) {
441+ Reached (node);
442+ }
443+ }
444+
445+ template <typename CallbackT>
446+ static Status DoTraversal (const PendingEdge& edge, TraversalContext<CallbackT>& context) {
416447 if (edge.table_tag == GSUB && edge.required_feature .has_value ()) {
417448 if (edge.required_context_set_index .has_value ()) {
418449 GlyphSet context_glyphs =
419450 TRY (GetContextSet (context.depend , context.full_closure ,
420451 *edge.required_context_set_index ));
421- context.traversal .VisitContextual (edge.dest , *edge.required_feature ,
452+ context.callback .VisitContextual (edge.dest , *edge.required_feature ,
422453 context_glyphs);
423454 } else {
424- context.traversal .VisitGsub (edge.dest , *edge.required_feature );
455+ context.callback .VisitGsub (edge.dest , *edge.required_feature );
425456 }
426457 } else {
427- context.traversal .Visit (edge.dest , edge.table_tag );
458+ context.callback .Visit (edge.dest , edge.table_tag );
428459 }
429460
430461 context.Reached (edge.dest );
431462 return absl::OkStatus ();
432463}
433464
434- StatusOr<bool > TraversalContext::CheckPending (hb_depend_t * depend_graph) {
465+ template <typename CallbackT>
466+ StatusOr<bool > TraversalContext<CallbackT>::CheckPending(hb_depend_t * depend_graph) {
467+ bool did_work = false ;
435468 auto it = pending_edges_.begin ();
436469 while (it != pending_edges_.end ()) {
437470 const auto & pending = *it;
@@ -440,21 +473,19 @@ StatusOr<bool> TraversalContext::CheckPending(hb_depend_t* depend_graph) {
440473 // Edge contstraints are now satisfied, can traverse the edge.
441474 TRYV (DoTraversal (pending, *this ));
442475 pending_edges_.erase (it);
476+ did_work = true ;
443477 } else {
444478 it++;
445479 }
446480 }
447481
448- return !next_. empty () ;
482+ return did_work ;
449483}
450484
451- Status TraversalContext::TraverseEdgeTo (Node dest, PendingEdge edge,
485+ template <typename CallbackT>
486+ Status TraversalContext<CallbackT>::TraverseEdgeTo(Node dest, PendingEdge edge,
452487 hb_tag_t table_tag) {
453- if (!table_filter.contains (table_tag)) {
454- return absl::OkStatus ();
455- }
456-
457- if (!ShouldFollow (dest, edge.required_feature )) {
488+ if (!ShouldFollow (dest, table_tag, edge.required_feature )) {
458489 return absl::OkStatus ();
459490 }
460491
@@ -469,14 +500,15 @@ Status TraversalContext::TraverseEdgeTo(Node dest, PendingEdge edge,
469500 return absl::OkStatus ();
470501}
471502
503+
472504StatusOr<Traversal> DependencyGraph::TraverseGraph (
473- TraversalContext* context) const {
505+ TraversalContext<ClosureState> * context) const {
474506 VLOG (1 ) << " DependencyGraph::TraverseGraph(...)" ;
475507
476508 while (true ) {
477- std::optional<Node> next = context->GetNext ();
509+ std::optional<Node> next = context->callback . GetNext ();
478510 if (!next.has_value ()) {
479- if (TRY (context->CheckPending (dependency_graph_.get ()))) {
511+ if (TRY (context->CheckPending (dependency_graph_.get ())) && !context-> callback . next . empty () ) {
480512 continue ;
481513 } else {
482514 // nothing left to traverse.
@@ -507,13 +539,13 @@ StatusOr<Traversal> DependencyGraph::TraverseGraph(
507539 }
508540
509541 for (const auto & edge : context->pending_edges ()) {
510- if (!context->Visited () .contains (edge.dest )) {
542+ if (!context->callback . visited .contains (edge.dest )) {
511543 // Only output pending edges where the dest has not been reached
512- context->traversal .AddPending (edge);
544+ context->callback . traversal .AddPending (edge);
513545 }
514546 }
515547
516- return std::move (context->traversal );
548+ return std::move (context->callback . traversal );
517549}
518550
519551StatusOr<Traversal> DependencyGraph::ClosureTraversal (
@@ -564,7 +596,7 @@ StatusOr<Traversal> DependencyGraph::ClosureTraversal(
564596 // _populate_gids_to_retain() from
565597 // https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-subset-plan.cc#L439
566598
567- TraversalContext base_context;
599+ TraversalContext<ClosureState> base_context;
568600 base_context.depend = dependency_graph_.get ();
569601 base_context.unicode_filter =
570602 unicode_filter_ptr ? unicode_filter_ptr : &non_init_font_codepoints;
@@ -581,7 +613,8 @@ StatusOr<Traversal> DependencyGraph::ClosureTraversal(
581613 /* ### Phase 1 + 2: Unicode and Unicode to glyph */
582614 {
583615 TraversalContext context = base_context;
584- context.SetStartNodes (nodes);
616+ context.SetReached (nodes);
617+ context.callback .SetStartNodes (nodes);
585618 context.table_filter = {cmap};
586619 context.node_type_filter = Node::NodeType::INIT_FONT |
587620 Node::NodeType::SEGMENT |
@@ -619,7 +652,7 @@ StatusOr<Traversal> DependencyGraph::ClosureTraversal(
619652}
620653
621654Status DependencyGraph::ClosureSubTraversal (
622- const TraversalContext* base_context, hb_tag_t table,
655+ const TraversalContext<ClosureState> * base_context, hb_tag_t table,
623656 Traversal& traversal_full) const {
624657 btree_set<Node> start_nodes;
625658 for (glyph_id_t gid : traversal_full.ReachedGlyphs ()) {
@@ -634,16 +667,18 @@ Status DependencyGraph::ClosureSubTraversal(
634667 }
635668 }
636669
637- TraversalContext context = *base_context;
638- context.SetStartNodes (start_nodes);
670+ TraversalContext<ClosureState> context = *base_context;
671+ context.SetReached (start_nodes);
672+ context.callback .SetStartNodes (start_nodes);
639673 context.table_filter = {table};
640674 context.node_type_filter = Node::NodeType::GLYPH;
641675 traversal_full.Merge (TRY (TraverseGraph (&context)));
642676 return absl::OkStatus ();
643677}
644678
679+ template <typename CallbackT>
645680Status DependencyGraph::HandleUnicodeOutgoingEdges (
646- hb_codepoint_t unicode, TraversalContext* context) const {
681+ hb_codepoint_t unicode, TraversalContext<CallbackT> * context) const {
647682 {
648683 auto it = unicode_to_gid_.find (unicode);
649684 if (it != unicode_to_gid_.end ()) {
@@ -669,8 +704,9 @@ Status DependencyGraph::HandleUnicodeOutgoingEdges(
669704 return absl::OkStatus ();
670705}
671706
707+ template <typename CallbackT>
672708Status DependencyGraph::HandleGlyphOutgoingEdges (
673- glyph_id_t gid, TraversalContext* context) const {
709+ glyph_id_t gid, TraversalContext<CallbackT> * context) const {
674710 hb_codepoint_t index = 0 ;
675711 hb_tag_t table_tag = HB_CODEPOINT_INVALID;
676712 hb_codepoint_t dep_gid = HB_CODEPOINT_INVALID;
@@ -699,10 +735,11 @@ Status DependencyGraph::HandleGlyphOutgoingEdges(
699735 return absl::OkStatus ();
700736}
701737
738+ template <typename CallbackT>
702739Status DependencyGraph::HandleGsubGlyphOutgoingEdges (
703740 glyph_id_t source_gid, glyph_id_t dest_gid, hb_tag_t layout_tag,
704741 hb_codepoint_t ligature_set, hb_codepoint_t context_set,
705- TraversalContext* context) const {
742+ TraversalContext<CallbackT> * context) const {
706743 if (context_set != HB_CODEPOINT_INVALID) {
707744 return context->TraverseContextualEdgeTo (source_gid, dest_gid, layout_tag,
708745 context_set);
@@ -714,8 +751,9 @@ Status DependencyGraph::HandleGsubGlyphOutgoingEdges(
714751 }
715752}
716753
754+ template <typename CallbackT>
717755Status DependencyGraph::HandleFeatureOutgoingEdges (
718- hb_tag_t feature_tag, TraversalContext* context) const {
756+ hb_tag_t feature_tag, TraversalContext<CallbackT> * context) const {
719757 if (!context->table_filter .contains (GSUB)) {
720758 // All feature edges are GSUB edges so we can skip
721759 // this if GSUB is being filtered out.
@@ -740,8 +778,9 @@ Status DependencyGraph::HandleFeatureOutgoingEdges(
740778 return absl::OkStatus ();
741779}
742780
781+ template <typename CallbackT>
743782void DependencyGraph::HandleSegmentOutgoingEdges (
744- segment_index_t id, TraversalContext* context) const {
783+ segment_index_t id, TraversalContext<CallbackT> * context) const {
745784 if (id >= segmentation_info_->Segments ().size ()) {
746785 // Unknown segment has no outgoing edges.
747786 return ;
@@ -751,8 +790,9 @@ void DependencyGraph::HandleSegmentOutgoingEdges(
751790 HandleSubsetDefinitionOutgoingEdges (s.Definition (), context);
752791}
753792
793+ template <typename CallbackT>
754794void DependencyGraph::HandleSubsetDefinitionOutgoingEdges (
755- const SubsetDefinition& subset_def, TraversalContext* context) const {
795+ const SubsetDefinition& subset_def, TraversalContext<CallbackT> * context) const {
756796 for (hb_codepoint_t u : subset_def.codepoints ) {
757797 context->TraverseEdgeTo (Node::Unicode (u));
758798 }
0 commit comments