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.
298 lines
6.6 KiB
C
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)
|