static MSCompFlush squash_ms_comp_flush_from_operation (SquashOperation operation) { switch (operation) { case SQUASH_OPERATION_PROCESS: return MSCOMP_NO_FLUSH; case SQUASH_OPERATION_FLUSH: return MSCOMP_FLUSH; case SQUASH_OPERATION_FINISH: return MSCOMP_FINISH; case SQUASH_OPERATION_TERMINATE: squash_assert_unreachable (); } squash_assert_unreachable(); }
static void squash_brotli_stream_init (SquashBrotliStream* s, SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options, SquashDestroyNotify destroy_notify) { SquashStream* stream = (SquashStream*) s; squash_stream_init (stream, codec, stream_type, (SquashOptions*) options, destroy_notify); s->finished = false; if (stream_type == SQUASH_STREAM_COMPRESS) { brotli::BrotliParams params; params.quality = squash_options_get_int_at (stream->options, stream->codec, SQUASH_BROTLI_OPT_LEVEL); params.mode = (brotli::BrotliParams::Mode) squash_options_get_int_at (stream->options, stream->codec, SQUASH_BROTLI_OPT_MODE); s->compressor = new brotli::BrotliCompressor (params); s->remaining_block_in = s->compressor->input_block_size(); s->remaining_out = 0; s->next_out = NULL; s->should_flush = false; s->should_seal = false; } else if (stream_type == SQUASH_STREAM_DECOMPRESS) { s->decompressor = BrotliCreateState(squash_brotli_malloc, squash_brotli_free, squash_codec_get_context (codec)); } else { squash_assert_unreachable(); } }
/** * Allocate an aligned buffer * * Memory allocated with this function is assumed not to support * reallocation. In reality, assuming nobody has installed thick * wrappers it should be possible to @ref squash_realloc the buffer, * but the result is not constrained to the alignment requirements * presented to the initial buffer. * * The value returned by this function must be freed with @ref * squash_aligned_free; While some implementations (such as C11's * aligned_alloc and the POSIX posix_memalign function) allow values * returned by @ref squash_aligned_alloc to be passed directly to * @ref squash_free, others (such as Windows' _aligned_malloc) do not. * Passing the result of this function to @ref squash_free is * considered undefined behavior. * * @note Values supported for the @a alignment parameter are * implementation defined, but a fair assumption is that they must be * a power of two and multiple of `sizeof(void*)`. * * @param ctx The context * @param alignment Alignment of the buffer * @param size Number of bytes to allocate */ void* squash_aligned_alloc (size_t alignment, size_t size) { if (squash_memfns.aligned_alloc != NULL) { return squash_memfns.aligned_alloc (alignment, size); } else { /* This code is only used when people provide custom memory * functions but don't bother providing aligned versions. AFAIK * all systems provide a way to get aligned memory, so this * shouldn't really be necessary unless you're writing your own * malloc implementations (e.g., to track memory usage), or using * something weird (PHP's emalloc, Python's PyMem_Malloc, etc.). * * Note that this function will call squash_malloc() with a much * larger buffer than is necessary. If you have a problem with * that then feel free to provide your own aligned_alloc * implementation. */ const size_t ms = size + alignment + sizeof(void*); const void* ptr = squash_memfns.malloc (ms); const uintptr_t addr = (uintptr_t) ptr; /* Figure out where to put the object. We want a pointer to the real allocation to immediately precede the object (so we can recover it later to pass to free()/ */ size_t padding = alignment - (addr % alignment); if (padding < sizeof(void*)) padding += alignment; assert ((padding + size) <= ms); memcpy ((void*) (addr + padding - sizeof(void*)), &ptr, sizeof(void*)); return (void*) (addr + padding); } squash_assert_unreachable (); }
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_bz2_process_stream (SquashStream* stream, SquashOperation operation) { switch (operation) { case SQUASH_OPERATION_PROCESS: return squash_bz2_process_stream_ex (stream, BZ_RUN); case SQUASH_OPERATION_FLUSH: return squash_bz2_process_stream_ex (stream, BZ_FLUSH); case SQUASH_OPERATION_FINISH: return squash_bz2_finish_stream (stream); case SQUASH_OPERATION_TERMINATE: squash_assert_unreachable (); break; } squash_assert_unreachable(); }
static SquashStatus squash_brotli_process_stream (SquashStream* stream, SquashOperation operation) { SquashBrotliStream* s = (SquashBrotliStream*) stream; if (stream->stream_type == SQUASH_STREAM_COMPRESS) { const int be_ret = BrotliEncoderCompressStream(s->ctx.encoder, squash_brotli_encoder_operation_from_squash_operation(operation), &(stream->avail_in), &(stream->next_in), &(stream->avail_out), &(stream->next_out), NULL); if (SQUASH_UNLIKELY(be_ret != 1)) return squash_error (SQUASH_FAILED); else if (stream->avail_in != 0 || BrotliEncoderHasMoreOutput(s->ctx.encoder)) return SQUASH_PROCESSING; else return SQUASH_OK; } else if (stream->stream_type == SQUASH_STREAM_DECOMPRESS) { const BrotliResult bd_ret = BrotliDecompressStream(&(stream->avail_in), &(stream->next_in), &(stream->avail_out), &(stream->next_out), NULL, s->ctx.decoder); switch (bd_ret) { case BROTLI_RESULT_SUCCESS: return SQUASH_OK; case BROTLI_RESULT_NEEDS_MORE_INPUT: return SQUASH_OK; case BROTLI_RESULT_NEEDS_MORE_OUTPUT: return SQUASH_PROCESSING; case BROTLI_RESULT_ERROR: return SQUASH_FAILED; } if (SQUASH_UNLIKELY(bd_ret != BROTLI_RESULT_SUCCESS)) return squash_error (SQUASH_FAILED); } else { squash_assert_unreachable (); } squash_assert_unreachable (); }
static SquashStatus squash_lz4f_process_stream (SquashStream* stream, SquashOperation operation) { switch (stream->stream_type) { case SQUASH_STREAM_COMPRESS: return squash_lz4f_compress_stream (stream, operation); case SQUASH_STREAM_DECOMPRESS: return squash_lz4f_decompress_stream (stream, operation); default: squash_assert_unreachable(); } }
static DENSITY_COMPRESSION_MODE squash_density_level_to_mode (int level) { switch (level) { case 1: return DENSITY_COMPRESSION_MODE_CHAMELEON_ALGORITHM; case 7: return DENSITY_COMPRESSION_MODE_CHEETAH_ALGORITHM; case 9: return DENSITY_COMPRESSION_MODE_LION_ALGORITHM; default: squash_assert_unreachable (); } }
/** * @brief Get the boolean value for an option * * Note that this function will not perform a conversion—if you use it * to request the value of an option which is not a boolean the result * is undefined. * * @param codec the relevant codec * @param options the options instance to retrieve the value from * @param key the name of the option to retrieve the value of * @return the value of the option */ bool squash_codec_get_option_bool (SquashCodec* codec, SquashOptions* options, const char* key) { SquashOptionType type; const SquashOptionValue* value = squash_codec_get_option_value_by_name (codec, options, key, &type); switch ((int) type) { case SQUASH_OPTION_TYPE_BOOL: return value->bool_value; default: squash_assert_unreachable(); } }
static MSCompFormat squash_ms_format_from_codec (SquashCodec* codec) { const char* name = squash_codec_get_name (codec); if (name[5] == 0) return MSCOMP_LZNT1; else if (name[6] == 0) return MSCOMP_XPRESS; else if (name[14] == 0) return MSCOMP_XPRESS_HUFF; else squash_assert_unreachable(); }
static void squash_brotli_stream_destroy (void* stream) { SquashBrotliStream* s = (SquashBrotliStream*) stream; if (((SquashStream*) stream)->stream_type == SQUASH_STREAM_COMPRESS) { delete s->compressor; } else if (((SquashStream*) stream)->stream_type == SQUASH_STREAM_DECOMPRESS) { BrotliDestroyState(s->decompressor); } else { squash_assert_unreachable(); } squash_stream_destroy (stream); }
static void squash_brotli_stream_destroy (void* stream) { SquashBrotliStream* s = (SquashBrotliStream*) stream; if (((SquashStream*) stream)->stream_type == SQUASH_STREAM_COMPRESS) { BrotliEncoderDestroyInstance(s->ctx.encoder); } else if (((SquashStream*) stream)->stream_type == SQUASH_STREAM_DECOMPRESS) { BrotliDestroyState(s->ctx.decoder); } else { squash_assert_unreachable(); } squash_stream_destroy (stream); }
static BrotliEncoderOperation squash_brotli_encoder_operation_from_squash_operation (const SquashOperation operation) { switch (operation) { case SQUASH_OPERATION_PROCESS: return BROTLI_OPERATION_PROCESS; case SQUASH_OPERATION_FLUSH: return BROTLI_OPERATION_FLUSH; case SQUASH_OPERATION_FINISH: return BROTLI_OPERATION_FINISH; case SQUASH_OPERATION_TERMINATE: default: squash_assert_unreachable (); } }
/** * @brief Get the integer value for an option * * Note that this function will not perform a conversion—if you use it * to request the value of an option which is not an integer the * result is undefined. * * @param codec the relevant codec * @param options the options instance to retrieve the value from * @param key the name of the option to retrieve the value of * @return the value of the option */ int squash_codec_get_option_int (SquashCodec* codec, SquashOptions* options, const char* key) { SquashOptionType type; const SquashOptionValue* value = squash_codec_get_option_value_by_name (codec, options, key, &type); switch ((int) type) { case SQUASH_OPTION_TYPE_INT: case SQUASH_OPTION_TYPE_RANGE_INT: case SQUASH_OPTION_TYPE_ENUM_STRING: return value->int_value; default: squash_assert_unreachable(); } }
static size_t squash_lz4f_block_size_id_to_size (blockSizeID_t blkid) { switch (blkid) { case max64KB: return 64 * 1024; case max256KB: return 256 * 1024; case max1MB: return 1 * 1024 * 1024; case max4MB: return 4 * 1024 * 1024; default: squash_assert_unreachable(); break; } }
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 int squash_lz4_level_to_fast_mode (const int level) { switch (level) { case 1: return 32; case 2: return 24; case 3: return 17; case 4: return 8; case 5: return 4; case 6: return 2; default: squash_assert_unreachable(); } }
static int squash_lz4_level_to_hc_level (const int level) { switch (level) { case 8: return 2; case 9: return 4; case 10: return 6; case 11: return 9; case 12: return 12; case 13: return 14; case 14: return 16; default: squash_assert_unreachable(); } }
static void squash_brotli_stream_init (SquashBrotliStream* s, SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options, SquashDestroyNotify destroy_notify) { SquashStream* stream = (SquashStream*) s; squash_stream_init (stream, codec, stream_type, (SquashOptions*) options, destroy_notify); if (stream_type == SQUASH_STREAM_COMPRESS) { s->ctx.encoder = BrotliEncoderCreateInstance(squash_brotli_malloc, squash_brotli_free, NULL); BrotliEncoderSetParameter(s->ctx.encoder, BROTLI_PARAM_QUALITY, squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_LEVEL)); BrotliEncoderSetParameter(s->ctx.encoder, BROTLI_PARAM_LGWIN, squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_WINDOW_SIZE)); BrotliEncoderSetParameter(s->ctx.encoder, BROTLI_PARAM_LGBLOCK, squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_BLOCK_SIZE)); BrotliEncoderSetParameter(s->ctx.encoder, BROTLI_PARAM_MODE, squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_MODE)); } else if (stream_type == SQUASH_STREAM_DECOMPRESS) { s->ctx.decoder = BrotliCreateState(squash_brotli_malloc, squash_brotli_free, NULL); } else { squash_assert_unreachable(); } }
static bool squash_density_flush_internal_buffer (SquashStream* stream) { SquashDensityStream* s = (SquashDensityStream*) stream; const size_t buffer_remaining = s->buffer_size - s->buffer_pos; const size_t cp_size = (stream->avail_out < buffer_remaining) ? stream->avail_out : buffer_remaining; if (cp_size > 0) { memcpy (stream->next_out, s->buffer + s->buffer_pos, 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_size = 0; s->buffer_pos = 0; return true; } else { return false; } } squash_assert_unreachable(); }
static SquashStatus squash_lz4f_get_status (size_t res) { if (!LZ4F_isError (res)) return SQUASH_OK; switch ((LZ4F_errorCodes) (-(int)(res))) { case LZ4F_OK_NoError: return SQUASH_OK; case LZ4F_ERROR_GENERIC: return squash_error (SQUASH_FAILED); case LZ4F_ERROR_maxBlockSize_invalid: case LZ4F_ERROR_blockMode_invalid: case LZ4F_ERROR_contentChecksumFlag_invalid: case LZ4F_ERROR_headerVersion_wrong: case LZ4F_ERROR_blockChecksum_unsupported: case LZ4F_ERROR_reservedFlag_set: case LZ4F_ERROR_frameHeader_incomplete: case LZ4F_ERROR_frameType_unknown: case LZ4F_ERROR_frameSize_wrong: case LZ4F_ERROR_headerChecksum_invalid: case LZ4F_ERROR_contentChecksum_invalid: return squash_error (SQUASH_INVALID_BUFFER); case LZ4F_ERROR_compressionLevel_invalid: return squash_error (SQUASH_BAD_VALUE); case LZ4F_ERROR_allocation_failed: return squash_error (SQUASH_MEMORY); case LZ4F_ERROR_srcSize_tooLarge: case LZ4F_ERROR_dstMaxSize_tooSmall: return squash_error (SQUASH_BUFFER_FULL); case LZ4F_ERROR_decompressionFailed: case LZ4F_ERROR_srcPtr_wrong: case LZ4F_ERROR_maxCode: return squash_error (SQUASH_FAILED); default: squash_assert_unreachable (); } }
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; }
static SquashStatus squash_ms_process_stream (SquashStream* stream, SquashOperation operation) { SquashStatus status = SQUASH_FAILED; MSCompStatus res; SquashMSCompStream* s = (SquashMSCompStream*) stream; uint8_t nul_buf; s->mscomp.in = stream->next_in; s->mscomp.in_avail = stream->avail_in; s->mscomp.out = stream->next_out; s->mscomp.out_avail = stream->avail_out; if (s->mscomp.in == NULL) { s->mscomp.in = &nul_buf; } if (stream->stream_type == SQUASH_STREAM_COMPRESS) { res = ms_deflate(&(s->mscomp), squash_ms_comp_flush_from_operation (operation)); } else { res = ms_inflate(&(s->mscomp)); } stream->next_in = s->mscomp.in; stream->avail_in = s->mscomp.in_avail; stream->next_out = s->mscomp.out; stream->avail_out = s->mscomp.out_avail; switch (stream->stream_type) { case SQUASH_STREAM_COMPRESS: switch (operation) { case SQUASH_OPERATION_PROCESS: switch ((int) res) { case MSCOMP_OK: status = (stream->avail_in == 0) ? SQUASH_OK : SQUASH_PROCESSING; break; default: status = squash_ms_status_to_squash_status (res); break; } break; case SQUASH_OPERATION_FLUSH: switch ((int) res) { case MSCOMP_OK: status = SQUASH_OK; break; default: status = squash_ms_status_to_squash_status (res); break; } break; case SQUASH_OPERATION_FINISH: switch ((int) res) { case MSCOMP_OK: status = SQUASH_PROCESSING; break; case MSCOMP_STREAM_END: status = SQUASH_OK; break; default: status = squash_ms_status_to_squash_status (res); break; } break; case SQUASH_OPERATION_TERMINATE: squash_assert_unreachable (); break; } break; case SQUASH_STREAM_DECOMPRESS: switch (operation) { case SQUASH_OPERATION_PROCESS: case SQUASH_OPERATION_FLUSH: case SQUASH_OPERATION_FINISH: switch ((int) res) { case MSCOMP_OK: case MSCOMP_POSSIBLE_STREAM_END: status = (stream->avail_in == 0 && stream->avail_out > 0) ? SQUASH_OK : SQUASH_PROCESSING; break; default: status = squash_ms_status_to_squash_status (res); break; } break; case SQUASH_OPERATION_TERMINATE: squash_assert_unreachable (); } break; } return status; }
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; } }
static SquashStatus squash_lz4f_compress_stream (SquashStream* stream, SquashOperation operation) { SquashLZ4FStream* s = (SquashLZ4FStream*) stream; bool progress = false; if (s->data.comp.output_buffer_size != 0) { const size_t buffer_remaining = s->data.comp.output_buffer_size - s->data.comp.output_buffer_pos; const size_t cp_size = (buffer_remaining < stream->avail_out) ? buffer_remaining : stream->avail_out; memcpy (stream->next_out, s->data.comp.output_buffer + s->data.comp.output_buffer_pos, cp_size); stream->next_out += cp_size; stream->avail_out -= cp_size; s->data.comp.output_buffer_pos += cp_size; if (cp_size == buffer_remaining) { s->data.comp.output_buffer_size = 0; s->data.comp.output_buffer_pos = 0; progress = true; } else { return SQUASH_PROCESSING; } } while ((stream->avail_in != 0 || operation != SQUASH_OPERATION_PROCESS) && stream->avail_out != 0) { if (s->data.comp.state == SQUASH_LZ4F_STATE_INIT) { s->data.comp.state = SQUASH_LZ4F_STATE_ACTIVE; if (stream->avail_out < 19) { s->data.comp.output_buffer_size = LZ4F_compressBegin (s->data.comp.ctx, squash_lz4f_stream_get_output_buffer (stream), squash_lz4f_stream_get_output_buffer_size (stream), &(s->data.comp.prefs)); break; } else { size_t written = LZ4F_compressBegin (s->data.comp.ctx, stream->next_out, stream->avail_out, &(s->data.comp.prefs)); stream->next_out += written; stream->avail_out -= written; progress = true; } } else { const size_t input_buffer_size = squash_lz4f_get_input_buffer_size (stream); const size_t total_input = stream->avail_in + s->data.comp.input_buffer_size; const size_t output_buffer_max_size = squash_lz4f_stream_get_output_buffer_size (stream); if (progress && (total_input < input_buffer_size || stream->avail_out < output_buffer_max_size)) break; uint8_t* obuf; size_t olen; const size_t input_size = (total_input > input_buffer_size) ? (input_buffer_size - s->data.comp.input_buffer_size) : stream->avail_in; if (input_size > 0) { obuf = (output_buffer_max_size > stream->avail_out) ? squash_lz4f_stream_get_output_buffer (stream) : stream->next_out; olen = LZ4F_compressUpdate (s->data.comp.ctx, obuf, output_buffer_max_size, stream->next_in, input_size, NULL); if (!LZ4F_isError (olen)) { if (input_size + s->data.comp.input_buffer_size == input_buffer_size) { s->data.comp.input_buffer_size = 0; } else { s->data.comp.input_buffer_size += input_size; assert (olen == 0); } stream->next_in += input_size; stream->avail_in -= input_size; } else { squash_assert_unreachable(); } } else if (operation == SQUASH_OPERATION_FLUSH) { assert (stream->avail_in == 0); olen = squash_lz4f_stream_get_output_buffer_size (stream); obuf = (olen > stream->avail_out) ? squash_lz4f_stream_get_output_buffer (stream) : stream->next_out; olen = LZ4F_flush (s->data.comp.ctx, obuf, olen, NULL); s->data.comp.input_buffer_size = 0; } else if (operation == SQUASH_OPERATION_FINISH) { assert (stream->avail_in == 0); olen = squash_lz4f_stream_get_output_buffer_size (stream); obuf = (olen > stream->avail_out) ? squash_lz4f_stream_get_output_buffer (stream) : stream->next_out; olen = LZ4F_compressEnd (s->data.comp.ctx, obuf, olen, NULL); s->data.comp.input_buffer_size = 0; } else if (progress) { break; } else { squash_assert_unreachable(); } if (SQUASH_UNLIKELY(LZ4F_isError (olen))) { squash_assert_unreachable(); return squash_error (SQUASH_FAILED); } else { if (olen != 0) { if (obuf == s->data.comp.output_buffer) { s->data.comp.output_buffer_size = olen; break; } else { stream->next_out += olen; stream->avail_out -= olen; } } } if (operation != SQUASH_OPERATION_PROCESS) break; } } if (s->data.comp.output_buffer_size != 0) { const size_t buffer_remaining = s->data.comp.output_buffer_size - s->data.comp.output_buffer_pos; const size_t cp_size = (buffer_remaining < stream->avail_out) ? buffer_remaining : stream->avail_out; memcpy (stream->next_out, s->data.comp.output_buffer + s->data.comp.output_buffer_pos, cp_size); stream->next_out += cp_size; stream->avail_out -= cp_size; s->data.comp.output_buffer_pos += cp_size; if (cp_size == buffer_remaining) { s->data.comp.output_buffer_size = 0; s->data.comp.output_buffer_pos = 0; progress = true; } else { return SQUASH_PROCESSING; } } return (stream->avail_in == 0 && s->data.comp.output_buffer_size == 0) ? SQUASH_OK : SQUASH_PROCESSING; }