/*
 * Copyright (c) 1995, 1996, 1997, 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick 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.
 *
 * Flick 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 Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <string.h>
#include <assert.h>

#include <mom/compiler.h>
#include <mom/libmint.h>
#include <mom/c/libcast.h>
#include <mom/c/libpres_c.h>
#include <mom/c/pbe.hh>

#include "data_types.hh"
#include "hash_const.hh"

/* This is a hash function of sorts.
   It takes a list of possibilities (the values to hash),
   and creates code that will assign var
   the index of the value encountered.
   Example:
   "operation1", "operation2", or "operation3"
   are the constants we wish to switch on.
   Since you can't switch on a string,
   we have to build a variable to switch on.
   
   switch (str[0]) {
   case 'o':
       switch (str[1]) {
       case 'p':
       ...
           switch (str[9]) {
	   case '1':
	      var = 0;
	      break;
	   case '2':
	      var = 1;
	      break;
	   case '3':
	      var = 2;
	      break;
	   default:
	      var = -1;
	   }
       default:
           var = -1;
       }
   default:
       var = -1;
   }
   
   Now we can switch on var...
   This code should build the 'hash' function
   */

/*****************************************************************************/

/* Previously, the `hash' method was designed only to hash complex values into
   a range of integer "cookie" values.  The code determined the cookie values
   for itself.
   
   But now, the code has been modified to use the "functor" mechanism.  Each
   value in the input domain is associated with a functor, which is expected to
   build the C code that will be executed when the corresponding value is
   recognized.
   
   The `hash_const_cookie_functor' is a functor that replicates the old
   "cookie" behavior.  When one of these functors is executed, it outputs an
   assignment statement that stores the cookie value into the cookie variable.
   
   Soon, however, this class (and the code that uses it) is expected to become
   obsolete.  The reason for cookifying our values is so that subsequent code
   can switch on the cookies.  But now, that subsequent code can be inlined
   directly into the `switch' generated by `hash()'.  So there's no reason to
   generate cookies at all.
   */

struct hash_const_cookie_functor : public functor {
	hash_const_cookie_functor(cast_expr var, int val) :
		variable(var), value(val) {}
	
	virtual void func(mu_state *must);
	
	cast_expr variable;
	int value;
};

void
hash_const_cookie_functor::func(mu_state *must)
{
	must->add_stmt(cast_new_stmt_expr(
		cast_new_expr_assign(variable,
				     cast_new_expr_lit_int(value,
							   CAST_MOD_SIGNED)
			)
		));
}


/*****************************************************************************/

/* Encodes 4 chars into a big-endian unsigned 32 bit int. */
static unsigned int
FourCharsTo32UInt(char c1, char c2, char c3, char c4)
{
	return (((((unsigned int)c1 << 8) + c2) << 8) + c3 << 8) + c4;
}

/* Encodes 2 chars into a big-endian unsigned 16 bit int. */
static unsigned short int
TwoCharsToUShort(char c1, char c2)
{
	return ((unsigned short int)c1 << 8) + c2;
}

/* Decodes a big-endian unsigned 32 bit int into a string of 4 chars. */
static char *
UInt32ToFourChars(unsigned int x)
{
	static char flick_case_InttoCharResult[5];
	
#if 0
	/* -O2 can wrongly optimize this to execute backwards! */
	for (int i = 0; i <= 3; i++) {
		flick_case_InttoCharResult[3 - i] = x & 0xFF;
		x = x >> 8;
	}
#else
	flick_case_InttoCharResult[0] = (x & 0xFF000000) >> 24;
	flick_case_InttoCharResult[1] = (x & 0x00FF0000) >> 16;
	flick_case_InttoCharResult[2] = (x & 0x0000FF00) >> 8;
	flick_case_InttoCharResult[3] = (x & 0x000000FF);
#endif
	flick_case_InttoCharResult[4] = 0;
	return flick_case_InttoCharResult;
}

/* Decodes a big-endian unsigned 16 bit int into a string of 2 chars. */
static char *
UShortToTwoChars(unsigned short int x)
{
	static char flick_case_InttoCharResult[3];
	
	flick_case_InttoCharResult[0] = x >> 8;
	flick_case_InttoCharResult[1] = x & 0xFF;
	flick_case_InttoCharResult[2] = 0;
	return flick_case_InttoCharResult;
}

