Add support for HHBC ops with 5 immediates
[hiphop-php.git] / hphp / runtime / base / string-buffer.cpp
blobf5153bd0fb06d35280b5788ea100324c81ba62a6
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.asCell();
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 KindOfPersistentArray:
193 case KindOfArray:
194 case KindOfObject:
195 case KindOfResource:
196 case KindOfRef:
197 append(v.toString());
201 void StringBuffer::appendHelper(char ch) {
202 if (!valid()) makeValid(1);
203 if (m_len == m_cap) {
204 growBy(1);
206 m_str->mutableData()[m_len++] = ch;
209 void StringBuffer::makeValid(uint32_t minCap) {
210 assertx(!valid());
211 assertx(!m_len);
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);
219 assertx(s);
220 assertx(len >= 0);
221 if (len <= 0) return;
223 if (len > m_cap - m_len) {
224 growBy(len);
226 memcpy(m_str->mutableData() + m_len, s, len);
227 m_len += len;
230 void StringBuffer::printf(const char *format, ...) {
231 va_list ap;
232 va_start(ap, format);
234 bool printed = false;
235 for (int len = 1024; !printed; len <<= 1) {
236 va_list v;
237 va_copy(v, ap);
239 char *buf = (char*)req::malloc_noptrs(len);
240 SCOPE_EXIT { req::free(buf); };
241 if (vsnprintf(buf, len, format, v) < len) {
242 append(buf);
243 printed = true;
246 va_end(v);
249 va_end(ap);
252 void StringBuffer::read(FILE* in, int page_size /* = 1024 */) {
253 assertx(in);
254 assertx(page_size > 0);
256 if (!valid()) makeValid(page_size);
257 while (true) {
258 int buffer_size = m_cap - m_len;
259 if (buffer_size < page_size) {
260 growBy(page_size);
261 buffer_size = m_cap - m_len;
263 size_t len = fread(m_str->mutableData() + m_len, 1, buffer_size, in);
264 if (len == 0) break;
265 m_len += len;
269 void StringBuffer::read(File* in, int page_size /* = 1024 */) {
270 assertx(in);
271 assertx(page_size > 0);
273 if (!valid()) makeValid(page_size);
274 while (true) {
275 int buffer_size = m_cap - m_len;
276 if (buffer_size < page_size) {
277 growBy(page_size);
278 buffer_size = m_cap - m_len;
280 int64_t len = in->readImpl(m_str->mutableData() + m_len, buffer_size);
281 assertx(len >= 0);
282 if (len == 0) break;
283 m_len += len;
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) {
299 new_size = minSize;
302 if (new_size > m_maxBytes) {
303 if (minSize > m_maxBytes) {
304 throw StringBufferLimitException(m_maxBytes, detach());
305 } else {
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());
314 m_str->release();
315 m_str = tmp;
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) {
329 struct stat sb;
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()));
337 m_cap = sb.st_size;
338 m_buffer = (char *)safe_malloc(m_cap + 1);
340 int fd = ::open(filename, O_RDONLY);
341 if (fd != -1) {
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;
346 if (len <= 0) break;
347 m_len += len;
349 ::close(fd);
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() {
360 free(m_buffer);
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");
369 assertx(m_buffer);
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);
378 m_cap = newcap - 1;
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);
387 m_buffer[m_len] = 0;
388 String s(m_buffer, m_len, AttachString);
389 m_buffer = 0;
390 m_len = m_cap = 0;
391 return s;
394 ///////////////////////////////////////////////////////////////////////////////