1// SPDX-License-Identifier: 0BSD
2
3///////////////////////////////////////////////////////////////////////////////
4//
5/// \file       alone_encoder.c
6/// \brief      Encoder for LZMA_Alone files
7//
8//  Author:     Lasse Collin
9//
10///////////////////////////////////////////////////////////////////////////////
11
12#include "common.h"
13#include "lzma_encoder.h"
14
15
16#define ALONE_HEADER_SIZE (1 + 4 + 8)
17
18
19typedef struct {
20	lzma_next_coder next;
21
22	enum {
23		SEQ_HEADER,
24		SEQ_CODE,
25	} sequence;
26
27	size_t header_pos;
28	uint8_t header[ALONE_HEADER_SIZE];
29} lzma_alone_coder;
30
31
32static lzma_ret
33alone_encode(void *coder_ptr, const lzma_allocator *allocator,
34		const uint8_t *restrict in, size_t *restrict in_pos,
35		size_t in_size, uint8_t *restrict out,
36		size_t *restrict out_pos, size_t out_size,
37		lzma_action action)
38{
39	lzma_alone_coder *coder = coder_ptr;
40
41	while (*out_pos < out_size)
42	switch (coder->sequence) {
43	case SEQ_HEADER:
44		lzma_bufcpy(coder->header, &coder->header_pos,
45				ALONE_HEADER_SIZE,
46				out, out_pos, out_size);
47		if (coder->header_pos < ALONE_HEADER_SIZE)
48			return LZMA_OK;
49
50		coder->sequence = SEQ_CODE;
51		break;
52
53	case SEQ_CODE:
54		return coder->next.code(coder->next.coder,
55				allocator, in, in_pos, in_size,
56				out, out_pos, out_size, action);
57
58	default:
59		assert(0);
60		return LZMA_PROG_ERROR;
61	}
62
63	return LZMA_OK;
64}
65
66
67static void
68alone_encoder_end(void *coder_ptr, const lzma_allocator *allocator)
69{
70	lzma_alone_coder *coder = coder_ptr;
71	lzma_next_end(&coder->next, allocator);
72	lzma_free(coder, allocator);
73	return;
74}
75
76
77static lzma_ret
78alone_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
79		const lzma_options_lzma *options)
80{
81	lzma_next_coder_init(&alone_encoder_init, next, allocator);
82
83	lzma_alone_coder *coder = next->coder;
84
85	if (coder == NULL) {
86		coder = lzma_alloc(sizeof(lzma_alone_coder), allocator);
87		if (coder == NULL)
88			return LZMA_MEM_ERROR;
89
90		next->coder = coder;
91		next->code = &alone_encode;
92		next->end = &alone_encoder_end;
93		coder->next = LZMA_NEXT_CODER_INIT;
94	}
95
96	// Basic initializations
97	coder->sequence = SEQ_HEADER;
98	coder->header_pos = 0;
99
100	// Encode the header:
101	// - Properties (1 byte)
102	if (lzma_lzma_lclppb_encode(options, coder->header))
103		return LZMA_OPTIONS_ERROR;
104
105	// - Dictionary size (4 bytes)
106	if (options->dict_size < LZMA_DICT_SIZE_MIN)
107		return LZMA_OPTIONS_ERROR;
108
109	// Round up to the next 2^n or 2^n + 2^(n - 1) depending on which
110	// one is the next unless it is UINT32_MAX. While the header would
111	// allow any 32-bit integer, we do this to keep the decoder of liblzma
112	// accepting the resulting files.
113	uint32_t d = options->dict_size - 1;
114	d |= d >> 2;
115	d |= d >> 3;
116	d |= d >> 4;
117	d |= d >> 8;
118	d |= d >> 16;
119	if (d != UINT32_MAX)
120		++d;
121
122	write32le(coder->header + 1, d);
123
124	// - Uncompressed size (always unknown and using EOPM)
125	memset(coder->header + 1 + 4, 0xFF, 8);
126
127	// Initialize the LZMA encoder.
128	const lzma_filter_info filters[2] = {
129		{
130			.id = LZMA_FILTER_LZMA1,
131			.init = &lzma_lzma_encoder_init,
132			.options = (void *)(options),
133		}, {
134			.init = NULL,
135		}
136	};
137
138	return lzma_next_filter_init(&coder->next, allocator, filters);
139}
140
141
142extern LZMA_API(lzma_ret)
143lzma_alone_encoder(lzma_stream *strm, const lzma_options_lzma *options)
144{
145	lzma_next_strm_init(alone_encoder_init, strm, options);
146
147	strm->internal->supported_actions[LZMA_RUN] = true;
148	strm->internal->supported_actions[LZMA_FINISH] = true;
149
150	return LZMA_OK;
151}
152