summaryrefslogtreecommitdiff
path: root/libsoup/soup-coding-gzip.c
blob: b1731d577b50d17b6cc95afc41a28643827c6441 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * soup-coding-gzip.c: "gzip" coding
 *
 * Copyright (C) 2005 Novell, Inc.
 * Copyright (C) 2008 Red Hat, Inc.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "soup-coding-gzip.h"

#include <zlib.h>

typedef struct {
	z_stream stream;

} SoupCodingGzipPrivate;
#define SOUP_CODING_GZIP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CODING_GZIP, SoupCodingGzipPrivate))

G_DEFINE_TYPE (SoupCodingGzip, soup_coding_gzip, SOUP_TYPE_CODING)

static void             constructed (GObject *object);
static void             finalize    (GObject *object);
static SoupCodingStatus apply_into  (SoupCoding *coding,
				     gconstpointer input, gsize input_length,
				     gsize *input_used,
				     gpointer output, gsize output_length,
				     gsize *output_used,
				     gboolean done, GError **error);

static void
soup_coding_gzip_init (SoupCodingGzip *gzip)
{
	SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (gzip);

	priv->stream.zalloc = Z_NULL;
	priv->stream.zfree = Z_NULL;
	priv->stream.opaque = Z_NULL;
}

static void
soup_coding_gzip_class_init (SoupCodingGzipClass *gzip_class)
{
	GObjectClass *object_class = G_OBJECT_CLASS (gzip_class);
	SoupCodingClass *coding_class = SOUP_CODING_CLASS (gzip_class);

	g_type_class_add_private (gzip_class, sizeof (SoupCodingGzipPrivate));

	coding_class->name = "gzip";

	object_class->constructed = constructed;
	object_class->finalize = finalize;

	coding_class->apply_into = apply_into;
}

static void
constructed (GObject *object)
{
	SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (object);

	/* All of these values are the defaults according to the zlib
	 * documentation. "16" is a magic number that means gzip
	 * instead of zlib.
	 */
	if (SOUP_CODING (object)->direction == SOUP_CODING_ENCODE) {
		deflateInit2 (&priv->stream, Z_DEFAULT_COMPRESSION,
			      Z_DEFLATED, MAX_WBITS | 16, 8,
			      Z_DEFAULT_STRATEGY);
	} else
		inflateInit2 (&priv->stream, MAX_WBITS | 16);
}

static void
finalize (GObject *object)
{
	SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (object);

	if (SOUP_CODING (object)->direction == SOUP_CODING_ENCODE)
		deflateEnd (&priv->stream);
	else
		inflateEnd (&priv->stream);

	G_OBJECT_CLASS (soup_coding_gzip_parent_class)->finalize (object);
}

static SoupCodingStatus
apply_into (SoupCoding *coding,
	    gconstpointer input, gsize input_length, gsize *input_used,
	    gpointer output, gsize output_length, gsize *output_used,
	    gboolean done, GError **error)
{
	SoupCodingGzipPrivate *priv = SOUP_CODING_GZIP_GET_PRIVATE (coding);
	int ret;

	priv->stream.avail_in = input_length;
	priv->stream.next_in  = (gpointer)input;
	priv->stream.total_in = 0;

	priv->stream.avail_out = output_length;
	priv->stream.next_out  = output;
	priv->stream.total_out = 0;

	if (coding->direction == SOUP_CODING_ENCODE)
		ret = deflate (&priv->stream, done ? Z_FINISH : Z_NO_FLUSH);
	else
		ret = inflate (&priv->stream, Z_SYNC_FLUSH);

	*input_used = priv->stream.total_in;
	*output_used = priv->stream.total_out;

	switch (ret) {
	case Z_NEED_DICT:
	case Z_DATA_ERROR:
	case Z_STREAM_ERROR:
		g_set_error_literal (error, SOUP_CODING_ERROR,
				     SOUP_CODING_ERROR_DATA_ERROR,
				     priv->stream.msg ? priv->stream.msg : "Bad data");
		return SOUP_CODING_STATUS_ERROR;

	case Z_BUF_ERROR:
	case Z_MEM_ERROR:
		g_set_error_literal (error, SOUP_CODING_ERROR,
				     SOUP_CODING_ERROR_INTERNAL_ERROR,
				     priv->stream.msg ? priv->stream.msg : "Internal error");
		return SOUP_CODING_STATUS_ERROR;

	case Z_STREAM_END:
		return SOUP_CODING_STATUS_COMPLETE;

	case Z_OK:
	default:
		if (*output_used == output_length &&
		    *input_used < input_length)
			return SOUP_CODING_STATUS_NEED_SPACE;
		else
			return SOUP_CODING_STATUS_OK;
	}
}