source: src/Common/Symbol.cpp @ d3b33d5

Last change on this file since d3b33d5 was 661e7b0, checked in by Andrew Beach <ajbeach@…>, 3 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
RevLine 
[661e7b0]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.