2015-01-06 01:28:56 +00:00
|
|
|
#include "intern.h"
|
|
|
|
#include <unordered_map>
|
2015-02-17 00:23:12 +00:00
|
|
|
#include <cstring>
|
2015-01-06 01:28:56 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
std::unordered_multimap<unsigned, std::string *> InternTable;
|
|
|
|
|
|
|
|
std::string EmptyString;
|
|
|
|
|
|
|
|
unsigned int DJBHash(const char* begin, size_t length)
|
|
|
|
{
|
|
|
|
unsigned int hash = 5381;
|
|
|
|
|
|
|
|
for(size_t i = 0; i < length; ++i)
|
2015-02-18 14:32:01 +00:00
|
|
|
{
|
2015-01-06 01:28:56 +00:00
|
|
|
hash = ((hash << 5) + hash) + (begin[i]);
|
2015-02-18 14:32:01 +00:00
|
|
|
}
|
2015-01-06 01:28:56 +00:00
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace Intern {
|
|
|
|
|
|
|
|
const std::string *String(std::string &&str)
|
|
|
|
{
|
|
|
|
size_t size = str.size();
|
|
|
|
if (!size) return &EmptyString;
|
|
|
|
|
|
|
|
unsigned hash = DJBHash(str.data(), size);
|
|
|
|
|
|
|
|
auto range = InternTable.equal_range(hash);
|
|
|
|
auto iter = range.first;
|
|
|
|
auto endit = range.second;
|
|
|
|
|
|
|
|
for( ; iter != endit; ++iter)
|
|
|
|
{
|
|
|
|
// hash matches, make sure the string does.
|
|
|
|
const std::string *s = iter->second;
|
|
|
|
|
|
|
|
if (s->size() == size && std::memcmp(s->data(), str.data(), size) == 0)
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert it. I suppose this could throw, in which case a string would leak.
|
|
|
|
std::string *s = new std::string(std::move(str));
|
|
|
|
InternTable.emplace(std::make_pair(hash, s));
|
2015-02-18 14:32:01 +00:00
|
|
|
return s;
|
2015-01-06 01:28:56 +00:00
|
|
|
}
|
2015-02-18 14:32:01 +00:00
|
|
|
|
2015-01-06 01:28:56 +00:00
|
|
|
const std::string *String(const char *begin, size_t size)
|
|
|
|
{
|
|
|
|
if (!size) return &EmptyString;
|
|
|
|
|
|
|
|
unsigned hash = DJBHash(begin, size);
|
|
|
|
|
|
|
|
auto range = InternTable.equal_range(hash);
|
|
|
|
auto iter = range.first;
|
|
|
|
auto endit = range.second;
|
|
|
|
|
|
|
|
for( ; iter != endit; ++iter)
|
|
|
|
{
|
|
|
|
// hash matches, make sure the string does.
|
|
|
|
const std::string *s = iter->second;
|
|
|
|
|
|
|
|
if (s->size() == size && std::memcmp(s->data(), begin, size) == 0)
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert it. I suppose this could throw, in which case a string would leak.
|
|
|
|
std::string *s = new std::string(begin, size);
|
|
|
|
InternTable.emplace(std::make_pair(hash, s));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string *String(const char *begin, const char *end)
|
|
|
|
{
|
|
|
|
return String(begin, end - begin);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const std::string *String(const char *cp)
|
|
|
|
{
|
|
|
|
if (!cp || !*cp) return &EmptyString;
|
|
|
|
|
|
|
|
return String(cp, strlen(cp));
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string *String(const std::string &s)
|
|
|
|
{
|
|
|
|
return String(s.data(), s.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|