data_type *
hash_const::get_optimized_domain(unsigned int *mostpackable)
{
	unsigned int i, j;
	
	*mostpackable = must->get_most_packable();
	
	if (*mostpackable >= 2) {
		// Find 2 or 4 consecutive chars for optimizing into `int' or
		// `short'.
		for (i = 0; i < (unsigned int) count; i++) {
			if ((domain[i][0].type
			     != data_field::TYPE_CHAR)
			    || (domain[i][0].category
				!= data_field::TYPE_LITERAL)) {
				*mostpackable = 0;
				break;
			}
			for (j = 1;
			     ((j < *mostpackable)
			      && (j < (unsigned int)domain[i].count()));
			     j++)
				if (domain[i][j].type != data_field::TYPE_CHAR)
					break; // quit the for j loop
			if (j < *mostpackable)
				*mostpackable = j;
		}
		if (*mostpackable >= 2) {
			// Optimization will be used.
			data_type *optimal_dom
				= new data_type[(unsigned int)count];
			
			if (*mostpackable >= 4) {
				*mostpackable = 4;
				// Found 4 packed chars.  Optimize to be a 4
				// byte int.
				for (i = 0; i < (unsigned int)count; i++) {
					optimal_dom[i]
						= data_type(
							*new data_field(
								FourCharsTo32UInt(
									domain[i][0].u.c,
									domain[i][1].u.c,
									domain[i][2].u.c,
									domain[i][3].u.c
									)));
					assert(optimal_dom[i][0].category
					       == data_field::TYPE_LITERAL);
					for (j = 4;
					     (j < ((unsigned int)
						   domain[i].count()));
					     j++)
						optimal_dom[i]
							= (optimal_dom[i]
							   + domain[i][j]);
				}
				
			} else {
				*mostpackable = 2;
				// Found 2 packed chars.  Optimize to be a 2
				// byte short.
				for (i = 0; i < (unsigned int) count; i++) {
					optimal_dom[i]
						= data_type(
							*new data_field(
								TwoCharsToUShort(
									domain[i][0].u.c,
									domain[i][1].u.c
									)));
					assert(optimal_dom[i][0].category
					       == data_field::TYPE_LITERAL);
					for (j = 2;
					     (j < ((unsigned int)
						   domain[i].count()));
					     j++)
						optimal_dom[i]
							= (optimal_dom[i]
							   + domain[i][j]);
				}
			}
			return optimal_dom;
		}
	}
	// No optimization possible.
	*mostpackable = 1;
	return domain;
}

// This creates a temporary variable to switch upon (switch_type, switch_var).
void
hash_const::create_switch_var(const data_field &t, mint_ref *itype)
{
	assert(itype);
	switch (t.type) {
	case data_field::TYPE_INT:
		switch_type = cast_new_prim_type(CAST_PRIM_INT,
						 CAST_MOD_SIGNED);
		switch_var = must->add_temp_var("switch_tag",
						switch_type,
						0,
						CAST_SC_AUTO);
		*itype = must->pres->mint.standard_refs.signed32_ref;
		break;
		
	case data_field::TYPE_UINT:
		switch_type = cast_new_prim_type(CAST_PRIM_INT,
						 CAST_MOD_UNSIGNED);
		switch_var = must->add_temp_var("switch_tag",
						switch_type,
						0,
						CAST_SC_AUTO);
		*itype = must->pres->mint.standard_refs.unsigned32_ref;
		break;
		
	case data_field::TYPE_USHORT:
		switch_type = cast_new_prim_type(CAST_PRIM_INT,
						 (CAST_MOD_UNSIGNED
						  | CAST_MOD_SHORT));
		switch_var = must->add_temp_var("switch_tag",
						switch_type,
						0,
						CAST_SC_AUTO);
		*itype = must->pres->mint.standard_refs.unsigned16_ref;
		break;
		
	case data_field::TYPE_FLOAT:
		switch_type = cast_new_prim_type(CAST_PRIM_FLOAT, 0);
		switch_var = must->add_temp_var("switch_tag",
						switch_type,
						0,
						CAST_SC_AUTO);
		*itype = must->pres->mint.standard_refs.float32_ref;
		break;
		
	case data_field::TYPE_CHAR:
		switch_type = cast_new_prim_type(CAST_PRIM_CHAR, 0);
		switch_var = must->add_temp_var("switch_tag",
						switch_type,
						0,
						CAST_SC_AUTO);
		*itype = must->pres->mint.standard_refs.char8_ref;
		break;
		
	default:
		panic("Incorrect type to get_switch_var()");
	}
	assert(switch_type);
	assert(switch_var);
	assert(*itype);
}

