/* %Z%%M% %I% %E% */

/*
 * Copyright (c) 1991-2005 by Wayne C. Gramlich.
 * All rights reserved.
 */

/*
 * This file implements the vector base type in ANSI-C.
 */

#ifndef STHEADERS_H
#include "stheaders.h"
#endif

#ifndef MEMORY_H
#include "memory.h"
#endif

#ifndef UNIX_ASSERT_H
#include "unix_assert.h"
#endif

typedef struct Vector_struct *Vector;
struct Vector_struct {
    void **data;	/* Data */
    unsigned limit;	/* Limit */
    unsigned size;	/* Size */
};


extern unsigned vector__address_get(Vector);
extern Vector vector__allocate(void);
extern void vector__append(Vector, void *);
extern void *vector__fetch1(Vector, unsigned);
extern void vector__delete(Vector, unsigned);
extern void vector__predict(Vector, unsigned);
extern void vector__resize(Vector, unsigned);
extern unsigned vector__size_get(Vector);
extern void vector__store1(Vector, unsigned, void *);
extern void vector__transfer(Vector,
  unsigned, Vector, unsigned, unsigned, void *);
extern void vector__truncate(Vector, unsigned);


/*
 * vector__address_get(vector)
 *	This procedure will return the address of {vector}.
 */
unsigned
vector__address_get(
    Vector vector)
{
    return (unsigned)vector;
}

/*
 * vector__allocate()
 *	This procedure will allocate and return a new {vector} object
 *	that is empty.
 */
Vector
vector__allocate(void)
{
    Vector vector;

    vector = memory_alloc(Vector);
    vector->data = memory_allocate(4);
    vector->limit = 1;
    vector->size = 0;
    /* (void)printf("vector__allocate()=>0x%x (data=0x%x)\n",
      vector, vector->data); */
    return vector;
}

/*
 * vector__append(vector, item)
 *	This procedure will append {item} to {vector}.
 */
void
vector__append(
    Vector vector,
    void *item)
{
    unsigned new_size;
    unsigned old_size;

    /* (void)printf("=>vector__append() called\n"); */

    old_size = vector->size;
    new_size = old_size + 1;
    if (new_size > vector->limit) {
	vector__resize(vector, new_size);
    }
    vector->data[old_size] = item;
    vector->size = new_size;

    /* (void)printf("<=vector__append() called\n"); */
}

/*
 * vector__fetch1(vector, index)
 *	This procedure will return the {index}'th item from {vector}.
 */
void *
vector__fetch1(
    Vector vector,
    unsigned index)
{
    assert(index < vector->size);
    return vector->data[index];
}

extern module___object vector__module__object;
extern type___reference vector_type_ref;

static type___reference type_reference = {
	"0",
	0,
	(type___reference **)0
};

static need___entry vector_initial_needs[3] = {
	"??", "integer", 0, (type___reference **)0,
		0, (int)(&((Vector)0)->data),
	"??", "integer", 0, (type___reference **)0,
		0, (int)(&((Vector)0)->limit),
	"??", "integer", 0, (type___reference **)0,
		0, (int)(&((Vector)0)->size),
};

int vector__object__size = sizeof(*((Vector)0));

static object___object vector_object_object = {
	"",				/* Package name */
	"vector",			/* Type name */
	"??",				/* Object name */
	&type_reference,		/* Type reference */
	&vector__object__size,		/* Pointer to vector size */
	0,				/* Static needs count */
	(need___entry *)0,		/* Static needs */
	0,				/* Parameter needs count */
	vector_initial_needs,		/* Parameter needs */
	(void **)0,			/* Object pointer */
	(instantiation___object *)0	/* Instantiation list */
};
static object___object *object_list[1] = {
	&vector_object_object,
};

void
vector__external__initialize(void)
{
    vector__module__object.object_count = 1;
    vector__module__object.objects = object_list;
}

/*
 * vector__limit_get(vector)
 *	This procedure will return the limit of {vector}.
 */
unsigned
vector__limit_get(
    Vector vector)
{
    return vector->limit;
}

/*
 * vector__predict(vector, new_limit)
 *	This procedure will ensure {vector} has exactly {new_limit}
 *	slots available.  If the size of {vector} is greater than
 *	{new_limit}, the size of {vector} is reduced to {new_limit}.
 */
void
vector__predict(
    Vector vector,
    unsigned new_limit)
{
    unsigned old_limit;

    /* (void)printf("=>vector__predict(0x%x, %d)\n", vector, new_limit); */
    old_limit = vector->limit;
    if (new_limit != old_limit) {
	vector->data =
	  memory_resize(vector->data, new_limit << 2, old_limit << 2);
	vector->limit = new_limit;
	if (vector->size > new_limit) {
	    vector->size = new_limit;
	}
    }
    /* (void)printf("<=vector__predict(0x%x, %d)\n", vector, new_limit); */
}

