microlzma_encoder.c 3.7 KB

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