cast_expr
hash_const::get_case_val(const data_field &d, unsigned int mostpackable,
			 cast_type *case_type)
{
	cast_expr case_val;
	assert(case_type);
	
	switch (d.category) {
	case data_field::TYPE_LITERAL:
		switch (d.type) {
		case data_field::TYPE_INT:
			case_val = cast_new_expr_lit_int(d.u.i,
							 CAST_MOD_SIGNED);
			*case_type = cast_new_prim_type(CAST_PRIM_INT,
							CAST_MOD_SIGNED);
			break;
			
		case data_field::TYPE_UINT:
			case_val = cast_new_expr_lit_int((signed long) d.u.u,
							 CAST_MOD_UNSIGNED);
			*case_type = cast_new_prim_type(CAST_PRIM_INT,
							CAST_MOD_UNSIGNED);
			if (mostpackable >= 4)
				if (have_switch)
					case_val = cast_new_expr_call_1(
						cast_new_expr_name(
							flick_asprintf(
								("/* %s */ "
								 "flick_mem_convert_big_endian_to_local_unsigned32"),
								UInt32ToFourChars(d.u.u)
								)),
						case_val);
				else if (must->op & MUST_DECODE) {
					/*
					 * The no-double-swap optimization.
					 * <GNATS #61>.  Set the flag that
					 * tells the `mu_state' that we are
					 * packing chars into a short or an
					 * int, so that no swapping will be
					 * done.
					 */
					must->now_packing = 1;
					case_val = cast_new_expr_call_1(
						cast_new_expr_name(
							flick_asprintf(
								("/* %s */ "
								 "flick_%s_convert_big_endian_to_"
								 "local_unsigned32"),
								UInt32ToFourChars(d.u.u),
								must->get_encode_name()
								)),
						case_val);
					/*
					 * Restore to the normal non-packing
					 * status.
					 */
					must->now_packing = 0;
					
				} else
					/* Do nothing. */;
				break;
			
		case data_field::TYPE_USHORT:
			case_val = cast_new_expr_lit_int(d.u.s,
							 (CAST_MOD_UNSIGNED
							  | CAST_MOD_SHORT));
			*case_type = cast_new_prim_type(CAST_PRIM_INT,
							(CAST_MOD_UNSIGNED
							 | CAST_MOD_SHORT));
			if (mostpackable >= 2)
				if (have_switch)
					case_val = cast_new_expr_call_1(
						cast_new_expr_name(
							flick_asprintf(
								("/* %s */ "
								 "flick_mem_convert_big_endian_to_local_unsigned16"),
								UShortToTwoChars((unsigned short) d.u.s)
								)),
						case_val);
				else if (must->op & MUST_DECODE) {
					/*
					 * The no-double-swap optimization.
					 * <GNATS #61>.  Set the flag that
					 * tells the `mu_state' that we are
					 * packing chars into a short or an
					 * int, so that no swapping will be
					 * done.
					 */
					must->now_packing = 1;
					case_val = cast_new_expr_call_1(
						cast_new_expr_name(
							flick_asprintf(
								("/* %s */ "
								 "flick_%s_convert_big_endian_to_"
								 "local_unsigned16"),
								UShortToTwoChars((unsigned short)d.u.s),
								must->get_encode_name()
								)),
						case_val);
					/*
					 * Restore to the normal non-packing
					 * status.
					 */
					must->now_packing = 0;
					
				} else
					/* Do nothing. */;
				break;
			
		case data_field::TYPE_CHAR:
			case_val = cast_new_expr_lit_char(d.u.c, 0);
			*case_type = cast_new_prim_type(CAST_PRIM_CHAR, 0);
			break;
			
		case data_field::TYPE_FLOAT:
			case_val = cast_new_expr_lit_float(d.u.f);
			*case_type = cast_new_prim_type(CAST_PRIM_DOUBLE, 0);
			break;
			
		default:
			panic("BAD error in `hash_const.cc'.");
		}
		break;
		
	case data_field::TYPE_SYMBOLIC:
		switch (d.type) {
		case data_field::TYPE_INT:
			*case_type = cast_new_prim_type(CAST_PRIM_INT,
							CAST_MOD_SIGNED);
			break;
			
		case data_field::TYPE_UINT:
			*case_type = cast_new_prim_type(CAST_PRIM_INT,
							CAST_MOD_UNSIGNED);
			break;
			
		case data_field::TYPE_USHORT:
			*case_type = cast_new_prim_type(CAST_PRIM_INT,
							(CAST_MOD_UNSIGNED
							 | CAST_MOD_SHORT));
			break;
			
		case data_field::TYPE_CHAR:
			*case_type = cast_new_prim_type(CAST_PRIM_CHAR, 0);
			break;
			
		case data_field::TYPE_FLOAT:
			*case_type = cast_new_prim_type(CAST_PRIM_DOUBLE, 0);
			break;
			
		default:
			panic("BAD error in `mu_hash_const.cc'.");
		}
		case_val = cast_new_expr_name(d.u.name);
		break;
		
	default:
		panic("BAD error in `mu_hash_const.cc'.");
	}
	
	assert(case_val);
	assert(*case_type);
	
	return case_val;
}

