27 #define _FILE_OFFSET_BITS 64
28 #define _POSIX_C_SOURCE 200112L
32 #ifndef SQUASH_FILE_BUF_SIZE
33 # define SQUASH_FILE_BUF_SIZE ((size_t) (1024 * 1024))
36 #include <sys/types.h>
47 typedef struct SquashMappedFile_s {
56 static const SquashMappedFile squash_mapped_file_empty = { MAP_FAILED, 0 };
64 SquashOptions* options;
65 uint8_t buf[SQUASH_FILE_BUF_SIZE];
66 #if defined(SQUASH_MMAP_IO)
72 __attribute__ ((__const__))
75 squash_npot (
size_t v) {
82 #if SIZE_MAX > UINT32_MAX
90 squash_mapped_file_init_full (SquashMappedFile* mapped, FILE* fp,
size_t length,
bool length_is_suggestion,
bool writable) {
91 assert (mapped != NULL);
94 if (mapped->data != MAP_FAILED)
95 munmap (mapped->data - mapped->window_offset, mapped->length + mapped->window_offset);
104 ires = fstat (fd, &fp_stat);
105 if (ires == -1 || !S_ISREG(fp_stat.st_mode) || (!writable && fp_stat.st_size == 0))
108 off_t offset = ftello (fp);
113 ires = ftruncate (fd, offset + (off_t) length);
117 const size_t remaining = fp_stat.st_size - (size_t) offset;
119 if (length == 0 || (length > remaining && length_is_suggestion)) {
121 }
else if (length > remaining) {
128 mapped->length = length;
130 const size_t page_size = (size_t) sysconf (_SC_PAGE_SIZE);
131 mapped->window_offset = (size_t) offset % page_size;
132 mapped->map_length = length + mapped->window_offset;
134 mapped->data = mmap (NULL, mapped->map_length, writable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, offset - mapped->window_offset);
135 if (mapped->data == MAP_FAILED)
138 mapped->data += mapped->window_offset;
140 mapped->writable = writable;
146 squash_mapped_file_init (SquashMappedFile* mapped, FILE* fp,
size_t length,
bool writable) {
147 return squash_mapped_file_init_full (mapped, fp, length,
false, writable);
151 squash_mapped_file_destroy (SquashMappedFile* mapped,
bool success) {
152 if (mapped->data != MAP_FAILED) {
153 munmap (mapped->data - mapped->window_offset, mapped->length + mapped->window_offset);
154 mapped->data = MAP_FAILED;
157 fseeko (mapped->fp, mapped->length, SEEK_CUR);
158 if (mapped->writable) {
159 ftruncate (fileno (mapped->fp), ftello (mapped->fp));
215 SquashOptions* options;
216 SquashCodec* codec_i;
218 assert (filename != NULL);
219 assert (mode != NULL);
220 assert (codec != NULL);
246 SquashOptions* options;
248 assert (filename != NULL);
249 assert (mode != NULL);
250 assert (codec != NULL);
271 assert (filename != NULL);
272 assert (mode != NULL);
273 assert (codec != NULL);
294 assert (filename != NULL);
295 assert (mode != NULL);
296 assert (codec != NULL);
298 FILE* fp = fopen (filename, mode);
320 SquashOptions* options;
323 assert (codec != NULL);
349 SquashOptions* options;
352 assert (codec != NULL);
375 assert (codec != NULL);
398 assert (codec != NULL);
400 SquashFile* file = malloc (
sizeof (SquashFile));
410 #if defined(SQUASH_MMAP_IO)
411 file->map = squash_mapped_file_empty;
440 size_t* decompressed_length,
441 uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)]) {
444 assert (file != NULL);
445 assert (decompressed_length != NULL);
446 assert (decompressed != NULL);
448 if (file->stream == NULL) {
450 if (file->stream == NULL)
453 SquashStream* stream = file->stream;
455 assert (stream->next_out == NULL);
456 assert (stream->avail_out == 0);
458 if (stream->state == SQUASH_STREAM_STATE_FINISHED) {
459 *decompressed_length = 0;
463 file->stream->next_out = decompressed;
464 file->stream->avail_out = *decompressed_length;
466 while (stream->avail_out != 0) {
467 if (file->last_status < 0) {
468 res = file->last_status;
472 if (stream->state == SQUASH_STREAM_STATE_FINISHED)
476 if (stream->state < SQUASH_STREAM_STATE_FINISHING) {
487 #if defined(SQUASH_MMAP_IO)
488 if (file->map.data != MAP_FAILED)
489 squash_mapped_file_destroy (&(file->map),
true);
491 if (squash_mapped_file_init_full(&(file->map), file->fp, SQUASH_FILE_BUF_SIZE,
true,
false)) {
492 stream->next_in = file->map.data;
493 stream->avail_in = file->map.length;
497 stream->next_in = file->buf;
498 stream->avail_in = fread (file->buf, 1, SQUASH_FILE_BUF_SIZE, file->fp);
501 if (stream->avail_in == 0) {
502 if (feof (file->fp)) {
513 *decompressed_length = (stream->next_out - decompressed);
514 stream->next_out = 0;
515 stream->avail_out = 0;
521 squash_file_write_internal (SquashFile* file,
522 size_t uncompressed_length,
523 const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)],
527 assert (file != NULL);
529 if (file->stream == NULL) {
531 if (file->stream == NULL)
535 assert (file->stream->next_in == NULL);
536 assert (file->stream->avail_in == 0);
537 assert (file->stream->next_out == NULL);
538 assert (file->stream->avail_out == 0);
540 file->stream->next_in = uncompressed;
541 file->stream->avail_in = uncompressed_length;
543 file->stream->next_in = uncompressed;
544 file->stream->avail_in = uncompressed_length;
547 file->stream->next_out = file->buf;
548 file->stream->avail_out = SQUASH_FILE_BUF_SIZE;
561 squash_assert_unreachable ();
565 if (res > 0 && file->stream->avail_out != SQUASH_FILE_BUF_SIZE) {
566 size_t bytes_written = fwrite (file->buf, 1, SQUASH_FILE_BUF_SIZE - file->stream->avail_out, file->fp);
567 if (bytes_written != SQUASH_FILE_BUF_SIZE - file->stream->avail_out)
572 file->stream->next_in = NULL;
573 file->stream->avail_in = 0;
574 file->stream->next_out = NULL;
575 file->stream->avail_out = 0;
602 size_t uncompressed_length,
603 const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)]) {
633 return (file->stream->state == SQUASH_STREAM_STATE_FINISHED) && (feof (file->fp));
656 assert (fp_in != NULL);
657 assert (fp_out != NULL);
664 SquashOptions* options = NULL;
666 va_start (ap, length);
693 assert (fp_in != NULL);
694 assert (fp_out != NULL);
696 assert (codec != NULL);
698 SquashOptions* options = NULL;
700 va_start (ap, length);
727 assert (fp_in != NULL);
728 assert (fp_out != NULL);
739 squash_splice_map (FILE* fp_in, FILE* fp_out,
size_t length,
SquashStreamType stream_type, SquashCodec* codec, SquashOptions* options) {
741 SquashMappedFile mapped_in = squash_mapped_file_empty;
742 SquashMappedFile mapped_out = squash_mapped_file_empty;
745 if (!squash_mapped_file_init (&mapped_in, fp_in, length,
false))
749 if (!squash_mapped_file_init (&mapped_out, fp_out, max_output_length,
true))
756 squash_mapped_file_destroy (&mapped_in,
true);
757 squash_mapped_file_destroy (&mapped_out,
true);
759 if (!squash_mapped_file_init (&mapped_in, fp_in, 0,
false))
765 size_t max_output_length = knows_uncompressed ?
767 squash_npot (mapped_in.length) << 3;
770 if (!squash_mapped_file_init (&mapped_out, fp_out, max_output_length,
true))
775 squash_mapped_file_destroy (&mapped_in,
true);
776 squash_mapped_file_destroy (&mapped_out,
true);
778 max_output_length <<= 1;
785 squash_mapped_file_destroy (&mapped_in,
false);
786 squash_mapped_file_destroy (&mapped_out,
false);
792 squash_splice_stream (FILE* fp_in,
797 SquashOptions* options) {
799 SquashFile* file = NULL;
800 size_t remaining = length;
801 uint8_t* data = NULL;
802 size_t data_length = 0;
803 #if defined(SQUASH_MMAP_IO)
804 bool first_block =
true;
805 SquashMappedFile map = squash_mapped_file_empty;
809 assert (file != NULL);
811 while (length == 0 || remaining != 0) {
812 const size_t req_size = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
813 if (!squash_mapped_file_init_full(&map, fp_in, req_size,
true,
false)) {
828 remaining -= map.length;
829 squash_mapped_file_destroy (&map,
true);
833 assert (file != NULL);
835 while (length == 0 || remaining > 0) {
836 const size_t req_size = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
837 if (!squash_mapped_file_init_full(&map, fp_out, req_size,
true,
true)) {
852 assert (map.length == 0);
854 squash_mapped_file_destroy (&map,
true);
858 squash_mapped_file_destroy (&map,
true);
872 data = malloc (SQUASH_FILE_BUF_SIZE);
879 while (length == 0 || remaining != 0) {
880 const size_t req_size = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
882 data_length = fread (data, 1, req_size, fp_in);
883 if (data_length == 0) {
892 if (remaining != 0) {
893 assert (data_length <= remaining);
894 remaining -= data_length;
898 while (length == 0 || remaining != 0) {
899 data_length = (length == 0 || remaining > SQUASH_FILE_BUF_SIZE) ? SQUASH_FILE_BUF_SIZE : remaining;
907 if (data_length > 0) {
908 size_t bytes_written = fwrite (data, 1, data_length, fp_out);
910 assert (bytes_written == data_length);
911 if (bytes_written == 0) {
916 if (remaining != 0) {
917 assert (data_length <= remaining);
918 remaining -= data_length;
933 #if defined(SQUASH_MMAP_IO)
934 squash_mapped_file_destroy (&map,
false);
965 SquashOptions* options) {
968 assert (fp_in != NULL);
969 assert (fp_out != NULL);
971 assert (codec != NULL);
973 if (codec->impl.create_stream == NULL)
974 res = squash_splice_map (fp_in, fp_out, length, stream_type, codec, options);
977 return squash_splice_stream (fp_in, fp_out, length, stream_type, codec, options);
1037 #if defined(SQUASH_MMAP_IO)
1038 squash_mapped_file_destroy (&(file->map),
false);
SquashStream * squash_codec_create_stream_with_options(SquashCodec *codec, SquashStreamType stream_type, SquashOptions *options)
Create a new stream with existing SquashOptions.
SquashStatus squash_stream_finish(SquashStream *stream)
Finish writing to a stream.
SquashCodec * squash_get_codec(const char *codec)
Retrieve a SquashCodec.
SquashCodecInfo
Information about the codec.
Reached the end of the stream while decoding.
Operation partially completed.
SquashOptions * squash_options_newv(SquashCodec *codec, va_list options)
Create a new group of options from a variadic list.
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.
SquashStatus squash_splice_codec_with_options(SquashCodec *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length, SquashOptions *options)
compress or decompress the contents of one file to another
bool squash_file_eof(SquashFile *file)
Determine whether the file has reached the end of file.
Insufficient space in buffer.
SquashFile * squash_file_open(const char *codec, const char *filename, const char *mode,...)
Open a file.
Continue processing the stream normally.
void * squash_object_unref(void *obj)
Decrement the reference count on an object.
size_t squash_codec_get_max_compressed_size(SquashCodec *codec, size_t uncompressed_length)
Get the maximum buffer size necessary to store compressed data.
SquashStatus squash_file_read(SquashFile *file, size_t *decompressed_length, uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)])
Read from a compressed file.
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.
Not enough memory is available.
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.
SquashStatus squash_stream_flush(SquashStream *stream)
Flush a stream.
SquashStatus squash_file_flush(SquashFile *file)
immediately write any buffered data to a file
SquashFile * squash_file_open_codec_with_options(SquashCodec *codec, const char *filename, const char *mode, SquashOptions *options)
Open a file using a codec instance with the specified options.
SquashFile * squash_file_open_with_options(const char *codec, const char *filename, const char *mode, SquashOptions *options)
Open a file with the specified options.
SquashStatus
Status codes.
SquashFile * squash_file_steal_codec(SquashCodec *codec, FILE *fp,...)
Open an existing stdio file using a codec instance.
SquashOperation
Operations to perform on a stream.
SquashStatus squash_file_close(SquashFile *file)
Close a file.
SquashStatus squash_error(SquashStatus status)
Emit an error.
SquashStatus squash_splice_with_options(const char *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length, SquashOptions *options)
compress or decompress the contents of one file to another
SquashFile * squash_file_steal_codec_with_options(SquashCodec *codec, FILE *fp, SquashOptions *options)
Open an existing stdio file using a codec instance with the specified options.
void * squash_object_ref(void *obj)
Increment the reference count on an object.
SquashStatus squash_splice(const char *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length,...)
compress or decompress the contents of one file to another
SquashStatus squash_stream_process(SquashStream *stream)
Process a stream.
SquashStatus squash_file_free(SquashFile *file, FILE **fp)
Free a file.
Finish processing the stream.
SquashFile * squash_file_steal_with_options(const char *codec, FILE *fp, SquashOptions *options)
Open an existing stdio file with the specified options.
SquashFile * squash_file_open_codec(SquashCodec *codec, const char *filename, const char *mode,...)
Open a file using a codec instance.
SquashCodecInfo squash_codec_get_info(SquashCodec *codec)
Get a bitmask of information about the codec.
Operation completed successfully.
The compressed data encodes the length of the uncompressed data without having to decompress it...
SquashStatus squash_splice_codec(SquashCodec *codec, SquashStreamType stream_type, FILE *fp_out, FILE *fp_in, size_t length,...)
compress or decompress the contents of one file to another
SquashStreamType
Stream type.
One or more of the parameters were not valid.
SquashFile * squash_file_steal(const char *codec, FILE *fp,...)
Open an existing stdio file.
SquashStatus squash_file_write(SquashFile *file, size_t uncompressed_length, const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)])
Write data to a compressed file.