microlzma_encoder.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. /// \file microlzma_encoder.c
  4. /// \brief Encode into MicroLZMA format
  5. //
  6. // Author: Lasse Collin
  7. //
  8. // This file has been put into the public domain.
  9. // You can do whatever you want with this file.
  10. //
  11. ///////////////////////////////////////////////////////////////////////////////
  12. #include "lzma_encoder.h"
  13. typedef struct {
  14. /// LZMA1 encoder
  15. lzma_next_coder lzma;
  16. /// LZMA properties byte (lc/lp/pb)
  17. uint8_t props;
  18. } lzma_microlzma_coder;
  19. static lzma_ret
  20. microlzma_encode(void *coder_ptr, const lzma_allocator *allocator,
  21. const uint8_t *restrict in, size_t *restrict in_pos,
  22. size_t in_size, uint8_t *restrict out,
  23. size_t *restrict out_pos, size_t out_size, lzma_action action)
  24. {
  25. lzma_microlzma_coder *coder = coder_ptr;
  26. // Remember *out_pos so that we can overwrite the first byte with
  27. // the LZMA properties byte.
  28. const size_t out_start = *out_pos;
  29. // Remember *in_pos so that we can set it based on how many
  30. // uncompressed bytes were actually encoded.
  31. const size_t in_start = *in_pos;
  32. // Set the output size limit based on the available output space.
  33. // We know that the encoder supports set_out_limit() so
  34. // LZMA_OPTIONS_ERROR isn't possible. LZMA_BUF_ERROR is possible
  35. // but lzma_code() has an assertion to not allow it to be returned
  36. // from here and I don't want to change that for now, so
  37. // LZMA_BUF_ERROR becomes LZMA_PROG_ERROR.
  38. uint64_t uncomp_size;
  39. if (coder->lzma.set_out_limit(coder->lzma.coder,
  40. &uncomp_size, out_size - *out_pos) != LZMA_OK)
  41. return LZMA_PROG_ERROR;
  42. // set_out_limit fails if this isn't true.
  43. assert(out_size - *out_pos >= 6);
  44. // Encode as much as possible.
  45. const lzma_ret ret = coder->lzma.code(coder->lzma.coder, allocator,
  46. in, in_pos, in_size, out, out_pos, out_size, action);
  47. if (ret != LZMA_STREAM_END) {
  48. if (ret == LZMA_OK) {
  49. assert(0);
  50. return LZMA_PROG_ERROR;
  51. }
  52. return ret;
  53. }
  54. // The first output byte is bitwise-negation of the properties byte.
  55. // We know that there is space for this byte because set_out_limit
  56. // and the actual encoding succeeded.
  57. out[out_start] = (uint8_t)(~coder->props);
  58. // The LZMA encoder likely read more input than it was able to encode.
  59. // Set *in_pos based on uncomp_size.
  60. assert(uncomp_size <= in_size - in_start);
  61. *in_pos = in_start + (size_t)(uncomp_size);
  62. return ret;
  63. }
  64. static void
  65. microlzma_encoder_end(void *coder_ptr, const lzma_allocator *allocator)
  66. {
  67. lzma_microlzma_coder *coder = coder_ptr;
  68. lzma_next_end(&coder->lzma, allocator);
  69. lzma_free(coder, allocator);
  70. return;
  71. }
  72. static lzma_ret
  73. microlzma_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
  74. const lzma_options_lzma *options)
  75. {
  76. lzma_next_coder_init(&microlzma_encoder_init, next, allocator);
  77. lzma_microlzma_coder *coder = next->coder;
  78. if (coder == NULL) {
  79. coder = lzma_alloc(sizeof(lzma_microlzma_coder), allocator);
  80. if (coder == NULL)
  81. return LZMA_MEM_ERROR;
  82. next->coder = coder;
  83. next->code = &microlzma_encode;
  84. next->end = &microlzma_encoder_end;
  85. coder->lzma = LZMA_NEXT_CODER_INIT;
  86. }
  87. // Encode the properties byte. Bitwise-negation of it will be the
  88. // first output byte.
  89. return_if_error(lzma_lzma_lclppb_encode(options, &coder->props));
  90. // Initialize the LZMA encoder.
  91. const lzma_filter_info filters[2] = {
  92. {
  93. .id = LZMA_FILTER_LZMA1,
  94. .init = &lzma_lzma_encoder_init,
  95. .options = (void *)(options),
  96. }, {
  97. .init = NULL,
  98. }
  99. };
  100. return lzma_next_filter_init(&coder->lzma, allocator, filters);
  101. }
  102. extern LZMA_API(lzma_ret)
  103. lzma_microlzma_encoder(lzma_stream *strm, const lzma_options_lzma *options)
  104. {
  105. lzma_next_strm_init(microlzma_encoder_init, strm, options);
  106. strm->internal->supported_actions[LZMA_FINISH] = true;
  107. return LZMA_OK;
  108. }