/*
 * This function builds a `switch' statement given a discriminator and a set of
 * values.  If the discriminator `switch_var' is null, a temporary variable is
 * created, used for unmarshaling, and discriminated upon.
 */
void
hash_const::hash()
{
	mu_memory_allocator_state *enter_case_state;
	cast_stmt last_block, default_block, temp;
	int *identical, groupcount = 0;
	
	// The most char's we can lump together as an int.
	// XXX NOTE: this should be sensitive to being in a packed array or
	// not.
	unsigned int mostpackable, i, j;
	mint_ref type_index = must->pres->mint.defs.defs_len;
	
	// This stuff is used for each individual group.
	functor **suc_funs;
	int num_in_group, curpos, flag;
	data_type *dom;		// The new domain (for this group of similar
				//   values).
	data_type *optimal_dom; // An intermediate domain for grouping
				//   char_arrays into longs
	
	cast_expr next_switch_slot;
	cast_expr case_val = 0;
	cast_type case_type = 0;
	
	struct mu_msg_span *union_span = 0, *parent_span = 0;
	
	/*****/
	
	optimal_dom = get_optimized_domain(&mostpackable);
	
	next_switch_slot = switch_slot;
	
	if (have_switch && switch_slot) {
		switch (switch_type->kind) {
		case CAST_TYPE_ARRAY:
		case CAST_TYPE_POINTER:
			if ((switch_slot->kind == CAST_EXPR_NAME)
			    && (!strcmp((switch_slot->cast_expr_u_u.name.
					 cast_scoped_name_val[0].name),
					"1+strlen")))
				next_switch_slot = cast_new_expr_lit_int(0, 0);
			else {
				if ((switch_slot->kind == CAST_EXPR_LIT_PRIM)
				    && (switch_slot->cast_expr_u_u.lit_prim.u.
					kind
					== CAST_PRIM_INT))
					next_switch_slot
						= cast_new_expr_lit_int(
							(mostpackable +
							 (switch_slot->
							  cast_expr_u_u.
							  lit_prim.u.
							  cast_lit_prim_u_u.
							  i)),
							0);
			}
			break;
			
		case CAST_TYPE_PRIMITIVE:
			panic("An atomic type have a specified slot to switch "
			      "upon.");
			break;
			
		default:
			panic("hash_const::hash() can only unmarshal into "
			      "atomic and array variables so far!");
		}
	}
	
	if (!have_switch) {
		// The discriminator must be unmarshaled.
		create_switch_var(optimal_dom[0][0], &type_index);
		assert(type_index
		       < (signed int) must->pres->mint.defs.defs_len);
		if (mostpackable > 1) {
			/*
			 * The no-double-swap optimization.  <GNATS #61>.
			 * Set the flag that tells the `mu_state' that we are
			 * packing chars into a short or an int, so that no
			 * swapping will be done.
			 */
			must->now_packing = 1;
			must->mu_mapping_direct(switch_var, switch_type,
						type_index);
			/* Restore to the normal non-packing status. */
			must->now_packing = 0;
		} else
			must->mu_mapping_direct(switch_var, switch_type,
						type_index);
	}
	
	assert(switch_var);
	assert(switch_type);
	
	must->break_chunk();
	
	if( must->current_span ) {
		if( !must->c_block ) {
			must->c_block = cast_new_block(0,0);
		}
		union_span = new mu_msg_span;
		union_span->set_kind(MSK_UNION);
		union_span->set_block(must->c_block);
		union_span->set_abort(must->abort_block);
		union_span->begin();
		parent_span = must->current_span;
		must->current_span = union_span;
	}
	
	// At this point, we take a ``snapshot'' of our glob/chunk memory
	// allocation state.  Every `case' in the `switch' will start from this
	// saved memory state.
	enter_case_state = must->memory_allocator_state();
	// Start a new block.
	last_block = must->c_block;
	must->c_block = 0;
	
	// Find identical cases in the `optimal_dom' and group them.
	identical = new int[count];
	for (i = 0; i < (unsigned int) count; i++)
		identical[i] = -1;
	for (i = 0; i < (unsigned int) count; i++) {
		if (identical[i] < 0) {
			for (j = i + 1; j < (unsigned int) count; j++)
				if ((identical[j] < 0)
				    && (optimal_dom[i][0]
					== optimal_dom[j][0]))
					identical[j] = groupcount;
			identical[i] = groupcount++;
		}
	}
	
	// Now step through each unique case and build a case statement for it.
	// If we're done with the identifier (length is 1) then set the value.
	// Otherwise, make a new hash object to hash the similar values.
	
	for (i = 0; i < (unsigned int) groupcount; i++) {
		num_in_group = 0;
		flag = 0;
		curpos = 0;
		// Find the number of members in this group.
		for (j = 0; j < (unsigned int) count; j++)
			num_in_group += ((unsigned int) identical[j] == i);
		
		suc_funs = new functor *[num_in_group];
		dom = new data_type[num_in_group];
		// Build the list of possibilities for this branch.
		for (j = 0; j < (unsigned int) count; j++) {
			if ((unsigned int) identical[j] == i) {
				suc_funs[curpos] = success_functors[j];
				// Eat the first identifier --- it will have
				// already been `case'd.
				dom[curpos++] = optimal_dom[j].crop();
				if (!flag) {
					case_val
						= get_case_val(
							optimal_dom[j][0],
							mostpackable,
							&case_type);
					flag = 1;
				}
			}
		}
		
		must->set_memory_allocator_state(enter_case_state);
		if (have_switch)
			add_case(dom, suc_funs, num_in_group, case_val,
				 switch_type, switch_var, next_switch_slot);
		else
			add_case(dom, suc_funs, num_in_group, case_val,
				 0, 0, 0); // Unmarshal into a temp var.
		delete[] dom;
		delete[] suc_funs;
	}
	
	if (optimal_dom != domain)
		delete[] optimal_dom;
	delete[] identical;
	
	// Add the failure case (default).
	temp = must->c_block;
	must->c_block = 0;
	must->set_memory_allocator_state(enter_case_state);
	failure_functor->func(must);
	must->add_stmt(cast_new_break());
	default_block = must->c_block;
	must->c_block = temp;
	must->add_stmt(cast_new_default(default_block));
	delete enter_case_state;
	
	// Now actually add the switch statement to the function body.
	temp = must->c_block;
	must->c_block = last_block;
	
	/*
	 * `next_switch_slot' is used here as simply a pointer holder so that
	 * the original `switch_var's value won't be lost.
	 */
	next_switch_slot = 0;
	if (have_switch && switch_slot) {
		switch (switch_type->kind) {
		case CAST_TYPE_ARRAY:
		case CAST_TYPE_POINTER:
			if (switch_slot->kind == CAST_EXPR_NAME)
				next_switch_slot
					= cast_new_expr_cast(
						cast_new_expr_call_1(
							switch_slot,
							switch_var
							),
						case_type);
			else {
				// switch_var[switch_slot]
				next_switch_slot
					= cast_new_unary_expr(
						CAST_UNARY_DEREF,
						cast_new_binary_expr(
							CAST_BINARY_ADD,
							switch_var,
							switch_slot)
						);
				
				// *((case_type *)&(switch_var[switch_slot]))
				next_switch_slot
					= cast_new_unary_expr(
						CAST_UNARY_DEREF,
						cast_new_expr_cast(
							cast_new_unary_expr(
								CAST_UNARY_ADDR,
								next_switch_slot
								),
							cast_new_pointer_type(
								case_type)
							));
			}
			break;
			
		default:
			next_switch_slot
				= cast_new_expr_cast(next_switch_slot,
						     case_type);
		}
	} else
		next_switch_slot = cast_new_expr_cast(switch_var, case_type);
	
	assert(next_switch_slot);
	must->add_stmt(cast_new_switch (next_switch_slot, temp));
	if( parent_span ) {
		union_span->end();
		parent_span->add_child(union_span);
		must->current_span = parent_span;
	}
}

