/*
BYTE_SWAPPING.C
Tuesday, August 22, 1995 8:58:12 AM  (Jason)

Thursday August 29, 1996 8:37 AM
	this is not your father's byte-swapping code.
*/

//#include "cseries.h"
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#include <XError.h>
#include "tags.h"
#include "byte_swapping.h"

/* ---------- private prototypes */

static void _byte_swap_data(char *name, void *data, byte_swap_code *codes, int *total_size_in_bytes, int *total_size_in_codes);
static void _byte_swap_move(char *name, void *dest, void *src, byte_swap_code *codes, int *total_size_in_bytes, int *total_size_in_codes);

/* ---------- code */

void byte_swap_move(
	char *name,
	void *destination,
	const void *source,
	int data_size,
	int data_count,
	byte_swap_code *codes)
{
//	assert(destination && source);
	
	memmove(destination, source, data_size * data_count);
	byte_swap_data(name, destination, data_size, data_count, codes);
	
	return;
}

void byte_swap_data(
	char *name,
	void *data,
	int data_size,
	int data_count,
	byte_swap_code *codes)
{
	int i;

//	assert(codes);
	
	if (data_size>=0)
	{
		int size_in_bytes, size_in_codes;
		_byte_swap_data(name, (void *) NULL, codes, &size_in_bytes, &size_in_codes);
		Assert(size_in_bytes==data_size);
		//vassert(size_in_bytes==data_size,
			//csprintf(temporary, "%s bs data @%p is #%d/#%d bytes", name, codes, size_in_bytes, data_size));
	}
	
	if (data)
	{
		for (i= 0; i<data_count; ++i)
		{
			_byte_swap_data(name, (byte *)data + i*data_size, codes, (int *) NULL, (int *) NULL);
		}
	}
	
	return;
}

void byte_swap_memory(
	char *name,
	void *memory,
	int count,
	byte_swap_code code)
{
	byte_swap_code codes[4];
	
//	assert(memory);
//	assert(count>=0);
//	assert(code==_2byte || code==_4byte);
	
	if(count<=32767)
	{
		codes[0]= _begin_bs_array;
		codes[1]= count;
		codes[2]= code;
		codes[3]= _end_bs_array;
	
		_byte_swap_data(name, memory, codes, (int *) NULL, (int *) NULL);
	} else {
		// bigger than SHORT_MAX, you have to rebuild..
		switch(code)
		{
			case _2byte:
				{
					short *s= (short *) memory;
				
					while(count--)
					{
						*s= SWAP2(*s);
						s++;
					}
				}
				break;

			case _4byte:
				{
					long *s= (long *) memory;
				
					while(count--)
					{
						*s= SWAP4(*s);
						s++;
					}
				}
				break;
				
			default:
				exit(-1);
				break;
		}
	}
	
	return;
}

/* ---------- code */

static void _byte_swap_data(
	char *name,
	void *data,
	byte_swap_code *codes,
	int *total_size_in_bytes,
	int *total_size_in_codes)
{
	int size_in_bytes = 0;	
	int size_in_codes = 0;
	
	int array_size, array_index;
	
	int done;
	
//	vassert(!(((int)data)&1),
//		csprintf(temporary, "bs @%p data pointer %p is not word aligned", codes, data));

	// header: _begin_bs_array
//	vassert(codes[0]==_begin_bs_array,
//		csprintf(temporary, "%s bs data @%p.#0 has bad start #%d", name, codes, codes[0]));
	
	// array size
	array_size= codes[1];
//	vassert(array_size>=0,
//		csprintf(temporary, "%s bs data @%p.#1 has invalid array size #%d", name, codes, array_size));

	size_in_bytes= 0;
	for (array_index= 0; array_index<array_size; ++array_index)
	{
		size_in_codes= 2;
		done= FALSE;
		
		while (!done)
		{
			int code= codes[size_in_codes];
			
			switch (code)
			{
				case _2byte:
//					vassert(!(size_in_bytes&1),
//						csprintf(temporary, "bs %p.#%d bad word alignment (offset #%d)", codes, size_in_codes, size_in_bytes));
					
					if (data)
					{
						word *p= (word *)((byte *)data + size_in_bytes);
#ifdef powerpc
						word q= __lhbrx(p, 0);
#else
						word q= *p;
						q= SWAP2(q);
#endif
						*p= q;
					}
					
					size_in_codes+= 1;
					size_in_bytes+= 2;

					break;
				
				case _4byte:
//					vassert(!(size_in_bytes&1),
//						csprintf(temporary, "bs %p.#%d bad word alignment (offset #%d)", codes, size_in_codes, size_in_bytes));
					
					if (data)
					{
						unsigned long *p= (unsigned long *)((byte *)data + size_in_bytes);
#ifdef powerpc
						unsigned long q= __lwbrx(p, 0);
#else
						unsigned long q= *p;
						q= SWAP4(q);
#endif
						*p= q;
					}

					size_in_codes+= 1;
					size_in_bytes+= 4;
					
					break;
				
				case _begin_bs_array:
				{
					int recursive_size_in_bytes, recursive_size_in_codes;
					
					_byte_swap_data(name, data ? ((byte *)data + size_in_bytes) : (void *) NULL, codes + size_in_codes, &recursive_size_in_bytes, &recursive_size_in_codes);
					
					size_in_codes+= recursive_size_in_codes;
					size_in_bytes+= recursive_size_in_bytes;
					
					break;
				}
				
				case _end_bs_array:
					size_in_codes+= 1;
					done= TRUE;
					break;
				
				default:
#if 1
					size_in_codes += 1;
					size_in_bytes += code;
#else				
					if (code>0)
					{
						size_in_codes+= 1;
						size_in_bytes+= code;
					}
					else
					{
//						vhalt(csprintf(temporary, "bs @%p.#%d has invalid code #%d", codes, size_in_codes, code));
					}
#endif					
			}
		}
	}
	
	if (total_size_in_bytes) *total_size_in_bytes= size_in_bytes;
	if (total_size_in_codes) *total_size_in_codes= size_in_codes;

	return;
}
