bool squash_mapped_file_destroy (SquashMappedFile* mapped, bool success) { if (mapped->data != MAP_FAILED) { munmap (mapped->data - mapped->window_offset, mapped->size + mapped->window_offset); mapped->data = MAP_FAILED; if (success) { const int sres = fseeko (mapped->fp, mapped->size, SEEK_CUR); if (SQUASH_LIKELY(sres != -1)) { if (mapped->writable) { const off_t pos = ftello (mapped->fp); if (SQUASH_LIKELY(pos != -1)) { const int tr = ftruncate (fileno (mapped->fp), pos); return SQUASH_LIKELY(tr != -1) ? true : false; } else { return false; } } } else { return false; } } } return true; }
/** * @brief Close a file * * If @a file is a compressor the stream will finish compressing, * writing any buffered data. For codecs which do not provide a * native streaming interface, *all* of the actual compression will * take place during this call. In other words, it may block for a * non-trivial period. If this is a problem please file a bug against * Squash (including your use case), and we can discuss adding a * function call which will simply abort compression. * * In addition to freeing the @ref SquashFile instance, this function * will close the underlying *FILE* pointer. If you wish to continue * using the *FILE* for something else, use @ref squash_file_free * instead. * * @param file file to close * @return @ref SQUASH_OK on success or a negative error code on * failure */ SquashStatus squash_file_close (SquashFile* file) { FILE* fp = NULL; SquashStatus res = squash_file_free (file, &fp); if (res > SQUASH_OK) res = SQUASH_OK; if (fp != NULL) { const SquashStatus cres = SQUASH_LIKELY(fclose (fp) == 0) ? SQUASH_OK : squash_error (SQUASH_IO); if (SQUASH_LIKELY(res > 0)) res = cres; } return res; }
static SquashStatus squash_brotli_decompress_stream (SquashStream* stream, SquashOperation operation) { SquashBrotliStream* s = (SquashBrotliStream*) stream; if (s->finished) { return SQUASH_OK; } try { size_t total_out; BrotliResult res = BrotliDecompressStream (&s->base_object.avail_in, &s->base_object.next_in, &s->base_object.avail_out, &s->base_object.next_out, &total_out, s->decompressor); if (res == BROTLI_RESULT_SUCCESS) { s->finished = true; return SQUASH_OK; } if (SQUASH_LIKELY(res == BROTLI_RESULT_NEEDS_MORE_OUTPUT || res == BROTLI_RESULT_NEEDS_MORE_INPUT)) { return (res == BROTLI_RESULT_NEEDS_MORE_OUTPUT) ? SQUASH_PROCESSING : SQUASH_OK; } return squash_error (SQUASH_FAILED); } catch (const std::bad_alloc& e) { (void) e; return squash_error (SQUASH_MEMORY); } catch (...) { return squash_error (SQUASH_FAILED); } }
SquashStatus squash_file_printf (SquashFile* file, const char* format, ...) { SquashStatus res = SQUASH_OK; va_list ap; int size; char buf[256]; char* heap_buf = NULL; va_start (ap, format); #if defined(_WIN32) size = _vscprintf (format, ap); #else size = vsnprintf (buf, sizeof (buf), format, ap); if (size >= (int) sizeof (buf)) #endif { heap_buf = malloc (size); if (SQUASH_UNLIKELY(heap_buf == NULL)) res = squash_error (SQUASH_MEMORY); const int written = vsnprintf (heap_buf, size, format, ap); if (SQUASH_UNLIKELY(written != size - 1)) res = squash_error (SQUASH_FAILED); } va_end (ap); if (SQUASH_LIKELY(res == SQUASH_OK)) res = squash_file_write (file, size, (uint8_t*) ((heap_buf == NULL) ? buf : heap_buf)); free (heap_buf); return res; }
static size_t squash_pithy_get_uncompressed_size (SquashCodec* codec, size_t compressed_size, const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_size)]) { size_t uncompressed_size = 0; int r = pithy_GetDecompressedLength ((const char*) compressed, compressed_size, &uncompressed_size); return SQUASH_LIKELY(r > 0) ? uncompressed_size : 0; }
static SquashStatus squash_brotli_decompress_buffer (SquashCodec* codec, size_t* decompressed_size, uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_size)], size_t compressed_size, const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_size)], SquashOptions* options) { const BrotliResult res = BrotliDecompressBuffer(compressed_size, compressed, decompressed_size, decompressed); return SQUASH_LIKELY(res == BROTLI_RESULT_SUCCESS) ? SQUASH_OK : squash_error (SQUASH_BUFFER_FULL); }
static SquashStatus squash_wflz_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 char* codec_name = squash_codec_get_name (codec); const uint32_t swap = ((uint32_t) squash_options_get_int_at (options, codec, SQUASH_WFLZ_OPT_ENDIANNESS) != SQUASH_WFLZ_HOST_ORDER); const int level = squash_options_get_int_at (options, codec, SQUASH_WFLZ_OPT_LEVEL); #if UINT32_MAX < SIZE_MAX if (SQUASH_UNLIKELY(UINT32_MAX < uncompressed_size)) return squash_error (SQUASH_RANGE); #endif if (*compressed_size < wfLZ_GetMaxCompressedSize ((uint32_t) uncompressed_size)) { return squash_error (SQUASH_BUFFER_FULL); } uint8_t* work_mem = (uint8_t*) malloc (wfLZ_GetWorkMemSize ()); uint32_t wres; if (codec_name[4] == '\0') { if (level == 1) { wres = wfLZ_CompressFast (uncompressed, (uint32_t) uncompressed_size, compressed, work_mem, swap); } else { wres = wfLZ_Compress (uncompressed, (uint32_t) uncompressed_size, compressed, work_mem, swap); } } else { wres = wfLZ_ChunkCompress ((uint8_t*) uncompressed, (uint32_t) uncompressed_size, squash_options_get_size_at (options, codec, SQUASH_WFLZ_OPT_CHUNK_SIZE), compressed, work_mem, swap, level == 1 ? 1 : 0); } #if SIZE_MAX < UINT32_MAX if (SQUASH_UNLIKELY(SIZE_MAX < wres)) { free (work_mem); return squash_error (SQUASH_RANGE); } #endif *compressed_size = (size_t) wres; free (work_mem); return SQUASH_LIKELY(*compressed_size > 0) ? SQUASH_OK : squash_error (SQUASH_FAILED); }
extern "C" SquashStatus squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) { const char* name = squash_codec_get_name (codec); if (SQUASH_LIKELY(strcmp ("csc", name) == 0)) { impl->options = squash_csc_options; impl->splice = squash_csc_splice; impl->get_max_compressed_size = squash_csc_get_max_compressed_size; } else { return squash_error (SQUASH_UNABLE_TO_LOAD); } return SQUASH_OK; }
SquashStatus squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) { const char* name = squash_codec_get_name (codec); if (SQUASH_LIKELY(strcmp ("brieflz", name) == 0)) { impl->get_uncompressed_size = squash_brieflz_get_uncompressed_size; impl->get_max_compressed_size = squash_brieflz_get_max_compressed_size; impl->decompress_buffer = squash_brieflz_decompress_buffer; impl->compress_buffer_unsafe = squash_brieflz_compress_buffer; } else { return squash_error (SQUASH_UNABLE_TO_LOAD); } return SQUASH_OK; }
SquashStatus squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) { const char* name = squash_codec_get_name (codec); if (SQUASH_LIKELY(strcmp ("density", name) == 0)) { impl->options = squash_density_options; impl->create_stream = squash_density_create_stream; impl->process_stream = squash_density_process_stream; impl->get_max_compressed_size = squash_density_get_max_compressed_size; } else { return squash_error (SQUASH_UNABLE_TO_LOAD); } return SQUASH_OK; }
static SquashStatus squash_brotli_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 quality = squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_LEVEL); const int lgwin = squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_WINDOW_SIZE); const BrotliEncoderMode mode = (BrotliEncoderMode) squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_MODE); const int res = BrotliEncoderCompress (quality, lgwin, mode, uncompressed_size, uncompressed, compressed_size, compressed); return SQUASH_LIKELY(res == 1) ? SQUASH_OK : squash_error (SQUASH_BUFFER_FULL); }
SquashStatus squash_plugin_init_lz4f (SquashCodec* codec, SquashCodecImpl* impl) { const char* name = squash_codec_get_name (codec); if (SQUASH_LIKELY(strcmp ("lz4", name) == 0)) { impl->info = SQUASH_CODEC_INFO_CAN_FLUSH; impl->options = squash_lz4f_options; impl->get_max_compressed_size = squash_lz4f_get_max_compressed_size; impl->create_stream = squash_lz4f_create_stream; impl->process_stream = squash_lz4f_process_stream; } else { return SQUASH_UNABLE_TO_LOAD; } return SQUASH_OK; }
static #endif SquashStatus squash_file_vwprintf (SquashFile* file, const wchar_t* format, va_list ap) { SquashStatus res = SQUASH_OK; int size; wchar_t* buf = NULL; assert (file != NULL); assert (format != NULL); size = _vscwprintf (format, ap); if (SQUASH_UNLIKELY(size < 0)) return squash_error (SQUASH_FAILED); buf = calloc (size + 1, sizeof (wchar_t)); if (SQUASH_UNLIKELY(buf == NULL)) return squash_error (SQUASH_MEMORY); #if !defined(_WIN32) const int written = vswprintf (buf, size + 1, format, ap); #else const int written = _vsnwprintf (buf, size + 1, format, ap); #endif if (SQUASH_UNLIKELY(written != size)) { res = squash_error (SQUASH_FAILED); } else { size_t data_size; char* data; bool conv_success = squash_charset_convert (&data_size, &data, "UTF-8", size * sizeof(wchar_t), (char*) buf, squash_charset_get_wide ()); if (SQUASH_LIKELY(conv_success)) res = squash_file_write (file, data_size, (uint8_t*) data); else res = squash_error (SQUASH_FAILED); } squash_free (buf); return res; }
static SquashStatus squash_pithy_decompress_buffer (SquashCodec* codec, size_t* decompressed_size, uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_size)], size_t compressed_size, const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_size)], SquashOptions* options) { size_t outlen = squash_pithy_get_uncompressed_size(codec, compressed_size, compressed); if (SQUASH_UNLIKELY(*decompressed_size < outlen)) return squash_error (SQUASH_BUFFER_FULL); if (SQUASH_LIKELY(pithy_Decompress ((const char*) compressed, compressed_size, (char*) decompressed, outlen))) { *decompressed_size = outlen; return SQUASH_OK; } else { return squash_error (SQUASH_FAILED); } }
SquashStatus squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) { const char* name = squash_codec_get_name (codec); if (SQUASH_LIKELY(strcmp ("brotli", name) == 0)) { impl->info = SQUASH_CODEC_INFO_CAN_FLUSH; impl->options = squash_brotli_options; impl->get_max_compressed_size = squash_brotli_get_max_compressed_size; impl->create_stream = squash_brotli_create_stream; impl->process_stream = squash_brotli_process_stream; impl->decompress_buffer = squash_brotli_decompress_buffer; impl->compress_buffer = squash_brotli_compress_buffer; } else { return squash_error (SQUASH_UNABLE_TO_LOAD); } return SQUASH_OK; }
SquashStatus squash_file_vprintf (SquashFile* file, const char* format, va_list ap) { SquashStatus res = SQUASH_OK; int size; char* heap_buf = NULL; assert (file != NULL); assert (format != NULL); #if defined(_WIN32) size = _vscprintf (format, ap); if (SQUASH_UNLIKELY(size < 0)) return squash_error (SQUASH_FAILED); #else char buf[256]; size = vsnprintf (buf, sizeof (buf), format, ap); if (SQUASH_UNLIKELY(size < 0)) return squash_error (SQUASH_FAILED); else if (size >= (int) sizeof (buf)) #endif { heap_buf = squash_malloc (size + 1); if (SQUASH_UNLIKELY(heap_buf == NULL)) return squash_error (SQUASH_MEMORY); const int written = vsnprintf (heap_buf, size + 1, format, ap); if (SQUASH_UNLIKELY(written != size)) res = squash_error (SQUASH_FAILED); } if (SQUASH_LIKELY(res == SQUASH_OK)) { res = squash_file_write (file, size, #if !defined(_WIN32) (heap_buf == NULL) ? (uint8_t*) buf : #endif (uint8_t*) heap_buf); } squash_free (heap_buf); return res; }
/** * @brief Initialize a codec * @private * * @param plugin The plugin. * @param codec The codec to initialize. * @param impl The function table to fill. * @returns A status code. */ SquashStatus squash_plugin_init_codec (SquashPlugin* plugin, SquashCodec* codec, SquashCodecImpl* impl) { SquashStatus res = SQUASH_OK; assert (plugin != NULL); if (plugin->plugin == NULL) { res = squash_plugin_init (plugin); if (res != SQUASH_OK) { return res; } } if (codec->initialized == 0) { SquashStatus (*init_codec_func) (SquashCodec*, SquashCodecImpl*); #if !defined(_WIN32) *(void **) (&init_codec_func) = dlsym (plugin->plugin, "squash_plugin_init_codec"); #else *(void **) (&init_codec_func) = GetProcAddress (plugin->plugin, "squash_plugin_init_codec"); #endif if (SQUASH_UNLIKELY(init_codec_func == NULL)) { return squash_error (SQUASH_UNABLE_TO_LOAD); } SQUASH_MTX_LOCK(codec_init); if (SQUASH_LIKELY(codec->initialized == 0)) { res = init_codec_func (codec, impl); codec->initialized = (res == SQUASH_OK); assert ((codec->impl.info & SQUASH_CODEC_INFO_AUTO_MASK) == 0); if (codec->impl.process_stream != NULL) codec->impl.info |= (SquashCodecInfo) SQUASH_CODEC_INFO_NATIVE_STREAMING; if (codec->impl.get_uncompressed_size != NULL || (codec->impl.info & SQUASH_CODEC_INFO_WRAP_SIZE)) codec->impl.info |= (SquashCodecInfo) SQUASH_CODEC_INFO_KNOWS_UNCOMPRESSED_SIZE; } SQUASH_MTX_UNLOCK(codec_init); } return res; }
/** * @brief Decrement the reference count on an object. * * Once the reference count reaches 0 the object will be freed. * * @param obj The object to decrease the reference count of. * @return NULL */ void* squash_object_unref (void* obj) { if (obj == NULL) return NULL; SquashObject* object = (SquashObject*) obj; unsigned int ref_count = squash_atomic_dec (&(object->ref_count)); if (ref_count == 1) { if (SQUASH_LIKELY(object->destroy_notify != NULL)) object->destroy_notify (obj); squash_free (obj); return NULL; } else { return NULL; } }
SquashStatus squash_plugin_init_codec (SquashCodec* codec, SquashCodecImpl* impl) { const char* name = squash_codec_get_name (codec); if (SQUASH_LIKELY(strcmp ("lznt1", name) == 0 || strcmp ("xpress", name) == 0 || strcmp ("xpress-huffman", name) == 0)) { impl->get_max_compressed_size = squash_ms_get_max_compressed_size; impl->decompress_buffer = squash_ms_decompress_buffer; impl->compress_buffer = squash_ms_compress_buffer; if (strcmp ("lznt1", name) == 0) { impl->info = SQUASH_CODEC_INFO_CAN_FLUSH; impl->create_stream = squash_ms_create_stream; impl->process_stream = squash_ms_process_stream; } } else { return squash_error (SQUASH_UNABLE_TO_LOAD); } return SQUASH_OK; }
static SquashStatus squash_brotli_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) { brotli::BrotliParams params; params.quality = squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_LEVEL); params.mode = (brotli::BrotliParams::Mode) squash_options_get_int_at (options, codec, SQUASH_BROTLI_OPT_MODE); try { int res = brotli::BrotliCompressBuffer (params, uncompressed_size, uncompressed, compressed_size, compressed); return SQUASH_LIKELY(res == 1) ? SQUASH_OK : squash_error (SQUASH_FAILED); } catch (const std::bad_alloc& e) { (void) e; return squash_error (SQUASH_MEMORY); } catch (...) { return squash_error (SQUASH_FAILED); } }
/** * @brief Get a list of options applicable to the codec * * @param codec The codec * @return a list of options, terminated by an option with a NULL name */ const SquashOptionInfo* squash_codec_get_option_info (SquashCodec* codec) { SquashCodecImpl* impl = squash_codec_get_impl (codec); return SQUASH_LIKELY(impl != NULL) ? impl->options : NULL; }
/** * @brief load a %SquashPlugin * * @note This function is generally only useful inside of a callback * passed to ::squash_foreach_plugin. Every other way to get a plugin * (such as ::squash_get_plugin) will initialize the plugin as well * (and return *NULL* instead of the plugin if initialization fails). * The foreach functions, however, do not initialize the plugin since * doing so requires actually loading the plugin. * * @param plugin The plugin to load. * @return A status code. * @retval SQUASH_OK The plugin has been loaded. * @retval SQUASH_UNABLE_TO_LOAD Unable to load plugin. */ SquashStatus squash_plugin_init (SquashPlugin* plugin) { if (plugin->plugin == NULL) { #if !defined(_WIN32) void* handle; #else HMODULE handle; #endif char* plugin_file_name; plugin_file_name = squash_strdup_printf ("%s/%ssquash%s-plugin-%s%s", plugin->directory, SQUASH_SHARED_LIBRARY_PREFIX, SQUASH_VERSION_API, plugin->name, SQUASH_SHARED_LIBRARY_SUFFIX); if (plugin_file_name == NULL) return squash_error (SQUASH_MEMORY); #if !defined(_WIN32) handle = dlopen (plugin_file_name, RTLD_LAZY); #else handle = LoadLibrary (TEXT(plugin_file_name)); if (handle == NULL) { squash_free (plugin_file_name); #if defined(_DEBUG) plugin_file_name = squash_strdup_printf ("%s/Debug/%ssquash%s-plugin-%s%s", plugin->directory, SQUASH_SHARED_LIBRARY_PREFIX, SQUASH_VERSION_API, plugin->name, SQUASH_SHARED_LIBRARY_SUFFIX); #else plugin_file_name = squash_strdup_printf ("%s/Release/%ssquash%s-plugin-%s%s", plugin->directory, SQUASH_SHARED_LIBRARY_PREFIX, SQUASH_VERSION_API, plugin->name, SQUASH_SHARED_LIBRARY_SUFFIX); #endif handle = LoadLibrary (TEXT(plugin_file_name)); } #endif squash_free (plugin_file_name); if (SQUASH_LIKELY(handle != NULL)) { SQUASH_MTX_LOCK(plugin_init); if (plugin->plugin == NULL) { plugin->plugin = handle; handle = NULL; } SQUASH_MTX_UNLOCK(plugin_init); } else { return squash_error (SQUASH_UNABLE_TO_LOAD); } if (handle != NULL) { #if !defined(_WIN32) dlclose (handle); #else FreeLibrary (handle); #endif } else { SquashStatus (*init_func) (SquashPlugin*); #if !defined(_WIN32) *(void **) (&init_func) = dlsym (plugin->plugin, "squash_plugin_init_plugin"); #else *(void **) (&init_func) = GetProcAddress (plugin->plugin, "squash_plugin_init_plugin"); #endif if (init_func != NULL) { init_func (plugin); } } } return SQUASH_LIKELY(plugin->plugin != NULL) ? SQUASH_OK : squash_error (SQUASH_UNABLE_TO_LOAD); }