void
hash_const::add_case(data_type *d,
		     functor **suc_funs, int cnt,
		     cast_expr case_val,
		     cast_type discrim_ctype, cast_expr discrim_cexpr,
		     cast_expr discrim_slot)
{
	// Add the case statement.
	cast_stmt temp_block = must->c_block;
	struct mu_msg_span *parent_span = 0;
	
	if( must->current_span ) {
		parent_span = must->current_span;
		must->current_span = new mu_msg_span;
		must->current_span->set_kind(MSK_SEQUENTIAL);
		must->current_span->begin();
	}
	// Add the stuff to do if this is the case within a {} of code.
	must->c_block = 0;
	if (!cnt) {
		/* Do nothing. */
		
	} else if ((cnt == 1) && (d->count() == 0)) {
		struct mu_abort_block *mab_thr, *mab_par;
		
		/*
		 * We need to add a child here to catch any abort code inside
		 * the blocks.  However, unlike other uses of aborts, we don't
		 * link the parent to the child.  This is due to the `switch'
		 * tags not being readily available for us to use, so a
		 * rollback from parent to child would be hard to do.
		 */
		mab_thr = new mu_abort_block();
		mab_par = must->abort_block;
		must->abort_block = mab_thr;
		mab_thr->set_kind(MABK_THREAD);
		mab_thr->begin();
		// Call the appropriate functor.
		suc_funs[0]->func(must);
		must->add_stmt(cast_new_break());
		mab_thr->end();
		mab_par->add_child(mab_thr, 0);
		must->abort_block = mab_par;
		must->add_stmt(mab_thr->get_block_label());
	} else {
		// We need to add the nested case for this set of values.
		hash_const next_slot(d, suc_funs, failure_functor, cnt,
				     discrim_ctype, discrim_cexpr,
				     discrim_slot, this);
		next_slot.hash();
		must->add_stmt(cast_new_break());
	}
	
	// Get the constant value to select
	cast_stmt this_case = cast_new_case(case_val, must->c_block);
	must->c_block = temp_block;
	must->add_stmt(this_case);
	if( parent_span ) {
		must->current_span->end();
		parent_span->add_child(must->current_span);
		must->current_span = parent_span;
	}
}

