source: src/Common/Symbol.cpp@ 35cc6d4

Last change on this file since 35cc6d4 was 661e7b0, checked in by Andrew Beach <ajbeach@…>, 13 months ago

After a years (or at least half a year) the CodeLocation optimization is merged in. Added Symbol (using Racket's name for interned strings), and used it for CodeLocation file names. The optimizes for the high number of copies - both instances with the same value and copy operations - and consistently brings down runtime by a few percent.

  • Property mode set to 100644
File size: 3.5 KB
Line 
1#include "Symbol.hpp"
2
3#include <unordered_map>
4
5// This class (and the member-like helpers) are the implementation details
6// of the Symbol class. It is just does reference counting to share strings.
7// It is designed for maintainablity, but also to optimize the copy cases.
8class SymbolData {
9public:
10 std::string const value;
11 // As long as we are using a pointer sized counter,
12 // overflow should never be an issue.
13 std::size_t refCount;
14
15 SymbolData(std::string const & value, std::size_t refCount) :
16 value(value), refCount(refCount)
17 {}
18};
19
20namespace {
21 template<typename T>
22 struct IndirectHash {
23 std::hash<T> hasher;
24 std::size_t operator()(T const * const & val) const {
25 return hasher(*val);
26 }
27 };
28
29 template<typename T>
30 struct IndirectEqualTo {
31 std::equal_to<T> equals;
32 bool operator()(T const * const & lhs, T const * const & rhs) const {
33 return equals(*lhs, *rhs);
34 }
35 };
36} // namespace
37
38using SymbolMap = std::unordered_map<std::string const *, SymbolData *,
39 IndirectHash<std::string>, IndirectEqualTo<std::string>>;
40
41/// Get the storage for the map of symbol data.
42// Note: This is basically static memory, but wrapping the data in a function
43// causes the constructor to run with the proper timing.
44static SymbolMap & getAllSymbols() {
45 static SymbolMap allSymbols;
46 return allSymbols;
47}
48
49/// Clear out all unsed SymbolData.
50static void eraseUnused() {
51 SymbolMap & allSymbols = getAllSymbols();
52
53 auto it = allSymbols.begin();
54 while (it != allSymbols.end()) {
55 if (it->second->refCount) {
56 ++it;
57 } else {
58 delete it->second;
59 it = allSymbols.erase(it);
60 }
61 }
62}
63
64/// Convert a string to a new or existing SymbolData.
65static SymbolData * getSymbolData(std::string const & str) {
66 SymbolMap & allSymbols = getAllSymbols();
67
68 // If there is an existing symbol, re-use it.
69 auto it = allSymbols.find(&str);
70 if (it != allSymbols.end()) {
71 SymbolData * data = it->second;
72 ++data->refCount;
73 return data;
74 }
75
76 // If these is no existing symbol, create the required data.
77 SymbolData * data = new SymbolData(str, 1);
78 allSymbols.insert(std::make_pair(&data->value, data));
79 return data;
80}
81
82/// Reduce reference count and free the SymbolData as needed.
83static void decSymbolData(SymbolData * data) {
84 // First comes the actual decrement.
85 --data->refCount;
86
87 // If it has hit zero, remove it from the map.
88 if (0 == data->refCount) {
89 eraseUnused();
90 }
91}
92
93Symbol::Symbol() : data(getSymbolData(std::string())) {}
94
95Symbol::Symbol(std::string const & str) : data(getSymbolData(str)) {}
96
97Symbol::Symbol(char const * str) : data(getSymbolData(str)) {}
98
99Symbol::Symbol(Symbol const & other) : data(other.data) {
100 ++data->refCount;
101}
102
103Symbol::Symbol(Symbol && other) : data(other.data) {
104 ++data->refCount;
105}
106
107Symbol::~Symbol() {
108 decSymbolData(data);
109}
110
111Symbol & Symbol::operator=(std::string const & str) {
112 SymbolData * dat = getSymbolData(str);
113 decSymbolData(data);
114 data = dat;
115 return *this;
116}
117
118Symbol & Symbol::operator=(char const * str) {
119 return this->operator=(std::string(str));
120}
121
122Symbol & Symbol::operator=(Symbol const & other) {
123 ++other.data->refCount;
124 decSymbolData(data);
125 data = other.data;
126 return *this;
127}
128
129Symbol & Symbol::operator=(Symbol && other) {
130 Symbol const & ref = other;
131 return this->operator=(ref);
132}
133
134bool Symbol::operator==(Symbol const & other) const {
135 return data == other.data;
136}
137
138bool Symbol::operator!=(Symbol const & other) const {
139 return data != other.data;
140}
141
142std::string const & Symbol::str() const {
143 return data->value;
144}
145
146char const * Symbol::c_str() const {
147 return data->value.c_str();
148}
Note: See TracBrowser for help on using the repository browser.