/** * @brief Decompress a buffer with an existing @ref SquashOptions * * @param codec The codec to use * @param[out] decompressed Location to store the decompressed data * @param[in,out] decompressed_length Location storing the size of the * @a decompressed buffer on input, replaced with the actual size of * the decompressed data * @param compressed The compressed data * @param compressed_length Length of the compressed data (in bytes) * @param options Compression options * @return A status code */ 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) { SquashCodecImpl* impl = NULL; assert (codec != NULL); impl = squash_codec_get_impl (codec); if (impl == NULL) return squash_error (SQUASH_UNABLE_TO_LOAD); if (decompressed == compressed) return squash_error (SQUASH_INVALID_BUFFER); if (impl->decompress_buffer != NULL) { SquashStatus res; res = impl->decompress_buffer (codec, decompressed_length, decompressed, compressed_length, compressed, squash_object_ref (options)); squash_object_unref (options); return res; } else { SquashStatus status; SquashStream* stream; stream = squash_codec_create_stream_with_options (codec, SQUASH_STREAM_DECOMPRESS, options); stream->next_in = compressed; stream->avail_in = compressed_length; stream->next_out = decompressed; stream->avail_out = *decompressed_length; do { status = squash_stream_process (stream); } while (status == SQUASH_PROCESSING); if (status == SQUASH_END_OF_STREAM) { status = SQUASH_OK; *decompressed_length = stream->total_out; } else if (status == SQUASH_OK) { do { status = squash_stream_finish (stream); } while (status == SQUASH_PROCESSING); if (status == SQUASH_OK) { *decompressed_length = stream->total_out; } } assert (stream->stream_type == SQUASH_STREAM_DECOMPRESS); squash_object_unref (stream); return status; } }
//---------- void Stream::read(const void * dataRaw, size_t size) { auto squashDirection = this->direction == Direction::Compress ? SQUASH_STREAM_COMPRESS : SQUASH_STREAM_DECOMPRESS; if (!this->squashStream) { if (!this->codec.isValid()) { OFXSQUASH_ERROR << "Cannot read stream. Codec is not initialized"; return; } this->squashStream = squash_stream_new(this->codec.getSquashCodec(), squashDirection, nullptr); if (!squashStream) { OFXSQUASH_ERROR << "Failed to initialize stream for codec [" << codec.getName() << "]"; } } const uint8_t* data = (const uint8_t*) dataRaw; int64_t incomingBytesRemaining = size; SquashStatus status; this->squashStream->next_in = data; this->squashStream->avail_in = size; do { this->squashStream->next_out = this->buffer.data(); this->squashStream->avail_out = this->buffer.size(); status = squash_stream_process(this->squashStream); if (status != SQUASH_OK && status != SQUASH_PROCESSING) { OFXSQUASH_ERROR << "Processing stream failed: " << squash_status_to_string(status); return; } const size_t outputSize = this->buffer.size() - this->squashStream->avail_out; if (outputSize > 0) { if (this->writeFunction) { WriteFunctionArguments args{ this->buffer.data(), outputSize, false }; this->writeFunction(args); } else { OFXSQUASH_WARNING << "Cannot write stream output. No WriteFunction has been set"; } } } while (status == SQUASH_PROCESSING); }
static SquashStatus squash_file_write_internal (SquashFile* file, size_t uncompressed_size, const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_size)], SquashOperation operation) { SquashStatus res; assert (file != NULL); if (SQUASH_UNLIKELY(file->last_status < 0)) return file->last_status; if (file->stream == NULL) { file->stream = squash_codec_create_stream_with_options (file->codec, SQUASH_STREAM_COMPRESS, file->options); if (SQUASH_UNLIKELY(file->stream == NULL)) { res = squash_error (SQUASH_FAILED); goto cleanup; } } assert (file->stream->next_in == NULL); assert (file->stream->avail_in == 0); assert (file->stream->next_out == NULL); assert (file->stream->avail_out == 0); file->stream->next_in = uncompressed; file->stream->avail_in = uncompressed_size; file->stream->next_in = uncompressed; file->stream->avail_in = uncompressed_size; do { file->stream->next_out = file->buf; file->stream->avail_out = SQUASH_FILE_BUF_SIZE; switch (operation) { case SQUASH_OPERATION_PROCESS: res = squash_stream_process (file->stream); break; case SQUASH_OPERATION_FLUSH: res = squash_stream_flush (file->stream); break; case SQUASH_OPERATION_FINISH: res = squash_stream_finish (file->stream); break; case SQUASH_OPERATION_TERMINATE: squash_assert_unreachable (); break; } if (res > 0 && file->stream->avail_out != SQUASH_FILE_BUF_SIZE) { size_t bytes_written = SQUASH_FWRITE_UNLOCKED(file->buf, 1, SQUASH_FILE_BUF_SIZE - file->stream->avail_out, file->fp); if (bytes_written != SQUASH_FILE_BUF_SIZE - file->stream->avail_out) { res = SQUASH_IO; goto cleanup; } } } while (res == SQUASH_PROCESSING); cleanup: if (file->stream != NULL) { file->stream->next_in = NULL; file->stream->avail_in = 0; file->stream->next_out = NULL; file->stream->avail_out = 0; } return file->last_status = res; }
/** * @brief Read from a compressed file * * This function is the same as @ref squash_file_read, except it will * not acquire a lock on the @ref SquashFile instance. It should be * used only when there is no possibility of other threads accessing * the file, or if you have already acquired the lock with @ref * squash_file_lock. * * @param file the file to read from * @param decompressed_size number of bytes to attempt to write to @a decompressed * @param decompressed buffer to write the decompressed data to * @return the result of the operation * @retval SQUASH_OK successfully read some data * @retval SQUASH_END_OF_STREAM the end of the file was reached */ SquashStatus squash_file_read_unlocked (SquashFile* file, size_t* decompressed_size, uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_size)]) { assert (file != NULL); assert (decompressed_size != NULL); assert (decompressed != NULL); if (SQUASH_UNLIKELY(file->last_status < 0)) return file->last_status; if (file->stream == NULL) { file->stream = squash_codec_create_stream_with_options (file->codec, SQUASH_STREAM_DECOMPRESS, file->options); if (SQUASH_UNLIKELY(file->stream == NULL)) { return file->last_status = squash_error (SQUASH_FAILED); } } SquashStream* stream = file->stream; assert (stream->next_out == NULL); assert (stream->avail_out == 0); if (stream->state == SQUASH_STREAM_STATE_FINISHED) { *decompressed_size = 0; return SQUASH_END_OF_STREAM; } file->stream->next_out = decompressed; file->stream->avail_out = *decompressed_size; while (stream->avail_out != 0) { if (file->last_status < 0) { break; } if (stream->state == SQUASH_STREAM_STATE_FINISHED) break; if (file->last_status == SQUASH_PROCESSING) { if (stream->state < SQUASH_STREAM_STATE_FINISHING) { file->last_status = squash_stream_process (stream); } else { file->last_status = squash_stream_finish (stream); } continue; } assert (file->last_status == SQUASH_OK); #if defined(SQUASH_MMAP_IO) if (file->map.data != MAP_FAILED) squash_mapped_file_destroy (&(file->map), true); if (squash_mapped_file_init_full(&(file->map), file->fp, SQUASH_FILE_BUF_SIZE, true, false)) { stream->next_in = file->map.data; stream->avail_in = file->map.size; } else #endif { stream->next_in = file->buf; stream->avail_in = SQUASH_FREAD_UNLOCKED(file->buf, 1, SQUASH_FILE_BUF_SIZE, file->fp); } if (stream->avail_in == 0) { if (feof (file->fp)) { file->last_status = squash_stream_finish (stream); } else { file->last_status = squash_error (SQUASH_IO); break; } } else { file->last_status = squash_stream_process (stream); } } *decompressed_size = (stream->next_out - decompressed); stream->next_out = 0; stream->avail_out = 0; return file->last_status; }
/** * @brief Compress a buffer with an existing @ref SquashOptions * * @param codec The codec to use * @param[out] compressed Location to store the compressed data * @param[in,out] compressed_length Location storing the size of the * @a compressed buffer on input, replaced with the actual size of * the compressed data * @param uncompressed The uncompressed data * @param uncompressed_length Length of the uncompressed data (in bytes) * @param options Compression options * @return A status code */ 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) { SquashCodecImpl* impl = NULL; assert (codec != NULL); assert (compressed != NULL); assert (uncompressed != NULL); impl = squash_codec_get_impl (codec); if (impl == NULL) return squash_error (SQUASH_UNABLE_TO_LOAD); if (compressed == uncompressed) return squash_error (SQUASH_INVALID_BUFFER); if (impl->compress_buffer || impl->compress_buffer_unsafe) { size_t max_compressed_length = squash_codec_get_max_compressed_size (codec, uncompressed_length); if (*compressed_length >= max_compressed_length) { if (impl->compress_buffer_unsafe != NULL) { return impl->compress_buffer_unsafe (codec, compressed_length, compressed, uncompressed_length, uncompressed, options); } else { return impl->compress_buffer (codec, compressed_length, compressed, uncompressed_length, uncompressed, options); } } else if (impl->compress_buffer != NULL) { return impl->compress_buffer (codec, compressed_length, compressed, uncompressed_length, uncompressed, options); } else { SquashStatus status; uint8_t* tmp_buf = malloc (max_compressed_length); if (tmp_buf == NULL) return squash_error (SQUASH_MEMORY); status = impl->compress_buffer_unsafe (codec, &max_compressed_length, tmp_buf, uncompressed_length, uncompressed, options); if (status == SQUASH_OK) { if (*compressed_length < max_compressed_length) { *compressed_length = max_compressed_length; free (tmp_buf); return squash_error (SQUASH_BUFFER_FULL); } else { *compressed_length = max_compressed_length; memcpy (compressed, tmp_buf, max_compressed_length); free (tmp_buf); return SQUASH_OK; } } else { free (tmp_buf); return status; } } } else { SquashStatus status; SquashStream* stream; stream = squash_codec_create_stream_with_options (codec, SQUASH_STREAM_COMPRESS, options); if (stream == NULL) return squash_error (SQUASH_FAILED); stream->next_in = uncompressed; stream->avail_in = uncompressed_length; stream->next_out = compressed; stream->avail_out = *compressed_length; do { status = squash_stream_process (stream); } while (status == SQUASH_PROCESSING); if (status != SQUASH_OK) { squash_object_unref (stream); return status; } do { status = squash_stream_finish (stream); } while (status == SQUASH_PROCESSING); if (status != SQUASH_OK) { squash_object_unref (stream); return status; } *compressed_length = stream->total_out; squash_object_unref (stream); return status; } }