hash_const::hash_const(data_type *d,
		       functor **succ_funs,
		       functor *fail_fun,
		       int cnt,
		       cast_type switch_ctype,
		       cast_expr switch_var_cexpr,
		       cast_expr switch_slot_cexpr,
		       hash_const *hc) :
	must(hc->must),
	domain(d),
	count(cnt),
	success_functors(succ_funs),
	failure_functor(fail_fun),
	switch_type(switch_ctype),
	switch_var(switch_var_cexpr),
	switch_slot(switch_slot_cexpr)
{
	have_switch = (switch_var_cexpr != 0) && (switch_ctype != 0);
}

void
mu_state::mu_hash_const(mint_const *domain,	// the 'domain' of inputs
			int domain_size,	// the # of inputs
			mint_ref /*const_type*/,// the type of the inputs
			cast_expr var)	        // the variable to hash to
{
	hash_const *hash_func;
	
	switch (domain[0][0].kind) {
	case MINT_CONST_INT:
		hash_func = new hash_const_int(this, domain, domain_size, var);
		break;
		
	case MINT_CONST_CHAR:
		hash_func = new hash_const_char(this, domain, domain_size,
						var);
		break;
		
	case MINT_CONST_FLOAT:
		hash_func = new hash_const_float(this, domain, domain_size,
						 var);
		break;
		
	case MINT_CONST_ARRAY:
		hash_func = new hash_const_array(this, domain, domain_size,
						 var);
		break;
		
	case MINT_CONST_STRUCT:
		hash_func = new hash_const_struct(this, domain, domain_size,
						  var);
		break;
		
	default:
		panic("mu_hash_const: unimplemented type for a constant.");
	}
	hash_func->hash();
}

