Add some data annotation to allow for data profiling
[hiphop-php.git] / hphp / runtime / base / static-string-table.cpp
blobe5b05c96697f899dac946712191f2abb9cab19ec
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/static-string-table.h"
18 #include "hphp/runtime/base/class-info.h"
19 #include "hphp/runtime/base/rds.h"
20 #include "hphp/runtime/base/runtime-option.h"
21 #include "hphp/runtime/vm/debug/debug.h"
23 #include "folly/AtomicHashMap.h"
25 namespace HPHP {
27 //////////////////////////////////////////////////////////////////////
29 namespace {
31 // Pointer to StringData, or pointer to StringSlice.
32 typedef intptr_t StrInternKey;
34 constexpr intptr_t kAhmMagicThreshold = -3;
36 StrInternKey make_intern_key(const StringData* sd) {
37 auto const ret = reinterpret_cast<StrInternKey>(sd);
38 assert(ret > 0);
39 return ret;
42 StrInternKey make_intern_key(const StringSlice* sl) {
43 auto const ret = -reinterpret_cast<StrInternKey>(sl);
44 assert(ret < 0 && ret < kAhmMagicThreshold);
45 return ret;
48 const StringData* to_sdata(StrInternKey key) {
49 assert(key > 0);
50 return reinterpret_cast<const StringData*>(key);
53 const StringSlice* to_sslice(StrInternKey key) {
54 assert(key < 0 && key < kAhmMagicThreshold);
55 return reinterpret_cast<const StringSlice*>(-key);
58 // To avoid extra instructions in strintern_eq, we currently are
59 // making use of the fact that StringSlice and StringData have the
60 // same initial layout. See the static_asserts in checkSane.
61 const StringSlice* to_sslice_punned(StrInternKey key) {
62 if (UNLIKELY(key < 0)) {
63 return reinterpret_cast<const StringSlice*>(-key);
65 // Actually a StringData*, but same layout.
66 return reinterpret_cast<const StringSlice*>(key);
69 struct strintern_eq {
70 bool operator()(StrInternKey k1, StrInternKey k2) const {
71 if (k1 < 0) {
72 // AHM only gives lookup keys on the rhs of the equal operator
73 assert(k1 >= kAhmMagicThreshold);
74 return false;
76 assert(k2 >= 0 || k2 < kAhmMagicThreshold);
77 auto const sd1 = to_sdata(k1);
78 auto const s2 = to_sslice_punned(k2);
79 return sd1->size() == s2->len &&
80 wordsame(sd1->data(), s2->ptr, s2->len);
84 struct strintern_hash {
85 size_t operator()(StrInternKey k) const {
86 assert(k > 0 || k < kAhmMagicThreshold);
87 if (LIKELY(k > 0)) {
88 return to_sdata(k)->hash();
90 auto const slice = *to_sslice(k);
91 return hash_string_inline(slice.ptr, slice.len);
95 // The uint32_t is used to hold RDS offsets for constants
96 typedef folly::AtomicHashMap<
97 StrInternKey,
98 RDS::Link<TypedValue>,
99 strintern_hash,
100 strintern_eq
101 > StringDataMap;
102 StringDataMap* s_stringDataMap;
104 // If a string is static it better be the one in the table.
105 DEBUG_ONLY bool checkStaticStr(const StringData* s) {
106 assert(s->isStatic());
107 auto DEBUG_ONLY const it = s_stringDataMap->find(make_intern_key(s));
108 assert(it != s_stringDataMap->end());
109 assert(to_sdata(it->first) == s);
110 return true;
113 void create_string_data_map() {
114 StringDataMap::Config config;
115 config.growthFactor = 1;
116 s_stringDataMap =
117 new StringDataMap(RuntimeOption::EvalInitialStaticStringTableSize,
118 config);
121 StringData** precompute_chars();
122 StringData** precompute_chars() {
123 StringData** raw = new StringData*[256];
124 for (int i = 0; i < 256; i++) {
125 char s[2] = { (char)i, 0 };
126 raw[i] = makeStaticString(&s[0], 1);
128 return raw;
131 StringData** precomputed_chars = precompute_chars();
133 StringData* insertStaticString(StringSlice slice) {
134 auto const sd = StringData::MakeStatic(slice);
135 auto pair = s_stringDataMap->insert(
136 make_intern_key(sd),
137 RDS::Link<TypedValue>(RDS::kInvalidHandle)
139 if (!pair.second) {
140 sd->destructStatic();
142 assert(to_sdata(pair.first->first) != nullptr);
143 return const_cast<StringData*>(to_sdata(pair.first->first));
148 //////////////////////////////////////////////////////////////////////
150 size_t makeStaticStringCount() {
151 if (!s_stringDataMap) return 0;
152 return s_stringDataMap->size();
155 StringData* makeStaticString(const StringData* str) {
156 if (UNLIKELY(!s_stringDataMap)) {
157 create_string_data_map();
159 if (str->isStatic()) {
160 assert(checkStaticStr(str));
161 return const_cast<StringData*>(str);
163 auto const it = s_stringDataMap->find(make_intern_key(str));
164 if (it != s_stringDataMap->end()) {
165 return const_cast<StringData*>(to_sdata(it->first));
167 return insertStaticString(str->slice());
170 StringData* makeStaticString(StringSlice slice) {
171 if (UNLIKELY(!s_stringDataMap)) {
172 create_string_data_map();
174 auto const it = s_stringDataMap->find(make_intern_key(&slice));
175 if (it != s_stringDataMap->end()) {
176 return const_cast<StringData*>(to_sdata(it->first));
178 return insertStaticString(slice);
181 StringData* lookupStaticString(const StringData *str) {
182 if (UNLIKELY(!s_stringDataMap)) return nullptr;
183 if (str->isStatic()) {
184 assert(checkStaticStr(str));
185 return const_cast<StringData*>(str);
187 auto const it = s_stringDataMap->find(make_intern_key(str));
188 if (it != s_stringDataMap->end()) {
189 return const_cast<StringData*>(to_sdata(it->first));
191 return nullptr;
194 StringData* makeStaticString(const String& str) {
195 assert(!str.isNull());
196 return makeStaticString(str.get());
199 StringData* makeStaticString(const char* str, size_t len) {
200 assert(len <= StringData::MaxSize);
201 return makeStaticString(StringSlice{str, static_cast<uint32_t>(len)});
204 StringData* makeStaticString(const std::string& str) {
205 assert(str.size() <= StringData::MaxSize);
206 return makeStaticString(
207 StringSlice{str.c_str(), static_cast<uint32_t>(str.size())}
211 StringData* makeStaticString(const char* str) {
212 return makeStaticString(str, strlen(str));
215 StringData* makeStaticString(char c) {
216 // TODO(#2880477): should this be inlined?
217 return precomputed_chars[(uint8_t)c];
220 RDS::Handle lookupCnsHandle(const StringData* cnsName) {
221 assert(s_stringDataMap);
222 auto const it = s_stringDataMap->find(make_intern_key(cnsName));
223 if (it != s_stringDataMap->end()) {
224 return it->second.handle();
226 return 0;
229 RDS::Handle makeCnsHandle(const StringData* cnsName, bool persistent) {
230 auto const val = lookupCnsHandle(cnsName);
231 if (val) return val;
232 if (!cnsName->isStatic()) {
233 // Its a dynamic constant, that doesn't correspond to
234 // an already allocated handle. We'll allocate it in
235 // the request local RDS::s_constants instead.
236 return 0;
238 auto const it = s_stringDataMap->find(make_intern_key(cnsName));
239 assert(it != s_stringDataMap->end());
240 if (!it->second.bound()) {
241 it->second.bind<kTVSimdAlign>(persistent ? RDS::Mode::Persistent
242 : RDS::Mode::Normal);
244 RDS::recordRds(it->second.handle(), sizeof(TypedValue),
245 "Cns", cnsName->data());
247 return it->second.handle();
250 const StaticString s_user("user");
251 const StaticString s_Core("Core");
253 Array lookupDefinedConstants(bool categorize /*= false */) {
254 assert(s_stringDataMap);
255 Array usr(RDS::s_constants());
256 Array sys;
258 for (auto it = s_stringDataMap->begin();
259 it != s_stringDataMap->end(); ++it) {
260 if (it->second.bound()) {
261 Array *tbl = (categorize &&
262 RDS::isPersistentHandle(it->second.handle()))
263 ? &sys : &usr;
264 auto& tv = *it->second;
265 if (tv.m_type != KindOfUninit) {
266 StrNR key(const_cast<StringData*>(to_sdata(it->first)));
267 tbl->set(key, tvAsVariant(&tv), true);
268 } else if (tv.m_data.pref) {
269 StrNR key(const_cast<StringData*>(to_sdata(it->first)));
270 ClassInfo::ConstantInfo* ci =
271 (ClassInfo::ConstantInfo*)(void*)tv.m_data.pref;
272 auto cns = ci->getDeferredValue();
273 if (cns.isInitialized()) {
274 tbl->set(key, cns, true);
280 if (categorize) {
281 Array ret;
282 ret.set(s_user, usr);
283 ret.set(s_Core, sys);
284 return ret;
285 } else {
286 return usr;
290 void refineStaticStringTableSize() {
291 if (RuntimeOption::EvalInitialStaticStringTableSize ==
292 kDefaultInitialStaticStringTableSize) {
293 return;
295 auto oldStringTable = s_stringDataMap;
296 if (!oldStringTable) return;
298 s_stringDataMap = nullptr;
299 create_string_data_map();
300 SCOPE_EXIT { delete oldStringTable; };
302 for (auto& kv : *oldStringTable) {
303 s_stringDataMap->insert(kv.first, kv.second);
307 //////////////////////////////////////////////////////////////////////