Cogs.Core
Strings.cpp
1#include "Strings.h"
2
3#include "MemoryContext.h"
4
5#include "Foundation/Memory/Allocator.h"
6#include "Foundation/Memory/MemoryBuffer.h"
7#include "Foundation/Platform/Threads.h"
8
9#include <string.h>
10#include <vector>
11#include <unordered_map>
12
13
14namespace
15{
16 struct StringData
17 {
18 Cogs::Core::StringRef next = Cogs::Core::NoString;
20 };
21
22 struct StringStorage
23 {
24 StringStorage() : strings(Cogs::MemBlockType::Strings)
25 {
26 StringData& null = strings.grow();
27 null.str = Cogs::StringView();
28 }
29
31 std::unordered_map<size_t, Cogs::Core::StringRef> stringsByHash;
32 Cogs::Mutex stringMutex;
33 };
34
35 StringStorage& getStorage()
36 {
37 static StringStorage storage;
38 return storage;
39 }
40
41}
42
43
44void Cogs::Core::Strings::cleanup()
45{
46 auto& storage = getStorage();
48 {
49 LockGuard lock(storage.stringMutex);
50 strings = std::move(storage.strings);
51 storage.strings.clear(); // This will trigger an assert if we try to use strings any more
52 storage.stringsByHash.clear();
53 }
54
55#if defined(__has_feature)
56#if __has_feature(address_sanitizer)
57#define ASAN 1
58#endif
59#endif
60
61#if defined(DEBUG) || defined(COGS_PARANOIA) || defined(ASAN) || defined(OSOMP_EnableProfiler)
62 // Not strictly necessary, but failing to deallocate these strings clutters leak analysis.
63 for (auto& str : strings) {
64 MemoryContext::getStringsAllocator()->deallocate(const_cast<char*>(str.str.data()), str.str.size() + 1, MemBlockType::Strings);
65 }
66#endif
67}
68
69
70Cogs::Core::StringRef Cogs::Core::Strings::add(StringView str)
71{
72 size_t hash = str.hash();
73
74 StringStorage& storage = getStorage();
75 LockGuard lock(storage.stringMutex);
76 assert(storage.strings.byteSize() != 0);
77
78 StringRef head = NoString;
79
80 // If we have seen the hash before, run through the linked strings and see if
81 // we find a match. Usually there is just one string, but in case of hash collisions,
82 // there might be more than one.
83 if (auto it = storage.stringsByHash.find(hash); it != storage.stringsByHash.end()) {
84 head = it->second;
85
86 StringRef ref = head;
87 while (ref != NoString) {
88 const StringData& stringData = storage.strings[ref.value];
89 if (stringData.str == str) {
90 // We have seen this string before, return reference.
91 return ref;
92 }
93 ref = stringData.next;
94 }
95
96 // We have seen the hash, but not the string, the variable head new contains
97 // the first string of the linked string of this hash.
98 }
99
100 // We have not seen this string before, allocate new string
101 StringRef ref = StringRef{ static_cast<uint32_t>(storage.strings.size()) };
102 char* strData = (char*)MemoryContext::getStringsAllocator()->allocate(str.size() + 1, 0, MemBlockType::Strings);
103#ifdef _WIN32
104 ::strncpy_s(strData, str.size() + 1, str.data(), str.size());
105 strData[str.size()] = '\0';
106#else
107 strncpy(strData, str.data(), str.size());
108 strData[str.size()] = '\0';
109#endif
110 StringData& stringData = storage.strings.grow();
111 stringData.str = StringView(strData, str.size());
112
113 // Link up new string by inserting it at the front.
114 stringData.next = head;
115 storage.stringsByHash[hash] = ref;
116
117 return ref;
118}
119
120Cogs::StringView Cogs::Core::Strings::get(StringRef ref)
121{
122 StringStorage& storage = getStorage();
123 LockGuard lock(storage.stringMutex);
124 assert(storage.strings.byteSize() != 0);
125
126 return storage.strings[ref.value].str;
127}
128
129const char* Cogs::Core::Strings::getC(StringRef ref)
130{
131 StringStorage& storage = getStorage();
132 LockGuard lock(storage.stringMutex);
133 assert(storage.strings.byteSize() != 0);
134
135 return storage.strings[ref.value].str.data();
136}
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
Contains all Cogs related functionality.
Definition: FieldSetter.h:23