hash_const_int::hash_const_int(mu_state *m, mint_const *d,
			       int domain_size, cast_expr var)
{
	must = m;
	count = domain_size;
	// Build the data_type domain.
	domain = new data_type[count];
	success_functors = new functor *[count];
	for (int i = 0; i < count; i++) {
		if (d[i]->kind != MINT_CONST_INT)
			panic(("Incorrect type in a domain of constants "
			       "(int expected)."));
		success_functors[i] = new hash_const_cookie_functor(var, i);
		domain[i] = build(d[i]->mint_const_u_u.const_int);
	}
	failure_functor = new hash_const_cookie_functor(var, -1);
}

hash_const_char::hash_const_char(mu_state *m, mint_const *d,
				 int domain_size, cast_expr var)
{
	must = m;
	count = domain_size;
	// Build the data_type domain.
	domain = new data_type[count];
	success_functors = new functor *[count];
	for (int i = 0; i < count; i++) {
		if (d[i]->kind != MINT_CONST_CHAR)
			panic(("Incorrect type in a domain of constants "
			       "(char expected)."));
		success_functors[i] = new hash_const_cookie_functor(var, i);
		domain[i] = build(d[i]->mint_const_u_u.const_char);
	}
	failure_functor = new hash_const_cookie_functor(var, -1);
}

hash_const_float::hash_const_float(mu_state *m, mint_const *d,
				   int domain_size, cast_expr var)
{
	must = m;
	count = domain_size;
	// Build the data_type domain.
	domain = new data_type[count];
	success_functors = new functor *[count];
	for (int i = 0; i < count; i++) {
		if (d[i]->kind != MINT_CONST_FLOAT)
			panic(("Incorrect type in a domain of constants "
			       "(float expected)."));
		success_functors[i] = new hash_const_cookie_functor(var, i);
		domain[i] = build(d[i]->mint_const_u_u.const_float);
	}
	failure_functor = new hash_const_cookie_functor(var, -1);
}

hash_const_array::hash_const_array(mu_state *m, mint_const *d,
				   int domain_size, cast_expr var)
{
	must = m;
	count = domain_size;
	// Build the data_type domain.
	domain = new data_type[count];
	success_functors = new functor *[count];
	for (int i = 0; i < count; i++) {
		if (d[i]->kind != MINT_CONST_ARRAY)
			panic("Incorrect type in a domain of constants.");
		success_functors[i] = new hash_const_cookie_functor(var, i);
		domain[i] = build(d[i]->mint_const_u_u.const_array);
	}
	failure_functor = new hash_const_cookie_functor(var, -1);
}

hash_const_struct::hash_const_struct(mu_state *m, mint_const *d,
				     int domain_size, cast_expr var)
{
	must = m;
	count = domain_size;
	// build the data_type domain...
	domain = new data_type[count];
	success_functors = new functor *[count];
	for (int i = 0; i < count; i++) {
		if (d[i]->kind != MINT_CONST_STRUCT)
			panic("Incorrect type in a domain of constants.");
		success_functors[i] = new hash_const_cookie_functor(var, i);
		domain[i] = build(d[i]->mint_const_u_u.const_struct);
	}
	failure_functor = new hash_const_cookie_functor(var, -1);
}