/*
 * vector__resize(vector, minimum_limit)
 *	This procedure will ensure that {vector} can contain at least
 *	{minimum_limit} items.
 */
void
vector__resize(
    Vector vector,
    unsigned minimum_limit)
{
    unsigned new_limit;
    unsigned old_limit;

    /* (void)printf("=>vector__resize(0x%x, %d)\n", vector, minimum_limit); */

    old_limit = vector->limit;
    new_limit = old_limit;
    if (new_limit == 0) {
	new_limit = 1;
    }
    while (new_limit < minimum_limit) {
	new_limit += new_limit;
    }
    if (new_limit != old_limit) {
	vector__predict(vector, new_limit);
    }
    /* (void)printf("<=vector__resize(0x%x, %d)\n", vector, minimum_limit); */
}

/*
 * vector__size_get(vector)
 *	This procedure will return the size of {vector}.
 */
unsigned
vector__size_get(
    Vector vector)
{
    return vector->size;
}

/*
 * vector__store1(vector, index, item)
 *	This procedure will store {item} into {vector} at the {index}'th
 *	location.
 */
void
vector__store1(
    Vector vector,
    unsigned index,
    void *item)
{
    assert(index < vector->size);
    vector->data[index] = item;
}

/*
 * vector__transfer(to_vector, to_start, from_vector, from_start, length,
 *   fill_item)
 *	This procedure will transfer the {length} items from {from_vector}
 *	starting at {from_start} to {to_vector} starting at {to_start}.
 *	This procedure allows overlapping transfers where both {from_vector}
 *	and {to_vector} refer to the same {vector}.  {to_vector} will
 *	have a size that is at lease {to_start} + {length}.  Any new
 *	slots in {to_vector} that are not over written by the transfer
 *	are filled with {fill_item}.
 */
void
vector__transfer(
    Vector to_vector,
    unsigned to_start,
    Vector from_vector,
    unsigned from_start,
    unsigned length,
    void *fill_item)
    /* int trace) */
{
    unsigned from_end;
    void **from_data;
    unsigned from_limit;
    void **from_pointer;
    unsigned from_size;
    void **to_data;
    unsigned to_end;
    unsigned to_limit;
    void **to_pointer;
    unsigned to_size;

    /* if (trace) {
        (void)printf(
	  "=>vector__transfer(tv=0x%x,ts=%d,fv=0x%x,fs=%d,len=%d,fi=0x%x)\n",
        (unsigned)to_vector, to_start, (unsigned)from_vector, from_start,
        length, fill_item);
    } */

    from_limit = from_vector->limit;
    from_size = from_vector->size;
    from_end = from_start + length;
    assert(from_start + length <= from_size);

    /* (void)printf("vector_transfer 1\n"); */
    to_limit = to_vector->limit;
    to_size = to_vector->size;

    to_end = to_start + length;
    if (to_end > to_limit) {
	/* if (trace) {
	    (void)printf("vector_transfer: =>resize(%d)\n", to_end);
	} */
	vector__resize(to_vector, to_end);
	/* (void)printf("vector_transfer: <=resize(%d)\n", to_end); */
    }
    if (to_end > to_size) {
	to_vector->size = to_end;
    }

    /* (void)printf("vector_transfer 2\n"); */

    /* Fill in any new items in {to_vector}: */
    to_data = to_vector->data;
    while (to_size < to_start) {
	to_data[to_size++] = fill_item;
    }
    from_data = from_vector->data;

    /* (void)printf("vector_transfer 3\n"); */
    if (length != 0) {
	if (from_start < to_start) {
	    /*
	     * Just in case this is an overlapping copy, copy from the
	     * back to the front:
	     */
	    /* if (trace) {
		(void)printf("vector_transfer 4\n");
	    } */
	    for (from_pointer = &from_data[from_end],
	      to_pointer = &to_data[to_end];
		length != 0; length--) {
		*--to_pointer = *--from_pointer;
	    }
	} else {
	    /*
	     * It should be safe to copy from front to back:
	     */
	    for (from_pointer = &from_data[from_start],
	      to_pointer = &to_data[to_start]; length != 0; length--) {
		*to_pointer++ = *from_pointer++;
	    }
	}
    }
    /* if (trace) {
        (void)printf(
	  "<=vector__transfer(tv=0x%x,ts=%d,fv=0x%x,fs=%d,len=%d,fi=0x%x)\n",
        (unsigned)to_vector, to_start, (unsigned)from_vector, from_start,
        length, fill_item);
    } */
}

/*
 * vector__truncate(vector, new_size)
 *	This procedure will truncate {vector} to {new_size}.
 */
void
vector__truncate(
    Vector vector,
    unsigned new_size)
{
    assert(new_size <= vector->size);
    vector->size = new_size;
}

