static SquashBZ2Stream* squash_bz2_stream_new (SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options) { int bz2_e = 0; SquashBZ2Stream* stream; assert (codec != NULL); assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS); stream = squash_malloc (sizeof (SquashBZ2Stream)); squash_bz2_stream_init (stream, codec, stream_type, options, squash_bz2_stream_destroy); if (stream_type == SQUASH_STREAM_COMPRESS) { bz2_e = BZ2_bzCompressInit (&(stream->stream), squash_codec_get_option_int_index (codec, options, SQUASH_BZ2_OPT_LEVEL), 0, squash_codec_get_option_int_index (codec, options, SQUASH_BZ2_OPT_WORK_FACTOR)); } else if (stream_type == SQUASH_STREAM_DECOMPRESS) { bz2_e = BZ2_bzDecompressInit (&(stream->stream), 0, squash_codec_get_option_int_index (codec, options, SQUASH_BZ2_OPT_SMALL)); } else { squash_assert_unreachable(); } if (bz2_e != BZ_OK) { /* We validate the params so OOM is really the only time this should happen, and that really shouldn't be happening here. */ stream = squash_object_unref (stream); } return stream; }
static SquashStatus squash_brotli_compress_stream (SquashStream* stream, SquashOperation operation) { SquashStatus res = SQUASH_FAILED; SquashBrotliStream* s = (SquashBrotliStream*) stream; brotli::BrotliParams params; params.mode = (brotli::BrotliParams::Mode) squash_codec_get_option_int_index (stream->codec, stream->options, SQUASH_BROTLI_OPT_MODE); params.enable_transforms = squash_codec_get_option_int_index (stream->codec, stream->options, SQUASH_BROTLI_OPT_ENABLE_TRANSFORMS); s->ctx.comp = new brotli::BrotliCompressor (params); s->ctx.comp->WriteStreamHeader (); int block_size; uint8_t* input_buffer = (uint8_t*) malloc (SQUASH_BROTLI_MAX_BLOCK_SIZE); uint8_t* output_buffer = (uint8_t*) malloc (SQUASH_BROTLI_MAX_OUT_SIZE); size_t output_size; if (input_buffer == NULL || output_buffer == NULL) { res = squash_error (SQUASH_MEMORY); goto cleanup; } while ((block_size = BrotliRead (s->in, input_buffer, SQUASH_BROTLI_MAX_BLOCK_SIZE)) != 0) { output_size = SQUASH_BROTLI_MAX_OUT_SIZE; s->ctx.comp->WriteMetaBlock (block_size, input_buffer, false, &output_size, output_buffer); if (BrotliWrite (s->out, output_buffer, output_size) != output_size) { res = SQUASH_FAILED; goto cleanup; } } output_size = SQUASH_BROTLI_MAX_OUT_SIZE; s->ctx.comp->WriteMetaBlock (block_size, input_buffer, true, &output_size, output_buffer); if (BrotliWrite (s->out, output_buffer, output_size) != output_size) { res = squash_error (SQUASH_FAILED); goto cleanup; } res = SQUASH_OK; cleanup: free (input_buffer); free (output_buffer); delete s->ctx.comp; s->ctx.comp = NULL; return res; }
static SquashLZ4FStream* squash_lz4f_stream_new (SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options) { SquashLZ4FStream* stream; LZ4F_errorCode_t ec; assert (codec != NULL); stream = (SquashLZ4FStream*) squash_malloc (sizeof (SquashLZ4FStream)); if (SQUASH_UNLIKELY(stream == NULL)) return (squash_error (SQUASH_MEMORY), NULL); squash_lz4f_stream_init (stream, codec, stream_type, options, squash_lz4f_stream_destroy); if (stream_type == SQUASH_STREAM_COMPRESS) { ec = LZ4F_createCompressionContext(&(stream->data.comp.ctx), LZ4F_VERSION); stream->data.comp.state = SQUASH_LZ4F_STATE_INIT; stream->data.comp.output_buffer = NULL; stream->data.comp.output_buffer_pos = 0; stream->data.comp.output_buffer_size = 0; stream->data.comp.input_buffer_size = 0; stream->data.comp.prefs = (LZ4F_preferences_t) { { (LZ4F_blockSizeID_t) squash_codec_get_option_int_index (codec, options, SQUASH_LZ4F_OPT_BLOCK_SIZE), blockLinked, squash_codec_get_option_bool_index (codec, options, SQUASH_LZ4F_OPT_CHECKSUM) ? contentChecksumEnabled : noContentChecksum, }, squash_codec_get_option_int_index (codec, options, SQUASH_LZ4F_OPT_LEVEL) }; } else { ec = LZ4F_createDecompressionContext(&(stream->data.decomp.ctx), LZ4F_VERSION); } if (SQUASH_UNLIKELY(LZ4F_isError (ec))) { squash_object_unref (stream); return (squash_error (SQUASH_FAILED), NULL); } return stream; }
static SquashStatus squash_pithy_compress_buffer (SquashCodec* codec, size_t* compressed_size, uint8_t compressed[SQUASH_ARRAY_PARAM(*compressed_size)], size_t uncompressed_size, const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_size)], SquashOptions* options) { const int level = squash_codec_get_option_int_index (codec, options, SQUASH_PITHY_OPT_LEVEL); *compressed_size = pithy_Compress ((const char*) uncompressed, uncompressed_size, (char*) compressed, *compressed_size, level); return (*compressed_size != 0) ? SQUASH_OK : SQUASH_FAILED; }
static SquashStatus squash_bz2_compress_buffer (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) { int bz2_res; unsigned int compressed_length_ui = (unsigned int) *compressed_length; bz2_res = BZ2_bzBuffToBuffCompress ((char*) compressed, &compressed_length_ui, (char*) uncompressed, (unsigned int) uncompressed_length, squash_codec_get_option_int_index (codec, options, SQUASH_BZ2_OPT_LEVEL), 0, squash_codec_get_option_int_index (codec, options, SQUASH_BZ2_OPT_WORK_FACTOR)); if (bz2_res == BZ_OK) { *compressed_length = compressed_length_ui; } return squash_bz2_status_to_squash_status (bz2_res); }
static SquashStatus squash_brotli_compress_buffer (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) { brotli::BrotliParams params; params.mode = (brotli::BrotliParams::Mode) squash_codec_get_option_int_index (codec, options, SQUASH_BROTLI_OPT_MODE); params.enable_transforms = squash_codec_get_option_int_index (codec, options, SQUASH_BROTLI_OPT_ENABLE_TRANSFORMS); try { int res = brotli::BrotliCompressBuffer (params, uncompressed_length, uncompressed, compressed_length, compressed); return (res == 1) ? SQUASH_OK : SQUASH_FAILED; } catch (const std::bad_alloc& e) { return SQUASH_MEMORY; } catch (...) { return SQUASH_FAILED; } }
static SquashStatus squash_fastlz_compress_buffer (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) { *compressed_length = fastlz_compress_level (squash_codec_get_option_int_index (codec, options, SQUASH_FASTLZ_OPT_LEVEL), (const void*) uncompressed, (int) uncompressed_length, (void*) compressed); return (*compressed_length == 0) ? squash_error (SQUASH_FAILED) : SQUASH_OK; }
static void squash_lz4f_stream_init (SquashLZ4FStream* stream, SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options, SquashDestroyNotify destroy_notify) { LZ4F_errorCode_t ec; squash_stream_init ((SquashStream*) stream, codec, stream_type, (SquashOptions*) options, destroy_notify); if (stream_type == SQUASH_STREAM_COMPRESS) { ec = LZ4F_createCompressionContext(&(stream->data.comp.ctx), LZ4F_VERSION); stream->data.comp.state = SQUASH_LZ4F_STATE_INIT; stream->data.comp.output_buffer = NULL; stream->data.comp.output_buffer_pos = 0; stream->data.comp.output_buffer_length = 0; stream->data.comp.input_buffer_length = 0; stream->data.comp.prefs = (LZ4F_preferences_t) { { squash_codec_get_option_int_index (codec, options, SQUASH_LZ4F_OPT_BLOCK_SIZE), blockLinked, squash_codec_get_option_bool_index (codec, options, SQUASH_LZ4F_OPT_CHECKSUM) ? contentChecksumEnabled : noContentChecksum, }, squash_codec_get_option_int_index (codec, options, SQUASH_LZ4F_OPT_LEVEL) }; } else { ec = LZ4F_createDecompressionContext(&(stream->data.decomp.ctx), LZ4F_VERSION); } assert (!LZ4F_isError (ec)); }
static SquashStatus squash_lz4_compress_buffer (SquashCodec* codec, size_t* compressed_size, uint8_t compressed[SQUASH_ARRAY_PARAM(*compressed_size)], size_t uncompressed_size, const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_size)], SquashOptions* options) { int level = squash_codec_get_option_int_index (codec, options, SQUASH_LZ4_OPT_LEVEL); #if INT_MAX < SIZE_MAX if (SQUASH_UNLIKELY(INT_MAX < uncompressed_size) || SQUASH_UNLIKELY(INT_MAX < *compressed_size)) return squash_error (SQUASH_RANGE); #endif int lz4_r; if (level == 7) { lz4_r = LZ4_compress_limitedOutput ((char*) uncompressed, (char*) compressed, (int) uncompressed_size, (int) *compressed_size); } else if (level < 7) { lz4_r = LZ4_compress_fast ((const char*) uncompressed, (char*) compressed, (int) uncompressed_size, (int) *compressed_size, squash_lz4_level_to_fast_mode (level)); } else if (level < 17) { lz4_r = LZ4_compressHC2_limitedOutput ((char*) uncompressed, (char*) compressed, (int) uncompressed_size, (int) *compressed_size, squash_lz4_level_to_hc_level (level)); } else { squash_assert_unreachable(); } #if SIZE_MAX < INT_MAX if (SQUASH_UNLIKELY(SIZE_MAX < lz4_r)) return squash_error (SQUASH_RANGE); #endif *compressed_size = lz4_r; return SQUASH_UNLIKELY(lz4_r == 0) ? squash_error (SQUASH_BUFFER_FULL) : SQUASH_OK; }
static size_t squash_lz4f_get_input_buffer_size (SquashStream* stream) { return squash_lz4f_block_size_id_to_size ((LZ4F_blockSizeID_t) squash_codec_get_option_int_index (stream->codec, stream->options, SQUASH_LZ4F_OPT_BLOCK_SIZE)); }
static SquashStatus squash_density_process_stream (SquashStream* stream, SquashOperation operation) { SquashDensityStream* s = (SquashDensityStream*) stream; if (s->buffer_size > 0) { squash_density_flush_internal_buffer (stream); return SQUASH_PROCESSING; } if (s->next == SQUASH_DENSITY_ACTION_INIT) { s->active_input_size = (stream->stream_type == SQUASH_STREAM_COMPRESS) ? ((stream->avail_in / SQUASH_DENSITY_INPUT_MULTIPLE) * SQUASH_DENSITY_INPUT_MULTIPLE) : stream->avail_in; if (stream->avail_out < DENSITY_MINIMUM_OUTPUT_BUFFER_SIZE) { s->buffer_active = true; s->state = density_stream_prepare (s->stream, (uint8_t*) stream->next_in, s->active_input_size, s->buffer, DENSITY_MINIMUM_OUTPUT_BUFFER_SIZE); } else { s->buffer_active = false; s->state = density_stream_prepare (s->stream, (uint8_t*) stream->next_in, s->active_input_size, stream->next_out, stream->avail_out); } if (SQUASH_UNLIKELY(s->state != DENSITY_STREAM_STATE_READY)) return squash_error (SQUASH_FAILED); } switch (s->state) { case DENSITY_STREAM_STATE_STALL_ON_INPUT: if (s->input_buffer_size != 0 || (stream->avail_in < SQUASH_DENSITY_INPUT_MULTIPLE && stream->stream_type == SQUASH_STREAM_COMPRESS && operation == SQUASH_OPERATION_PROCESS)) { const size_t remaining = SQUASH_DENSITY_INPUT_MULTIPLE - s->input_buffer_size; const size_t cp_size = remaining < stream->avail_in ? remaining : stream->avail_in; if (cp_size != 0) { memcpy (s->input_buffer + s->input_buffer_size, stream->next_in, cp_size); s->input_buffer_size += cp_size; stream->next_in += cp_size; stream->avail_in -= cp_size; assert (cp_size != 0); } } if (s->input_buffer_size != 0) { if (s->input_buffer_size == SQUASH_DENSITY_INPUT_MULTIPLE || operation != SQUASH_OPERATION_PROCESS) { s->active_input_size = s->input_buffer_size; s->input_buffer_active = true; density_stream_update_input (s->stream, s->input_buffer, s->input_buffer_size); s->state = DENSITY_STREAM_STATE_READY; } else { assert (stream->avail_in == 0); return SQUASH_OK; } } else { s->active_input_size = (stream->stream_type == SQUASH_STREAM_COMPRESS) ? ((stream->avail_in / SQUASH_DENSITY_INPUT_MULTIPLE) * SQUASH_DENSITY_INPUT_MULTIPLE) : stream->avail_in; density_stream_update_input (s->stream, stream->next_in, s->active_input_size); s->state = DENSITY_STREAM_STATE_READY; } break; case DENSITY_STREAM_STATE_STALL_ON_OUTPUT: { if (!s->output_invalid) { const size_t written = density_stream_output_available_for_use (s->stream); total_bytes_written += written; if (s->buffer_active) { s->buffer_size = written; s->buffer_pos = 0; const size_t cp_size = s->buffer_size < stream->avail_out ? s->buffer_size : stream->avail_out; memcpy (stream->next_out, s->buffer, cp_size); stream->next_out += cp_size; stream->avail_out -= cp_size; s->buffer_pos += cp_size; if (s->buffer_pos == s->buffer_size) { s->buffer_pos = 0; s->buffer_size = 0; } } else { assert (written <= stream->avail_out); stream->next_out += written; stream->avail_out -= written; } s->output_invalid = true; return SQUASH_PROCESSING; } else { if (stream->avail_out < DENSITY_MINIMUM_OUTPUT_BUFFER_SIZE) { s->buffer_active = true; density_stream_update_output (s->stream, s->buffer, DENSITY_MINIMUM_OUTPUT_BUFFER_SIZE); } else { s->buffer_active = false; density_stream_update_output (s->stream, stream->next_out, stream->avail_out); } s->output_invalid = false; s->state = DENSITY_STREAM_STATE_READY; } } break; case DENSITY_STREAM_STATE_READY: break; case DENSITY_STREAM_STATE_ERROR_OUTPUT_BUFFER_TOO_SMALL: case DENSITY_STREAM_STATE_ERROR_INVALID_INTERNAL_STATE: case DENSITY_STREAM_STATE_ERROR_INTEGRITY_CHECK_FAIL: return squash_error (SQUASH_FAILED); } assert (s->output_invalid == false); while (s->state == DENSITY_STREAM_STATE_READY && s->next != SQUASH_DENSITY_ACTION_FINISHED) { switch (s->next) { case SQUASH_DENSITY_ACTION_INIT: if (stream->stream_type == SQUASH_STREAM_COMPRESS) { DENSITY_COMPRESSION_MODE compression_mode = squash_density_level_to_mode (squash_codec_get_option_int_index (stream->codec, stream->options, SQUASH_DENSITY_OPT_LEVEL)); DENSITY_BLOCK_TYPE block_type = squash_codec_get_option_bool_index (stream->codec, stream->options, SQUASH_DENSITY_OPT_CHECKSUM) ? DENSITY_BLOCK_TYPE_WITH_HASHSUM_INTEGRITY_CHECK : DENSITY_BLOCK_TYPE_DEFAULT; s->state = density_stream_compress_init (s->stream, compression_mode, block_type); } else { s->state = density_stream_decompress_init (s->stream, NULL); } if (SQUASH_UNLIKELY(s->state != DENSITY_STREAM_STATE_READY)) return squash_error (SQUASH_FAILED); s->next = SQUASH_DENSITY_ACTION_CONTINUE; break; case SQUASH_DENSITY_ACTION_CONTINUE_OR_FINISH: s->next = (operation == SQUASH_OPERATION_PROCESS) ? SQUASH_DENSITY_ACTION_CONTINUE : SQUASH_DENSITY_ACTION_FINISH; break; case SQUASH_DENSITY_ACTION_CONTINUE: if (stream->stream_type == SQUASH_STREAM_COMPRESS) { s->state = density_stream_compress_continue (s->stream); } else { s->state = density_stream_decompress_continue (s->stream); } if (s->state == DENSITY_STREAM_STATE_STALL_ON_INPUT) s->next = SQUASH_DENSITY_ACTION_CONTINUE_OR_FINISH; break; case SQUASH_DENSITY_ACTION_FINISH: if (stream->stream_type == SQUASH_STREAM_COMPRESS) { s->state = density_stream_compress_finish (s->stream); } else { s->state = density_stream_decompress_finish (s->stream); } if (s->state == DENSITY_STREAM_STATE_READY) { s->state = DENSITY_STREAM_STATE_STALL_ON_OUTPUT; s->output_invalid = false; s->next = SQUASH_DENSITY_ACTION_FINISHED; } break; case SQUASH_DENSITY_ACTION_FINISHED: default: squash_assert_unreachable(); break; } } if (s->state == DENSITY_STREAM_STATE_STALL_ON_INPUT) { if (s->input_buffer_active) { assert (s->active_input_size == s->input_buffer_size); s->input_buffer_active = false; s->input_buffer_size = 0; } else { assert (s->active_input_size <= stream->avail_in); stream->next_in += s->active_input_size; stream->avail_in -= s->active_input_size; } s->active_input_size = 0; } else if (s->state == DENSITY_STREAM_STATE_STALL_ON_OUTPUT) { { if (!s->output_invalid) { const size_t written = density_stream_output_available_for_use (s->stream); total_bytes_written += written; if (s->buffer_active) { s->buffer_size = written; s->buffer_pos = 0; const size_t cp_size = s->buffer_size < stream->avail_out ? s->buffer_size : stream->avail_out; memcpy (stream->next_out, s->buffer, cp_size); stream->next_out += cp_size; stream->avail_out -= cp_size; s->buffer_pos += cp_size; if (s->buffer_pos == s->buffer_size) { s->buffer_pos = 0; s->buffer_size = 0; } } else { assert (written <= stream->avail_out); stream->next_out += written; stream->avail_out -= written; } s->output_invalid = true; return SQUASH_PROCESSING; } else { squash_assert_unreachable(); } } } if (operation == SQUASH_OPERATION_FINISH) total_bytes_written = 0; if (stream->avail_in == 0) { return SQUASH_OK; } else { return SQUASH_PROCESSING; } }