167 lines
4.2 KiB
C
167 lines
4.2 KiB
C
|
|
#pragma once
|
||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdarg.h>
|
||
|
|
#include <stdio.h> // 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 <size_t N> 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)
|