Squash  0.7.0
buffer-stream.c
1 /* Copyright (c) 2013-2015 The Squash Authors
2  *
3  * Permission is hereby granted, free of charge, to any person
4  * obtaining a copy of this software and associated documentation
5  * files (the "Software"), to deal in the Software without
6  * restriction, including without limitation the rights to use, copy,
7  * modify, merge, publish, distribute, sublicense, and/or sell copies
8  * of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be
12  * included in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  * Authors:
24  * Evan Nemerson <evan@nemerson.com>
25  */
26 
27 #include <assert.h>
28 #include <string.h>
29 
30 #include <stdio.h>
31 
32 #include "internal.h"
33 
34 static void
35 squash_buffer_stream_init (void* stream,
36  SquashCodec* codec,
37  SquashStreamType stream_type,
38  SquashOptions* options,
39  SquashDestroyNotify destroy_notify) {
40  SquashBufferStream* s = (SquashBufferStream*) stream;
41 
42  squash_stream_init (stream, codec, stream_type, options, destroy_notify);
43 
44  s->head = NULL;
45  s->last = NULL;
46  s->last_pos = 0;
47  s->input = NULL;
48  s->output = NULL;
49 }
50 
51 static void
52 squash_buffer_stream_destroy (void* stream) {
53  SquashBufferStream* s = (SquashBufferStream*) stream;
54 
55  squash_slist_foreach ((SquashSList*) s->head, (SquashSListForeachFunc) free);
56  if (s->input != NULL) {
57  squash_buffer_free (s->input);
58  }
59  if (s->output != NULL) {
60  squash_buffer_free (s->output);
61  }
62 
63  squash_stream_destroy (stream);
64 }
65 
66 static void
67 squash_buffer_stream_free (void* options) {
68  squash_buffer_stream_destroy (options);
69  free (options);
70 }
71 
72 SquashBufferStream*
73 squash_buffer_stream_new (SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options) {
74  SquashBufferStream* stream;
75 
76  stream = (SquashBufferStream*) malloc (sizeof (SquashBufferStream));
77  squash_buffer_stream_init (stream, codec, stream_type, options, squash_buffer_stream_free);
78 
79  return stream;
80 }
81 
82 #ifndef MIN
83 # define MIN(a,b) (((a) < (b)) ? (a) : (b))
84 #endif
85 
87 squash_buffer_stream_process (SquashBufferStream* stream) {
88  if (stream->base_object.avail_in == 0)
89  return SQUASH_OK;
90 
91  while (stream->base_object.avail_in > 0) {
92  if (stream->last_pos == SQUASH_BUFFER_STREAM_BUFFER_SIZE ||
93  stream->last == NULL) {
94  stream->last = SQUASH_SLIST_APPEND(stream->last, SquashBufferStreamSList);
95  stream->last_pos = 0;
96 
97  if (stream->head == NULL) {
98  stream->head = stream->last;
99  }
100  }
101 
102  const size_t copy_size = MIN(SQUASH_BUFFER_STREAM_BUFFER_SIZE - stream->last_pos, stream->base_object.avail_in);
103  memcpy (stream->last->data + stream->last_pos, stream->base_object.next_in, copy_size);
104  stream->base_object.next_in += copy_size;
105  stream->base_object.avail_in -= copy_size;
106  stream->last_pos += copy_size;
107  }
108 
109  return SQUASH_OK;
110 }
111 
112 static void
113 squash_buffer_stream_consolidate (SquashBufferStream* stream) {
114  if (stream->input == NULL) {
115  stream->input = squash_buffer_new (stream->base_object.total_in);
116  }
117 
118  {
119  SquashBufferStreamSList* next;
120  SquashBufferStreamSList* list = stream->head;
121 
122  for ( next = NULL ; list != NULL ; list = next ) {
123  next = (SquashBufferStreamSList*) list->base.next;
124  squash_buffer_append (stream->input, list->data, (next != NULL) ? SQUASH_BUFFER_STREAM_BUFFER_SIZE : stream->last_pos);
125  free (list);
126  }
127  }
128 
129  stream->head = NULL;
130  stream->last = NULL;
131  stream->last_pos = 0;
132 }
133 
135 squash_buffer_stream_finish (SquashBufferStream* stream) {
136  SquashStatus res;
137 
138  if (stream->base_object.avail_out == 0) {
140  }
141 
142  if (stream->input == NULL) {
143  if (stream->base_object.avail_in > 0) {
144  squash_buffer_stream_process (stream);
145  }
146  squash_buffer_stream_consolidate (stream);
147  } else {
148  if (stream->base_object.avail_in > 0) {
149  return squash_error (SQUASH_STATE);
150  }
151  }
152 
153  if (stream->output == NULL) {
154  size_t decompressed_size;
155 
156  if (stream->base_object.stream_type == SQUASH_STREAM_COMPRESS) {
157  size_t compressed_size = squash_codec_get_max_compressed_size (stream->base_object.codec, stream->input->length);
158  stream->output = squash_buffer_new (compressed_size);
159  res = squash_codec_compress_with_options (stream->base_object.codec,
160  &compressed_size, stream->output->data,
161  stream->input->length, stream->input->data,
162  stream->base_object.options);
163 
164  if (res != SQUASH_OK) {
165  return res;
166  }
167 
168  stream->output->length = compressed_size;
169  } else if (stream->base_object.stream_type == SQUASH_STREAM_DECOMPRESS) {
170  if (stream->base_object.codec->impl.get_uncompressed_size != NULL) {
171  decompressed_size = squash_codec_get_uncompressed_size (stream->base_object.codec, stream->input->length, stream->input->data);
172  stream->output = squash_buffer_new (decompressed_size);
173  squash_buffer_set_size (stream->output, decompressed_size);
174  res = squash_codec_decompress_with_options (stream->base_object.codec,
175  &decompressed_size, stream->output->data,
176  stream->input->length, stream->input->data, NULL);
177 
178  if (res != SQUASH_OK) {
179  return res;
180  }
181  } else {
182  /* Yes, this is horrible. If you're hitting this code you
183  should really try to change your application to use the
184  buffer API or use a container codec which does contain size
185  information (e.g., zlib or gzip instead of deflate). */
186  decompressed_size = stream->input->length;
187 
188  /* Round decompressed_size up the the next highest power of
189  two. */
190  decompressed_size--;
191  decompressed_size |= decompressed_size >> 1;
192  decompressed_size |= decompressed_size >> 2;
193  decompressed_size |= decompressed_size >> 4;
194  decompressed_size |= decompressed_size >> 8;
195  decompressed_size |= decompressed_size >> 16;
196  decompressed_size++;
197 
198  /* A bit more, to (hopefully) catch all but the most highly
199  compressed data. */
200  decompressed_size <<= 2;
201 
202  stream->output = squash_buffer_new (decompressed_size);
203 
204  res = SQUASH_BUFFER_FULL;
205  while ( res == SQUASH_BUFFER_FULL ) {
206  /* free/malloc instead of realloc (avoid a copy) */
207  if (stream->output->allocated < decompressed_size)
208  squash_buffer_clear (stream->output);
209  squash_buffer_set_size (stream->output, decompressed_size);
210  res = squash_codec_decompress_with_options (stream->base_object.codec,
211  &(stream->output->length), stream->output->data,
212  stream->input->length, stream->input->data, NULL);
213  decompressed_size <<= 1;
214  }
215  }
216  } else {
217  squash_assert_unreachable();
218  }
219  }
220 
221  size_t copy_size = MIN(stream->output->length - stream->base_object.total_out, stream->base_object.avail_out);
222  memcpy (stream->base_object.next_out, stream->output->data + stream->base_object.total_out, copy_size);
223  stream->base_object.next_out += copy_size;
224  stream->base_object.avail_out -= copy_size;
225 
226  return ((stream->base_object.total_out + copy_size) == stream->output->length) ?
227  SQUASH_OK :
229 }
Performing the requested operation from the current state is not supported.
Definition: status.h:48
void(* SquashDestroyNotify)(void *data)
Callback to be invoked when information data is no longer needed.
Definition: object.h:43
Operation partially completed.
Definition: status.h:38
A decompression stream.
Definition: stream.h:40
size_t squash_codec_get_uncompressed_size(SquashCodec *codec, size_t compressed_length, const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)])
Get the uncompressed size of the compressed buffer.
Definition: codec.c:383
Insufficient space in buffer.
Definition: status.h:46
size_t squash_codec_get_max_compressed_size(SquashCodec *codec, size_t uncompressed_length)
Get the maximum buffer size necessary to store compressed data.
Definition: codec.c:433
SquashStatus squash_codec_compress_with_options(SquashCodec *codec, size_t *compressed_length, uint8_t compressed[SQUASH_ARRAY_PARAM(*compressed_length)], size_t uncompressed_length, const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)], SquashOptions *options)
Compress a buffer with an existing SquashOptions.
Definition: codec.c:545
SquashStatus squash_codec_decompress_with_options(SquashCodec *codec, size_t *decompressed_length, uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)], size_t compressed_length, const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)], SquashOptions *options)
Decompress a buffer with an existing SquashOptions.
Definition: codec.c:698
void squash_stream_init(void *stream, SquashCodec *codec, SquashStreamType stream_type, SquashOptions *options, SquashDestroyNotify destroy_notify)
Initialize a stream.
Definition: stream.c:277
SquashStatus
Status codes.
Definition: status.h:36
A compression stream.
Definition: stream.h:39
SquashStatus squash_error(SquashStatus status)
Emit an error.
Definition: status.c:173
void squash_stream_destroy(void *stream)
Destroy a stream.
Definition: stream.c:342
Operation completed successfully.
Definition: status.h:37
SquashStreamType
Stream type.
Definition: stream.h:38