You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

477 lines
13 KiB

/* -*- coding: utf-8 -*-
* ----------------------------------------------------------------------
* Copyright © 2011-2012, RedJack, LLC.
* All rights reserved.
*
* Please see the COPYING file in this distribution for license
* details.
* ----------------------------------------------------------------------
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "libcork/core/allocator.h"
#include "libcork/core/types.h"
#include "libcork/ds/buffer.h"
#include "libcork/ds/managed-buffer.h"
#include "libcork/ds/stream.h"
#include "libcork/helpers/errors.h"
void
cork_buffer_init(struct cork_buffer *buffer)
{
buffer->buf = NULL;
buffer->size = 0;
buffer->allocated_size = 0;
}
struct cork_buffer *
cork_buffer_new(void)
{
struct cork_buffer *buffer = cork_new(struct cork_buffer);
cork_buffer_init(buffer);
return buffer;
}
void
cork_buffer_done(struct cork_buffer *buffer)
{
if (buffer->buf != NULL) {
free(buffer->buf);
buffer->buf = NULL;
}
buffer->size = 0;
buffer->allocated_size = 0;
}
void
cork_buffer_free(struct cork_buffer *buffer)
{
cork_buffer_done(buffer);
free(buffer);
}
bool
cork_buffer_equal(const struct cork_buffer *buffer1,
const struct cork_buffer *buffer2)
{
if (buffer1 == buffer2) {
return true;
}
if (buffer1->size != buffer2->size) {
return false;
}
return (memcmp(buffer1->buf, buffer2->buf, buffer1->size) == 0);
}
static void
cork_buffer_ensure_size_int(struct cork_buffer *buffer, size_t desired_size)
{
size_t new_size;
if (CORK_LIKELY(buffer->allocated_size >= desired_size)) {
return;
}
/* Make sure we at least double the old size when reallocating. */
new_size = buffer->allocated_size * 2;
if (desired_size > new_size) {
new_size = desired_size;
}
buffer->buf = cork_realloc(buffer->buf, new_size);
buffer->allocated_size = new_size;
}
void
cork_buffer_ensure_size(struct cork_buffer *buffer, size_t desired_size)
{
cork_buffer_ensure_size_int(buffer, desired_size);
}
void
cork_buffer_clear(struct cork_buffer *buffer)
{
buffer->size = 0;
if (buffer->buf != NULL) {
((char *) buffer->buf)[0] = '\0';
}
}
void
cork_buffer_truncate(struct cork_buffer *buffer, size_t length)
{
if (buffer->size > length) {
buffer->size = length;
if (length == 0) {
if (buffer->buf != NULL) {
((char *) buffer->buf)[0] = '\0';
}
} else {
((char *) buffer->buf)[length] = '\0';
}
}
}
void
cork_buffer_set(struct cork_buffer *buffer, const void *src, size_t length)
{
cork_buffer_ensure_size_int(buffer, length+1);
memcpy(buffer->buf, src, length);
((char *) buffer->buf)[length] = '\0';
buffer->size = length;
}
void
cork_buffer_append(struct cork_buffer *buffer, const void *src, size_t length)
{
cork_buffer_ensure_size_int(buffer, buffer->size + length + 1);
memcpy(buffer->buf + buffer->size, src, length);
buffer->size += length;
((char *) buffer->buf)[buffer->size] = '\0';
}
void
cork_buffer_set_string(struct cork_buffer *buffer, const char *str)
{
cork_buffer_set(buffer, str, strlen(str));
}
void
cork_buffer_append_string(struct cork_buffer *buffer, const char *str)
{
cork_buffer_append(buffer, str, strlen(str));
}
void
cork_buffer_append_vprintf(struct cork_buffer *buffer, const char *format,
va_list args)
{
size_t format_size;
va_list args1;
va_copy(args1, args);
format_size = vsnprintf(buffer->buf + buffer->size,
buffer->allocated_size - buffer->size,
format, args1);
va_end(args1);
/* If the first call works, then set buffer->size and return. */
if (format_size < (buffer->allocated_size - buffer->size)) {
buffer->size += format_size;
return;
}
/* If the first call fails, resize buffer and try again. */
cork_buffer_ensure_size_int
(buffer, buffer->allocated_size + format_size + 1);
format_size = vsnprintf(buffer->buf + buffer->size,
buffer->allocated_size - buffer->size,
format, args);
buffer->size += format_size;
}
void
cork_buffer_vprintf(struct cork_buffer *buffer, const char *format,
va_list args)
{
cork_buffer_clear(buffer);
cork_buffer_append_vprintf(buffer, format, args);
}
void
cork_buffer_append_printf(struct cork_buffer *buffer, const char *format, ...)
{
va_list args;
va_start(args, format);
cork_buffer_append_vprintf(buffer, format, args);
va_end(args);
}
void
cork_buffer_printf(struct cork_buffer *buffer, const char *format, ...)
{
va_list args;
va_start(args, format);
cork_buffer_vprintf(buffer, format, args);
va_end(args);
}
void
cork_buffer_append_indent(struct cork_buffer *buffer, size_t indent)
{
cork_buffer_ensure_size_int(buffer, buffer->size + indent + 1);
memset(buffer->buf + buffer->size, ' ', indent);
buffer->size += indent;
((char *) buffer->buf)[buffer->size] = '\0';
}
/* including space */
#define is_sprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e)
/* not including space */
#define is_print(ch) ((ch) > 0x20 && (ch) <= 0x7e)
#define is_space(ch) \
((ch) == ' ' || \
(ch) == '\f' || \
(ch) == '\n' || \
(ch) == '\r' || \
(ch) == '\t' || \
(ch) == '\v')
#define to_hex(nybble) \
((nybble) < 10? '0' + (nybble): 'a' - 10 + (nybble))
void
cork_buffer_append_c_string(struct cork_buffer *dest,
const char *chars, size_t length)
{
size_t i;
cork_buffer_append(dest, "\"", 1);
for (i = 0; i < length; i++) {
char ch = chars[i];
switch (ch) {
case '\"':
cork_buffer_append_literal(dest, "\\\"");
break;
case '\\':
cork_buffer_append_literal(dest, "\\\\");
break;
case '\f':
cork_buffer_append_literal(dest, "\\f");
break;
case '\n':
cork_buffer_append_literal(dest, "\\n");
break;
case '\r':
cork_buffer_append_literal(dest, "\\r");
break;
case '\t':
cork_buffer_append_literal(dest, "\\t");
break;
case '\v':
cork_buffer_append_literal(dest, "\\v");
break;
default:
if (is_sprint(ch)) {
cork_buffer_append(dest, &chars[i], 1);
} else {
uint8_t byte = ch;
cork_buffer_append_printf(dest, "\\x%02" PRIx8, byte);
}
break;
}
}
cork_buffer_append(dest, "\"", 1);
}
void
cork_buffer_append_hex_dump(struct cork_buffer *dest, size_t indent,
const char *chars, size_t length)
{
char hex[3 * 16];
char print[16];
char *curr_hex = hex;
char *curr_print = print;
size_t i;
size_t column = 0;
for (i = 0; i < length; i++) {
char ch = chars[i];
uint8_t u8 = ch;
*curr_hex++ = to_hex(u8 >> 4);
*curr_hex++ = to_hex(u8 & 0x0f);
*curr_hex++ = ' ';
*curr_print++ = is_sprint(ch)? ch: '.';
if (column == 0 && i != 0) {
cork_buffer_append_literal(dest, "\n");
cork_buffer_append_indent(dest, indent);
column++;
} else if (column == 15) {
cork_buffer_append_printf
(dest, "%-48.*s", (int) (curr_hex - hex), hex);
cork_buffer_append_literal(dest, " |");
cork_buffer_append(dest, print, curr_print - print);
cork_buffer_append_literal(dest, "|");
curr_hex = hex;
curr_print = print;
column = 0;
} else {
column++;
}
}
if (column > 0) {
cork_buffer_append_printf(dest, "%-48.*s", (int) (curr_hex - hex), hex);
cork_buffer_append_literal(dest, " |");
cork_buffer_append(dest, print, curr_print - print);
cork_buffer_append_literal(dest, "|");
}
}
void
cork_buffer_append_multiline(struct cork_buffer *dest, size_t indent,
const char *chars, size_t length)
{
size_t i;
for (i = 0; i < length; i++) {
char ch = chars[i];
if (ch == '\n') {
cork_buffer_append_literal(dest, "\n");
cork_buffer_append_indent(dest, indent);
} else {
cork_buffer_append(dest, &chars[i], 1);
}
}
}
void
cork_buffer_append_binary(struct cork_buffer *dest, size_t indent,
const char *chars, size_t length)
{
size_t i;
bool newline = false;
/* If there are any non-printable characters, print out a hex dump */
for (i = 0; i < length; i++) {
if (!is_print(chars[i]) && !is_space(chars[i])) {
cork_buffer_append_literal(dest, "(hex)\n");
cork_buffer_append_indent(dest, indent);
cork_buffer_append_hex_dump(dest, indent, chars, length);
return;
} else if (chars[i] == '\n') {
newline = true;
/* Don't immediately use the multiline format, since there might be
* a non-printable character later on that kicks us over to the hex
* dump format. */
}
}
if (newline) {
cork_buffer_append_literal(dest, "(multiline)\n");
cork_buffer_append_indent(dest, indent);
cork_buffer_append_multiline(dest, indent, chars, length);
} else {
cork_buffer_append(dest, chars, length);
}
}
struct cork_buffer__managed_buffer {
struct cork_managed_buffer parent;
struct cork_buffer *buffer;
};
static void
cork_buffer__managed_free(struct cork_managed_buffer *vself)
{
struct cork_buffer__managed_buffer *self =
cork_container_of(vself, struct cork_buffer__managed_buffer, parent);
cork_buffer_free(self->buffer);
free(self);
}
static struct cork_managed_buffer_iface CORK_BUFFER__MANAGED_BUFFER = {
cork_buffer__managed_free
};
struct cork_managed_buffer *
cork_buffer_to_managed_buffer(struct cork_buffer *buffer)
{
struct cork_buffer__managed_buffer *self =
cork_new(struct cork_buffer__managed_buffer);
self->parent.buf = buffer->buf;
self->parent.size = buffer->size;
self->parent.ref_count = 1;
self->parent.iface = &CORK_BUFFER__MANAGED_BUFFER;
self->buffer = buffer;
return &self->parent;
}
int
cork_buffer_to_slice(struct cork_buffer *buffer, struct cork_slice *slice)
{
struct cork_managed_buffer *managed =
cork_buffer_to_managed_buffer(buffer);
/* We don't have to check for NULL; cork_managed_buffer_slice_offset
* will do that for us. */
int rc = cork_managed_buffer_slice_offset(slice, managed, 0);
/* Before returning, drop our reference to the managed buffer. If
* the slicing succeeded, then there will be one remaining reference
* in the slice. If it didn't succeed, this will free the managed
* buffer for us. */
cork_managed_buffer_unref(managed);
return rc;
}
struct cork_buffer__stream_consumer {
struct cork_stream_consumer consumer;
struct cork_buffer *buffer;
};
static int
cork_buffer_stream_consumer_data(struct cork_stream_consumer *consumer,
const void *buf, size_t size,
bool is_first_chunk)
{
struct cork_buffer__stream_consumer *bconsumer = cork_container_of
(consumer, struct cork_buffer__stream_consumer, consumer);
if (is_first_chunk) {
cork_buffer_clear(bconsumer->buffer);
}
cork_buffer_append(bconsumer->buffer, buf, size);
return 0;
}
static int
cork_buffer_stream_consumer_eof(struct cork_stream_consumer *consumer)
{
return 0;
}
static void
cork_buffer_stream_consumer_free(struct cork_stream_consumer *consumer)
{
struct cork_buffer__stream_consumer *bconsumer =
cork_container_of
(consumer, struct cork_buffer__stream_consumer, consumer);
free(bconsumer);
}
struct cork_stream_consumer *
cork_buffer_to_stream_consumer(struct cork_buffer *buffer)
{
struct cork_buffer__stream_consumer *bconsumer =
cork_new(struct cork_buffer__stream_consumer);
bconsumer->consumer.data = cork_buffer_stream_consumer_data;
bconsumer->consumer.eof = cork_buffer_stream_consumer_eof;
bconsumer->consumer.free = cork_buffer_stream_consumer_free;
bconsumer->buffer = buffer;
return &bconsumer->consumer;
}