/*****************************************************************************/

hash_const::hash_const(mu_state *mu, mint_const *dom, int dom_size,
		       functor **success_funs, functor *failure_fun,
		       cast_type discrim_ctype, cast_expr discrim_cexpr)
{
	mint_const_kind dom_mint_kind;
	int i;
	
	must = mu;
	domain = new data_type[dom_size];
	count = dom_size;
	success_functors = success_funs;
	failure_functor = failure_fun;
	switch_type = discrim_ctype;
	switch_var = discrim_cexpr;
	switch_slot = 0; /* to be filled in... */
	have_switch = (switch_var != 0) && (switch_type != 0);
	if (have_switch) {
		switch (switch_type->kind) {
		case CAST_TYPE_ARRAY:
			if ((switch_type->cast_type_u_u.array_type.
			     element_type->kind == CAST_TYPE_PRIMITIVE)
			    && (switch_type->cast_type_u_u.array_type.
				element_type->cast_type_u_u.primitive_type.kind
				== CAST_PRIM_CHAR))
				switch_slot = cast_new_expr_name("1+strlen");
			break;
		case CAST_TYPE_POINTER:
			if ((switch_type->cast_type_u_u.pointer_type.
			     target->kind == CAST_TYPE_PRIMITIVE)
			    && (switch_type->cast_type_u_u.pointer_type.
				target->cast_type_u_u.primitive_type.kind ==
				CAST_PRIM_CHAR))
				switch_slot = cast_new_expr_name("1+strlen");
			break;
		default:
			;
		}
	}
	
	if (count > 0) {
		dom_mint_kind = dom[0]->kind;
		for (i = 0; i < count; ++i)
			if (dom[i]->kind != dom_mint_kind)
				panic("Inconsistent constant types!");
		
		switch (dom[0]->kind) {
		case MINT_CONST_INT:
			for (i = 0; i < count; ++i)
				domain[i] = build(dom[i]->
						  mint_const_u_u.const_int);
			break;
		case MINT_CONST_FLOAT:
			for (i = 0; i < count; ++i)
				domain[i] = build(dom[i]->
						  mint_const_u_u.const_float);
			break;
		case MINT_CONST_CHAR:
			for (i = 0; i < count; ++i)
				domain[i] = build(dom[i]->
						  mint_const_u_u.const_char);
			break;
		case MINT_CONST_ARRAY:
			for (i = 0; i < count; ++i)
				domain[i] = build(dom[i]->
						  mint_const_u_u.const_array);
			break;
		case MINT_CONST_STRUCT:
			for (i = 0; i < count; ++i)
				domain[i] = build(dom[i]->
						  mint_const_u_u.const_struct);
			break;
		default:
			panic("Unrecognized MINT constant kind.");
			break;
		}
	}
}

/*****************************************************************************/

/*
 * `mu_discriminate' can either unmarshal the discriminator as it goes or use
 * a provided variable (e.g., a stored union discriminator).  It cannot
 * unmarshal into a provided variable since it would be hard to incrementally
 * unmarshal a value if it could use different amounts of memory for each case
 * (such as for a variable length string).
 */
void
mu_state::mu_discriminate(mint_const *domain,
			  mint_ref domain_itype,
			  int domain_size,
			  functor **success_functors,
			  functor *failure_functor,
			  cast_type discrim_ctype, /* may be void */
			  cast_expr discrim_expr)  /* may be void */
{
	hash_const *hash_func;
	
	if (op & MUST_DECODE) {
		hash_func = new hash_const(this, domain,
					   domain_size,
					   success_functors, failure_functor,
					   discrim_ctype, discrim_expr);
		hash_func->hash();
		delete hash_func;
	}
	if (op & MUST_ENCODE) {
		if (discrim_expr) {
			hash_func = new hash_const(this, domain,
						   domain_size,
						   success_functors,
						   failure_functor,
						   discrim_ctype,
						   discrim_expr);
			hash_func->hash();
			delete hash_func;
		} else {
			assert(domain_size == 1);
			mu_encode_const(domain[0], domain_itype);
			success_functors[0]->func(this);
		}
	}
	/* What about allocation-only or deallocation-only passes? */
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1