aboutsummaryrefslogtreecommitdiff
path: root/xdelta3-decode.h
diff options
context:
space:
mode:
Diffstat (limited to 'xdelta3-decode.h')
-rw-r--r--xdelta3-decode.h1115
1 files changed, 1115 insertions, 0 deletions
diff --git a/xdelta3-decode.h b/xdelta3-decode.h
new file mode 100644
index 0000000..bf2b0b1
--- /dev/null
+++ b/xdelta3-decode.h
@@ -0,0 +1,1115 @@
+/* xdelta 3 - delta compression tools and library
+ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007. Joshua P. MacDonald
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XDELTA3_DECODE_H_
+#define _XDELTA3_DECODE_H_
+
+#define SRCORTGT(x) ((((x) & VCD_SRCORTGT) == VCD_SOURCE) ? \
+ VCD_SOURCE : ((((x) & VCD_SRCORTGT) == \
+ VCD_TARGET) ? VCD_TARGET : 0))
+
+/* Initialize the decoder for a new window. The dec_tgtlen value is
+ * preserved across successive window decodings, and the update to
+ * dec_winstart is delayed until a new window actually starts. This
+ * is to avoid throwing an error due to overflow until the last
+ * possible moment. This makes it possible to encode exactly 4GB
+ * through a 32-bit encoder. */
+static int
+xd3_decode_init_window (xd3_stream *stream)
+{
+ stream->dec_cpylen = 0;
+ stream->dec_cpyoff = 0;
+ stream->dec_cksumbytes = 0;
+
+ xd3_init_cache (& stream->acache);
+
+ return 0;
+}
+
+/* Allocates buffer space for the target window and possibly the
+ * VCD_TARGET copy-window. Also sets the base of the two copy
+ * segments. */
+static int
+xd3_decode_setup_buffers (xd3_stream *stream)
+{
+ /* If VCD_TARGET is set then the previous buffer may be reused. */
+ if (stream->dec_win_ind & VCD_TARGET)
+ {
+ /* But this implementation only supports copying from the last
+ * target window. If the offset is outside that range, it can't
+ * be done. */
+ if (stream->dec_cpyoff < stream->dec_laststart)
+ {
+ stream->msg = "unsupported VCD_TARGET offset";
+ return XD3_INVALID_INPUT;
+ }
+
+ /* See if the two windows are the same. This indicates the
+ * first time VCD_TARGET is used. This causes a second buffer
+ * to be allocated, after that the two are swapped in the
+ * DEC_FINISH case. */
+ if (stream->dec_lastwin == stream->next_out)
+ {
+ stream->next_out = NULL;
+ stream->space_out = 0;
+ }
+
+ // TODO: VCD_TARGET mode, this is broken
+ stream->dec_cpyaddrbase = stream->dec_lastwin +
+ (usize_t) (stream->dec_cpyoff - stream->dec_laststart);
+ }
+
+ /* See if the current output window is large enough. */
+ if (stream->space_out < stream->dec_tgtlen)
+ {
+ xd3_free (stream, stream->dec_buffer);
+
+ stream->space_out =
+ xd3_round_blksize (stream->dec_tgtlen, XD3_ALLOCSIZE);
+
+ if ((stream->dec_buffer =
+ (uint8_t*) xd3_alloc (stream, stream->space_out, 1)) == NULL)
+ {
+ return ENOMEM;
+ }
+
+ stream->next_out = stream->dec_buffer;
+ }
+
+ /* dec_tgtaddrbase refers to an invalid base address, but it is
+ * always used with a sufficiently large instruction offset (i.e.,
+ * beyond the copy window). This condition is enforced by
+ * xd3_decode_output_halfinst. */
+ stream->dec_tgtaddrbase = stream->next_out - stream->dec_cpylen;
+
+ return 0;
+}
+
+static int
+xd3_decode_allocate (xd3_stream *stream,
+ usize_t size,
+ uint8_t **buf_ptr,
+ usize_t *buf_alloc)
+{
+ if (*buf_ptr != NULL && *buf_alloc < size)
+ {
+ xd3_free (stream, *buf_ptr);
+ *buf_ptr = NULL;
+ }
+
+ if (*buf_ptr == NULL)
+ {
+ *buf_alloc = xd3_round_blksize (size, XD3_ALLOCSIZE);
+
+ if ((*buf_ptr = (uint8_t*) xd3_alloc (stream, *buf_alloc, 1)) == NULL)
+ {
+ return ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int
+xd3_decode_section (xd3_stream *stream,
+ xd3_desect *section,
+ xd3_decode_state nstate,
+ int copy)
+{
+ XD3_ASSERT (section->pos <= section->size);
+ XD3_ASSERT (stream->dec_state != nstate);
+
+ if (section->pos < section->size)
+ {
+ usize_t sect_take;
+
+ if (stream->avail_in == 0)
+ {
+ return XD3_INPUT;
+ }
+
+ if ((copy == 0) && (section->pos == 0))
+ {
+ /* No allocation/copy needed */
+ section->buf = stream->next_in;
+ sect_take = section->size;
+ }
+ else
+ {
+ usize_t sect_need = section->size - section->pos;
+
+ /* Allocate and copy */
+ sect_take = min (sect_need, stream->avail_in);
+
+ if (section->pos == 0)
+ {
+ int ret;
+
+ if ((ret = xd3_decode_allocate (stream,
+ section->size,
+ & section->copied1,
+ & section->alloc1)))
+ {
+ return ret;
+ }
+
+ section->buf = section->copied1;
+ }
+
+ memcpy (section->copied1 + section->pos,
+ stream->next_in,
+ sect_take);
+ }
+
+ section->pos += sect_take;
+
+ stream->dec_winbytes += sect_take;
+
+ DECODE_INPUT (sect_take);
+ }
+
+ if (section->pos < section->size)
+ {
+ stream->msg = "further input required";
+ return XD3_INPUT;
+ }
+
+ XD3_ASSERT (section->pos == section->size);
+
+ stream->dec_state = nstate;
+ section->buf_max = section->buf + section->size;
+ section->pos = 0;
+ return 0;
+}
+
+/* Decode the size and address for half of an instruction (i.e., a
+ * single opcode). This updates the stream->dec_position, which are
+ * bytes already output prior to processing this instruction. Perform
+ * bounds checking for sizes and copy addresses, which uses the
+ * dec_position (which is why these checks are done here). */
+static int
+xd3_decode_parse_halfinst (xd3_stream *stream, xd3_hinst *inst)
+{
+ int ret;
+
+ /* If the size from the instruction table is zero then read a size value. */
+ if ((inst->size == 0) &&
+ (ret = xd3_read_size (stream,
+ & stream->inst_sect.buf,
+ stream->inst_sect.buf_max,
+ & inst->size)))
+ {
+ return XD3_INVALID_INPUT;
+ }
+
+ /* For copy instructions, read address. */
+ if (inst->type >= XD3_CPY)
+ {
+ IF_DEBUG2 ({
+ static int cnt = 0;
+ DP(RINT "DECODE:%u: COPY at %"Q"u (winoffset %u) size %u winaddr %u\n",
+ cnt++,
+ stream->total_out + (stream->dec_position -
+ stream->dec_cpylen),
+ (stream->dec_position - stream->dec_cpylen),
+ inst->size,
+ inst->addr);
+ });
+
+ if ((ret = xd3_decode_address (stream,
+ stream->dec_position,
+ inst->type - XD3_CPY,
+ & stream->addr_sect.buf,
+ stream->addr_sect.buf_max,
+ & inst->addr)))
+ {
+ return ret;
+ }
+
+ /* Cannot copy an address before it is filled-in. */
+ if (inst->addr >= stream->dec_position)
+ {
+ stream->msg = "address too large";
+ return XD3_INVALID_INPUT;
+ }
+
+ /* Check: a VCD_TARGET or VCD_SOURCE copy cannot exceed the remaining
+ * buffer space in its own segment. */
+ if (inst->addr < stream->dec_cpylen &&
+ inst->addr + inst->size > stream->dec_cpylen)
+ {
+ stream->msg = "size too large";
+ return XD3_INVALID_INPUT;
+ }
+ }
+ else
+ {
+ IF_DEBUG2 ({
+ if (inst->type == XD3_ADD)
+ {
+ static int cnt;
+ DP(RINT "DECODE:%d: ADD at %"Q"u (winoffset %u) size %u\n",
+ cnt++,
+ (stream->total_out + stream->dec_position - stream->dec_cpylen),
+ stream->dec_position - stream->dec_cpylen,
+ inst->size);
+ }
+ else
+ {
+ static int cnt;
+ XD3_ASSERT (inst->type == XD3_RUN);
+ DP(RINT "DECODE:%d: RUN at %"Q"u (winoffset %u) size %u\n",
+ cnt++,
+ stream->total_out + stream->dec_position - stream->dec_cpylen,
+ stream->dec_position - stream->dec_cpylen,
+ inst->size);
+ }
+ });
+ }
+
+ /* Check: The instruction will not overflow the output buffer. */
+ if (stream->dec_position + inst->size > stream->dec_maxpos)
+ {
+ stream->msg = "size too large";
+ return XD3_INVALID_INPUT;
+ }
+
+ stream->dec_position += inst->size;
+ return 0;
+}
+
+/* Decode a single opcode and then decode the two half-instructions. */
+static int
+xd3_decode_instruction (xd3_stream *stream)
+{
+ int ret;
+ const xd3_dinst *inst;
+
+ if (stream->inst_sect.buf == stream->inst_sect.buf_max)
+ {
+ stream->msg = "instruction underflow";
+ return XD3_INVALID_INPUT;
+ }
+
+ inst = &stream->code_table[*stream->inst_sect.buf++];
+
+ stream->dec_current1.type = inst->type1;
+ stream->dec_current2.type = inst->type2;
+ stream->dec_current1.size = inst->size1;
+ stream->dec_current2.size = inst->size2;
+
+ /* For each instruction with a real operation, decode the
+ * corresponding size and addresses if necessary. Assume a
+ * code-table may have NOOP in either position, although this is
+ * unlikely. */
+ if (inst->type1 != XD3_NOOP &&
+ (ret = xd3_decode_parse_halfinst (stream, & stream->dec_current1)))
+ {
+ return ret;
+ }
+ if (inst->type2 != XD3_NOOP &&
+ (ret = xd3_decode_parse_halfinst (stream, & stream->dec_current2)))
+ {
+ return ret;
+ }
+ return 0;
+}
+
+/* Output the result of a single half-instruction. OPT: This the
+ decoder hotspot. */
+static int
+xd3_decode_output_halfinst (xd3_stream *stream, xd3_hinst *inst)
+{
+ /* To make this reentrant, set take = min (inst->size, available
+ space)... */
+ usize_t take = inst->size;
+
+ XD3_ASSERT (inst->type != XD3_NOOP);
+
+ switch (inst->type)
+ {
+ case XD3_RUN:
+ {
+ /* Only require a single data byte. */
+ if (stream->data_sect.buf == stream->data_sect.buf_max)
+ {
+ stream->msg = "data underflow";
+ return XD3_INVALID_INPUT;
+ }
+
+ memset (stream->next_out + stream->avail_out,
+ stream->data_sect.buf[0],
+ take);
+
+ stream->data_sect.buf += 1;
+ stream->avail_out += take;
+ inst->type = XD3_NOOP;
+ break;
+ }
+ case XD3_ADD:
+ {
+ /* Require at least TAKE data bytes. */
+ if (stream->data_sect.buf + take > stream->data_sect.buf_max)
+ {
+ stream->msg = "data underflow";
+ return XD3_INVALID_INPUT;
+ }
+
+ memcpy (stream->next_out + stream->avail_out,
+ stream->data_sect.buf,
+ take);
+
+ stream->data_sect.buf += take;
+ stream->avail_out += take;
+ inst->type = XD3_NOOP;
+ break;
+ }
+ default:
+ {
+ usize_t i;
+ const uint8_t *src;
+ uint8_t *dst;
+
+ /* See if it copies from the VCD_TARGET/VCD_SOURCE window or
+ * the target window. Out-of-bounds checks for the addresses
+ * and sizes are performed in xd3_decode_parse_halfinst. */
+ if (inst->addr < stream->dec_cpylen)
+ {
+ if (stream->dec_win_ind & VCD_TARGET)
+ {
+ /* For VCD_TARGET we know the entire range is
+ * in-memory, as established by
+ * decode_setup_buffers.
+ *
+ * TODO: this is totally bogus, VCD_TARGET won't work.
+ */
+ src = stream->dec_cpyaddrbase + inst->addr;
+ inst->type = XD3_NOOP;
+ inst->size = 0;
+ }
+ else
+ {
+ /* In this case we have to read a source block, which
+ * could return control to the caller. We need to
+ * know the first block number needed for this
+ * copy. */
+ xd3_source *source;
+ xoff_t block;
+ usize_t blkoff;
+ usize_t blksize;
+ int ret;
+
+ more:
+
+ source = stream->src;
+ block = source->cpyoff_blocks;
+ blkoff = source->cpyoff_blkoff + inst->addr;
+ blksize = source->blksize;
+
+ while (blkoff >= blksize)
+ {
+ block += 1;
+ blkoff -= blksize;
+ }
+
+ if ((ret = xd3_getblk (stream, block)))
+ {
+ /* could be a XD3_GETSRCBLK failure. */
+ if (ret == XD3_TOOFARBACK)
+ {
+ ret = XD3_INTERNAL;
+ }
+ return ret;
+ }
+
+ src = source->curblk + blkoff;
+
+ /* This block either contains enough data or the source file
+ * is short. */
+ if ((source->onblk != blksize) &&
+ (blkoff + take > source->onblk))
+ {
+ stream->msg = "source file too short";
+ return XD3_INVALID_INPUT;
+
+ }
+
+ XD3_ASSERT (blkoff != blksize);
+
+ if (blkoff + take <= blksize)
+ {
+ inst->type = XD3_NOOP;
+ inst->size = 0;
+ }
+ else
+ {
+ /* This block doesn't contain all the data, modify
+ * the instruction, do not set to XD3_NOOP. */
+ take = blksize - blkoff;
+ inst->size -= take;
+ inst->addr += take;
+ }
+ }
+ }
+ else
+ {
+ /* For a target-window copy, we know the entire range is
+ * in-memory. The dec_tgtaddrbase is negatively offset by
+ * dec_cpylen because the addresses start beyond that
+ * point. */
+ src = stream->dec_tgtaddrbase + inst->addr;
+ inst->type = XD3_NOOP;
+ inst->size = 0;
+ }
+
+ dst = stream->next_out + stream->avail_out;
+
+ stream->avail_out += take;
+
+ /* Can't just memcpy here due to possible overlap. */
+ for (i = take; i != 0; i -= 1)
+ {
+ *dst++ = *src++;
+ }
+
+ take = inst->size;
+
+ /* If there is more to copy, call getblk again. */
+ if (inst->type != XD3_NOOP)
+ {
+ XD3_ASSERT (take > 0);
+ goto more;
+ }
+ else
+ {
+ XD3_ASSERT (take == 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+xd3_decode_finish_window (xd3_stream *stream)
+{
+ stream->dec_winbytes = 0;
+ stream->dec_state = DEC_FINISH;
+
+ stream->data_sect.pos = 0;
+ stream->inst_sect.pos = 0;
+ stream->addr_sect.pos = 0;
+
+ return XD3_OUTPUT;
+}
+
+static int
+xd3_decode_secondary_sections (xd3_stream *secondary_stream)
+{
+#if SECONDARY_ANY
+ int ret;
+#define DECODE_SECONDARY_SECTION(UPPER,LOWER) \
+ ((secondary_stream->dec_del_ind & VCD_ ## UPPER ## COMP) && \
+ (ret = xd3_decode_secondary (secondary_stream, \
+ & secondary_stream-> LOWER ## _sect, \
+ & xd3_sec_ ## LOWER (secondary_stream))))
+
+ if (DECODE_SECONDARY_SECTION (DATA, data) ||
+ DECODE_SECONDARY_SECTION (INST, inst) ||
+ DECODE_SECONDARY_SECTION (ADDR, addr))
+ {
+ return ret;
+ }
+#undef DECODE_SECONDARY_SECTION
+#endif
+ return 0;
+}
+
+static int
+xd3_decode_sections (xd3_stream *stream)
+{
+ usize_t need, more, take;
+ int copy, ret;
+
+ if ((stream->flags & XD3_JUST_HDR) != 0)
+ {
+ /* Nothing left to do. */
+ return xd3_decode_finish_window (stream);
+ }
+
+ /* To avoid copying, need this much data available */
+ need = (stream->inst_sect.size +
+ stream->addr_sect.size +
+ stream->data_sect.size);
+
+ /* The window may be entirely processed. */
+ XD3_ASSERT (stream->dec_winbytes <= need);
+
+ /* Compute how much more input is needed. */
+ more = (need - stream->dec_winbytes);
+
+ /* How much to consume. */
+ take = min (more, stream->avail_in);
+
+ /* See if the input is completely available, to avoid copy. */
+ copy = (take != more);
+
+ /* If the window is skipped... */
+ if ((stream->flags & XD3_SKIP_WINDOW) != 0)
+ {
+ /* Skip the available input. */
+ DECODE_INPUT (take);
+
+ stream->dec_winbytes += take;
+
+ if (copy)
+ {
+ stream->msg = "further input required";
+ return XD3_INPUT;
+ }
+
+ return xd3_decode_finish_window (stream);
+ }
+
+ /* Process all but the DATA section. */
+ switch (stream->dec_state)
+ {
+ default:
+ stream->msg = "internal error";
+ return XD3_INVALID_INPUT;
+
+ case DEC_DATA:
+ if ((ret = xd3_decode_section (stream, & stream->data_sect,
+ DEC_INST, copy))) { return ret; }
+ case DEC_INST:
+ if ((ret = xd3_decode_section (stream, & stream->inst_sect,
+ DEC_ADDR, copy))) { return ret; }
+ case DEC_ADDR:
+ if ((ret = xd3_decode_section (stream, & stream->addr_sect,
+ DEC_EMIT, copy))) { return ret; }
+ }
+
+ XD3_ASSERT (stream->dec_winbytes == need);
+
+ if ((ret = xd3_decode_secondary_sections (stream))) { return ret; }
+
+ if (stream->flags & XD3_SKIP_EMIT)
+ {
+ return xd3_decode_finish_window (stream);
+ }
+
+ /* OPT: A possible optimization is to avoid allocating memory in
+ * decode_setup_buffers and to avoid a large memcpy when the window
+ * consists of a single VCD_SOURCE copy instruction. The only
+ * potential problem is if the following window is a VCD_TARGET,
+ * then you need to remember... */
+ if ((ret = xd3_decode_setup_buffers (stream))) { return ret; }
+
+ return 0;
+}
+
+static int
+xd3_decode_emit (xd3_stream *stream)
+{
+ int ret;
+
+ /* Produce output: originally structured to allow reentrant code
+ * that fills as much of the output buffer as possible, but VCDIFF
+ * semantics allows to copy from anywhere from the target window, so
+ * instead allocate a sufficiently sized buffer after the target
+ * window length is decoded.
+ *
+ * This code still needs to be reentrant to allow XD3_GETSRCBLK to
+ * return control. This is handled by setting the
+ * stream->dec_currentN instruction types to XD3_NOOP after they
+ * have been processed. */
+ XD3_ASSERT (! (stream->flags & XD3_SKIP_EMIT));
+ XD3_ASSERT (stream->dec_tgtlen <= stream->space_out);
+
+ while (stream->inst_sect.buf != stream->inst_sect.buf_max ||
+ stream->dec_current1.type != XD3_NOOP ||
+ stream->dec_current2.type != XD3_NOOP)
+ {
+ /* Decode next instruction pair. */
+ if ((stream->dec_current1.type == XD3_NOOP) &&
+ (stream->dec_current2.type == XD3_NOOP) &&
+ (ret = xd3_decode_instruction (stream))) { return ret; }
+
+ /* Output for each instruction. */
+ if ((stream->dec_current1.type != XD3_NOOP) &&
+ (ret = xd3_decode_output_halfinst (stream, & stream->dec_current1)))
+ {
+ return ret;
+ }
+
+ if ((stream->dec_current2.type != XD3_NOOP) &&
+ (ret = xd3_decode_output_halfinst (stream, & stream->dec_current2)))
+ {
+ return ret;
+ }
+ }
+
+ if (stream->avail_out != stream->dec_tgtlen)
+ {
+ IF_DEBUG1 (DP(RINT "AVAIL_OUT(%d) != DEC_TGTLEN(%d)\n",
+ stream->avail_out, stream->dec_tgtlen));
+ stream->msg = "wrong window length";
+ return XD3_INVALID_INPUT;
+ }
+
+ if (stream->data_sect.buf != stream->data_sect.buf_max)
+ {
+ stream->msg = "extra data section";
+ return XD3_INVALID_INPUT;
+ }
+
+ if (stream->addr_sect.buf != stream->addr_sect.buf_max)
+ {
+ stream->msg = "extra address section";
+ return XD3_INVALID_INPUT;
+ }
+
+ /* OPT: Should cksum computation be combined with the above loop? */
+ if ((stream->dec_win_ind & VCD_ADLER32) != 0 &&
+ (stream->flags & XD3_ADLER32_NOVER) == 0)
+ {
+ uint32_t a32 = adler32 (1L, stream->next_out, stream->avail_out);
+
+ if (a32 != stream->dec_adler32)
+ {
+ stream->msg = "target window checksum mismatch";
+ return XD3_INVALID_INPUT;
+ }
+ }
+
+ /* Finished with a window. */
+ return xd3_decode_finish_window (stream);
+}
+
+int
+xd3_decode_input (xd3_stream *stream)
+{
+ int ret;
+
+ if (stream->enc_state != 0)
+ {
+ stream->msg = "encoder/decoder transition";
+ return XD3_INVALID_INPUT;
+ }
+
+#define BYTE_CASE(expr,x,nstate) \
+ do { \
+ if ( (expr) && \
+ ((ret = xd3_decode_byte (stream, & (x))) != 0) ) { return ret; } \
+ stream->dec_state = (nstate); \
+ } while (0)
+
+#define OFFSET_CASE(expr,x,nstate) \
+ do { \
+ if ( (expr) && \
+ ((ret = xd3_decode_offset (stream, & (x))) != 0) ) { return ret; } \
+ stream->dec_state = (nstate); \
+ } while (0)
+
+#define SIZE_CASE(expr,x,nstate) \
+ do { \
+ if ( (expr) && \
+ ((ret = xd3_decode_size (stream, & (x))) != 0) ) { return ret; } \
+ stream->dec_state = (nstate); \
+ } while (0)
+
+ switch (stream->dec_state)
+ {
+ case DEC_VCHEAD:
+ {
+ if ((ret = xd3_decode_bytes (stream, stream->dec_magic,
+ & stream->dec_magicbytes, 4)))
+ {
+ return ret;
+ }
+
+ if (stream->dec_magic[0] != VCDIFF_MAGIC1 ||
+ stream->dec_magic[1] != VCDIFF_MAGIC2 ||
+ stream->dec_magic[2] != VCDIFF_MAGIC3)
+ {
+ stream->msg = "not a VCDIFF input";
+ return XD3_INVALID_INPUT;
+ }
+
+ if (stream->dec_magic[3] != 0)
+ {
+ stream->msg = "VCDIFF input version > 0 is not supported";
+ return XD3_INVALID_INPUT;
+ }
+
+ stream->dec_state = DEC_HDRIND;
+ }
+ case DEC_HDRIND:
+ {
+ if ((ret = xd3_decode_byte (stream, & stream->dec_hdr_ind)))
+ {
+ return ret;
+ }
+
+ if ((stream->dec_hdr_ind & VCD_INVHDR) != 0)
+ {
+ stream->msg = "unrecognized header indicator bits set";
+ return XD3_INVALID_INPUT;
+ }
+
+ stream->dec_state = DEC_SECONDID;
+ }
+
+ case DEC_SECONDID:
+ /* Secondary compressor ID: only if VCD_SECONDARY is set */
+ if ((stream->dec_hdr_ind & VCD_SECONDARY) != 0)
+ {
+ BYTE_CASE (1, stream->dec_secondid, DEC_TABLEN);
+
+ switch (stream->dec_secondid)
+ {
+ case VCD_FGK_ID:
+ FGK_CASE (stream);
+ case VCD_DJW_ID:
+ DJW_CASE (stream);
+ default:
+ stream->msg = "unknown secondary compressor ID";
+ return XD3_INVALID_INPUT;
+ }
+ }
+
+ case DEC_TABLEN:
+ /* Length of code table data: only if VCD_CODETABLE is set */
+ SIZE_CASE ((stream->dec_hdr_ind & VCD_CODETABLE) != 0,
+ stream->dec_codetblsz, DEC_NEAR);
+
+ /* The codetblsz counts the two NEAR/SAME bytes */
+ if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0) {
+ if (stream->dec_codetblsz <= 2) {
+ stream->msg = "invalid code table size";
+ return ENOMEM;
+ }
+ stream->dec_codetblsz -= 2;
+ }
+ case DEC_NEAR:
+ /* Near modes: only if VCD_CODETABLE is set */
+ BYTE_CASE((stream->dec_hdr_ind & VCD_CODETABLE) != 0,
+ stream->acache.s_near, DEC_SAME);
+ case DEC_SAME:
+ /* Same modes: only if VCD_CODETABLE is set */
+ BYTE_CASE((stream->dec_hdr_ind & VCD_CODETABLE) != 0,
+ stream->acache.s_same, DEC_TABDAT);
+ case DEC_TABDAT:
+ /* Compressed code table data */
+
+ if ((stream->dec_hdr_ind & VCD_CODETABLE) != 0)
+ {
+ /* Get the code table data. */
+ if ((stream->dec_codetbl == NULL) &&
+ (stream->dec_codetbl =
+ (uint8_t*) xd3_alloc (stream,
+ stream->dec_codetblsz, 1)) == NULL)
+ {
+ return ENOMEM;
+ }
+
+ if ((ret = xd3_decode_bytes (stream, stream->dec_codetbl,
+ & stream->dec_codetblbytes,
+ stream->dec_codetblsz)))
+ {
+ return ret;
+ }
+
+ if ((ret = xd3_apply_table_encoding (stream, stream->dec_codetbl,
+ stream->dec_codetblbytes)))
+ {
+ return ret;
+ }
+ }
+ else
+ {
+ /* Use the default table. */
+ stream->acache.s_near = __rfc3284_code_table_desc.near_modes;
+ stream->acache.s_same = __rfc3284_code_table_desc.same_modes;
+ stream->code_table = xd3_rfc3284_code_table ();
+ }
+
+ if ((ret = xd3_alloc_cache (stream))) { return ret; }
+
+ stream->dec_state = DEC_APPLEN;
+
+ case DEC_APPLEN:
+ /* Length of application data */
+ SIZE_CASE((stream->dec_hdr_ind & VCD_APPHEADER) != 0,
+ stream->dec_appheadsz, DEC_APPDAT);
+
+ case DEC_APPDAT:
+ /* Application data */
+ if (stream->dec_hdr_ind & VCD_APPHEADER)
+ {
+ /* Note: we add an additional byte for padding, to allow
+ 0-termination. */
+ if ((stream->dec_appheader == NULL) &&
+ (stream->dec_appheader =
+ (uint8_t*) xd3_alloc (stream,
+ stream->dec_appheadsz+1, 1)) == NULL)
+ {
+ return ENOMEM;
+ }
+
+ stream->dec_appheader[stream->dec_appheadsz] = 0;
+
+ if ((ret = xd3_decode_bytes (stream, stream->dec_appheader,
+ & stream->dec_appheadbytes,
+ stream->dec_appheadsz)))
+ {
+ return ret;
+ }
+ }
+
+ stream->dec_hdrsize = stream->total_in;
+ stream->dec_state = DEC_WININD;
+
+ case DEC_WININD:
+ {
+ /* Start of a window: the window indicator */
+ if ((ret = xd3_decode_byte (stream, & stream->dec_win_ind)))
+ {
+ return ret;
+ }
+
+ stream->current_window = stream->dec_window_count;
+
+ if (XOFF_T_OVERFLOW (stream->dec_winstart, stream->dec_tgtlen))
+ {
+ stream->msg = "decoder file offset overflow";
+ return XD3_INVALID_INPUT;
+ }
+
+ stream->dec_winstart += stream->dec_tgtlen;
+
+ if ((stream->dec_win_ind & VCD_INVWIN) != 0)
+ {
+ stream->msg = "unrecognized window indicator bits set";
+ return XD3_INVALID_INPUT;
+ }
+
+ if ((ret = xd3_decode_init_window (stream))) { return ret; }
+
+ stream->dec_state = DEC_CPYLEN;
+
+ IF_DEBUG1 (DP(RINT "--------- TARGET WINDOW %"Q"u -----------\n",
+ stream->current_window));
+ }
+
+ case DEC_CPYLEN:
+ /* Copy window length: only if VCD_SOURCE or VCD_TARGET is set */
+ SIZE_CASE(SRCORTGT (stream->dec_win_ind), stream->dec_cpylen,
+ DEC_CPYOFF);
+
+ /* Set the initial, logical decoder position (HERE address) in
+ * dec_position. This is set to just after the source/copy
+ * window, as we are just about to output the first byte of
+ * target window. */
+ stream->dec_position = stream->dec_cpylen;
+
+ case DEC_CPYOFF:
+ /* Copy window offset: only if VCD_SOURCE or VCD_TARGET is set */
+ OFFSET_CASE(SRCORTGT (stream->dec_win_ind), stream->dec_cpyoff,
+ DEC_ENCLEN);
+
+ /* Copy offset and copy length may not overflow. */
+ if (XOFF_T_OVERFLOW (stream->dec_cpyoff, stream->dec_cpylen))
+ {
+ stream->msg = "decoder copy window overflows a file offset";
+ return XD3_INVALID_INPUT;
+ }
+
+ /* Check copy window bounds: VCD_TARGET window may not exceed
+ current position. */
+ if ((stream->dec_win_ind & VCD_TARGET) &&
+ (stream->dec_cpyoff + (xoff_t) stream->dec_cpylen >
+ stream->dec_winstart))
+ {
+ stream->msg = "VCD_TARGET window out of bounds";
+ return XD3_INVALID_INPUT;
+ }
+
+ case DEC_ENCLEN:
+ /* Length of the delta encoding */
+ SIZE_CASE(1, stream->dec_enclen, DEC_TGTLEN);
+ case DEC_TGTLEN:
+ /* Length of target window */
+ SIZE_CASE(1, stream->dec_tgtlen, DEC_DELIND);
+
+ /* Set the maximum decoder position, beyond which we should not
+ * decode any data. This is the maximum value for dec_position.
+ * This may not exceed the size of a usize_t. */
+ if (USIZE_T_OVERFLOW (stream->dec_cpylen, stream->dec_tgtlen))
+ {
+ stream->msg = "decoder target window overflows a usize_t";
+ return XD3_INVALID_INPUT;
+ }
+
+ /* Check for malicious files. */
+ if (stream->dec_tgtlen > XD3_HARDMAXWINSIZE)
+ {
+ stream->msg = "hard window size exceeded";
+ return XD3_INVALID_INPUT;
+ }
+
+ stream->dec_maxpos = stream->dec_cpylen + stream->dec_tgtlen;
+
+ case DEC_DELIND:
+ /* Delta indicator */
+ BYTE_CASE(1, stream->dec_del_ind, DEC_DATALEN);
+
+ if ((stream->dec_del_ind & VCD_INVDEL) != 0)
+ {
+ stream->msg = "unrecognized delta indicator bits set";
+ return XD3_INVALID_INPUT;
+ }
+
+ /* Delta indicator is only used with secondary compression. */
+ if ((stream->dec_del_ind != 0) && (stream->sec_type == NULL))
+ {
+ stream->msg = "invalid delta indicator bits set";
+ return XD3_INVALID_INPUT;
+ }
+
+ /* Section lengths */
+ case DEC_DATALEN:
+ SIZE_CASE(1, stream->data_sect.size, DEC_INSTLEN);
+ case DEC_INSTLEN:
+ SIZE_CASE(1, stream->inst_sect.size, DEC_ADDRLEN);
+ case DEC_ADDRLEN:
+ SIZE_CASE(1, stream->addr_sect.size, DEC_CKSUM);
+
+ case DEC_CKSUM:
+ /* Window checksum. */
+ if ((stream->dec_win_ind & VCD_ADLER32) != 0)
+ {
+ int i;
+
+ if ((ret = xd3_decode_bytes (stream, stream->dec_cksum,
+ & stream->dec_cksumbytes, 4)))
+ {
+ return ret;
+ }
+
+ for (i = 0; i < 4; i += 1)
+ {
+ stream->dec_adler32 =
+ (stream->dec_adler32 << 8) | stream->dec_cksum[i];
+ }
+ }
+
+ stream->dec_state = DEC_DATA;
+
+ /* Check dec_enclen for redundency, otherwise it is not really used. */
+ {
+ usize_t enclen_check =
+ (1 + (xd3_sizeof_size (stream->dec_tgtlen) +
+ xd3_sizeof_size (stream->data_sect.size) +
+ xd3_sizeof_size (stream->inst_sect.size) +
+ xd3_sizeof_size (stream->addr_sect.size)) +
+ stream->data_sect.size +
+ stream->inst_sect.size +
+ stream->addr_sect.size +
+ ((stream->dec_win_ind & VCD_ADLER32) ? 4 : 0));
+
+ if (stream->dec_enclen != enclen_check)
+ {
+ stream->msg = "incorrect encoding length (redundent)";
+ return XD3_INVALID_INPUT;
+ }
+ }
+
+ /* Returning here gives the application a chance to inspect the
+ * header, skip the window, etc. */
+ if (stream->current_window == 0) { return XD3_GOTHEADER; }
+ else { return XD3_WINSTART; }
+
+ case DEC_DATA:
+ case DEC_INST:
+ case DEC_ADDR:
+ /* Next read the three sections. */
+ if ((ret = xd3_decode_sections (stream))) { return ret; }
+
+ case DEC_EMIT:
+
+ /* To speed VCD_SOURCE block-address calculations, the source
+ * cpyoff_blocks and cpyoff_blkoff are pre-computed. */
+ if (stream->dec_win_ind & VCD_SOURCE)
+ {
+ xd3_source *src = stream->src;
+
+ if (src == NULL)
+ {
+ stream->msg = "source input required";
+ return XD3_INVALID_INPUT;
+ }
+
+ xd3_blksize_div(stream->dec_cpyoff, src,
+ &src->cpyoff_blocks,
+ &src->cpyoff_blkoff);
+ }
+
+ /* xd3_decode_emit returns XD3_OUTPUT on every success. */
+ if ((ret = xd3_decode_emit (stream)) == XD3_OUTPUT)
+ {
+ stream->total_out += (xoff_t) stream->avail_out;
+ }
+
+ return ret;
+
+ case DEC_FINISH:
+ {
+ if (stream->dec_win_ind & VCD_TARGET)
+ {
+ if (stream->dec_lastwin == NULL)
+ {
+ stream->dec_lastwin = stream->next_out;
+ stream->dec_lastspace = stream->space_out;
+ }
+ else
+ {
+ xd3_swap_uint8p (& stream->dec_lastwin,
+ & stream->next_out);
+ xd3_swap_usize_t (& stream->dec_lastspace,
+ & stream->space_out);
+ }
+ }
+
+ stream->dec_lastlen = stream->dec_tgtlen;
+ stream->dec_laststart = stream->dec_winstart;
+ stream->dec_window_count += 1;
+
+ /* Note: the updates to dec_winstart & current_window are
+ * deferred until after the next DEC_WININD byte is read. */
+ stream->dec_state = DEC_WININD;
+ return XD3_WINFINISH;
+ }
+
+ default:
+ stream->msg = "invalid state";
+ return XD3_INVALID_INPUT;
+ }
+}
+
+#endif // _XDELTA3_DECODE_H_