2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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/perf-warning.h"
19 #include "hphp/runtime/base/rds.h"
20 #include "hphp/runtime/base/runtime-option.h"
21 #include "hphp/runtime/vm/debug/debug.h"
22 #include "hphp/runtime/vm/reverse-data-map.h"
24 #include "hphp/runtime/server/memory-stats.h"
26 #include "hphp/util/low-ptr.h"
28 #include <folly/AtomicHashMap.h>
30 #include <type_traits>
34 //////////////////////////////////////////////////////////////////////
38 // the string key will one of these values:
39 // * a valid LowPtr<StringData>, or
40 // * -1, -2, or -3 AHM magic values.
41 // Note that only the magic values have 1s in the low 3 bits
42 // since StringData's are at least 8-aligned.
44 using StrInternKey
= LowStringPtr::storage_type
;
46 // Return true if k is one of AHM's magic values. Valid pointers are
47 // 8-aligned, so test the low 3 bits.
48 bool isMagicKey(StrInternKey k
) {
52 const StringData
* to_sdata(StrInternKey key
) {
53 assert(!isMagicKey(key
));
54 static_assert(std::is_unsigned
<StrInternKey
>(), "cast must zero-extend");
55 return reinterpret_cast<const StringData
*>(key
);
59 bool operator()(StrInternKey k1
, StrInternKey k2
) const {
60 assert(!isMagicKey(k2
)); // no magic values on rhs
61 return operator()(k1
, to_sdata(k2
));
64 bool operator()(StrInternKey k1
, const StringData
* string2
) const {
65 if (isMagicKey(k1
)) return false; // magic values
66 auto const sd1
= to_sdata(k1
);
67 auto const len1
= sd1
->size();
68 auto const data1
= sd1
->data();
69 if (len1
!= string2
->size()) return false;
70 // only use wordsame on 8-byte aligned addresses
71 return wordsame(data1
, string2
->data(), len1
);
74 bool operator()(StrInternKey k1
, folly::StringPiece slice2
) const {
75 if (isMagicKey(k1
)) return false; // magic values
76 auto const sd1
= to_sdata(k1
);
77 auto const len1
= sd1
->size();
78 auto const data1
= sd1
->data();
79 if (len1
!= slice2
.size()) return false;
80 return !memcmp(data1
, slice2
.begin(), len1
);
84 struct strintern_hash
{
85 size_t operator()(StrInternKey k
) const {
86 assert(!isMagicKey(k
)); // no magic values get here
87 return operator()(to_sdata(k
));
90 size_t operator()(const StringData
* sd
) const {
94 size_t operator()(folly::StringPiece slice
) const {
95 return StringData::hash(slice
.data(), slice
.size());
99 // The uint32_t is used to hold RDS offsets for constants
100 using StringDataMap
= folly::AtomicHashMap
<
102 rds::Link
<TypedValue
>,
108 struct EmbeddedStringMap
{
109 explicit operator bool() const { return inited
; }
111 StringDataMap
* operator->() {
113 return reinterpret_cast<StringDataMap
*>(&data
);
115 StringDataMap
& operator*() { assert(inited
); return *operator->(); }
117 void emplace(uint32_t size
, const StringDataMap::Config
& config
) {
119 new (&data
) StringDataMap(size
, config
);
125 operator*().~StringDataMap();
131 typename
std::aligned_storage
<
132 sizeof(StringDataMap
),
133 alignof(StringDataMap
)
138 EmbeddedStringMap s_stringDataMap
;
140 // If a string is static it better be the one in the table.
141 DEBUG_ONLY
bool checkStaticStr(const StringData
* s
) {
142 assert(s
->isStatic());
143 assert(s_stringDataMap
);
144 auto DEBUG_ONLY
const it
= s_stringDataMap
->find(s
);
145 assert(it
!= s_stringDataMap
->end());
146 assert(to_sdata(it
->first
) == s
);
150 StringData
** precompute_chars();
151 StringData
** precompute_chars() {
152 StringData
** raw
= new StringData
*[256];
153 for (int i
= 0; i
< 256; i
++) {
154 char s
[2] = { (char)i
, 0 };
155 raw
[i
] = makeStaticStringSafe(&s
[0], 1);
160 StringData
* insertStaticString(StringData
* sd
) {
161 assert(sd
->isStatic());
162 auto pair
= s_stringDataMap
->insert(
163 safe_cast
<StrInternKey
>(reinterpret_cast<uintptr_t>(sd
)),
164 rds::Link
<TypedValue
>(rds::kUninitHandle
)
168 sd
->destructStatic();
170 MemoryStats::GetInstance()->LogStaticStringAlloc(
171 sd
->size() + sizeof(StringData
)
173 if (RuntimeOption::EvalEnableReverseDataMap
) {
174 data_map::register_start(sd
);
176 static std::atomic
<bool> signaled
{false};
177 checkAHMSubMaps(*s_stringDataMap
, "static string table", signaled
);
179 assert(to_sdata(pair
.first
->first
) != nullptr);
181 return const_cast<StringData
*>(to_sdata(pair
.first
->first
));
184 inline StringData
* insertStaticStringSlice(folly::StringPiece slice
) {
185 return insertStaticString(StringData::MakeStatic(slice
));
188 void create_string_data_map() {
189 always_assert(!s_stringDataMap
);
190 StringDataMap::Config config
;
191 config
.growthFactor
= 1;
192 config
.entryCountThreadCacheSize
= 10;
193 MemoryStats::GetInstance()->ResetStaticStringSize();
195 s_stringDataMap
.emplace(RuntimeOption::EvalInitialStaticStringTableSize
,
197 insertStaticString(StringData::MakeEmpty());
202 //////////////////////////////////////////////////////////////////////
204 StringData
** precomputed_chars
= precompute_chars();
206 size_t makeStaticStringCount() {
207 if (!s_stringDataMap
) return 0;
208 return s_stringDataMap
->size();
211 StringData
* makeStaticString(const StringData
* str
) {
212 if (str
->isStatic()) {
213 assert(checkStaticStr(str
));
214 return const_cast<StringData
*>(str
);
216 auto const it
= s_stringDataMap
->find(str
);
217 if (it
!= s_stringDataMap
->end()) {
218 return const_cast<StringData
*>(to_sdata(it
->first
));
220 return insertStaticStringSlice(str
->slice());
223 StringData
* makeStaticString(folly::StringPiece slice
) {
224 auto const it
= s_stringDataMap
->find(slice
);
225 if (it
!= s_stringDataMap
->end()) {
226 return const_cast<StringData
*>(to_sdata(it
->first
));
228 return insertStaticStringSlice(slice
);
231 StringData
* lookupStaticString(const StringData
*str
) {
232 assert(s_stringDataMap
&& !str
->isStatic());
233 auto const it
= s_stringDataMap
->find(str
);
234 if (it
!= s_stringDataMap
->end()) {
235 return const_cast<StringData
*>(to_sdata(it
->first
));
240 StringData
* makeStaticString(const String
& str
) {
241 assert(!str
.isNull());
242 return makeStaticString(str
.get());
245 StringData
* makeStaticString(const char* str
, size_t len
) {
246 assert(len
<= StringData::MaxSize
);
247 return makeStaticString(folly::StringPiece
{str
, len
});
250 StringData
* makeStaticString(const std::string
& str
) {
251 assert(str
.size() <= StringData::MaxSize
);
252 return makeStaticString(folly::StringPiece
{str
.c_str(), str
.size()});
255 StringData
* makeStaticString(const char* str
) {
256 return makeStaticString(str
, strlen(str
));
259 StringData
* makeStaticString(char c
) {
260 // TODO(#2880477): should this be inlined?
261 return precomputed_chars
[(uint8_t)c
];
264 StringData
* makeStaticStringSafe(const char* str
, size_t len
) {
265 assert(len
<= StringData::MaxSize
);
266 if (UNLIKELY(!s_stringDataMap
)) {
267 create_string_data_map();
269 return makeStaticString(str
, len
);
272 StringData
* makeStaticStringSafe(const char* str
) {
273 if (UNLIKELY(!s_stringDataMap
)) {
274 create_string_data_map();
276 return makeStaticString(str
);
279 bool bindPersistentCns(const StringData
* cnsName
, const Cell
& value
) {
280 assert(s_stringDataMap
);
281 auto const it
= s_stringDataMap
->find(cnsName
);
282 assertx(it
!= s_stringDataMap
->end());
286 rds::alloc
<TypedValue
, kTVSimdAlign
>(rds::Mode::Persistent
);
288 rds::recordRds(link
.handle(), sizeof(TypedValue
),
289 "Cns", cnsName
->data());
290 return link
.handle();
293 return it
->second
.isPersistent();
296 rds::Handle
lookupCnsHandle(const StringData
* cnsName
) {
297 assert(s_stringDataMap
);
298 auto const it
= s_stringDataMap
->find(cnsName
);
299 if (it
!= s_stringDataMap
->end()) {
300 return it
->second
.maybeHandle();
302 return rds::kUninitHandle
;
305 rds::Handle
makeCnsHandle(const StringData
* cnsName
) {
306 auto const val
= lookupCnsHandle(cnsName
);
307 if (rds::isHandleBound(val
)) return val
;
308 if (!cnsName
->isStatic()) {
309 // Its a dynamic constant, that doesn't correspond to
310 // an already allocated handle. We'll allocate it in
311 // the request local rds::s_constants instead.
312 return rds::kUninitHandle
;
314 auto const it
= s_stringDataMap
->find(cnsName
);
315 assert(it
!= s_stringDataMap
->end());
316 if (!it
->second
.bound()) {
317 it
->second
.bind
<kTVSimdAlign
>(rds::Mode::Normal
);
319 rds::recordRds(it
->second
.handle(), sizeof(TypedValue
),
320 "Cns", cnsName
->data());
322 return it
->second
.handle();
325 std::vector
<StringData
*> lookupDefinedStaticStrings() {
326 assert(s_stringDataMap
);
327 std::vector
<StringData
*> ret
;
329 for (auto it
= s_stringDataMap
->begin();
330 it
!= s_stringDataMap
->end(); ++it
) {
331 ret
.push_back(const_cast<StringData
*>(to_sdata(it
->first
)));
338 const StaticString
s_user("user");
339 const StaticString
s_Core("Core");
341 Array
lookupDefinedConstants(bool categorize
/*= false */) {
342 assert(s_stringDataMap
);
343 Array
usr(rds::s_constants());
346 for (auto it
= s_stringDataMap
->begin();
347 it
!= s_stringDataMap
->end(); ++it
) {
348 if (it
->second
.bound()) {
349 if (!it
->second
.isInit()) continue;
351 Array
*tbl
= (categorize
&& it
->second
.isPersistent()) ? &sys
: &usr
;
352 auto& tv
= *it
->second
;
354 if (tv
.m_type
!= KindOfUninit
) {
355 StrNR
key(const_cast<StringData
*>(to_sdata(it
->first
)));
356 tbl
->set(key
, tvAsVariant(&tv
), true);
358 assertx(tv
.m_data
.pref
);
359 StrNR
key(const_cast<StringData
*>(to_sdata(it
->first
)));
361 reinterpret_cast<Native::ConstantCallback
>(tv
.m_data
.pref
);
362 auto cns
= callback();
363 if (cns
.isInitialized()) {
364 tbl
->set(key
, cns
, true);
372 ret
.set(s_user
, usr
);
373 ret
.set(s_Core
, sys
);
380 size_t countStaticStringConstants() {
381 if (!s_stringDataMap
) return 0;
383 for (auto it
= s_stringDataMap
->begin();
384 it
!= s_stringDataMap
->end(); ++it
) {
385 if (it
->second
.bound()) {
392 void refineStaticStringTableSize() {
393 if (RuntimeOption::EvalInitialStaticStringTableSize
==
394 kDefaultInitialStaticStringTableSize
||
399 std::vector
<StringDataMap::value_type
>
400 oldStringTable(s_stringDataMap
->begin(), s_stringDataMap
->end());
402 s_stringDataMap
.clear();
403 create_string_data_map();
405 for (auto& kv
: oldStringTable
) {
406 s_stringDataMap
->insert(kv
.first
, kv
.second
);
410 //////////////////////////////////////////////////////////////////////