track total size of static array and Unit/Class/Func
[hiphop-php.git] / hphp / runtime / base / string-buffer.cpp
blob3df926d680735f4feb9809c0ec91c9cfc650c487
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
18 #include <algorithm>
20 #include <sys/types.h>
21 #include <sys/stat.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"
32 namespace HPHP {
33 ///////////////////////////////////////////////////////////////////////////////
35 StringBuffer::StringBuffer(uint32_t initialSize /* = SmallStringReserve */)
36 : m_initialCap(initialSize)
37 , m_maxBytes(kDefaultOutputLimit)
38 , m_len(0)
40 m_str = StringData::Make(initialSize);
41 m_cap = m_str->capacity();
44 StringBuffer::~StringBuffer() {
45 if (m_str) {
46 assertx(m_str->hasExactlyOneRef());
47 assertx((m_str->setSize(0), true)); // appease StringData::checkSane()
48 m_str->release();
52 const char* StringBuffer::data() const {
53 if (m_str && m_len) {
54 auto buffer = m_str->mutableData();
55 buffer[m_len] = '\0'; // fixup
56 return buffer;
58 return nullptr;
61 String StringBuffer::detach() {
62 if (m_str && m_len) {
63 assertx(m_str->hasExactlyOneRef());
64 auto str = String::attach(m_str);
65 str.setSize(m_len);
66 m_str = nullptr;
67 m_len = 0;
68 m_cap = 0;
69 return str;
71 return empty_string();
74 String StringBuffer::copy() const {
75 return String(data(), size(), CopyString);
78 void StringBuffer::absorb(StringBuffer& buf) {
79 if (empty()) {
80 StringData* str = m_str;
82 m_str = buf.m_str;
83 m_len = buf.m_len;
84 m_cap = buf.m_cap;
86 buf.m_str = str;
87 if (str) {
88 buf.m_len = str->size();
89 buf.m_cap = str->capacity();
90 } else {
91 buf.m_len = 0;
92 buf.m_cap = 0;
94 buf.clear();
95 return;
98 append(buf.detach());
101 void StringBuffer::clear() {
102 m_len = 0;
105 void StringBuffer::release() {
106 if (m_str) {
107 assertx(m_str->hasExactlyOneRef());
108 if (debug) {
109 m_str->mutableData()[m_len] = 0; // appease StringData::checkSane()
111 m_str->release();
113 m_str = nullptr;
114 m_len = m_cap = 0;
117 void StringBuffer::resize(uint32_t size) {
118 assertx(size <= m_cap);
119 if (size <= m_cap) {
120 m_len = size;
124 char* StringBuffer::appendCursor(int size) {
125 if (!m_str) {
126 makeValid(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());
132 m_str->release();
133 m_str = tmp;
135 m_cap = m_str->capacity();
137 return m_str->mutableData() + m_len;
140 void StringBuffer::append(int n) {
141 char buf[12];
142 int len;
143 auto const sd = String::GetIntegerStringData(n);
144 char *p;
145 if (!sd) {
146 auto sl = conv_10(n, buf + 12);
147 p = const_cast<char*>(sl.data());
148 len = sl.size();
149 } else {
150 p = (char *)sd->data();
151 len = sd->size();
153 append(p, len);
156 void StringBuffer::append(int64_t n) {
157 char buf[21];
158 int len;
159 auto const sd = String::GetIntegerStringData(n);
160 char *p;
161 if (!sd) {
162 auto sl = conv_10(n, buf + 21);
163 p = const_cast<char*>(sl.data());
164 len = sl.size();
165 } else {
166 p = (char *)sd->data();
167 len = sd->size();
169 append(p, len);
172 void StringBuffer::append(const Variant& v) {
173 auto const cell = v.asTypedValue();
174 switch (cell->m_type) {
175 case KindOfInt64:
176 append(cell->m_data.num);
177 break;
178 case KindOfPersistentString:
179 case KindOfString:
180 append(cell->m_data.pstr);
181 break;
182 case KindOfUninit:
183 case KindOfNull:
184 case KindOfBoolean:
185 case KindOfDouble:
186 case KindOfPersistentVec:
187 case KindOfVec:
188 case KindOfPersistentDict:
189 case KindOfDict:
190 case KindOfPersistentKeyset:
191 case KindOfKeyset:
192 case KindOfPersistentDArray:
193 case KindOfDArray:
194 case KindOfPersistentVArray:
195 case KindOfVArray:
196 case KindOfPersistentArray:
197 case KindOfArray:
198 case KindOfObject:
199 case KindOfResource:
200 case KindOfRFunc:
201 case KindOfFunc:
202 case KindOfClass:
203 case KindOfClsMeth:
204 case KindOfRecord:
205 append(v.toString());
209 void StringBuffer::appendHelper(char ch) {
210 if (!valid()) makeValid(1);
211 if (m_len == m_cap) {
212 growBy(1);
214 m_str->mutableData()[m_len++] = ch;
217 void StringBuffer::makeValid(uint32_t minCap) {
218 assertx(!valid());
219 assertx(!m_len);
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);
227 assertx(s);
228 assertx(len >= 0);
229 if (len <= 0) return;
231 if (len > m_cap - m_len) {
232 growBy(len);
234 memcpy(m_str->mutableData() + m_len, s, len);
235 m_len += len;
238 void StringBuffer::printf(const char *format, ...) {
239 va_list ap;
240 va_start(ap, format);
242 bool printed = false;
243 for (int len = 1024; !printed; len <<= 1) {
244 va_list v;
245 va_copy(v, ap);
247 char *buf = (char*)req::malloc_noptrs(len);
248 SCOPE_EXIT { req::free(buf); };
249 if (vsnprintf(buf, len, format, v) < len) {
250 append(buf);
251 printed = true;
254 va_end(v);
257 va_end(ap);
260 void StringBuffer::read(FILE* in, int page_size /* = 1024 */) {
261 assertx(in);
262 assertx(page_size > 0);
264 if (!valid()) makeValid(page_size);
265 while (true) {
266 int buffer_size = m_cap - m_len;
267 if (buffer_size < page_size) {
268 growBy(page_size);
269 buffer_size = m_cap - m_len;
271 size_t len = fread(m_str->mutableData() + m_len, 1, buffer_size, in);
272 if (len == 0) break;
273 m_len += len;
277 void StringBuffer::read(File* in, int page_size /* = 1024 */) {
278 assertx(in);
279 assertx(page_size > 0);
281 if (!valid()) makeValid(page_size);
282 while (true) {
283 int buffer_size = m_cap - m_len;
284 if (buffer_size < page_size) {
285 growBy(page_size);
286 buffer_size = m_cap - m_len;
288 int64_t len = in->readImpl(m_str->mutableData() + m_len, buffer_size);
289 assertx(len >= 0);
290 if (len == 0) break;
291 m_len += len;
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) {
307 new_size = minSize;
310 if (new_size > m_maxBytes) {
311 if (minSize > m_maxBytes) {
312 throw StringBufferLimitException(m_maxBytes, detach());
313 } else {
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());
322 m_str->release();
323 m_str = tmp;
325 m_cap = m_str->capacity();
328 ///////////////////////////////////////////////////////////////////////////////