/*
 * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

struct unpacker;

#define INT_MAX_VALUE ((int)0x7FFFFFFF)
#define INT_MIN_VALUE ((int)0x80000000)

#define CODING_SPEC(B, H, S, D) ((B) << 20 | (H) << 8 | (S) << 4 | (D) << 0)
#define CODING_B(x) ((x) >> 20 & 0xF)
#define CODING_H(x) ((x) >> 8 & 0xFFF)
#define CODING_S(x) ((x) >> 4 & 0xF)
#define CODING_D(x) ((x) >> 0 & 0xF)

#define CODING_INIT(B, H, S, D)                                                                \
	{                                                                                          \
		CODING_SPEC(B, H, S, D), 0, 0, 0, 0, 0, 0, 0, 0                                        \
	}

// For debugging purposes, some compilers do not like this and will complain.
//    #define long do_not_use_C_long_types_use_jlong_or_int
// Use of the type "long" is problematic, do not use it.

struct coding
{
	int spec; // B,H,S,D

	// Handy values derived from the spec:
	int B()
	{
		return CODING_B(spec);
	}
	int H()
	{
		return CODING_H(spec);
	}
	int S()
	{
		return CODING_S(spec);
	}
	int D()
	{
		return CODING_D(spec);
	}
	int L()
	{
		return 256 - CODING_H(spec);
	}
	int min, max;
	int umin, umax;
	char isSigned, isSubrange, isFullRange, isMalloc;

	coding *init(); // returns self or nullptr if error
	coding *initFrom(int spec_)
	{
		assert(this->spec == 0);
		this->spec = spec_;
		return init();
	}

	static coding *findBySpec(int spec);
	static coding *findBySpec(int B, int H, int S = 0, int D = 0);
	static coding *findByIndex(int irregularCodingIndex);

	static uint32_t parse(byte *&rp, int B, int H);
	static uint32_t parse_lgH(byte *&rp, int B, int H, int lgH);
	static void parseMultiple(byte *&rp, int N, byte *limit, int B, int H);

	uint32_t parse(byte *&rp)
	{
		return parse(rp, CODING_B(spec), CODING_H(spec));
	}
	void parseMultiple(byte *&rp, int N, byte *limit)
	{
		parseMultiple(rp, N, limit, CODING_B(spec), CODING_H(spec));
	}

	bool canRepresent(int x)
	{
		return (x >= min && x <= max);
	}
	bool canRepresentUnsigned(int x)
	{
		return (x >= umin && x <= umax);
	}

	int sumInUnsignedRange(int x, int y);

	int readFrom(byte *&rpVar, int *dbase);
	void readArrayFrom(byte *&rpVar, int *dbase, int length, int *values);
	void skipArrayFrom(byte *&rpVar, int length)
	{
		readArrayFrom(rpVar, (int *)NULL, length, (int *)NULL);
	}

	void free(); // free self if isMalloc
};

enum coding_method_kind
{
	cmk_ERROR,
	cmk_BHS,
	cmk_BHS0,
	cmk_BHS1,
	cmk_BHSD1,
	cmk_BHS1D1full, // isFullRange
	cmk_BHS1D1sub,  // isSubRange

	// special cases hand-optimized (~50% of all decoded values)
	cmk_BYTE1,	 //(1,256)      6%
	cmk_CHAR3,	 //(3,128)      7%
	cmk_UNSIGNED5, //(5,64)      13%
	cmk_DELTA5,	//(5,64,1,1)   5%
	cmk_BCI5,	  //(5,4)       18%
	cmk_BRANCH5,   //(5,4,2)      4%
				   // cmk_UNSIGNED5H16,  //(5,16)       5%
				   // cmk_UNSIGNED2H4,   //(2,4)        6%
				   // cmk_DELTA4H8,      //(4,8,1,1)   10%
	// cmk_DELTA3H16,     //(3,16,1,1)   9%
	cmk_BHS_LIMIT,
	cmk_pop,
	cmk_pop_BHS0,
	cmk_pop_BYTE1,
	cmk_pop_LIMIT,
	cmk_LIMIT
};

enum
{
	BYTE1_spec = CODING_SPEC(1, 256, 0, 0),
	CHAR3_spec = CODING_SPEC(3, 128, 0, 0),
	UNSIGNED4_spec = CODING_SPEC(4, 256, 0, 0),
	UNSIGNED5_spec = CODING_SPEC(5, 64, 0, 0),
	SIGNED5_spec = CODING_SPEC(5, 64, 1, 0),
	DELTA5_spec = CODING_SPEC(5, 64, 1, 1),
	UDELTA5_spec = CODING_SPEC(5, 64, 0, 1),
	MDELTA5_spec = CODING_SPEC(5, 64, 2, 1),
	BCI5_spec = CODING_SPEC(5, 4, 0, 0),
	BRANCH5_spec = CODING_SPEC(5, 4, 2, 0)
};

enum
{
	B_MAX = 5,
	C_SLOP = B_MAX * 10
};

struct coding_method;

// iterator under the control of a meta-coding
struct value_stream
{
	// current coding of values or values
	coding c;			   // B,H,S,D,etc.
	coding_method_kind cmk; // type of decoding needed
	byte *rp;			   // read pointer
	byte *rplimit;		  // final value of read pointer
	int sum;				// partial sum of all values so far (D=1 only)
	coding_method *cm;	  // coding method that defines this stream

	void init(byte *band_rp, byte *band_limit, coding *defc);
	void init(byte *band_rp, byte *band_limit, int spec)
	{
		init(band_rp, band_limit, coding::findBySpec(spec));
	}

	void setCoding(coding *c);
	void setCoding(int spec)
	{
		setCoding(coding::findBySpec(spec));
	}

	// Parse and decode a single value.
	int getInt();

	// Parse and decode a single byte, with no error checks.
	int getByte()
	{
		assert(cmk == cmk_BYTE1);
		assert(rp < rplimit);
		return *rp++ & 0xFF;
	}

	// Used only for asserts.
	bool hasValue();

	void done()
	{
		assert(!hasValue());
	}

	// Sometimes a value stream has an auxiliary (but there are never two).
	value_stream *helper()
	{
		assert(hasHelper());
		return this + 1;
	}
	bool hasHelper();
};

struct coding_method
{
	value_stream vs0; // initial state snapshot (vs.meta==this)

	coding_method *next; // what to do when we run out of bytes

	// these fields are used for pop codes only:
	int *fValues;		   // favored value array
	int fVlength;		   // maximum favored value token
	coding_method *uValues; // unfavored value stream

	// pointer to outer unpacker, for error checks etc.
	unpacker *u;

	// Initialize a value stream.
	void reset(value_stream *state);

	// Parse a band header, size a band, and initialize for further action.
	// band_rp advances (but not past band_limit), and meta_rp advances.
	// The mode gives context, such as "inside a pop".
	// The defc and N are the incoming parameters to a meta-coding.
	// The value sink is used to collect output values, when desired.
	void init(byte *&band_rp, byte *band_limit, byte *&meta_rp, int mode, coding *defc, int N,
			  intlist *valueSink);
};