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
.asCell();
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 KindOfPersistentArray
:
197 append(v
.toString());
201 void StringBuffer::appendHelper(char ch
) {
202 if (!valid()) makeValid(1);
203 if (m_len
== m_cap
) {
206 m_str
->mutableData()[m_len
++] = ch
;
209 void StringBuffer::makeValid(uint32_t minCap
) {
212 m_str
= StringData::Make(std::max(m_initialCap
, minCap
));
213 m_cap
= m_str
->capacity();
216 void StringBuffer::appendHelper(const char *s
, int len
) {
217 if (!valid()) makeValid(len
);
221 if (len
<= 0) return;
223 if (len
> m_cap
- m_len
) {
226 memcpy(m_str
->mutableData() + m_len
, s
, len
);
230 void StringBuffer::printf(const char *format
, ...) {
232 va_start(ap
, format
);
234 bool printed
= false;
235 for (int len
= 1024; !printed
; len
<<= 1) {
239 char *buf
= (char*)req::malloc_noptrs(len
);
240 SCOPE_EXIT
{ req::free(buf
); };
241 if (vsnprintf(buf
, len
, format
, v
) < len
) {
252 void StringBuffer::read(FILE* in
, int page_size
/* = 1024 */) {
254 assertx(page_size
> 0);
256 if (!valid()) makeValid(page_size
);
258 int buffer_size
= m_cap
- m_len
;
259 if (buffer_size
< page_size
) {
261 buffer_size
= m_cap
- m_len
;
263 size_t len
= fread(m_str
->mutableData() + m_len
, 1, buffer_size
, in
);
269 void StringBuffer::read(File
* in
, int page_size
/* = 1024 */) {
271 assertx(page_size
> 0);
273 if (!valid()) makeValid(page_size
);
275 int buffer_size
= m_cap
- m_len
;
276 if (buffer_size
< page_size
) {
278 buffer_size
= m_cap
- m_len
;
280 int64_t len
= in
->readImpl(m_str
->mutableData() + m_len
, buffer_size
);
287 void StringBuffer::growBy(int spaceRequired
) {
289 * The default initial size is a power-of-two minus 1.
290 * This doubling scheme keeps the total block size a
291 * power of two, which should be good for memory allocators.
292 * But note there is no guarantee either that the initial size
293 * is power-of-two minus 1, or that it stays that way
294 * (new_size < minSize below).
296 auto new_size
= m_cap
* 2 + 1;
297 auto const minSize
= static_cast<unsigned>(m_cap
) + spaceRequired
;
298 if (new_size
< minSize
) {
302 if (new_size
> m_maxBytes
) {
303 if (minSize
> m_maxBytes
) {
304 throw StringBufferLimitException(m_maxBytes
, detach());
306 new_size
= m_maxBytes
;
310 m_str
->setSize(m_len
);
311 auto const tmp
= m_str
->reserve(new_size
);
312 if (UNLIKELY(tmp
!= m_str
)) {
313 assertx(m_str
->hasExactlyOneRef());
317 m_cap
= m_str
->capacity();
320 //////////////////////////////////////////////////////////////////////
322 CstrBuffer::CstrBuffer(int cap
)
323 : m_buffer((char*)safe_malloc(cap
+ 1)), m_len(0), m_cap(cap
) {
324 assertx(unsigned(cap
) <= kMaxCap
);
327 CstrBuffer::CstrBuffer(const char *filename
)
328 : m_buffer(nullptr), m_len(0) {
330 if (stat(filename
, &sb
) == 0) {
331 if (sb
.st_size
> kMaxCap
- 1) {
332 auto const str
= folly::to
<std::string
>(
333 "file ", filename
, " is too large"
335 throw StringBufferLimitException(kMaxCap
, String(str
.c_str()));
338 m_buffer
= (char *)safe_malloc(m_cap
+ 1);
340 int fd
= ::open(filename
, O_RDONLY
);
342 while (m_len
< m_cap
) {
343 int buffer_size
= m_cap
- m_len
;
344 int len
= ::read(fd
, m_buffer
+ m_len
, buffer_size
);
345 if (len
== -1 && errno
== EINTR
) continue;
354 CstrBuffer::CstrBuffer(char* data
, int len
)
355 : m_buffer(data
), m_len(len
), m_cap(len
) {
356 assertx(unsigned(len
) < kMaxCap
);
359 CstrBuffer::~CstrBuffer() {
363 void CstrBuffer::append(folly::StringPiece slice
) {
364 auto const data
= slice
.data();
365 auto const len
= slice
.size();
367 static_assert(std::is_unsigned
<decltype(len
)>::value
,
368 "len is supposed to be unsigned");
371 unsigned newlen
= m_len
+ len
;
372 if (newlen
+ 1 > m_cap
) {
373 if (newlen
+ 1 > kMaxCap
) {
374 throw StringBufferLimitException(kMaxCap
, detach());
376 unsigned newcap
= folly::nextPowTwo(newlen
+ 1);
377 m_buffer
= (char*)safe_realloc(m_buffer
, newcap
);
379 assertx(newlen
+ 1 <= m_cap
);
381 memcpy(m_buffer
+ m_len
, data
, len
);
382 m_buffer
[m_len
= newlen
] = 0;
385 String
CstrBuffer::detach() {
386 assertx(m_len
<= m_cap
);
388 String
s(m_buffer
, m_len
, AttachString
);
394 ///////////////////////////////////////////////////////////////////////////////