/* Copyright (C) 1990, 1996, 1998, 1999 artofcode LLC.  All rights reserved.
  
  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.

*/

/*$Id: ibnum.c,v 1.2.6.1.2.1 2003/01/17 00:49:04 giles Exp $ */
/* Level 2 encoded number reading utilities for Ghostscript */
#include "math_.h"
#include "memory_.h"
#include "ghost.h"
#include "errors.h"
#include "stream.h"
#include "ibnum.h"
#include "imemory.h"		/* for iutil.h */
#include "iutil.h"

/* Define the number of bytes for a given format of encoded number. */
const byte enc_num_bytes[] = {
    enc_num_bytes_values
};

/* ------ Encoded number reading ------ */

/* Set up to read from an encoded number array/string. */
/* Return <0 for error, or a number format. */
int
num_array_format(const ref * op)
{
    switch (r_type(op)) {
	case t_string:
	    {
		/* Check that this is a legitimate encoded number string. */
		const byte *bp = op->value.bytes;
		int format;

		if (r_size(op) < 4 || bp[0] != bt_num_array_value)
		    return_error(e_rangecheck);
		format = bp[1];
		if (!num_is_valid(format) ||
		    sdecodeshort(bp + 2, format) !=
		    (r_size(op) - 4) / encoded_number_bytes(format)
		    )
		    return_error(e_rangecheck);
		return format;
	    }
	case t_array:
	case t_mixedarray:
	case t_shortarray:
	    return num_array;
	default:
	    return_error(e_typecheck);
    }
}

/* Get the number of elements in an encoded number array/string. */
uint
num_array_size(const ref * op, int format)
{
    return (format == num_array ? r_size(op) :
	    (r_size(op) - 4) / encoded_number_bytes(format));
}

/* Get an encoded number from an array/string according to the given format. */
/* Put the value in np->value.{intval,realval}. */
/* Return t_int if integer, t_real if real, t_null if end of stream, */
/* or an error if the format is invalid. */
int
num_array_get(const ref * op, int format, uint index, ref * np)
{
    if (format == num_array) {
	int code = array_get(op, (long)index, np);

	if (code < 0)
	    return t_null;
	switch (r_type(np)) {
	    case t_integer:
		return t_integer;
	    case t_real:
		return t_real;
	    default:
		return_error(e_rangecheck);
	}
    } else {
	uint nbytes = encoded_number_bytes(format);

	if (index >= (r_size(op) - 4) / nbytes)
	    return t_null;
	return sdecode_number(op->value.bytes + 4 + index * nbytes,
			      format, np);
    }
}

/* Internal routine to decode a number in a given format. */
/* Same returns as sget_encoded_number. */
static const double binary_scale[32] = {
#define EXPN2(n) (0.5 / (1L << (n-1)))
    1.0, EXPN2(1), EXPN2(2), EXPN2(3),
    EXPN2(4), EXPN2(5), EXPN2(6), EXPN2(7),
    EXPN2(8), EXPN2(9), EXPN2(10), EXPN2(11),
    EXPN2(12), EXPN2(13), EXPN2(14), EXPN2(15),
    EXPN2(16), EXPN2(17), EXPN2(18), EXPN2(19),
    EXPN2(20), EXPN2(21), EXPN2(22), EXPN2(23),
    EXPN2(24), EXPN2(25), EXPN2(26), EXPN2(27),
    EXPN2(28), EXPN2(29), EXPN2(30), EXPN2(31)
#undef EXPN2
};
int
sdecode_number(const byte * str, int format, ref * np)
{
    switch (format & 0x170) {
	case num_int32:
	case num_int32 + 16:
	    if ((format & 31) == 0) {
		np->value.intval = sdecodelong(str, format);
		return t_integer;
	    } else {
		np->value.realval =
		    (double)sdecodelong(str, format) *
		    binary_scale[format & 31];
		return t_real;
	    }
	case num_int16:
	    if ((format & 15) == 0) {
		np->value.intval = sdecodeshort(str, format);
		return t_integer;
	    } else {
		np->value.realval =
		    sdecodeshort(str, format) *
		    binary_scale[format & 15];
		return t_real;
	    }
	case num_float:
	    np->value.realval = sdecodefloat(str, format);
	    return t_real;
	default:
	    return_error(e_syntaxerror);	/* invalid format?? */
    }
}

/* ------ Decode number ------ */

/* Decode encoded numbers from a string according to format. */

/* Decode a (16-bit, signed or unsigned) short. */
uint
sdecodeushort(const byte * p, int format)
{
    int a = p[0], b = p[1];

    return (num_is_lsb(format) ? (b << 8) + a : (a << 8) + b);
}
int
sdecodeshort(const byte * p, int format)
{
    int v = (int)sdecodeushort(p, format);

    return (v & 0x7fff) - (v & 0x8000);
}

/* Decode a (32-bit, signed) long. */
long
sdecodelong(const byte * p, int format)
{
    int a = p[0], b = p[1], c = p[2], d = p[3];
    long v = (num_is_lsb(format) ?
	      ((long)d << 24) + ((long)c << 16) + (b << 8) + a :
	      ((long)a << 24) + ((long)b << 16) + (c << 8) + d);

#if arch_sizeof_long > 4
    /* Propagate bit 31 as the sign. */
    v = (v ^ 0x80000000L) - 0x80000000L;
#endif
    return v;
}

/* Decode a float.  We assume that native floats occupy 32 bits. */
float
sdecodefloat(const byte * p, int format)
{
    bits32 lnum = (bits32) sdecodelong(p, format);
    float fnum;

#if !arch_floats_are_IEEE
    if (format != num_float_native) {
	/* We know IEEE floats take 32 bits. */
	/* Convert IEEE float to native float. */
	int sign_expt = lnum >> 23;
	int expt = sign_expt & 0xff;
	long mant = lnum & 0x7fffff;

	if (expt == 0 && mant == 0)
	    fnum = 0;
	else {
	    mant += 0x800000;
	    fnum = (float)ldexp((float)mant, expt - 127 - 23);
	}
	if (sign_expt & 0x100)
	    fnum = -fnum;
    } else
#endif
	fnum = *(float *)&lnum;
    return fnum;
}
