2 +----------------------------------------------------------------------+
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"
27 //////////////////////////////////////////////////////////////////////
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
);
42 StrInternKey
make_intern_key(const StringSlice
* sl
) {
43 auto const ret
= -reinterpret_cast<StrInternKey
>(sl
);
44 assert(ret
< 0 && ret
< kAhmMagicThreshold
);
48 const StringData
* to_sdata(StrInternKey key
) {
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
);
70 bool operator()(StrInternKey k1
, StrInternKey k2
) const {
72 // AHM only gives lookup keys on the rhs of the equal operator
73 assert(k1
>= kAhmMagicThreshold
);
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
);
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
<
98 RDS::Link
<TypedValue
>,
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
);
113 void create_string_data_map() {
114 StringDataMap::Config config
;
115 config
.growthFactor
= 1;
117 new StringDataMap(RuntimeOption::EvalInitialStaticStringTableSize
,
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);
131 StringData
** precomputed_chars
= precompute_chars();
133 StringData
* insertStaticString(StringSlice slice
) {
134 auto const sd
= StringData::MakeStatic(slice
);
135 auto pair
= s_stringDataMap
->insert(
137 RDS::Link
<TypedValue
>(RDS::kInvalidHandle
)
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
));
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();
229 RDS::Handle
makeCnsHandle(const StringData
* cnsName
, bool persistent
) {
230 auto const val
= lookupCnsHandle(cnsName
);
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.
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());
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()))
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);
282 ret
.set(s_user
, usr
);
283 ret
.set(s_Core
, sys
);
290 void refineStaticStringTableSize() {
291 if (RuntimeOption::EvalInitialStaticStringTableSize
==
292 kDefaultInitialStaticStringTableSize
) {
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 //////////////////////////////////////////////////////////////////////