| 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.
 | 
|---|
| 8 | class SymbolData {
 | 
|---|
| 9 | public:
 | 
|---|
| 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 | 
 | 
|---|
| 20 | namespace {
 | 
|---|
| 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 | 
 | 
|---|
| 38 | using 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.
 | 
|---|
| 44 | static SymbolMap & getAllSymbols() {
 | 
|---|
| 45 |         static SymbolMap allSymbols;
 | 
|---|
| 46 |         return allSymbols;
 | 
|---|
| 47 | }
 | 
|---|
| 48 | 
 | 
|---|
| 49 | /// Clear out all unsed SymbolData.
 | 
|---|
| 50 | static 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.
 | 
|---|
| 65 | static 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.
 | 
|---|
| 83 | static 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 | 
 | 
|---|
| 93 | Symbol::Symbol() : data(getSymbolData(std::string())) {}
 | 
|---|
| 94 | 
 | 
|---|
| 95 | Symbol::Symbol(std::string const & str) : data(getSymbolData(str)) {}
 | 
|---|
| 96 | 
 | 
|---|
| 97 | Symbol::Symbol(char const * str) : data(getSymbolData(str)) {}
 | 
|---|
| 98 | 
 | 
|---|
| 99 | Symbol::Symbol(Symbol const & other) : data(other.data) {
 | 
|---|
| 100 |         ++data->refCount;
 | 
|---|
| 101 | }
 | 
|---|
| 102 | 
 | 
|---|
| 103 | Symbol::Symbol(Symbol && other) : data(other.data) {
 | 
|---|
| 104 |         ++data->refCount;
 | 
|---|
| 105 | }
 | 
|---|
| 106 | 
 | 
|---|
| 107 | Symbol::~Symbol() {
 | 
|---|
| 108 |         decSymbolData(data);
 | 
|---|
| 109 | }
 | 
|---|
| 110 | 
 | 
|---|
| 111 | Symbol & Symbol::operator=(std::string const & str) {
 | 
|---|
| 112 |         SymbolData * dat = getSymbolData(str);
 | 
|---|
| 113 |         decSymbolData(data);
 | 
|---|
| 114 |         data = dat;
 | 
|---|
| 115 |         return *this;
 | 
|---|
| 116 | }
 | 
|---|
| 117 | 
 | 
|---|
| 118 | Symbol & Symbol::operator=(char const * str) {
 | 
|---|
| 119 |         return this->operator=(std::string(str));
 | 
|---|
| 120 | }
 | 
|---|
| 121 | 
 | 
|---|
| 122 | Symbol & Symbol::operator=(Symbol const & other) {
 | 
|---|
| 123 |         ++other.data->refCount;
 | 
|---|
| 124 |         decSymbolData(data);
 | 
|---|
| 125 |         data = other.data;
 | 
|---|
| 126 |         return *this;
 | 
|---|
| 127 | }
 | 
|---|
| 128 | 
 | 
|---|
| 129 | Symbol & Symbol::operator=(Symbol && other) {
 | 
|---|
| 130 |         Symbol const & ref = other;
 | 
|---|
| 131 |         return this->operator=(ref);
 | 
|---|
| 132 | }
 | 
|---|
| 133 | 
 | 
|---|
| 134 | bool Symbol::operator==(Symbol const & other) const {
 | 
|---|
| 135 |         return data == other.data;
 | 
|---|
| 136 | }
 | 
|---|
| 137 | 
 | 
|---|
| 138 | bool Symbol::operator!=(Symbol const & other) const {
 | 
|---|
| 139 |         return data != other.data;
 | 
|---|
| 140 | }
 | 
|---|
| 141 | 
 | 
|---|
| 142 | std::string const & Symbol::str() const {
 | 
|---|
| 143 |         return data->value;
 | 
|---|
| 144 | }
 | 
|---|
| 145 | 
 | 
|---|
| 146 | char const * Symbol::c_str() const {
 | 
|---|
| 147 |         return data->value.c_str();
 | 
|---|
| 148 | }
 | 
|---|