#pragma once #include #include #include // vsnprintf NAMESPACE_BEGIN(NB_NAMESPACE) NAMESPACE_BEGIN(detail) struct Buffer { public: // Disable copy/move constructor and assignment Buffer(const Buffer &) = delete; Buffer(Buffer &&) = delete; Buffer &operator=(const Buffer &) = delete; Buffer &operator=(Buffer &&) = delete; Buffer(size_t size = 0) : m_start((char *) malloc(size)) { if (!m_start) { fprintf(stderr, "Buffer::Buffer(): out of memory (unrecoverable error)!"); abort(); } m_end = m_start + size; if (size) clear(); } ~Buffer() { free(m_start); } /// Append a string with the specified length void put(const char *str, size_t size) { if (m_cur + size >= m_end) expand(size + 1 - remain()); memcpy(m_cur, str, size); m_cur += size; *m_cur = '\0'; } /// Append a string template void put(const char (&str)[N]) { put(str, N - 1); } /// Append a dynamic string void put_dstr(const char *str) { put(str, strlen(str)); } /// Append a single character to the buffer void put(char c) { if (m_cur + 1 >= m_end) expand(); *m_cur++ = c; *m_cur = '\0'; } /// Append multiple copies of a single character to the buffer void put(char c, size_t count) { if (m_cur + count >= m_end) expand(count + 1 - remain()); for (size_t i = 0; i < count; ++i) *m_cur++ = c; *m_cur = '\0'; } /// Append a formatted (printf-style) string to the buffer #if defined(__GNUC__) __attribute__((__format__ (__printf__, 2, 3))) #endif size_t fmt(const char *format, ...) { size_t written; do { size_t size = remain(); va_list args; va_start(args, format); written = (size_t) vsnprintf(m_cur, size, format, args); va_end(args); if (written + 1 < size) { m_cur += written; break; } expand(); } while (true); return written; } const char *get() { return m_start; } void clear() { m_cur = m_start; if (m_start != m_end) m_start[0] = '\0'; } /// Remove the last 'n' characters void rewind(size_t n) { if (m_cur < m_start + n) m_cur = m_start; else m_cur -= n; *m_cur = '\0'; } /// Append an unsigned 32 bit integer void put_uint32(uint32_t value) { const int digits = 10; const char *num = "0123456789"; char buf[digits]; size_t i = digits; do { buf[--i] = num[value % 10]; value /= 10; } while (value); return put(buf + i, digits - i); } char *copy(size_t offset = 0) const { size_t copy_size = size() + 1 - offset; char *tmp = (char *) malloc(copy_size); if (!tmp) { fprintf(stderr, "Buffer::copy(): out of memory (unrecoverable error)!"); abort(); } memcpy(tmp, m_start + offset, copy_size); return tmp; } size_t size() const { return (size_t) (m_cur - m_start); } size_t remain() const { return (size_t) (m_end - m_cur); } private: NB_NOINLINE void expand(size_t minval = 2) { size_t old_alloc_size = m_end - m_start, new_alloc_size = 2 * old_alloc_size + minval, used_size = (size_t) (m_cur - m_start), copy_size = used_size + 1; if (old_alloc_size < copy_size) copy_size = old_alloc_size; char *tmp = (char *) malloc(new_alloc_size); if (!tmp) { fprintf(stderr, "Buffer::expand(): out of memory (unrecoverable error)!"); abort(); } memcpy(tmp, m_start, copy_size); free(m_start); m_start = tmp; m_end = m_start + new_alloc_size; m_cur = m_start + used_size; } private: char *m_start{nullptr}, *m_cur{nullptr}, *m_end{nullptr}; }; NAMESPACE_END(detail) NAMESPACE_END(NB_NAMESPACE)