Exemplo n.º 1
0
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 {
          HEDLEY_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 {
        HEDLEY_UNREACHABLE();
      }

      if (HEDLEY_UNLIKELY(LZ4F_isError (olen))) {
        HEDLEY_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;
}
Exemplo n.º 2
0
Arquivo: file.c Projeto: jibsen/squash
/**
 * @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;
}
Exemplo n.º 3
0
static SquashStatus
squash_stream_process_internal (SquashStream* stream, SquashOperation operation) {
    SquashCodec* codec;
    SquashCodecImpl* impl = NULL;
    SquashStatus res = SQUASH_OK;
    SquashOperation current_operation = SQUASH_OPERATION_PROCESS;

    assert (stream != NULL);
    codec = stream->codec;
    assert (codec != NULL);
    impl = squash_codec_get_impl (codec);
    assert (impl != NULL);

    /* Flush is optional, so return an error if it doesn't exist but
       flushing was requested. */
    if (SQUASH_UNLIKELY(operation == SQUASH_OPERATION_FLUSH && ((impl->info & SQUASH_CODEC_INFO_CAN_FLUSH) == 0))) {
        return squash_error (SQUASH_INVALID_OPERATION);
    }

    /* In order to take some of the load off of the plugins, there is
       some extra logic here which may seem a bit disorienting at first
       glance.  Basically, instead of requiring that plugins handle
       flushing or finishing with arbitrarily large inputs, we first try
       to process as much input as we can.  So, when someone calls
       squash_stream_flush or squash_stream finish Squash may, depending
       on the stream state, first call the process function.  Note that
       Squash will not flush a stream before finishing it (unless there
       is logic to do so in the plugin) as it could cause an increase in
       the output size (it does with zlib).

       One interesting consequence of this is that the stream_state
       field may not be what you're expecting.  If an earlier operation
       returned SQUASH_PROCESSING, stream_type may never transition to
       the new value.  In this case, the stream_type does accurately
       represent the state of the stream, though it probably isn't wise
       to depend on that behavior. */

    if ((operation == SQUASH_OPERATION_PROCESS && stream->state > SQUASH_STREAM_STATE_RUNNING) ||
            (operation == SQUASH_OPERATION_FLUSH   && stream->state > SQUASH_STREAM_STATE_FLUSHING) ||
            (operation == SQUASH_OPERATION_FINISH  && stream->state > SQUASH_STREAM_STATE_FINISHING)) {
        return squash_error (SQUASH_STATE);
    }

    switch (stream->state) {
    case SQUASH_STREAM_STATE_IDLE:
    case SQUASH_STREAM_STATE_RUNNING:
        current_operation = SQUASH_OPERATION_PROCESS;
        break;
    case SQUASH_STREAM_STATE_FLUSHING:
        current_operation = SQUASH_OPERATION_FLUSH;
        break;
    case SQUASH_STREAM_STATE_FINISHING:
        current_operation = SQUASH_OPERATION_FINISH;
        break;
    case SQUASH_STREAM_STATE_FINISHED:
        current_operation = (SquashOperation) (SQUASH_OPERATION_FINISH + 1);
        break;
    }

    if (SQUASH_UNLIKELY(current_operation > operation)) {
        return squash_error (SQUASH_STATE);
    }

    const size_t avail_in = stream->avail_in;
    const size_t avail_out = stream->avail_out;

    /* Some libraries (like zlib) will realize that we're not providing
       it any room for output and are eager to tell us that we don't
       have any space instead of decoding the stream enough to know if we
       actually need that space.

       In cases where this might be problematic, we provide a
       single-byte buffer to the plugin instead.  If anything actually
       gets written to it then we'll return an error
       (SQUASH_BUFFER_FULL), which is non-recoverable.

       There are a few cases where this might reasonably be a problem:

        * Decompression streams which know the exact size of the
          decompressed output, when using codecs which contain extra
          data at the end, such as a footer or EOS marker.

        * Compression streams writing to a fixed buffer with a size of
          less than or equal to max_compressed_size bytes.  This is a
          pretty reasonable thing to do, since you might want to only
          bother using compression if you can achieve a certain ratio.

       For consumers which don't satisfy either of these conditions,
       this code should never be reached. */

    uint8_t* next_out = NULL;
    uint8_t output_sbb = 0;
    if (stream->avail_out == 0) {
        next_out = stream->next_out;
        stream->avail_out = 1;
        stream->next_out = &output_sbb;
    }

    while (current_operation <= operation) {
        if (current_operation == SQUASH_OPERATION_PROCESS) {
            if (stream->avail_in == 0 && stream->state == SQUASH_STREAM_STATE_IDLE) {
                res = SQUASH_OK;
            } else {
                stream->state = SQUASH_STREAM_STATE_RUNNING;

                if (impl->process_stream != NULL) {
                    res = impl->process_stream (stream, current_operation);
                } else if (impl->splice != NULL) {
                    res = squash_stream_send_to_thread (stream, current_operation);
                } else {
                    res = squash_buffer_stream_process ((SquashBufferStream*) stream);
                }
            }

            switch ((int) res) {
            case SQUASH_OK:
                stream->state = SQUASH_STREAM_STATE_IDLE;
                break;
            case SQUASH_PROCESSING:
                stream->state = SQUASH_STREAM_STATE_RUNNING;
                break;
            case SQUASH_END_OF_STREAM:
                stream->state = SQUASH_STREAM_STATE_FINISHED;
                break;
            default:
                return res;
            }
        } else if (current_operation == SQUASH_OPERATION_FLUSH) {
            stream->state = SQUASH_STREAM_STATE_FLUSHING;

            if (current_operation == operation) {
                if ((impl->info & SQUASH_CODEC_INFO_CAN_FLUSH) == SQUASH_CODEC_INFO_CAN_FLUSH) {
                    assert (impl->process_stream != NULL);

                    res = impl->process_stream (stream, current_operation);
                } else {
                    /* We aready checked to make sure the stream is flushable if
                       the user called flush directly, so if this code is
                       reached the user didn't call flush, they called finish
                       which attempts to flush internally.  Just pretend it
                       worked so we can proceed to finishing. */
                    res = SQUASH_OK;
                }
            }

            switch ((int) res) {
            case SQUASH_OK:
                stream->state = SQUASH_STREAM_STATE_IDLE;
                break;
            case SQUASH_PROCESSING:
                stream->state = SQUASH_STREAM_STATE_FLUSHING;
                break;
            case SQUASH_END_OF_STREAM:
                stream->state = SQUASH_STREAM_STATE_FINISHED;
                break;
            default:
                return res;
            }
        } else if (current_operation == SQUASH_OPERATION_FINISH) {
            stream->state = SQUASH_STREAM_STATE_FINISHING;

            if (impl->process_stream != NULL) {
                res = impl->process_stream (stream, current_operation);
            } else if (impl->splice) {
                res = squash_stream_send_to_thread (stream, current_operation);
            } else {
                res = squash_buffer_stream_finish ((SquashBufferStream*) stream);
            }

            /* Plugins *should* return SQUASH_OK, not SQUASH_END_OF_STREAM,
               from the finish function, but it's an easy mistake to make
               (and correct), so... */
            if (SQUASH_UNLIKELY(res == SQUASH_END_OF_STREAM)) {
                res = SQUASH_OK;
            }

            switch ((int) res) {
            case SQUASH_OK:
                stream->state = SQUASH_STREAM_STATE_FINISHED;
                break;
            case SQUASH_PROCESSING:
                stream->state = SQUASH_STREAM_STATE_FINISHING;
                break;
            default:
                return res;
            }
        }

        /* Check our internal single byte buffer */
        if (next_out != 0) {
            if (SQUASH_UNLIKELY(stream->avail_out == 0)) {
                res = squash_error (SQUASH_BUFFER_FULL);
            }
        }

        if (res == SQUASH_PROCESSING) {
            break;
        } else if (res == SQUASH_END_OF_STREAM || (current_operation == SQUASH_OPERATION_FINISH && res == SQUASH_OK)) {
            assert (stream->state == SQUASH_STREAM_STATE_FINISHED);
            current_operation++;
            break;
        } else if (res == SQUASH_OK) {
            current_operation++;
        } else {
            break;
        }
    }

    if (next_out != 0) {
        stream->avail_out = 0;
        stream->next_out = next_out;
    }

    stream->total_in += (avail_in - stream->avail_in);
    stream->total_out += (avail_out - stream->avail_out);

    return res;
}
Exemplo n.º 4
0
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;
  }
}
Exemplo n.º 5
0
static SquashStatus
squash_file_write_internal (SquashFile* file,
                            size_t uncompressed_size,
                            const uint8_t uncompressed[HEDLEY_ARRAY_PARAM(uncompressed_size)],
                            SquashOperation operation) {
  SquashStatus res;

  assert (file != NULL);

  if (HEDLEY_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 (HEDLEY_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;

  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:
        HEDLEY_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;
}
Exemplo n.º 6
0
/**
 * @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);
}
Exemplo n.º 7
0
/**
 * @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;
  }
}