Allocate uncounted strings with malloc.
[hiphop-php.git] / hphp / runtime / base / apc-handle.cpp
blob9fc81d96e5b76ec80f14f15d763c4d9a3cb14a67
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/apc-handle.h"
18 #include "hphp/runtime/base/apc-typed-value.h"
19 #include "hphp/runtime/base/apc-string.h"
20 #include "hphp/runtime/base/apc-array.h"
21 #include "hphp/runtime/base/apc-object.h"
22 #include "hphp/runtime/ext/ext_apc.h"
23 #include "hphp/runtime/base/array-iterator.h"
24 #include "hphp/runtime/base/mixed-array.h"
26 namespace HPHP {
28 ///////////////////////////////////////////////////////////////////////////////
30 APCHandle* APCHandle::Create(const Variant& source,
31 size_t& size,
32 bool serialized,
33 bool inner /* = false */,
34 bool unserializeObj /* = false*/) {
35 return CreateSharedType(source, size, serialized, inner, unserializeObj);
38 APCHandle* APCHandle::CreateSharedType(const Variant& source,
39 size_t& size,
40 bool serialized,
41 bool inner,
42 bool unserializeObj) {
43 auto type = source.getType(); // this gets rid of the ref, if it was one
44 switch (type) {
45 case KindOfBoolean: {
46 auto value = new APCTypedValue(type,
47 static_cast<int64_t>(source.getBoolean()));
48 size = sizeof(APCTypedValue);
49 return value->getHandle();
51 case KindOfInt64: {
52 auto value = new APCTypedValue(type, source.getInt64());
53 size = sizeof(APCTypedValue);
54 return value->getHandle();
56 case KindOfDouble: {
57 auto value = new APCTypedValue(type, source.getDouble());
58 size = sizeof(APCTypedValue);
59 return value->getHandle();
61 case KindOfUninit:
62 case KindOfNull: {
63 auto value = new APCTypedValue(type);
64 size = sizeof(APCTypedValue);
65 return value->getHandle();
68 case KindOfStaticString: {
69 if (serialized) goto StringCase;
71 auto value = new APCTypedValue(type, source.getStringData());
72 size = sizeof(APCTypedValue);
73 return value->getHandle();
75 StringCase:
76 case KindOfString: {
77 StringData* s = source.getStringData();
78 if (serialized) {
79 // It is priming, and there might not be the right class definitions
80 // for unserialization.
81 return APCObject::MakeShared(apc_reserialize(s), size);
84 auto const st = lookupStaticString(s);
85 if (st) {
86 APCTypedValue* value = new APCTypedValue(KindOfStaticString, st);
87 size = sizeof(APCTypedValue);
88 return value->getHandle();
91 assert(!s->isStatic()); // would've been handled above
92 if (!inner && apcExtension::UseUncounted) {
93 StringData* st = StringData::MakeUncounted(s->slice());
94 APCTypedValue* value = new APCTypedValue(st);
95 size = sizeof(APCTypedValue) + st->size();
96 return value->getHandle();
98 return APCString::MakeShared(type, s, size);
101 case KindOfArray:
102 return APCArray::MakeShared(source.getArrayData(),
103 size,
104 inner,
105 unserializeObj);
107 case KindOfResource:
108 // TODO Task #2661075: Here and elsewhere in the runtime, we convert
109 // Resources to the empty array during various serialization operations,
110 // which does not match Zend behavior. We should fix this.
111 size = sizeof(APCArray);
112 return APCArray::MakeShared();
114 case KindOfObject:
115 return unserializeObj ?
116 APCObject::Construct(source.getObjectData(), size) :
117 APCObject::MakeShared(apc_serialize(source), size);
119 default:
120 return nullptr;
124 Variant APCHandle::toLocal() {
125 switch (m_type) {
126 case KindOfBoolean:
127 return APCTypedValue::fromHandle(this)->getBoolean();
128 case KindOfInt64:
129 return APCTypedValue::fromHandle(this)->getInt64();
130 case KindOfDouble:
131 return APCTypedValue::fromHandle(this)->getDouble();
132 case KindOfUninit:
133 case KindOfNull:
134 return init_null(); // shortcut.. no point to forward
135 case KindOfStaticString:
136 return APCTypedValue::fromHandle(this)->getStringData();
138 case KindOfString:
139 return APCString::MakeString(this);
141 case KindOfArray:
142 return APCArray::MakeArray(this);
144 case KindOfObject:
145 return APCObject::MakeObject(this);
147 default:
148 assert(false);
149 return init_null();
153 void APCHandle::deleteShared() {
154 assert(!getUncounted());
155 switch (m_type) {
156 case KindOfBoolean:
157 case KindOfInt64:
158 case KindOfDouble:
159 case KindOfUninit:
160 case KindOfNull:
161 case KindOfStaticString:
162 delete APCTypedValue::fromHandle(this);
163 break;
165 case KindOfString:
166 delete APCString::fromHandle(this);
167 break;
169 case KindOfArray:
170 APCArray::Delete(this);
171 break;
173 case KindOfObject:
174 APCObject::Delete(this);
175 break;
177 default:
178 assert(false);
182 ///////////////////////////////////////////////////////////////////////////////
184 APCHandle* APCTypedValue::MakeSharedArray(ArrayData* array) {
185 assert(apcExtension::UseUncounted);
186 auto value = new APCTypedValue(
187 array->isPacked() ? MixedArray::MakeUncountedPacked(array)
188 : MixedArray::MakeUncounted(array));
189 return value->getHandle();
192 void APCTypedValue::deleteUncounted() {
193 assert(m_handle.getUncounted());
194 DataType type = m_handle.getType();
195 assert(type == KindOfString || type == KindOfArray);
196 if (type == KindOfString) {
197 m_data.str->destructUncounted();
198 } else if (type == KindOfArray) {
199 if (m_data.arr->isPacked()) {
200 MixedArray::ReleaseUncountedPacked(m_data.arr);
201 } else {
202 MixedArray::ReleaseUncounted(m_data.arr);
205 delete this;
208 ///////////////////////////////////////////////////////////////////////////////
210 void DataWalker::traverseData(ArrayData* data,
211 DataFeature& features,
212 PointerSet& visited) const {
213 // shared arrays by definition do not contain circular references or
214 // collections
215 if (data->isSharedArray()) {
216 // If not looking for references to objects/resources OR
217 // if one was already found we can bail out
218 if (!(m_features & LookupFeature::HasObjectOrResource) ||
219 features.hasObjectOrResource()) {
220 features.m_hasRefCountReference = true; // just in case, cheap enough...
221 return;
225 for (ArrayIter iter(data); iter; ++iter) {
226 const Variant& var = iter.secondRef();
228 if (var.isReferenced()) {
229 Variant *pvar = var.getRefData();
230 if (markVisited(pvar, features, visited)) {
231 // don't recurse forever
232 if (canStopWalk(features)) {
233 return;
235 continue;
237 markVisited(pvar, features, visited);
240 DataType type = var.getType();
241 // cheap enough, do it always
242 features.m_hasRefCountReference = IS_REFCOUNTED_TYPE(type);
243 if (type == KindOfObject) {
244 features.m_hasObjectOrResource = true;
245 traverseData(var.getObjectData(), features, visited);
246 } else if (type == KindOfArray) {
247 traverseData(var.getArrayData(), features, visited);
248 } else if (type == KindOfResource) {
249 features.m_hasObjectOrResource = true;
251 if (canStopWalk(features)) return;
255 void DataWalker::traverseData(
256 ObjectData* data,
257 DataFeature& features,
258 PointerSet& visited) const {
259 objectFeature(data, features);
260 if (markVisited(data, features, visited)) {
261 return; // avoid infinite recursion
263 if (!canStopWalk(features)) {
264 traverseData(data->o_toArray().get(), features, visited);
268 inline
269 bool DataWalker::markVisited(
270 void* pvar,
271 DataFeature& features,
272 PointerSet& visited) const {
273 if (visited.find(pvar) != visited.end()) {
274 features.m_circular = true;
275 return true;
277 visited.insert(pvar);
278 return false;
281 inline
282 void DataWalker::objectFeature(
283 ObjectData* pobj,
284 DataFeature& features) const {
285 // REVIEW: right now collections always stop the walk, not clear
286 // if they should do so moving forward. Revisit...
287 // Notice that worst case scenario here we will be serializing things
288 // that we could keep in better format so it should not break anything
289 if (pobj->isCollection()) {
290 features.m_hasCollection = true;
291 } else if ((m_features & LookupFeature::DetectSerializable) &&
292 pobj->instanceof(SystemLib::s_SerializableClass)) {
293 features.m_serializable = true;
297 inline
298 bool DataWalker::canStopWalk(DataFeature& features) const {
299 auto refCountCheck =
300 features.hasRefCountReference() ||
301 !(m_features & LookupFeature::RefCountedReference);
302 auto objectCheck =
303 features.hasObjectOrResource() ||
304 !(m_features & LookupFeature::HasObjectOrResource);
305 auto defaultChecks =
306 features.isCircular() || features.hasCollection() ||
307 features.hasSerializableReference();
308 return refCountCheck && objectCheck && defaultChecks;
311 ///////////////////////////////////////////////////////////////////////////////