cairo-output-stream.c [plain text]
#define _BSD_SOURCE
#include "cairoint.h"
#include "cairo-output-stream-private.h"
#include "cairo-error-private.h"
#include "cairo-compiler-private.h"
#include <stdio.h>
#include <locale.h>
#include <errno.h>
#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6
#define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1))
void
_cairo_output_stream_init (cairo_output_stream_t *stream,
cairo_output_stream_write_func_t write_func,
cairo_output_stream_flush_func_t flush_func,
cairo_output_stream_close_func_t close_func)
{
stream->write_func = write_func;
stream->flush_func = flush_func;
stream->close_func = close_func;
stream->position = 0;
stream->status = CAIRO_STATUS_SUCCESS;
stream->closed = FALSE;
}
cairo_status_t
_cairo_output_stream_fini (cairo_output_stream_t *stream)
{
return _cairo_output_stream_close (stream);
}
const cairo_output_stream_t _cairo_output_stream_nil = {
NULL,
NULL,
NULL,
0,
CAIRO_STATUS_NO_MEMORY,
FALSE
};
static const cairo_output_stream_t _cairo_output_stream_nil_write_error = {
NULL,
NULL,
NULL,
0,
CAIRO_STATUS_WRITE_ERROR,
FALSE
};
typedef struct _cairo_output_stream_with_closure {
cairo_output_stream_t base;
cairo_write_func_t write_func;
cairo_close_func_t close_func;
void *closure;
} cairo_output_stream_with_closure_t;
static cairo_status_t
closure_write (cairo_output_stream_t *stream,
const unsigned char *data, unsigned int length)
{
cairo_output_stream_with_closure_t *stream_with_closure =
(cairo_output_stream_with_closure_t *) stream;
if (stream_with_closure->write_func == NULL)
return CAIRO_STATUS_SUCCESS;
return stream_with_closure->write_func (stream_with_closure->closure,
data, length);
}
static cairo_status_t
closure_close (cairo_output_stream_t *stream)
{
cairo_output_stream_with_closure_t *stream_with_closure =
(cairo_output_stream_with_closure_t *) stream;
if (stream_with_closure->close_func != NULL)
return stream_with_closure->close_func (stream_with_closure->closure);
else
return CAIRO_STATUS_SUCCESS;
}
cairo_output_stream_t *
_cairo_output_stream_create (cairo_write_func_t write_func,
cairo_close_func_t close_func,
void *closure)
{
cairo_output_stream_with_closure_t *stream;
stream = malloc (sizeof (cairo_output_stream_with_closure_t));
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
_cairo_output_stream_init (&stream->base,
closure_write, NULL, closure_close);
stream->write_func = write_func;
stream->close_func = close_func;
stream->closure = closure;
return &stream->base;
}
cairo_output_stream_t *
_cairo_output_stream_create_in_error (cairo_status_t status)
{
cairo_output_stream_t *stream;
if (status == CAIRO_STATUS_NO_MEMORY)
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
if (status == CAIRO_STATUS_WRITE_ERROR)
return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error;
stream = malloc (sizeof (cairo_output_stream_t));
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
_cairo_output_stream_init (stream, NULL, NULL, NULL);
stream->status = status;
return stream;
}
cairo_status_t
_cairo_output_stream_flush (cairo_output_stream_t *stream)
{
cairo_status_t status;
if (stream->closed)
return stream->status;
if (stream == &_cairo_output_stream_nil ||
stream == &_cairo_output_stream_nil_write_error)
{
return stream->status;
}
if (stream->flush_func) {
status = stream->flush_func (stream);
if (stream->status == CAIRO_STATUS_SUCCESS)
stream->status = status;
}
return stream->status;
}
cairo_status_t
_cairo_output_stream_close (cairo_output_stream_t *stream)
{
cairo_status_t status;
if (stream->closed)
return stream->status;
if (stream == &_cairo_output_stream_nil ||
stream == &_cairo_output_stream_nil_write_error)
{
return stream->status;
}
if (stream->close_func) {
status = stream->close_func (stream);
if (stream->status == CAIRO_STATUS_SUCCESS)
stream->status = status;
}
stream->closed = TRUE;
return stream->status;
}
cairo_status_t
_cairo_output_stream_destroy (cairo_output_stream_t *stream)
{
cairo_status_t status;
assert (stream != NULL);
if (stream == &_cairo_output_stream_nil ||
stream == &_cairo_output_stream_nil_write_error)
{
return stream->status;
}
status = _cairo_output_stream_fini (stream);
free (stream);
return status;
}
void
_cairo_output_stream_write (cairo_output_stream_t *stream,
const void *data, size_t length)
{
if (length == 0)
return;
if (stream->status)
return;
stream->status = stream->write_func (stream, data, length);
stream->position += length;
}
void
_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
const unsigned char *data,
size_t length)
{
const char hex_chars[] = "0123456789abcdef";
char buffer[2];
unsigned int i, column;
if (stream->status)
return;
for (i = 0, column = 0; i < length; i++, column++) {
if (column == 38) {
_cairo_output_stream_write (stream, "\n", 1);
column = 0;
}
buffer[0] = hex_chars[(data[i] >> 4) & 0x0f];
buffer[1] = hex_chars[data[i] & 0x0f];
_cairo_output_stream_write (stream, buffer, 2);
}
}
static void
_cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision)
{
struct lconv *locale_data;
const char *decimal_point;
int decimal_point_len;
char *p;
int decimal_len;
int num_zeros, decimal_digits;
if (d == 0.0)
d = 0.0;
locale_data = localeconv ();
decimal_point = locale_data->decimal_point;
decimal_point_len = strlen (decimal_point);
assert (decimal_point_len != 0);
if (limited_precision) {
snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d);
} else {
if (fabs (d) >= 0.1) {
snprintf (buffer, size, "%f", d);
} else {
snprintf (buffer, size, "%.18f", d);
p = buffer;
if (*p == '+' || *p == '-')
p++;
while (_cairo_isdigit (*p))
p++;
if (strncmp (p, decimal_point, decimal_point_len) == 0)
p += decimal_point_len;
num_zeros = 0;
while (*p++ == '0')
num_zeros++;
decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL;
if (decimal_digits < 18)
snprintf (buffer, size, "%.*f", decimal_digits, d);
}
}
p = buffer;
if (*p == '+' || *p == '-')
p++;
while (_cairo_isdigit (*p))
p++;
if (strncmp (p, decimal_point, decimal_point_len) == 0) {
*p = '.';
decimal_len = strlen (p + decimal_point_len);
memmove (p + 1, p + decimal_point_len, decimal_len);
p[1 + decimal_len] = 0;
for (p = p + decimal_len; *p == '0'; p--)
*p = 0;
if (*p == '.') {
*p = 0;
p--;
}
}
}
enum {
LENGTH_MODIFIER_LONG = 0x100
};
void
_cairo_output_stream_vprintf (cairo_output_stream_t *stream,
const char *fmt, va_list ap)
{
#define SINGLE_FMT_BUFFER_SIZE 32
char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE];
int single_fmt_length;
char *p;
const char *f, *start;
int length_modifier, width;
cairo_bool_t var_width;
if (stream->status)
return;
f = fmt;
p = buffer;
while (*f != '\0') {
if (p == buffer + sizeof (buffer)) {
_cairo_output_stream_write (stream, buffer, sizeof (buffer));
p = buffer;
}
if (*f != '%') {
*p++ = *f++;
continue;
}
start = f;
f++;
if (*f == '0')
f++;
var_width = FALSE;
if (*f == '*') {
var_width = TRUE;
f++;
}
while (_cairo_isdigit (*f))
f++;
length_modifier = 0;
if (*f == 'l') {
length_modifier = LENGTH_MODIFIER_LONG;
f++;
}
single_fmt_length = f - start + 1;
assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE);
memcpy (single_fmt, start, single_fmt_length);
single_fmt[single_fmt_length] = '\0';
_cairo_output_stream_write (stream, buffer, p - buffer);
switch (*f | length_modifier) {
case '%':
buffer[0] = *f;
buffer[1] = 0;
break;
case 'd':
case 'u':
case 'o':
case 'x':
case 'X':
if (var_width) {
width = va_arg (ap, int);
snprintf (buffer, sizeof buffer,
single_fmt, width, va_arg (ap, int));
} else {
snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int));
}
break;
case 'd' | LENGTH_MODIFIER_LONG:
case 'u' | LENGTH_MODIFIER_LONG:
case 'o' | LENGTH_MODIFIER_LONG:
case 'x' | LENGTH_MODIFIER_LONG:
case 'X' | LENGTH_MODIFIER_LONG:
if (var_width) {
width = va_arg (ap, int);
snprintf (buffer, sizeof buffer,
single_fmt, width, va_arg (ap, long int));
} else {
snprintf (buffer, sizeof buffer,
single_fmt, va_arg (ap, long int));
}
break;
case 's':
snprintf (buffer, sizeof buffer,
single_fmt, va_arg (ap, const char *));
break;
case 'f':
_cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE);
break;
case 'g':
_cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE);
break;
case 'c':
buffer[0] = va_arg (ap, int);
buffer[1] = 0;
break;
default:
ASSERT_NOT_REACHED;
}
p = buffer + strlen (buffer);
f++;
}
_cairo_output_stream_write (stream, buffer, p - buffer);
}
void
_cairo_output_stream_printf (cairo_output_stream_t *stream,
const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
_cairo_output_stream_vprintf (stream, fmt, ap);
va_end (ap);
}
long
_cairo_output_stream_get_position (cairo_output_stream_t *stream)
{
return stream->position;
}
cairo_status_t
_cairo_output_stream_get_status (cairo_output_stream_t *stream)
{
return stream->status;
}
typedef struct _stdio_stream {
cairo_output_stream_t base;
FILE *file;
} stdio_stream_t;
static cairo_status_t
stdio_write (cairo_output_stream_t *base,
const unsigned char *data, unsigned int length)
{
stdio_stream_t *stream = (stdio_stream_t *) base;
if (fwrite (data, 1, length, stream->file) != length)
return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
stdio_flush (cairo_output_stream_t *base)
{
stdio_stream_t *stream = (stdio_stream_t *) base;
fflush (stream->file);
if (ferror (stream->file))
return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
else
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
stdio_close (cairo_output_stream_t *base)
{
cairo_status_t status;
stdio_stream_t *stream = (stdio_stream_t *) base;
status = stdio_flush (base);
fclose (stream->file);
return status;
}
cairo_output_stream_t *
_cairo_output_stream_create_for_file (FILE *file)
{
stdio_stream_t *stream;
if (file == NULL) {
_cairo_error_throw (CAIRO_STATUS_WRITE_ERROR);
return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error;
}
stream = malloc (sizeof *stream);
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
_cairo_output_stream_init (&stream->base,
stdio_write, stdio_flush, stdio_flush);
stream->file = file;
return &stream->base;
}
cairo_output_stream_t *
_cairo_output_stream_create_for_filename (const char *filename)
{
stdio_stream_t *stream;
FILE *file;
if (filename == NULL)
return _cairo_null_stream_create ();
file = fopen (filename, "wb");
if (file == NULL) {
switch (errno) {
case ENOMEM:
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
default:
_cairo_error_throw (CAIRO_STATUS_WRITE_ERROR);
return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error;
}
}
stream = malloc (sizeof *stream);
if (unlikely (stream == NULL)) {
fclose (file);
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
_cairo_output_stream_init (&stream->base,
stdio_write, stdio_flush, stdio_close);
stream->file = file;
return &stream->base;
}
typedef struct _memory_stream {
cairo_output_stream_t base;
cairo_array_t array;
} memory_stream_t;
static cairo_status_t
memory_write (cairo_output_stream_t *base,
const unsigned char *data, unsigned int length)
{
memory_stream_t *stream = (memory_stream_t *) base;
return _cairo_array_append_multiple (&stream->array, data, length);
}
static cairo_status_t
memory_close (cairo_output_stream_t *base)
{
memory_stream_t *stream = (memory_stream_t *) base;
_cairo_array_fini (&stream->array);
return CAIRO_STATUS_SUCCESS;
}
cairo_output_stream_t *
_cairo_memory_stream_create (void)
{
memory_stream_t *stream;
stream = malloc (sizeof *stream);
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
_cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close);
_cairo_array_init (&stream->array, 1);
return &stream->base;
}
cairo_status_t
_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream,
unsigned char **data_out,
unsigned long *length_out)
{
memory_stream_t *stream;
cairo_status_t status;
status = abstract_stream->status;
if (unlikely (status))
return _cairo_output_stream_destroy (abstract_stream);
stream = (memory_stream_t *) abstract_stream;
*length_out = _cairo_array_num_elements (&stream->array);
*data_out = malloc (*length_out);
if (unlikely (*data_out == NULL)) {
status = _cairo_output_stream_destroy (abstract_stream);
assert (status == CAIRO_STATUS_SUCCESS);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out);
return _cairo_output_stream_destroy (abstract_stream);
}
void
_cairo_memory_stream_copy (cairo_output_stream_t *base,
cairo_output_stream_t *dest)
{
memory_stream_t *stream = (memory_stream_t *) base;
if (dest->status)
return;
if (base->status) {
dest->status = base->status;
return;
}
_cairo_output_stream_write (dest,
_cairo_array_index (&stream->array, 0),
_cairo_array_num_elements (&stream->array));
}
int
_cairo_memory_stream_length (cairo_output_stream_t *base)
{
memory_stream_t *stream = (memory_stream_t *) base;
return _cairo_array_num_elements (&stream->array);
}
static cairo_status_t
null_write (cairo_output_stream_t *base,
const unsigned char *data, unsigned int length)
{
return CAIRO_STATUS_SUCCESS;
}
cairo_output_stream_t *
_cairo_null_stream_create (void)
{
cairo_output_stream_t *stream;
stream = malloc (sizeof *stream);
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
_cairo_output_stream_init (stream, null_write, NULL, NULL);
return stream;
}