core: stringstream: convert to a b_object type inheriting from b_stream
This commit is contained in:
@@ -1,27 +1,36 @@
|
|||||||
#ifndef BLUELIB_CORE_STRINGSTREAM_H_
|
#ifndef BLUE_CORE_STRINGSTREAM_H_
|
||||||
#define BLUELIB_CORE_STRINGSTREAM_H_
|
#define BLUE_CORE_STRINGSTREAM_H_
|
||||||
|
|
||||||
|
#include <blue/core/macros.h>
|
||||||
#include <blue/core/misc.h>
|
#include <blue/core/misc.h>
|
||||||
#include <blue/core/status.h>
|
#include <blue/core/status.h>
|
||||||
#include <blue/core/stream.h>
|
#include <blue/core/stream.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
typedef struct b_stringstream {
|
B_DECLS_BEGIN;
|
||||||
b_stream ss_base;
|
|
||||||
char *ss_buf;
|
|
||||||
size_t ss_len;
|
|
||||||
size_t ss_max;
|
|
||||||
unsigned char ss_alloc;
|
|
||||||
int *ss_istack;
|
|
||||||
int ss_add_indent;
|
|
||||||
size_t ss_istack_ptr, ss_istack_size;
|
|
||||||
} b_stringstream;
|
|
||||||
|
|
||||||
BLUE_API void b_stringstream_begin(b_stringstream *strv, char *buf, size_t max);
|
#define B_TYPE_STRINGSTREAM (b_stringstream_get_type())
|
||||||
BLUE_API void b_stringstream_begin_dynamic(b_stringstream *strv);
|
|
||||||
|
B_DECLARE_TYPE(b_stringstream);
|
||||||
|
|
||||||
|
B_TYPE_CLASS_DECLARATION_BEGIN(b_stringstream)
|
||||||
|
B_TYPE_CLASS_DECLARATION_END(b_stringstream)
|
||||||
|
|
||||||
|
BLUE_API b_type b_stringstream_get_type(void);
|
||||||
|
|
||||||
|
BLUE_API b_stringstream *b_stringstream_create(void);
|
||||||
|
BLUE_API b_stringstream *b_stringstream_create_with_buffer(char *buf, size_t max);
|
||||||
|
|
||||||
|
BLUE_API b_status b_stringstream_reset(b_stringstream *strv);
|
||||||
|
BLUE_API b_status b_stringstream_reset_with_buffer(
|
||||||
|
b_stringstream *strv, char *buf, size_t max);
|
||||||
|
|
||||||
|
BLUE_API const char *b_stringstream_ptr(const b_stringstream *strv);
|
||||||
|
BLUE_API char *b_stringstream_steal(b_stringstream *strv);
|
||||||
|
|
||||||
BLUE_API size_t b_stringstream_get_length(const b_stringstream *strv);
|
BLUE_API size_t b_stringstream_get_length(const b_stringstream *strv);
|
||||||
|
|
||||||
|
#if 0
|
||||||
BLUE_API void b_stringstream_push_indent(b_stringstream *strv, int indent);
|
BLUE_API void b_stringstream_push_indent(b_stringstream *strv, int indent);
|
||||||
BLUE_API void b_stringstream_pop_indent(b_stringstream *strv);
|
BLUE_API void b_stringstream_pop_indent(b_stringstream *strv);
|
||||||
|
|
||||||
@@ -33,6 +42,8 @@ BLUE_API b_status b_stringstream_addvf(
|
|||||||
BLUE_API b_status b_stringstream_addvl(
|
BLUE_API b_status b_stringstream_addvl(
|
||||||
b_stringstream *strv, const char **strs, size_t count);
|
b_stringstream *strv, const char **strs, size_t count);
|
||||||
BLUE_API b_status b_stringstream_add_many(b_stringstream *strv, ...);
|
BLUE_API b_status b_stringstream_add_many(b_stringstream *strv, ...);
|
||||||
BLUE_API char *b_stringstream_end(b_stringstream *strv);
|
#endif
|
||||||
|
|
||||||
|
B_DECLS_END;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,223 +4,229 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static b_status ss_builder_push_string(
|
#define DEFAULT_CAPACITY 15
|
||||||
b_stringstream *ss, const char *s, size_t len);
|
|
||||||
|
|
||||||
enum b_status stream_write(
|
/*** PRIVATE DATA *************************************************************/
|
||||||
struct b_stream *stream, const unsigned char *buf, size_t count,
|
|
||||||
size_t *nr_written)
|
struct b_stringstream_p {
|
||||||
|
char *ss_buf;
|
||||||
|
size_t ss_len;
|
||||||
|
size_t ss_max;
|
||||||
|
unsigned char ss_alloc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*** PRIVATE FUNCTIONS ********************************************************/
|
||||||
|
|
||||||
|
static enum b_status __puts(
|
||||||
|
struct b_stringstream_p *ss, const char *s, size_t len, size_t *nr_written)
|
||||||
{
|
{
|
||||||
struct b_stringstream *s = (struct b_stringstream *)stream;
|
size_t available = ss->ss_max - ss->ss_len;
|
||||||
|
size_t to_copy = len;
|
||||||
|
|
||||||
enum b_status status = ss_builder_push_string(s, (const char *)buf, count);
|
if (to_copy > available && ss->ss_alloc == 1) {
|
||||||
|
char *new_buf = realloc(ss->ss_buf, ss->ss_len + to_copy + 1);
|
||||||
|
if (!new_buf) {
|
||||||
|
return B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
stream->s_cursor += *nr_written;
|
ss->ss_buf = new_buf;
|
||||||
|
ss->ss_max = ss->ss_len + to_copy;
|
||||||
|
available = ss->ss_max - ss->ss_len;
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
if (available < to_copy) {
|
||||||
|
to_copy = available;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ss->ss_buf + ss->ss_len, s, to_copy);
|
||||||
|
|
||||||
|
/* increment the length by the full string length, even if only a
|
||||||
|
* portion was copied */
|
||||||
|
ss->ss_len += len;
|
||||||
|
|
||||||
|
if (nr_written) {
|
||||||
|
*nr_written = to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void b_stringstream_begin(struct b_stringstream *ss, char *buf, size_t max)
|
static enum b_status stringstream_reset(struct b_stringstream_p *ss)
|
||||||
{
|
{
|
||||||
memset(ss, 0x0, sizeof *ss);
|
ss->ss_len = 0;
|
||||||
|
|
||||||
ss->ss_base.s_mode = B_STREAM_WRITE | Z__B_STREAM_STATIC;
|
if (ss->ss_buf) {
|
||||||
ss->ss_base.s_write = stream_write;
|
*ss->ss_buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum b_status stringstream_reset_with_buffer(
|
||||||
|
struct b_stringstream_p *ss, char *buf, size_t max)
|
||||||
|
{
|
||||||
|
ss->ss_len = 0;
|
||||||
|
|
||||||
|
if (ss->ss_buf && ss->ss_alloc) {
|
||||||
|
free(ss->ss_buf);
|
||||||
|
}
|
||||||
|
|
||||||
ss->ss_buf = buf;
|
ss->ss_buf = buf;
|
||||||
ss->ss_max = max;
|
ss->ss_max = max;
|
||||||
ss->ss_len = 0;
|
|
||||||
ss->ss_alloc = 0;
|
ss->ss_alloc = 0;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void b_stringstream_begin_dynamic(struct b_stringstream *ss)
|
static size_t stringstream_get_length(const struct b_stringstream_p *strv)
|
||||||
{
|
|
||||||
memset(ss, 0x0, sizeof *ss);
|
|
||||||
|
|
||||||
ss->ss_base.s_mode = B_STREAM_WRITE | Z__B_STREAM_STATIC;
|
|
||||||
ss->ss_base.s_write = stream_write;
|
|
||||||
|
|
||||||
ss->ss_buf = NULL;
|
|
||||||
ss->ss_max = 0;
|
|
||||||
ss->ss_len = 0;
|
|
||||||
ss->ss_alloc = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t b_stringstream_get_length(const struct b_stringstream *strv)
|
|
||||||
{
|
{
|
||||||
return strv->ss_len;
|
return strv->ss_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int current_indent(struct b_stringstream *ss)
|
static const char *stringstream_ptr(const struct b_stringstream_p *ss)
|
||||||
{
|
{
|
||||||
if (!ss->ss_istack || !ss->ss_istack_size) {
|
return ss->ss_buf;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ss->ss_istack[ss->ss_istack_ptr];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __formatter_putchar(struct b_stringstream *ss, char c)
|
static char *stringstream_steal(struct b_stringstream_p *ss)
|
||||||
{
|
{
|
||||||
if (ss->ss_len + 1 >= ss->ss_max && ss->ss_alloc == 1) {
|
char *out = ss->ss_buf;
|
||||||
char *new_buf = realloc(ss->ss_buf, ss->ss_len + 8);
|
|
||||||
if (!new_buf) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ss->ss_buf = new_buf;
|
memset(ss, 0x0, sizeof *ss);
|
||||||
ss->ss_max = ss->ss_len + 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ss->ss_len < ss->ss_max - 1) {
|
ss->ss_alloc = 1;
|
||||||
ss->ss_buf[ss->ss_len] = c;
|
|
||||||
ss->ss_buf[ss->ss_len + 1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
ss->ss_len++;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void formatter_putchar(struct b_stringstream *f, char c)
|
/*** PUBLIC FUNCTIONS *********************************************************/
|
||||||
{
|
|
||||||
if (f->ss_add_indent && c != '\n') {
|
|
||||||
int indent = current_indent(f);
|
|
||||||
for (int i = 0; i < indent; i++) {
|
|
||||||
__formatter_putchar(f, ' ');
|
|
||||||
__formatter_putchar(f, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
f->ss_add_indent = 0;
|
b_stringstream *b_stringstream_create_with_buffer(char *buf, size_t max)
|
||||||
|
{
|
||||||
|
b_stringstream *s = b_object_create(B_TYPE_STRINGSTREAM);
|
||||||
|
if (!s) {
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
__formatter_putchar(f, c);
|
b_stream_cfg *cfg = b_object_get_protected(s, B_TYPE_STREAM);
|
||||||
|
struct b_stringstream_p *p = b_object_get_private(s, B_TYPE_STRINGSTREAM);
|
||||||
|
|
||||||
if (c == '\n') {
|
cfg->s_mode = B_STREAM_WRITE | Z__B_STREAM_STATIC;
|
||||||
f->ss_add_indent = 1;
|
|
||||||
|
p->ss_buf = buf;
|
||||||
|
p->ss_max = max;
|
||||||
|
p->ss_len = 0;
|
||||||
|
p->ss_alloc = 0;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_stringstream *b_stringstream_create(void)
|
||||||
|
{
|
||||||
|
b_stringstream *s = b_object_create(B_TYPE_STRINGSTREAM);
|
||||||
|
if (!s) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_stream_cfg *cfg = b_object_get_protected(s, B_TYPE_STREAM);
|
||||||
|
struct b_stringstream_p *p = b_object_get_private(s, B_TYPE_STRINGSTREAM);
|
||||||
|
|
||||||
|
cfg->s_mode = B_STREAM_WRITE | Z__B_STREAM_STATIC;
|
||||||
|
|
||||||
|
p->ss_buf = malloc(DEFAULT_CAPACITY + 1);
|
||||||
|
if (!p->ss_buf) {
|
||||||
|
b_stringstream_unref(s);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(p->ss_buf, 0x0, DEFAULT_CAPACITY + 1);
|
||||||
|
p->ss_max = DEFAULT_CAPACITY;
|
||||||
|
p->ss_len = 0;
|
||||||
|
p->ss_alloc = 1;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b_stringstream_get_length(const b_stringstream *strv)
|
||||||
|
{
|
||||||
|
B_CLASS_DISPATCH_STATIC_0(
|
||||||
|
B_TYPE_STRINGSTREAM, stringstream_get_length, strv);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum b_status b_stringstream_reset(b_stringstream *strv)
|
||||||
|
{
|
||||||
|
B_CLASS_DISPATCH_STATIC_0(B_TYPE_STRINGSTREAM, stringstream_reset, strv);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum b_status b_stringstream_reset_with_buffer(
|
||||||
|
b_stringstream *strv, char *buf, size_t max)
|
||||||
|
{
|
||||||
|
B_CLASS_DISPATCH_STATIC(
|
||||||
|
B_TYPE_STRINGSTREAM, stringstream_reset_with_buffer, strv, buf,
|
||||||
|
max);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *b_stringstream_ptr(const b_stringstream *ss)
|
||||||
|
{
|
||||||
|
B_CLASS_DISPATCH_STATIC_0(B_TYPE_STRINGSTREAM, stringstream_ptr, ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *b_stringstream_steal(b_stringstream *ss)
|
||||||
|
{
|
||||||
|
B_CLASS_DISPATCH_STATIC_0(B_TYPE_STRINGSTREAM, stringstream_steal, ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** PUBLIC ALIAS FUNCTIONS ***************************************************/
|
||||||
|
/*** VIRTUAL FUNCTIONS ********************************************************/
|
||||||
|
|
||||||
|
static void stringstream_init(b_object *obj, void *priv)
|
||||||
|
{
|
||||||
|
struct b_stringstream_p *stream = priv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stringstream_fini(b_object *obj, void *priv)
|
||||||
|
{
|
||||||
|
struct b_stringstream_p *stream = priv;
|
||||||
|
|
||||||
|
if (stream->ss_alloc && stream->ss_buf) {
|
||||||
|
free(stream->ss_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static b_status ss_builder_push_string(b_stringstream *ss, const char *s, size_t len)
|
enum b_status stream_write(
|
||||||
|
b_stream *stream, const unsigned char *buf, size_t count, size_t *nr_written)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < len; i++) {
|
struct b_stringstream_p *s
|
||||||
formatter_putchar(ss, s[i]);
|
= b_object_get_private(stream, B_TYPE_STRINGSTREAM);
|
||||||
}
|
|
||||||
|
|
||||||
return B_SUCCESS;
|
enum b_status status = __puts(s, (const char *)buf, count, nr_written);
|
||||||
}
|
|
||||||
|
|
||||||
void b_stringstream_push_indent(struct b_stringstream *ss, int indent)
|
|
||||||
{
|
|
||||||
if (!ss->ss_istack) {
|
|
||||||
ss->ss_istack = calloc(4, sizeof(int));
|
|
||||||
ss->ss_istack_size = 4;
|
|
||||||
ss->ss_istack_ptr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ss->ss_istack_ptr + 1 > ss->ss_istack_size) {
|
|
||||||
int *buf = realloc(
|
|
||||||
ss->ss_istack, (ss->ss_istack_size + 4) * sizeof(int));
|
|
||||||
if (!buf) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ss->ss_istack = buf;
|
|
||||||
ss->ss_istack_size += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cur_indent = ss->ss_istack[ss->ss_istack_ptr];
|
|
||||||
ss->ss_istack[++ss->ss_istack_ptr] = cur_indent + indent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void b_stringstream_pop_indent(b_stringstream *strv)
|
|
||||||
{
|
|
||||||
if (!strv->ss_istack || !strv->ss_istack_size || !strv->ss_istack_ptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
strv->ss_istack_ptr--;
|
|
||||||
}
|
|
||||||
|
|
||||||
b_status b_stringstream_add(struct b_stringstream *ss, const char *str)
|
|
||||||
{
|
|
||||||
return ss_builder_push_string(ss, str, strlen(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
b_status b_stringstream_addf(struct b_stringstream *ss, const char *format, ...)
|
|
||||||
{
|
|
||||||
char str[1024];
|
|
||||||
va_list arg;
|
|
||||||
va_start(arg, format);
|
|
||||||
enum b_status status = b_stringstream_addvf(ss, format, arg);
|
|
||||||
va_end(arg);
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
b_status b_stringstream_addvf(
|
/*** CLASS DEFINITION *********************************************************/
|
||||||
struct b_stringstream *ss, const char *format, va_list arg)
|
|
||||||
{
|
|
||||||
char str[1024];
|
|
||||||
size_t len = vsnprintf(str, sizeof str, format, arg);
|
|
||||||
|
|
||||||
return ss_builder_push_string(ss, str, len);
|
B_TYPE_CLASS_DEFINITION_BEGIN(b_stringstream)
|
||||||
}
|
B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT)
|
||||||
|
B_INTERFACE_ENTRY(to_string) = NULL;
|
||||||
|
B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT)
|
||||||
|
|
||||||
b_status b_stringstream_addv(b_stringstream *ss, const char **strs)
|
B_TYPE_CLASS_INTERFACE_BEGIN(b_stream, B_TYPE_STREAM)
|
||||||
{
|
B_INTERFACE_ENTRY(s_close) = NULL;
|
||||||
for (size_t i = 0; strs[i]; i++) {
|
B_INTERFACE_ENTRY(s_seek) = NULL;
|
||||||
size_t len = strlen(strs[i]);
|
B_INTERFACE_ENTRY(s_tell) = NULL;
|
||||||
b_status status = ss_builder_push_string(ss, strs[i], len);
|
B_INTERFACE_ENTRY(s_getc) = NULL;
|
||||||
if (B_ERR(status)) {
|
B_INTERFACE_ENTRY(s_read) = NULL;
|
||||||
return status;
|
B_INTERFACE_ENTRY(s_write) = stream_write;
|
||||||
}
|
B_INTERFACE_ENTRY(s_reserve) = NULL;
|
||||||
}
|
B_TYPE_CLASS_INTERFACE_END(b_stream, B_TYPE_STREAM)
|
||||||
|
B_TYPE_CLASS_DEFINITION_END(b_stringstream)
|
||||||
|
|
||||||
return B_SUCCESS;
|
B_TYPE_DEFINITION_BEGIN(b_stringstream)
|
||||||
}
|
B_TYPE_ID(0x508a609a, 0xfac5, 0x4d31, 0x843a, 0x44b68ad329f3);
|
||||||
|
B_TYPE_EXTENDS(B_TYPE_STREAM);
|
||||||
b_status b_stringstream_addvl(b_stringstream *ss, const char **strs, size_t count)
|
B_TYPE_CLASS(b_stringstream_class);
|
||||||
{
|
B_TYPE_INSTANCE_PRIVATE(struct b_stringstream_p);
|
||||||
for (size_t i = 0; i < count; i++) {
|
B_TYPE_INSTANCE_INIT(stringstream_init);
|
||||||
if (!strs[i]) {
|
B_TYPE_INSTANCE_FINI(stringstream_fini);
|
||||||
continue;
|
B_TYPE_DEFINITION_END(b_stringstream)
|
||||||
}
|
|
||||||
|
|
||||||
size_t len = strlen(strs[i]);
|
|
||||||
b_status status = ss_builder_push_string(ss, strs[i], len);
|
|
||||||
if (B_ERR(status)) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
b_status b_stringstream_add_many(b_stringstream *ss, ...)
|
|
||||||
{
|
|
||||||
va_list arg;
|
|
||||||
va_start(arg, ss);
|
|
||||||
const char *s = NULL;
|
|
||||||
|
|
||||||
while ((s = va_arg(arg, const char *))) {
|
|
||||||
size_t len = strlen(s);
|
|
||||||
b_status status = ss_builder_push_string(ss, s, len);
|
|
||||||
if (B_ERR(status)) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *b_stringstream_end(b_stringstream *ss)
|
|
||||||
{
|
|
||||||
char *out = ss->ss_buf;
|
|
||||||
|
|
||||||
if (ss->ss_istack) {
|
|
||||||
free(ss->ss_istack);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(ss, 0x0, sizeof *ss);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user