2 +----------------------------------------------------------------------+
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"
29 const String null_string
= String();
30 const StaticString
empty_string("");
32 ///////////////////////////////////////////////////////////////////////////////
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
);
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
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 ///////////////////////////////////////////////////////////////////////////////
81 StringData
* buildStringData(int n
) {
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
);
95 assert(sd
->isStatic());
96 m_px
= (StringData
*)sd
;
99 m_px
= buildStringData(n
);
100 m_px
->setRefCount(1);
103 StringData
* buildStringData(int64_t n
) {
110 p
= conv_10(n
, &is_negative
, &tmpbuf
[20], &len
);
111 return NEW(StringData
)(p
, len
, CopyString
);
115 String::String(int64_t n
) {
116 const StringData
*sd
= GetIntegerStringData(n
);
118 assert(sd
->isStatic());
119 m_px
= (StringData
*)sd
;
122 m_px
= buildStringData(n
);
123 m_px
->setRefCount(1);
126 StringData
* buildStringData(double n
) {
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 ///////////////////////////////////////////////////////////////////////////////
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
);
159 return substr(pos
+ 1);
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
,
171 int String::find(const char *s
, int pos
/* = 0 */,
172 bool caseSensitive
/* = true */) const {
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
),
183 int String::find(CStrRef s
, int pos
/* = 0 */,
184 bool caseSensitive
/* = true */) const {
185 if (empty()) return -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
,
200 int String::rfind(const char *s
, int pos
/* = 0 */,
201 bool caseSensitive
/* = true */) const {
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
),
211 int String::rfind(CStrRef s
, int pos
/* = 0 */,
212 bool caseSensitive
/* = true */) const {
213 if (empty()) return -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 {
223 char *ret
= string_replace(data(), len
, start
, length
, replacement
.data(),
225 return String(ret
, len
, AttachString
);
228 String
String::replace(CStrRef search
, CStrRef replacement
) const {
230 return replace(search
, replacement
, count
, true);
233 String
String::replace(CStrRef search
, CStrRef replacement
, int &count
,
234 bool caseSensitive
) const {
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
,
242 return String(ret
, len
, AttachString
);
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();
281 ///////////////////////////////////////////////////////////////////////////////
284 String
&String::operator=(litstr s
) {
285 if (m_px
) decRefStr(m_px
);
287 m_px
= NEW(StringData
)(s
, AttachLiteral
);
288 m_px
->setRefCount(1);
295 String
&String::operator=(StringData
*data
) {
296 StringBase::operator=(data
);
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);
308 String
&String::operator=(CStrRef str
) {
309 StringBase::operator=(str
.m_px
);
313 String
&String::operator=(CVarRef var
) {
314 return operator=(var
.toString());
317 ///////////////////////////////////////////////////////////////////////////////
318 // concatenation and increments
320 String
&String::operator+=(litstr s
) {
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
));
328 StringData
* px
= NEW(StringData
)(m_px
, s
);
337 String
&String::operator+=(CStrRef str
) {
340 StringBase::operator=(str
.m_px
);
341 } else if (m_px
->getCount() == 1) {
342 m_px
->append(str
.slice());
344 StringData
* px
= NEW(StringData
)(m_px
, str
.slice());
353 String
& String::operator+=(const StringSlice
& slice
) {
354 if (slice
.size() == 0) {
357 if (m_px
&& m_px
->getCount() == 1) {
362 if (m_px
) decRefStr(m_px
);
363 m_px
= NEW(StringData
)(slice
.begin(), slice
.size(), CopyString
);
364 m_px
->setRefCount(1);
367 StringData
* px
= NEW(StringData
)(m_px
, slice
);
374 String
& String::operator+=(const MutableSlice
& slice
) {
375 return (*this += StringSlice(slice
.begin(), slice
.size()));
378 String
&& operator+(String
&& lhs
, litstr 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
);
390 String
&& operator+(String
&& lhs
, String
&& rhs
) {
391 return std::move(lhs
+= rhs
);
395 String
operator+(String
&& lhs
, const String
& rhs
) {
396 return std::move(lhs
+= rhs
);
400 String
operator+(const String
& lhs
, String
&& rhs
) {
401 return NEW(StringData
)(lhs
.slice(), rhs
.slice());
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
));
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();
435 char *copy
= nullptr;
438 copy
= string_duplicate(s2
, len2
);
439 for (int i
= 0; i
< len1
; i
++) copy
[i
] |= s1
[i
];
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);
451 String
&String::operator&=(CStrRef v
) {
452 const char *s1
= data();
453 const char *s2
= v
.data();
457 char *copy
= nullptr;
460 copy
= string_duplicate(s2
, len2
);
461 for (int i
= 0; i
< len2
; i
++) copy
[i
] &= s1
[i
];
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);
473 String
&String::operator^=(CStrRef v
) {
474 const char *s1
= data();
475 const char *s2
= v
.data();
479 char *copy
= nullptr;
482 copy
= string_duplicate(s2
, len2
);
483 for (int i
= 0; i
< len2
; i
++) copy
[i
] ^= s1
[i
];
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);
495 ///////////////////////////////////////////////////////////////////////////////
499 VarNR
String::toKey() const {
500 if (!m_px
) return VarNR(empty_string
);
502 if (m_px
->isStrictlyInteger(n
)) {
509 ///////////////////////////////////////////////////////////////////////////////
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 ///////////////////////////////////////////////////////////////////////////////
670 void String::serialize(VariableSerializer
*serializer
) const {
672 serializer
->write(m_px
->data(), m_px
->size());
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
));
686 throw Exception("Size of serialized string (%d) must not be negative",
690 char ch
= uns
->readChar();
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
);
703 if (m_px
) decRefStr(m_px
);
707 ch
= uns
->readChar();
708 if (ch
!= delimiter1
) {
709 throw Exception("Expected '%c' but got '%c'", delimiter1
, ch
);
713 ///////////////////////////////////////////////////////////////////////////////
716 void String::dump() const {
724 ///////////////////////////////////////////////////////////////////////////////
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());
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);
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
)) {
770 //////////////////////////////////////////////////////////////////////////////