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/string-buffer.h"
20 #include <sys/types.h>
23 #include <folly/Conv.h>
24 #include <folly/portability/Fcntl.h>
26 #include "hphp/runtime/base/file.h"
27 #include "hphp/runtime/base/zend-functions.h"
29 #include "hphp/util/alloc.h"
30 #include "hphp/util/conv-10.h"
33 ///////////////////////////////////////////////////////////////////////////////
35 StringBuffer::StringBuffer(uint32_t initialSize
/* = SmallStringReserve */)
36 : m_initialCap(initialSize
)
37 , m_maxBytes(kDefaultOutputLimit
)
40 m_str
= StringData::Make(initialSize
);
41 m_cap
= m_str
->capacity();
44 StringBuffer::~StringBuffer() {
46 assertx(m_str
->hasExactlyOneRef());
47 assertx((m_str
->setSize(0), true)); // appease StringData::checkSane()
52 const char* StringBuffer::data() const {
54 auto buffer
= m_str
->mutableData();
55 buffer
[m_len
] = '\0'; // fixup
61 String
StringBuffer::detach() {
63 assertx(m_str
->hasExactlyOneRef());
64 auto str
= String::attach(m_str
);
71 return empty_string();
74 String
StringBuffer::copy() const {
75 return String(data(), size(), CopyString
);
78 void StringBuffer::absorb(StringBuffer
& buf
) {
80 StringData
* str
= m_str
;
88 buf
.m_len
= str
->size();
89 buf
.m_cap
= str
->capacity();
101 void StringBuffer::clear() {
105 void StringBuffer::release() {
107 assertx(m_str
->hasExactlyOneRef());
109 m_str
->mutableData()[m_len
] = 0; // appease StringData::checkSane()
117 void StringBuffer::resize(uint32_t size
) {
118 assertx(size
<= m_cap
);
124 char* StringBuffer::appendCursor(int size
) {
127 } else if (m_cap
- m_len
< size
) {
128 m_str
->setSize(m_len
);
129 auto const tmp
= m_str
->reserve(m_len
+ size
);
130 if (UNLIKELY(tmp
!= m_str
)) {
131 assertx(m_str
->hasExactlyOneRef());
135 m_cap
= m_str
->capacity();
137 return m_str
->mutableData() + m_len
;
140 void StringBuffer::append(int n
) {
143 auto const sd
= String::GetIntegerStringData(n
);
146 auto sl
= conv_10(n
, buf
+ 12);
147 p
= const_cast<char*>(sl
.data());
150 p
= (char *)sd
->data();
156 void StringBuffer::append(int64_t n
) {
159 auto const sd
= String::GetIntegerStringData(n
);
162 auto sl
= conv_10(n
, buf
+ 21);
163 p
= const_cast<char*>(sl
.data());
166 p
= (char *)sd
->data();
172 void StringBuffer::append(const Variant
& v
) {
173 auto const cell
= v
.asTypedValue();
174 switch (cell
->m_type
) {
176 append(cell
->m_data
.num
);
178 case KindOfPersistentString
:
180 append(cell
->m_data
.pstr
);
186 case KindOfPersistentVec
:
188 case KindOfPersistentDict
:
190 case KindOfPersistentKeyset
:
192 case KindOfPersistentDArray
:
194 case KindOfPersistentVArray
:
196 case KindOfPersistentArray
:
205 append(v
.toString());
209 void StringBuffer::appendHelper(char ch
) {
210 if (!valid()) makeValid(1);
211 if (m_len
== m_cap
) {
214 m_str
->mutableData()[m_len
++] = ch
;
217 void StringBuffer::makeValid(uint32_t minCap
) {
220 m_str
= StringData::Make(std::max(m_initialCap
, minCap
));
221 m_cap
= m_str
->capacity();
224 void StringBuffer::appendHelper(const char *s
, int len
) {
225 if (!valid()) makeValid(len
);
229 if (len
<= 0) return;
231 if (len
> m_cap
- m_len
) {
234 memcpy(m_str
->mutableData() + m_len
, s
, len
);
238 void StringBuffer::printf(const char *format
, ...) {
240 va_start(ap
, format
);
242 bool printed
= false;
243 for (int len
= 1024; !printed
; len
<<= 1) {
247 char *buf
= (char*)req::malloc_noptrs(len
);
248 SCOPE_EXIT
{ req::free(buf
); };
249 if (vsnprintf(buf
, len
, format
, v
) < len
) {
260 void StringBuffer::read(FILE* in
, int page_size
/* = 1024 */) {
262 assertx(page_size
> 0);
264 if (!valid()) makeValid(page_size
);
266 int buffer_size
= m_cap
- m_len
;
267 if (buffer_size
< page_size
) {
269 buffer_size
= m_cap
- m_len
;
271 size_t len
= fread(m_str
->mutableData() + m_len
, 1, buffer_size
, in
);
277 void StringBuffer::read(File
* in
, int page_size
/* = 1024 */) {
279 assertx(page_size
> 0);
281 if (!valid()) makeValid(page_size
);
283 int buffer_size
= m_cap
- m_len
;
284 if (buffer_size
< page_size
) {
286 buffer_size
= m_cap
- m_len
;
288 int64_t len
= in
->readImpl(m_str
->mutableData() + m_len
, buffer_size
);
295 void StringBuffer::growBy(int spaceRequired
) {
297 * The default initial size is a power-of-two minus 1.
298 * This doubling scheme keeps the total block size a
299 * power of two, which should be good for memory allocators.
300 * But note there is no guarantee either that the initial size
301 * is power-of-two minus 1, or that it stays that way
302 * (new_size < minSize below).
304 auto new_size
= m_cap
* 2 + 1;
305 auto const minSize
= static_cast<unsigned>(m_cap
) + spaceRequired
;
306 if (new_size
< minSize
) {
310 if (new_size
> m_maxBytes
) {
311 if (minSize
> m_maxBytes
) {
312 throw StringBufferLimitException(m_maxBytes
, detach());
314 new_size
= m_maxBytes
;
318 m_str
->setSize(m_len
);
319 auto const tmp
= m_str
->reserve(new_size
);
320 if (UNLIKELY(tmp
!= m_str
)) {
321 assertx(m_str
->hasExactlyOneRef());
325 m_cap
= m_str
->capacity();
328 ///////////////////////////////////////////////////////////////////////////////