xref: /aosp_15_r20/external/stg/graph.h (revision 9e3b08ae94a55201065475453d799e8b1378bea6)
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2020-2024 Google LLC
5 //
6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7 // "License"); you may not use this file except in compliance with the
8 // License.  You may obtain a copy of the License at
9 //
10 //     https://llvm.org/LICENSE.txt
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 // Author: Maria Teguiani
19 // Author: Giuliano Procida
20 // Author: Ignes Simeonova
21 
22 #ifndef STG_GRAPH_H_
23 #define STG_GRAPH_H_
24 
25 #include <cstddef>
26 #include <cstdint>
27 #include <exception>
28 #include <functional>
29 #include <map>
30 #include <optional>
31 #include <ostream>
32 #include <sstream>
33 #include <string>
34 #include <type_traits>
35 #include <utility>
36 #include <vector>
37 
38 #include "error.h"
39 
40 namespace stg {
41 
42 // A wrapped (for type safety) array index.
43 struct Id {
44   // defined in graph.cc as maximum value for index type
45   static const Id kInvalid;
IdId46   explicit Id(size_t ix) : ix_(ix) {}
47   auto operator<=>(const Id&) const = default;
48   size_t ix_;
49 };
50 
51 std::ostream& operator<<(std::ostream& os, Id id);
52 
53 using Pair = std::pair<Id, Id>;
54 
55 }  // namespace stg
56 
57 namespace std {
58 
59 template <>
60 struct hash<stg::Id> {
61   size_t operator()(const stg::Id& id) const {
62     return hash<decltype(id.ix_)>()(id.ix_);
63   }
64 };
65 
66 template <>
67 struct hash<stg::Pair> {
68   size_t operator()(const stg::Pair& comparison) const {
69     const hash<stg::Id> h;
70     auto h1 = h(comparison.first);
71     auto h2 = h(comparison.second);
72     // assumes 64-bit size_t, would be better if std::hash_combine existed
73     return h1 ^ (h2 + 0x9e3779b97f4a7c15 + (h1 << 12) + (h1 >> 4));
74   }
75 };
76 
77 }  // namespace std
78 
79 namespace stg {
80 
81 struct Special {
82   enum class Kind {
83     VOID,
84     VARIADIC,
85     NULLPTR,
86   };
87   explicit Special(Kind kind)
88       : kind(kind) {}
89 
90   Kind kind;
91 };
92 
93 struct PointerReference {
94   enum class Kind {
95     POINTER,
96     LVALUE_REFERENCE,
97     RVALUE_REFERENCE,
98   };
99   PointerReference(Kind kind, Id pointee_type_id)
100       : kind(kind), pointee_type_id(pointee_type_id) {}
101 
102   Kind kind;
103   Id pointee_type_id;
104 };
105 
106 struct PointerToMember {
107   PointerToMember(Id containing_type_id, Id pointee_type_id)
108       : containing_type_id(containing_type_id), pointee_type_id(pointee_type_id)
109   {}
110 
111   Id containing_type_id;
112   Id pointee_type_id;
113 };
114 
115 struct Typedef {
116   Typedef(const std::string& name, Id referred_type_id)
117       : name(name), referred_type_id(referred_type_id) {}
118 
119   std::string name;
120   Id referred_type_id;
121 };
122 
123 enum class Qualifier { CONST, VOLATILE, RESTRICT, ATOMIC };
124 
125 std::ostream& operator<<(std::ostream& os, Qualifier qualifier);
126 
127 struct Qualified {
128   Qualified(Qualifier qualifier, Id qualified_type_id)
129       : qualifier(qualifier), qualified_type_id(qualified_type_id) {}
130 
131   Qualifier qualifier;
132   Id qualified_type_id;
133 };
134 
135 struct Primitive {
136   enum class Encoding {
137     BOOLEAN,
138     SIGNED_INTEGER,
139     UNSIGNED_INTEGER,
140     SIGNED_CHARACTER,
141     UNSIGNED_CHARACTER,
142     REAL_NUMBER,
143     COMPLEX_NUMBER,
144     UTF,
145   };
146   Primitive(const std::string& name, std::optional<Encoding> encoding,
147             uint32_t bytesize)
148       : name(name), encoding(encoding), bytesize(bytesize) {}
149 
150   std::string name;
151   std::optional<Encoding> encoding;
152   uint32_t bytesize;
153 };
154 
155 struct Array {
156   Array(uint64_t number_of_elements, Id element_type_id)
157       : number_of_elements(number_of_elements),
158         element_type_id(element_type_id)  {}
159 
160   uint64_t number_of_elements;
161   Id element_type_id;
162 };
163 
164 struct BaseClass {
165   enum class Inheritance { NON_VIRTUAL, VIRTUAL };
166   BaseClass(Id type_id, uint64_t offset, Inheritance inheritance)
167       : type_id(type_id), offset(offset), inheritance(inheritance) {}
168 
169   Id type_id;
170   uint64_t offset;
171   Inheritance inheritance;
172 };
173 
174 std::ostream& operator<<(std::ostream& os, BaseClass::Inheritance inheritance);
175 
176 struct Method {
177   Method(const std::string& mangled_name, const std::string& name,
178          uint64_t vtable_offset, Id type_id)
179       : mangled_name(mangled_name), name(name),
180         vtable_offset(vtable_offset), type_id(type_id) {}
181 
182   std::string mangled_name;
183   std::string name;
184   uint64_t vtable_offset;
185   Id type_id;
186 };
187 
188 struct Member {
189   Member(const std::string& name, Id type_id, uint64_t offset, uint64_t bitsize)
190       : name(name), type_id(type_id), offset(offset), bitsize(bitsize) {}
191 
192   std::string name;
193   Id type_id;
194   uint64_t offset;
195   uint64_t bitsize;
196 };
197 
198 struct VariantMember {
199   VariantMember(const std::string& name,
200                 std::optional<int64_t> discriminant_value, Id type_id)
201       : name(name), discriminant_value(discriminant_value), type_id(type_id) {}
202 
203   std::string name;
204   std::optional<int64_t> discriminant_value;
205   Id type_id;
206 };
207 
208 struct StructUnion {
209   enum class Kind { STRUCT, UNION };
210   struct Definition {
211     uint64_t bytesize;
212     std::vector<Id> base_classes;
213     std::vector<Id> methods;
214     std::vector<Id> members;
215   };
216   StructUnion(Kind kind, const std::string& name) : kind(kind), name(name) {}
217   StructUnion(Kind kind, const std::string& name, uint64_t bytesize,
218               const std::vector<Id>& base_classes,
219               const std::vector<Id>& methods, const std::vector<Id>& members)
220       : kind(kind), name(name),
221         definition({bytesize, base_classes, methods, members}) {}
222 
223   Kind kind;
224   std::string name;
225   std::optional<Definition> definition;
226 };
227 
228 std::ostream& operator<<(std::ostream& os, StructUnion::Kind kind);
229 std::string& operator+=(std::string& os, StructUnion::Kind kind);
230 
231 struct Enumeration {
232   using Enumerators = std::vector<std::pair<std::string, int64_t>>;
233   struct Definition {
234     Id underlying_type_id;
235     Enumerators enumerators;
236   };
237   explicit Enumeration(const std::string& name) : name(name) {}
238   Enumeration(const std::string& name, Id underlying_type_id,
239               const Enumerators& enumerators)
240       : name(name), definition({underlying_type_id, enumerators}) {}
241 
242   std::string name;
243   std::optional<Definition> definition;
244 };
245 
246 struct Variant {
247   Variant(const std::string& name, uint64_t bytesize,
248           std::optional<Id> discriminant, const std::vector<Id>& members)
249       : name(name),
250         bytesize(bytesize),
251         discriminant(discriminant),
252         members(members) {}
253 
254   std::string name;
255   uint64_t bytesize;
256   std::optional<Id> discriminant;
257   std::vector<Id> members;
258 };
259 
260 struct Function {
261   Function(Id return_type_id, const std::vector<Id>& parameters)
262       : return_type_id(return_type_id), parameters(parameters) {}
263 
264   Id return_type_id;
265   std::vector<Id> parameters;
266 };
267 
268 struct ElfSymbol {
269   enum class SymbolType { NOTYPE, OBJECT, FUNCTION, COMMON, TLS, GNU_IFUNC };
270   enum class Binding { GLOBAL, LOCAL, WEAK, GNU_UNIQUE };
271   enum class Visibility { DEFAULT, PROTECTED, HIDDEN, INTERNAL };
272   struct VersionInfo {
273     auto operator<=>(const VersionInfo&) const = default;
274     bool is_default;
275     std::string name;
276   };
277   struct CRC {
278     explicit CRC(uint32_t number) : number(number) {}
279     auto operator<=>(const CRC&) const = default;
280     uint32_t number;
281   };
282   ElfSymbol(const std::string& symbol_name,
283             std::optional<VersionInfo> version_info,
284             bool is_defined,
285             SymbolType symbol_type,
286             Binding binding,
287             Visibility visibility,
288             std::optional<CRC> crc,
289             std::optional<std::string> ns,
290             std::optional<Id> type_id,
291             const std::optional<std::string>& full_name)
292       : symbol_name(symbol_name),
293         version_info(version_info),
294         is_defined(is_defined),
295         symbol_type(symbol_type),
296         binding(binding),
297         visibility(visibility),
298         crc(crc),
299         ns(ns),
300         type_id(type_id),
301         full_name(full_name) {}
302 
303   std::string symbol_name;
304   std::optional<VersionInfo> version_info;
305   bool is_defined;
306   SymbolType symbol_type;
307   Binding binding;
308   Visibility visibility;
309   std::optional<CRC> crc;
310   std::optional<std::string> ns;
311   std::optional<Id> type_id;
312   std::optional<std::string> full_name;
313 };
314 
315 std::ostream& operator<<(std::ostream& os, ElfSymbol::SymbolType);
316 std::ostream& operator<<(std::ostream& os, ElfSymbol::Binding);
317 std::ostream& operator<<(std::ostream& os, ElfSymbol::Visibility);
318 
319 std::string VersionInfoToString(const ElfSymbol::VersionInfo& version_info);
320 std::string VersionedSymbolName(const ElfSymbol&);
321 
322 std::ostream& operator<<(std::ostream& os, ElfSymbol::CRC crc);
323 
324 struct Interface {
325   explicit Interface(const std::map<std::string, Id>& symbols)
326       : symbols(symbols) {}
327   Interface(const std::map<std::string, Id>& symbols,
328             const std::map<std::string, Id>& types)
329       : symbols(symbols), types(types) {}
330 
331   std::map<std::string, Id> symbols;
332   std::map<std::string, Id> types;
333 };
334 
335 std::ostream& operator<<(std::ostream& os, Primitive::Encoding encoding);
336 
337 // Concrete graph type.
338 class Graph {
339  public:
340   Id Limit() const {
341     return Id(indirection_.size());
342   }
343 
344   bool Is(Id id) const {
345     return indirection_[id.ix_].first != Which::ABSENT;
346   }
347 
348   Id Allocate() {
349     const auto id = Limit();
350     indirection_.emplace_back(Which::ABSENT, 0);
351     return id;
352   }
353 
354   template <typename Node, typename... Args>
355   void Set(Id id, Args&&... args) {
356     auto& reference = indirection_[id.ix_];
357     if (reference.first != Which::ABSENT) {
358       Die() << "node value already set: " << id;
359     }
360     if constexpr (std::is_same_v<Node, Special>) {
361       reference = {Which::SPECIAL, special_.size()};
362       special_.emplace_back(std::forward<Args>(args)...);
363     } else if constexpr (std::is_same_v<Node, PointerReference>) {
364       reference = {Which::POINTER_REFERENCE, pointer_reference_.size()};
365       pointer_reference_.emplace_back(std::forward<Args>(args)...);
366     } else if constexpr (std::is_same_v<Node, PointerToMember>) {
367       reference = {Which::POINTER_TO_MEMBER, pointer_to_member_.size()};
368       pointer_to_member_.emplace_back(std::forward<Args>(args)...);
369     } else if constexpr (std::is_same_v<Node, Typedef>) {
370       reference = {Which::TYPEDEF, typedef_.size()};
371       typedef_.emplace_back(std::forward<Args>(args)...);
372     } else if constexpr (std::is_same_v<Node, Qualified>) {
373       reference = {Which::QUALIFIED, qualified_.size()};
374       qualified_.emplace_back(std::forward<Args>(args)...);
375     } else if constexpr (std::is_same_v<Node, Primitive>) {
376       reference = {Which::PRIMITIVE, primitive_.size()};
377       primitive_.emplace_back(std::forward<Args>(args)...);
378     } else if constexpr (std::is_same_v<Node, Array>) {
379       reference = {Which::ARRAY, array_.size()};
380       array_.emplace_back(std::forward<Args>(args)...);
381     } else if constexpr (std::is_same_v<Node, BaseClass>) {
382       reference = {Which::BASE_CLASS, base_class_.size()};
383       base_class_.emplace_back(std::forward<Args>(args)...);
384     } else if constexpr (std::is_same_v<Node, Method>) {
385       reference = {Which::METHOD, method_.size()};
386       method_.emplace_back(std::forward<Args>(args)...);
387     } else if constexpr (std::is_same_v<Node, Member>) {
388       reference = {Which::MEMBER, member_.size()};
389       member_.emplace_back(std::forward<Args>(args)...);
390     } else if constexpr (std::is_same_v<Node, VariantMember>) {
391       reference = {Which::VARIANT_MEMBER, variant_member_.size()};
392       variant_member_.emplace_back(std::forward<Args>(args)...);
393     } else if constexpr (std::is_same_v<Node, StructUnion>) {
394       reference = {Which::STRUCT_UNION, struct_union_.size()};
395       struct_union_.emplace_back(std::forward<Args>(args)...);
396     } else if constexpr (std::is_same_v<Node, Enumeration>) {
397       reference = {Which::ENUMERATION, enumeration_.size()};
398       enumeration_.emplace_back(std::forward<Args>(args)...);
399     } else if constexpr (std::is_same_v<Node, Variant>) {
400       reference = {Which::VARIANT, variant_.size()};
401       variant_.emplace_back(std::forward<Args>(args)...);
402     } else if constexpr (std::is_same_v<Node, Function>) {
403       reference = {Which::FUNCTION, function_.size()};
404       function_.emplace_back(std::forward<Args>(args)...);
405     } else if constexpr (std::is_same_v<Node, ElfSymbol>) {
406       reference = {Which::ELF_SYMBOL, elf_symbol_.size()};
407       elf_symbol_.emplace_back(std::forward<Args>(args)...);
408     } else if constexpr (std::is_same_v<Node, Interface>) {
409       reference = {Which::INTERFACE, interface_.size()};
410       interface_.emplace_back(std::forward<Args>(args)...);
411     } else {
412       // unfortunately we cannot static_assert(false, "missing case")
413       static_assert(std::is_same<Node, Node*>::value, "missing case");
414     }
415   }
416 
417   template <typename Node, typename... Args>
418   Id Add(Args&&... args) {
419     auto id = Allocate();
420     Set<Node>(id, std::forward<Args>(args)...);
421     return id;
422   }
423 
424   void Deallocate(Id) {
425     // don't actually do anything, not supported
426   }
427 
428   void Unset(Id id) {
429     auto& reference = indirection_[id.ix_];
430     if (reference.first == Which::ABSENT) {
431       Die() << "node value already unset: " << id;
432     }
433     reference = {Which::ABSENT, 0};
434   }
435 
436   void Remove(Id id) {
437     Unset(id);
438     Deallocate(id);
439   }
440 
441   template <typename FunctionObject, typename... Args>
442   decltype(auto) Apply(FunctionObject&& function, Id id, Args&&... args) const;
443 
444   template <typename FunctionObject, typename... Args>
445   decltype(auto) Apply2(
446       FunctionObject&& function, Id id1, Id id2, Args&&... args) const;
447 
448   template <typename FunctionObject, typename... Args>
449   decltype(auto) Apply(FunctionObject&& function, Id id, Args&&... args);
450 
451   template <typename FunctionObject>
452   void ForEach(Id start, Id limit, FunctionObject&& function) const {
453     for (size_t ix = start.ix_; ix < limit.ix_; ++ix) {
454       const Id id(ix);
455       if (Is(id)) {
456         function(id);
457       }
458     }
459   }
460 
461  private:
462   enum class Which {
463     ABSENT,
464     SPECIAL,
465     POINTER_REFERENCE,
466     POINTER_TO_MEMBER,
467     TYPEDEF,
468     QUALIFIED,
469     PRIMITIVE,
470     ARRAY,
471     BASE_CLASS,
472     METHOD,
473     MEMBER,
474     VARIANT_MEMBER,
475     STRUCT_UNION,
476     ENUMERATION,
477     VARIANT,
478     FUNCTION,
479     ELF_SYMBOL,
480     INTERFACE,
481   };
482 
483   std::vector<std::pair<Which, size_t>> indirection_;
484 
485   std::vector<Special> special_;
486   std::vector<PointerReference> pointer_reference_;
487   std::vector<PointerToMember> pointer_to_member_;
488   std::vector<Typedef> typedef_;
489   std::vector<Qualified> qualified_;
490   std::vector<Primitive> primitive_;
491   std::vector<Array> array_;
492   std::vector<BaseClass> base_class_;
493   std::vector<Method> method_;
494   std::vector<Member> member_;
495   std::vector<VariantMember> variant_member_;
496   std::vector<StructUnion> struct_union_;
497   std::vector<Enumeration> enumeration_;
498   std::vector<Variant> variant_;
499   std::vector<Function> function_;
500   std::vector<ElfSymbol> elf_symbol_;
501   std::vector<Interface> interface_;
502 };
503 
504 template <typename FunctionObject, typename... Args>
505 decltype(auto) Graph::Apply(
506     FunctionObject&& function, Id id, Args&&... args) const {
507   const auto& [which, ix] = indirection_[id.ix_];
508   switch (which) {
509     case Which::ABSENT:
510       Die() << "undefined node: " << id;
511     case Which::SPECIAL:
512       return function(special_[ix], std::forward<Args>(args)...);
513     case Which::POINTER_REFERENCE:
514       return function(pointer_reference_[ix], std::forward<Args>(args)...);
515     case Which::POINTER_TO_MEMBER:
516       return function(pointer_to_member_[ix], std::forward<Args>(args)...);
517     case Which::TYPEDEF:
518       return function(typedef_[ix], std::forward<Args>(args)...);
519     case Which::QUALIFIED:
520       return function(qualified_[ix], std::forward<Args>(args)...);
521     case Which::PRIMITIVE:
522       return function(primitive_[ix], std::forward<Args>(args)...);
523     case Which::ARRAY:
524       return function(array_[ix], std::forward<Args>(args)...);
525     case Which::BASE_CLASS:
526       return function(base_class_[ix], std::forward<Args>(args)...);
527     case Which::METHOD:
528       return function(method_[ix], std::forward<Args>(args)...);
529     case Which::MEMBER:
530       return function(member_[ix], std::forward<Args>(args)...);
531     case Which::VARIANT_MEMBER:
532       return function(variant_member_[ix], std::forward<Args>(args)...);
533     case Which::STRUCT_UNION:
534       return function(struct_union_[ix], std::forward<Args>(args)...);
535     case Which::ENUMERATION:
536       return function(enumeration_[ix], std::forward<Args>(args)...);
537     case Which::VARIANT:
538       return function(variant_[ix], std::forward<Args>(args)...);
539     case Which::FUNCTION:
540       return function(function_[ix], std::forward<Args>(args)...);
541     case Which::ELF_SYMBOL:
542       return function(elf_symbol_[ix], std::forward<Args>(args)...);
543     case Which::INTERFACE:
544       return function(interface_[ix], std::forward<Args>(args)...);
545   }
546 }
547 
548 template <typename FunctionObject, typename... Args>
549 decltype(auto) Graph::Apply2(
550     FunctionObject&& function, Id id1, Id id2, Args&&... args) const {
551   const auto& [which1, ix1] = indirection_[id1.ix_];
552   const auto& [which2, ix2] = indirection_[id2.ix_];
553   if (which1 != which2) {
554     return function.Mismatch(std::forward<Args>(args)...);
555   }
556   switch (which1) {
557     case Which::ABSENT:
558       Die() << "undefined nodes: " << id1 << ", " << id2;
559     case Which::SPECIAL:
560       return function(special_[ix1], special_[ix2],
561                       std::forward<Args>(args)...);
562     case Which::POINTER_REFERENCE:
563       return function(pointer_reference_[ix1], pointer_reference_[ix2],
564                       std::forward<Args>(args)...);
565     case Which::POINTER_TO_MEMBER:
566       return function(pointer_to_member_[ix1], pointer_to_member_[ix2],
567                       std::forward<Args>(args)...);
568     case Which::TYPEDEF:
569       return function(typedef_[ix1], typedef_[ix2],
570                       std::forward<Args>(args)...);
571     case Which::QUALIFIED:
572       return function(qualified_[ix1], qualified_[ix2],
573                       std::forward<Args>(args)...);
574     case Which::PRIMITIVE:
575       return function(primitive_[ix1], primitive_[ix2],
576                       std::forward<Args>(args)...);
577     case Which::ARRAY:
578       return function(array_[ix1], array_[ix2],
579                       std::forward<Args>(args)...);
580     case Which::BASE_CLASS:
581       return function(base_class_[ix1], base_class_[ix2],
582                       std::forward<Args>(args)...);
583     case Which::METHOD:
584       return function(method_[ix1], method_[ix2],
585                       std::forward<Args>(args)...);
586     case Which::MEMBER:
587       return function(member_[ix1], member_[ix2],
588                       std::forward<Args>(args)...);
589     case Which::VARIANT_MEMBER:
590       return function(variant_member_[ix1], variant_member_[ix2],
591                       std::forward<Args>(args)...);
592     case Which::STRUCT_UNION:
593       return function(struct_union_[ix1], struct_union_[ix2],
594                       std::forward<Args>(args)...);
595     case Which::ENUMERATION:
596       return function(enumeration_[ix1], enumeration_[ix2],
597                       std::forward<Args>(args)...);
598     case Which::VARIANT:
599       return function(variant_[ix1], variant_[ix2],
600                       std::forward<Args>(args)...);
601     case Which::FUNCTION:
602       return function(function_[ix1], function_[ix2],
603                       std::forward<Args>(args)...);
604     case Which::ELF_SYMBOL:
605       return function(elf_symbol_[ix1], elf_symbol_[ix2],
606                       std::forward<Args>(args)...);
607     case Which::INTERFACE:
608       return function(interface_[ix1], interface_[ix2],
609                       std::forward<Args>(args)...);
610   }
611 }
612 
613 template <typename FunctionObject, typename... Args>
614 struct ConstAdapter {
615   explicit ConstAdapter(FunctionObject& function) : function(function) {}
616   template <typename Node>
617   decltype(auto) operator()(const Node& node, Args&&... args) {
618     return function(const_cast<Node&>(node), std::forward<Args>(args)...);
619   }
620   FunctionObject& function;
621 };
622 
623 template <typename FunctionObject, typename... Args>
624 decltype(auto) Graph::Apply(FunctionObject&& function, Id id, Args&&... args) {
625   ConstAdapter<FunctionObject, Args&&...> adapter(function);
626   return static_cast<const Graph&>(*this).Apply(
627       adapter, id, std::forward<Args>(args)...);
628 }
629 
630 struct InterfaceKey {
631   explicit InterfaceKey(const Graph& graph) : graph(graph) {}
632 
633   std::string operator()(Id id) const {
634     return graph.Apply(*this, id);
635   }
636 
637   std::string operator()(const stg::Typedef& x) const {
638     return x.name;
639   }
640 
641   std::string operator()(const stg::StructUnion& x) const {
642     if (x.name.empty()) {
643       Die() << "anonymous struct/union interface type";
644     }
645     std::ostringstream os;
646     os << x.kind << ' ' << x.name;
647     return os.str();
648   }
649 
650   std::string operator()(const stg::Enumeration& x) const {
651     if (x.name.empty()) {
652       Die() << "anonymous enum interface type";
653     }
654     return "enum " + x.name;
655   }
656 
657   std::string operator()(const stg::Variant& x) const {
658     if (x.name.empty()) {
659       Die() << "anonymous variant interface type";
660     }
661     return "variant " + x.name;
662   }
663 
664   std::string operator()(const stg::ElfSymbol& x) const {
665     return VersionedSymbolName(x);
666   }
667 
668   template <typename Node>
669   std::string operator()(const Node&) const {
670     Die() << "unexpected interface type";
671   }
672 
673   const Graph& graph;
674 };
675 
676 // Roughly equivalent to std::set<Id> but with constant time operations and
677 // key set limited to allocated Ids.
678 class DenseIdSet {
679  public:
680   explicit DenseIdSet(Id start) : offset_(start.ix_) {}
681   void Reserve(Id limit) {
682     ids_.reserve(limit.ix_ - offset_);
683   }
684   bool Insert(Id id) {
685     const auto ix = id.ix_;
686     if (ix < offset_) {
687       Die() << "DenseIdSet: out of range access to " << id;
688     }
689     const auto offset_ix = ix - offset_;
690     if (offset_ix >= ids_.size()) {
691       ids_.resize(offset_ix + 1, false);
692     }
693     if (ids_[offset_ix]) {
694       return false;
695     }
696     ids_[offset_ix] = true;
697     return true;
698   }
699 
700  private:
701   size_t offset_;
702   std::vector<bool> ids_;
703 };
704 
705 // Roughly equivalent to std::map<Id, Id>, defaulted to the identity mapping,
706 // but with constant time operations and key set limited to allocated Ids.
707 class DenseIdMapping {
708  public:
709   explicit DenseIdMapping(Id start) : offset_(start.ix_) {}
710   void Reserve(Id limit) {
711     ids_.reserve(limit.ix_ - offset_);
712   }
713   Id& operator[](Id id) {
714     const auto ix = id.ix_;
715     if (ix < offset_) {
716       Die() << "DenseIdMapping: out of range access to " << id;
717     }
718     Populate(ix + 1);
719     return ids_[ix - offset_];
720   }
721 
722  private:
723   void Populate(size_t size) {
724     for (size_t ix = offset_ + ids_.size(); ix < size; ++ix) {
725       ids_.emplace_back(ix);
726     }
727   }
728 
729   size_t offset_;
730   std::vector<Id> ids_;
731 };
732 
733 template <typename ExternalId>
734 class Maker {
735  public:
736   explicit Maker(Graph& graph) : graph_(graph) {}
737 
738   ~Maker() noexcept(false) {
739     if (std::uncaught_exceptions() == 0) {
740       if (undefined_ > 0) {
741         Die die;
742         die << "undefined nodes:";
743         for (const auto& [external_id, id] : map_) {
744           if (!graph_.Is(id)) {
745             die << ' ' << external_id;
746           }
747         }
748       }
749     }
750   }
751 
752   Id Get(const ExternalId& external_id) {
753     auto [it, inserted] = map_.emplace(external_id, 0);
754     if (inserted) {
755       it->second = graph_.Allocate();
756       ++undefined_;
757     }
758     return it->second;
759   }
760 
761   template <typename Node, typename... Args>
762   Id Set(const ExternalId& external_id, Args&&... args) {
763     return Set<Node>(DieDuplicate, external_id, std::forward<Args>(args)...);
764   }
765 
766   template <typename Node, typename... Args>
767   Id MaybeSet(const ExternalId& external_id, Args&&... args) {
768     return Set<Node>(WarnDuplicate, external_id, std::forward<Args>(args)...);
769   }
770 
771   template <typename Node, typename... Args>
772   Id Add(Args&&... args) {
773     return graph_.Add<Node>(std::forward<Args>(args)...);
774   }
775 
776  private:
777   Graph& graph_;
778   size_t undefined_ = 0;
779   std::unordered_map<ExternalId, Id> map_;
780 
781   template <typename Node, typename... Args>
782   Id Set(void(& fail)(const ExternalId&), const ExternalId& external_id,
783          Args&&... args) {
784     const Id id = Get(external_id);
785     if (graph_.Is(id)) {
786       fail(external_id);
787     } else {
788       graph_.Set<Node>(id, std::forward<Args>(args)...);
789       --undefined_;
790     }
791     return id;
792   }
793 
794   // These helpers should probably not be inlined.
795   [[noreturn]] static void DieDuplicate(const ExternalId& external_id) {
796     Die() << "duplicate definition of node: " << external_id;
797   }
798   static void WarnDuplicate(const ExternalId& external_id) {
799     Warn() << "ignoring duplicate definition of node: " << external_id;
800   }
801 };
802 
803 }  // namespace stg
804 
805 #endif  // STG_GRAPH_H_
806