Files
bluelib/core/stringstream.c
Max Wash bdcd4163c7 core: stringstream: added read-support
any characters written to a stringstream can be optionally read back again
using the b_stream read API.

this functions similar to a ringbuffer, with two key differences:
 1) the buffer is not circular, and will continuously expand to accomodate all incoming data.
 2) reading data from the stringstream does not remove it from the buffer.
2026-02-03 14:33:06 +00:00

298 lines
6.6 KiB
C

#include <blue/core/stringstream.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_CAPACITY 15
/*** PRIVATE DATA *************************************************************/
struct b_stringstream_p {
char *ss_buf;
size_t ss_ptr;
size_t ss_len;
size_t ss_max;
unsigned char ss_alloc;
};
/*** PRIVATE FUNCTIONS ********************************************************/
static enum b_status __getc(struct b_stringstream_p *ss, b_wchar *out)
{
size_t available = ss->ss_len - ss->ss_ptr;
const char *p = ss->ss_buf + ss->ss_ptr;
size_t to_copy = b_wchar_utf8_codepoint_stride(p);
if (to_copy > available) {
return B_ERR_BAD_STATE;
}
b_wchar c = b_wchar_utf8_codepoint_decode(p);
*out = c;
if (c == B_WCHAR_INVALID) {
return B_ERR_BAD_STATE;
}
ss->ss_ptr += to_copy;
return B_SUCCESS;
}
static enum b_status __gets(
struct b_stringstream_p *ss, char *s, size_t len, size_t *nr_read)
{
size_t available = ss->ss_len - ss->ss_ptr;
size_t to_copy = len;
if (available < to_copy) {
to_copy = available;
}
memcpy(s, ss->ss_buf + ss->ss_ptr, to_copy);
ss->ss_ptr += to_copy;
if (nr_read) {
*nr_read = to_copy;
}
return B_SUCCESS;
}
static enum b_status __puts(
struct b_stringstream_p *ss, const char *s, size_t len, size_t *nr_written)
{
size_t available = ss->ss_max - ss->ss_len;
size_t to_copy = len;
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;
}
ss->ss_buf = new_buf;
ss->ss_max = ss->ss_len + to_copy;
available = ss->ss_max - ss->ss_len;
}
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;
}
static enum b_status stringstream_reset(struct b_stringstream_p *ss)
{
ss->ss_len = 0;
if (ss->ss_buf) {
*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_max = max;
ss->ss_alloc = 0;
return B_SUCCESS;
}
static size_t stringstream_get_length(const struct b_stringstream_p *strv)
{
return strv->ss_len;
}
static const char *stringstream_ptr(const struct b_stringstream_p *ss)
{
return ss->ss_buf;
}
static char *stringstream_steal(struct b_stringstream_p *ss)
{
char *out = ss->ss_buf;
memset(ss, 0x0, sizeof *ss);
ss->ss_alloc = 1;
return out;
}
/*** PUBLIC FUNCTIONS *********************************************************/
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;
}
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_READ | B_STREAM_WRITE | Z__B_STREAM_STATIC;
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_READ | 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);
}
}
enum b_status stream_getc(b_stream *stream, b_wchar *c)
{
struct b_stringstream_p *s
= b_object_get_private(stream, B_TYPE_STRINGSTREAM);
enum b_status status = __getc(s, c);
return status;
}
enum b_status stream_read(b_stream *stream, void *buf, size_t count, size_t *nr_read)
{
struct b_stringstream_p *s
= b_object_get_private(stream, B_TYPE_STRINGSTREAM);
enum b_status status = __gets(s, buf, count, nr_read);
return status;
}
enum b_status stream_write(
b_stream *stream, const void *buf, size_t count, size_t *nr_written)
{
struct b_stringstream_p *s
= b_object_get_private(stream, B_TYPE_STRINGSTREAM);
enum b_status status = __puts(s, (const char *)buf, count, nr_written);
return status;
}
/*** CLASS DEFINITION *********************************************************/
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_TYPE_CLASS_INTERFACE_BEGIN(b_stream, B_TYPE_STREAM)
B_INTERFACE_ENTRY(s_close) = NULL;
B_INTERFACE_ENTRY(s_seek) = NULL;
B_INTERFACE_ENTRY(s_tell) = NULL;
B_INTERFACE_ENTRY(s_getc) = stream_getc;
B_INTERFACE_ENTRY(s_read) = stream_read;
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)
B_TYPE_DEFINITION_BEGIN(b_stringstream)
B_TYPE_ID(0x508a609a, 0xfac5, 0x4d31, 0x843a, 0x44b68ad329f3);
B_TYPE_EXTENDS(B_TYPE_STREAM);
B_TYPE_CLASS(b_stringstream_class);
B_TYPE_INSTANCE_PRIVATE(struct b_stringstream_p);
B_TYPE_INSTANCE_INIT(stringstream_init);
B_TYPE_INSTANCE_FINI(stringstream_fini);
B_TYPE_DEFINITION_END(b_stringstream)