Skip to content

Commit 41bcd5e

Browse files
committed
Refactor TraversalContext to allow it to be reused outside of closure.
Adds a templated callback type so other graph algorithms can utilize it.
1 parent 86ccbd6 commit 41bcd5e

File tree

2 files changed

+133
-70
lines changed

2 files changed

+133
-70
lines changed

ift/dep_graph/dependency_graph.cc

Lines changed: 102 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ static StatusOr<GlyphSet> GetContextSet(
8282
}
8383

8484
// Tracks the details of an inprogress traversal.
85+
template<typename CallbackT>
8586
class 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+
472504
StatusOr<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

519551
StatusOr<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

621654
Status 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>
645680
Status 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>
672708
Status 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>
702739
Status 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>
717755
Status 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>
743782
void 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>
754794
void 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

Comments
 (0)