make PHP_SAPI dynamic based on execution mode
[hiphop-php.git] / hphp / runtime / base / type_string.cpp
blobcb1c5a7372bcd2a6145602798361dffd8556351b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/complex_types.h"
18 #include "hphp/runtime/base/string_offset.h"
19 #include "hphp/runtime/base/type_conversions.h"
20 #include "hphp/runtime/base/builtin_functions.h"
21 #include "hphp/runtime/base/comparisons.h"
22 #include "hphp/runtime/base/variable_serializer.h"
23 #include "hphp/runtime/base/zend/zend_functions.h"
24 #include "hphp/runtime/base/zend/zend_string.h"
25 #include "hphp/runtime/base/zend/zend_printf.h"
27 namespace HPHP {
29 const String null_string = String();
30 const StaticString empty_string("");
32 ///////////////////////////////////////////////////////////////////////////////
33 // statics
35 #define NUM_CONVERTED_INTEGERS \
36 (String::MaxPrecomputedInteger - String::MinPrecomputedInteger + 1)
38 StringData const **String::converted_integers_raw;
39 StringData const **String::converted_integers;
41 String::IntegerStringDataMap String::integer_string_data_map;
43 void String::PreConvertInteger(int64_t n) {
44 IntegerStringDataMap::const_iterator it =
45 integer_string_data_map.find(n);
46 if (it != integer_string_data_map.end()) return;
47 integer_string_data_map[n] = StringData::convert_integer_helper(n);
50 const StringData *String::ConvertInteger(int64_t n) {
51 StringData const **psd = converted_integers + n;
52 const StringData *sd = StringData::convert_integer_helper(n);
53 *psd = sd;
54 return sd;
57 static int precompute_integers() ATTRIBUTE_COLD;
58 static int precompute_integers() {
59 String::converted_integers_raw =
60 (StringData const **)calloc(NUM_CONVERTED_INTEGERS, sizeof(StringData*));
61 String::converted_integers = String::converted_integers_raw
62 - String::MinPrecomputedInteger;
63 if (RuntimeOption::ServerExecutionMode()) {
64 // Proactively populate, in order to increase cache locality for sequential
65 // access patterns.
66 for (int n = String::MinPrecomputedInteger;
67 n <= String::MaxPrecomputedInteger; n++) {
68 String::ConvertInteger(n);
71 return NUM_CONVERTED_INTEGERS;
74 static int ATTRIBUTE_UNUSED initIntegers = precompute_integers();
76 ///////////////////////////////////////////////////////////////////////////////
77 // constructors
79 String::~String() {}
81 StringData* buildStringData(int n) {
82 char tmpbuf[12];
83 char* p;
84 int is_negative;
85 int len;
87 tmpbuf[11] = '\0';
88 p = conv_10(n, &is_negative, &tmpbuf[11], &len);
89 return NEW(StringData)(p, len, CopyString);
92 String::String(int n) {
93 const StringData *sd = GetIntegerStringData(n);
94 if (sd) {
95 assert(sd->isStatic());
96 m_px = (StringData *)sd;
97 return;
99 m_px = buildStringData(n);
100 m_px->setRefCount(1);
103 StringData* buildStringData(int64_t n) {
104 char tmpbuf[21];
105 char* p;
106 int is_negative;
107 int len;
109 tmpbuf[20] = '\0';
110 p = conv_10(n, &is_negative, &tmpbuf[20], &len);
111 return NEW(StringData)(p, len, CopyString);
114 HOT_FUNC
115 String::String(int64_t n) {
116 const StringData *sd = GetIntegerStringData(n);
117 if (sd) {
118 assert(sd->isStatic());
119 m_px = (StringData *)sd;
120 return;
122 m_px = buildStringData(n);
123 m_px->setRefCount(1);
126 StringData* buildStringData(double n) {
127 char *buf;
129 if (n == 0.0) n = 0.0; // so to avoid "-0" output
130 vspprintf(&buf, 0, "%.*G", 14, n);
131 return NEW(StringData)(buf, AttachString);
134 String::String(double n) {
135 m_px = buildStringData(n);
136 m_px->setRefCount(1);
139 StringData* buildStringData(litstr s) {
140 return NEW(StringData)(s, AttachLiteral);
143 ///////////////////////////////////////////////////////////////////////////////
144 // informational
146 String String::substr(int start, int length /* = 0x7FFFFFFF */,
147 bool nullable /* = false */) const {
148 StringSlice r = slice();
149 // string_substr_check() will update start & length to a legal range.
150 if (string_substr_check(r.len, start, length)) {
151 return String(r.ptr + start, length, CopyString);
153 return nullable ? String() : String("", 0, CopyString);
156 String String::lastToken(char delimiter) {
157 int pos = rfind(delimiter);
158 if (pos >= 0) {
159 return substr(pos + 1);
161 return *this;
164 int String::find(char ch, int pos /* = 0 */,
165 bool caseSensitive /* = true */) const {
166 if (empty()) return -1;
167 return string_find(m_px->data(), m_px->size(), ch, pos,
168 caseSensitive);
171 int String::find(const char *s, int pos /* = 0 */,
172 bool caseSensitive /* = true */) const {
173 assert(s);
174 if (empty()) return -1;
175 if (*s && *(s+1) == 0) {
176 return find(*s, pos, caseSensitive);
178 return string_find(m_px->data(), m_px->size(), s, strlen(s),
179 pos, caseSensitive);
182 HOT_FUNC
183 int String::find(CStrRef s, int pos /* = 0 */,
184 bool caseSensitive /* = true */) const {
185 if (empty()) return -1;
186 if (s.size() == 1) {
187 return find(*s.data(), pos, caseSensitive);
189 return string_find(m_px->data(), m_px->size(),
190 s.data(), s.size(), pos, caseSensitive);
193 int String::rfind(char ch, int pos /* = 0 */,
194 bool caseSensitive /* = true */) const {
195 if (empty()) return -1;
196 return string_rfind(m_px->data(), m_px->size(), ch,
197 pos, caseSensitive);
200 int String::rfind(const char *s, int pos /* = 0 */,
201 bool caseSensitive /* = true */) const {
202 assert(s);
203 if (empty()) return -1;
204 if (*s && *(s+1) == 0) {
205 return rfind(*s, pos, caseSensitive);
207 return string_rfind(m_px->data(), m_px->size(), s, strlen(s),
208 pos, caseSensitive);
211 int String::rfind(CStrRef s, int pos /* = 0 */,
212 bool caseSensitive /* = true */) const {
213 if (empty()) return -1;
214 if (s.size() == 1) {
215 return rfind(*s.data(), pos, caseSensitive);
217 return string_rfind(m_px->data(), m_px->size(),
218 s.data(), s.size(), pos, caseSensitive);
221 String String::replace(int start, int length, CStrRef replacement) const {
222 int len = size();
223 char *ret = string_replace(data(), len, start, length, replacement.data(),
224 replacement.size());
225 return String(ret, len, AttachString);
228 String String::replace(CStrRef search, CStrRef replacement) const {
229 int count;
230 return replace(search, replacement, count, true);
233 String String::replace(CStrRef search, CStrRef replacement, int &count,
234 bool caseSensitive) const {
235 count = 0;
236 if (!search.empty() && !empty()) {
237 int len = m_px->size();
238 char *ret = string_replace(m_px->data(), len, search.data(), search.size(),
239 replacement.data(), replacement.size(), count,
240 caseSensitive);
241 if (ret) {
242 return String(ret, len, AttachString);
245 return *this;
248 ///////////////////////////////////////////////////////////////////////////////
249 // offset functions: cannot inline these due to dependencies
251 String String::rvalAt(CArrRef key) const {
252 return rvalAtImpl(key.toInt32());
255 String String::rvalAt(CObjRef key) const {
256 return rvalAtImpl(key.toInt32());
259 String String::rvalAt(CVarRef key) const {
260 return rvalAtImpl(key.toInt32());
263 StringOffset String::lvalAt(CArrRef key) {
264 return lvalAtImpl(key.toInt32());
267 StringOffset String::lvalAt(CObjRef key) {
268 return lvalAtImpl(key.toInt32());
271 StringOffset String::lvalAt(CVarRef key) {
272 return lvalAtImpl(key.toInt32());
275 char String::charAt(int pos) const {
276 assert(pos >= 0 && pos <= size());
277 const char *s = data();
278 return s[pos];
281 ///////////////////////////////////////////////////////////////////////////////
282 // assignments
284 String &String::operator=(litstr s) {
285 if (m_px) decRefStr(m_px);
286 if (s) {
287 m_px = NEW(StringData)(s, AttachLiteral);
288 m_px->setRefCount(1);
289 } else {
290 m_px = nullptr;
292 return *this;
295 String &String::operator=(StringData *data) {
296 StringBase::operator=(data);
297 return *this;
300 String &String::operator=(const std::string & s) {
301 if (m_px) decRefStr(m_px);
302 m_px = NEW(StringData)(s.c_str(), s.size(), CopyString);
303 m_px->setRefCount(1);
304 return *this;
307 HOT_FUNC
308 String &String::operator=(CStrRef str) {
309 StringBase::operator=(str.m_px);
310 return *this;
313 String &String::operator=(CVarRef var) {
314 return operator=(var.toString());
317 ///////////////////////////////////////////////////////////////////////////////
318 // concatenation and increments
320 String &String::operator+=(litstr s) {
321 if (s && *s) {
322 if (empty()) {
323 m_px = NEW(StringData)(s, AttachLiteral);
324 m_px->setRefCount(1);
325 } else if (m_px->getCount() == 1) {
326 m_px->append(s, strlen(s));
327 } else {
328 StringData* px = NEW(StringData)(m_px, s);
329 px->setRefCount(1);
330 decRefStr(m_px);
331 m_px = px;
334 return *this;
337 String &String::operator+=(CStrRef str) {
338 if (!str.empty()) {
339 if (empty()) {
340 StringBase::operator=(str.m_px);
341 } else if (m_px->getCount() == 1) {
342 m_px->append(str.slice());
343 } else {
344 StringData* px = NEW(StringData)(m_px, str.slice());
345 decRefStr(m_px);
346 px->setRefCount(1);
347 m_px = px;
350 return *this;
353 String& String::operator+=(const StringSlice& slice) {
354 if (slice.size() == 0) {
355 return *this;
357 if (m_px && m_px->getCount() == 1) {
358 m_px->append(slice);
359 return *this;
361 if (empty()) {
362 if (m_px) decRefStr(m_px);
363 m_px = NEW(StringData)(slice.begin(), slice.size(), CopyString);
364 m_px->setRefCount(1);
365 return *this;
367 StringData* px = NEW(StringData)(m_px, slice);
368 px->setRefCount(1);
369 decRefStr(m_px);
370 m_px = px;
371 return *this;
374 String& String::operator+=(const MutableSlice& slice) {
375 return (*this += StringSlice(slice.begin(), slice.size()));
378 String&& operator+(String&& lhs, litstr rhs) {
379 lhs += rhs;
380 return std::move(lhs);
383 String operator+(const String & lhs, litstr rhs) {
384 if (lhs.empty()) return rhs;
385 if (!rhs || !*rhs) return lhs;
386 return NEW(StringData)(lhs.slice(), rhs);
389 HOT_FUNC
390 String&& operator+(String&& lhs, String&& rhs) {
391 return std::move(lhs += rhs);
394 HOT_FUNC
395 String operator+(String&& lhs, const String & rhs) {
396 return std::move(lhs += rhs);
399 HOT_FUNC
400 String operator+(const String & lhs, String&& rhs) {
401 return NEW(StringData)(lhs.slice(), rhs.slice());
404 HOT_FUNC
405 String operator+(const String & lhs, const String & rhs) {
406 if (lhs.empty()) return rhs;
407 if (rhs.empty()) return lhs;
408 return NEW(StringData)(lhs.slice(), rhs.slice());
411 String String::operator~() const {
412 String ret(NEW(StringData)(slice(), CopyString));
413 ret->negate();
414 return ret;
417 String String::operator|(CStrRef v) const {
418 return String(m_px).operator|=(v);
421 String String::operator&(CStrRef v) const {
422 return String(m_px).operator&=(v);
425 String String::operator^(CStrRef v) const {
426 return String(m_px).operator^=(v);
429 String &String::operator|=(CStrRef v) {
430 const char *s1 = data();
431 const char *s2 = v.data();
432 int len1 = size();
433 int len2 = v.size();
434 int len;
435 char *copy = nullptr;
436 if (len2 > len1) {
437 len = len2;
438 copy = string_duplicate(s2, len2);
439 for (int i = 0; i < len1; i++) copy[i] |= s1[i];
440 } else {
441 len = len1;
442 copy = string_duplicate(s1, len1);
443 for (int i = 0; i < len2; i++) copy[i] |= s2[i];
445 if (m_px) decRefStr(m_px);
446 m_px = NEW(StringData)(copy, len, AttachString);
447 m_px->setRefCount(1);
448 return *this;
451 String &String::operator&=(CStrRef v) {
452 const char *s1 = data();
453 const char *s2 = v.data();
454 int len1 = size();
455 int len2 = v.size();
456 int len;
457 char *copy = nullptr;
458 if (len2 < len1) {
459 len = len2;
460 copy = string_duplicate(s2, len2);
461 for (int i = 0; i < len2; i++) copy[i] &= s1[i];
462 } else {
463 len = len1;
464 copy = string_duplicate(s1, len1);
465 for (int i = 0; i < len1; i++) copy[i] &= s2[i];
467 if (m_px) decRefStr(m_px);
468 m_px = NEW(StringData)(copy, len, AttachString);
469 m_px->setRefCount(1);
470 return *this;
473 String &String::operator^=(CStrRef v) {
474 const char *s1 = data();
475 const char *s2 = v.data();
476 int len1 = size();
477 int len2 = v.size();
478 int len;
479 char *copy = nullptr;
480 if (len2 < len1) {
481 len = len2;
482 copy = string_duplicate(s2, len2);
483 for (int i = 0; i < len2; i++) copy[i] ^= s1[i];
484 } else {
485 len = len1;
486 copy = string_duplicate(s1, len1);
487 for (int i = 0; i < len1; i++) copy[i] ^= s2[i];
489 if (m_px) decRefStr(m_px);
490 m_px = NEW(StringData)(copy, len, AttachString);
491 m_px->setRefCount(1);
492 return *this;
495 ///////////////////////////////////////////////////////////////////////////////
496 // conversions
498 HOT_FUNC
499 VarNR String::toKey() const {
500 if (!m_px) return VarNR(empty_string);
501 int64_t n = 0;
502 if (m_px->isStrictlyInteger(n)) {
503 return VarNR(n);
504 } else {
505 return VarNR(m_px);
509 ///////////////////////////////////////////////////////////////////////////////
510 // comparisons
512 bool String::same(litstr v2) const {
513 return HPHP::same(m_px, v2);
516 bool String::same(const StringData *v2) const {
517 return HPHP::same(m_px, v2);
520 bool String::same(CStrRef v2) const {
521 return HPHP::same(m_px, v2);
524 bool String::same(CArrRef v2) const {
525 return HPHP::same(m_px, v2);
528 bool String::same(CObjRef v2) const {
529 return HPHP::same(m_px, v2);
532 bool String::equal(litstr v2) const {
533 return HPHP::equal(m_px, v2);
536 bool String::equal(const StringData *v2) const {
537 return HPHP::equal(m_px, v2);
540 bool String::equal(CStrRef v2) const {
541 return HPHP::equal(m_px, v2);
544 bool String::equal(CArrRef v2) const {
545 return HPHP::equal(m_px, v2);
548 bool String::equal(CObjRef v2) const {
549 return HPHP::equal(m_px, v2);
552 bool String::less(litstr v2) const {
553 return HPHP::less(m_px, v2);
556 bool String::less(const StringData *v2) const {
557 return HPHP::less(m_px, v2);
560 bool String::less(CStrRef v2) const {
561 return HPHP::less(m_px, v2);
564 bool String::less(CArrRef v2) const {
565 return HPHP::less(m_px, v2);
568 bool String::less(CObjRef v2) const {
569 return HPHP::less(m_px, v2);
572 bool String::more(litstr v2) const {
573 return HPHP::more(m_px, v2);
576 bool String::more(const StringData *v2) const {
577 return HPHP::more(m_px, v2);
580 bool String::more(CStrRef v2) const {
581 return HPHP::more(m_px, v2);
584 bool String::more(CArrRef v2) const {
585 return HPHP::more(m_px, v2);
588 bool String::more(CObjRef v2) const {
589 return HPHP::more(m_px, v2);
592 ///////////////////////////////////////////////////////////////////////////////
593 // comparison operators
595 bool String::operator==(litstr v) const {
596 return HPHP::equal(m_px, v);
599 bool String::operator!=(litstr v) const {
600 return !HPHP::equal(m_px, v);
603 bool String::operator>=(litstr v) const {
604 return more_or_equal(m_px, v);
607 bool String::operator<=(litstr v) const {
608 return less_or_equal(m_px, v);
611 bool String::operator>(litstr v) const {
612 return HPHP::more(m_px, v);
615 bool String::operator<(litstr v) const {
616 return HPHP::less(m_px, v);
619 bool String::operator==(CStrRef v) const {
620 return HPHP::equal(m_px, v);
623 bool String::operator!=(CStrRef v) const {
624 return !HPHP::equal(m_px, v);
627 bool String::operator>=(CStrRef v) const {
628 return more_or_equal(m_px, v);
631 bool String::operator<=(CStrRef v) const {
632 return less_or_equal(m_px, v);
635 bool String::operator>(CStrRef v) const {
636 return HPHP::more(m_px, v);
639 bool String::operator<(CStrRef v) const {
640 return HPHP::less(m_px, v);
643 bool String::operator==(CVarRef v) const {
644 return HPHP::equal(m_px, v);
647 bool String::operator!=(CVarRef v) const {
648 return !HPHP::equal(m_px, v);
651 bool String::operator>=(CVarRef v) const {
652 return more_or_equal(m_px, v);
655 bool String::operator<=(CVarRef v) const {
656 return less_or_equal(m_px, v);
659 bool String::operator>(CVarRef v) const {
660 return HPHP::more(m_px, v);
663 bool String::operator<(CVarRef v) const {
664 return HPHP::less(m_px, v);
667 ///////////////////////////////////////////////////////////////////////////////
668 // input/output
670 void String::serialize(VariableSerializer *serializer) const {
671 if (m_px) {
672 serializer->write(m_px->data(), m_px->size());
673 } else {
674 serializer->writeNull();
678 void String::unserialize(VariableUnserializer *uns,
679 char delimiter0 /* = '"' */,
680 char delimiter1 /* = '"' */) {
681 int64_t size = uns->readInt();
682 if (size >= RuntimeOption::MaxSerializedStringSize) {
683 throw Exception("Size of serialized string (%d) exceeds max", int(size));
685 if (size < 0) {
686 throw Exception("Size of serialized string (%d) must not be negative",
687 int(size));
690 char ch = uns->readChar();
691 if (ch != ':') {
692 throw Exception("Expected ':' but got '%c'", ch);
694 ch = uns->readChar();
695 if (ch != delimiter0) {
696 throw Exception("Expected '%c' but got '%c'", delimiter0, ch);
698 StringData *px = NEW(StringData)(int(size));
699 MutableSlice buf = px->mutableSlice();
700 assert(size <= buf.len);
701 uns->read(buf.ptr, size);
702 px->setSize(size);
703 if (m_px) decRefStr(m_px);
704 m_px = px;
705 px->setRefCount(1);
707 ch = uns->readChar();
708 if (ch != delimiter1) {
709 throw Exception("Expected '%c' but got '%c'", delimiter1, ch);
713 ///////////////////////////////////////////////////////////////////////////////
714 // debugging
716 void String::dump() const {
717 if (m_px) {
718 m_px->dump();
719 } else {
720 printf("(null)\n");
724 ///////////////////////////////////////////////////////////////////////////////
725 // StaticString
727 StaticString::StaticString(litstr s) {
728 StackStringData sd(s);
729 m_px = StringData::GetStaticString(&sd);
732 StaticString::StaticString(litstr s, int length) {
733 StackStringData sd(s, length, AttachLiteral);
734 m_px = StringData::GetStaticString(&sd);
737 StaticString::StaticString(std::string s) {
738 StackStringData sd(s.c_str(), s.size(), CopyString);
739 m_px = StringData::GetStaticString(&sd);
742 StaticString::StaticString(const StaticString &str) {
743 assert(m_px->isStatic());
744 m_px = str.m_px;
747 StaticString& StaticString::operator=(const StaticString &str) {
748 // Assignment to a StaticString is ignored. Generated code
749 // should never use a StaticString on the left-hand side of
750 // assignment. A StaticString can only be initialized by a
751 // StaticString constructor or StaticString::init().
752 always_assert(false);
753 return *this;
756 String::String(Variant&& src) : StringBase(src.toString()) {
759 String& String::operator=(Variant&& src) {
760 return *this = src.toString();
763 void String::checkStaticHelper() {
764 if (StringData* t = StringData::FindStaticString(m_px)) {
765 decRefStr(m_px);
766 m_px = t;
770 //////////////////////////////////////////////////////////////////////////////