/*#io
docCopyright("Steve Dekorte", 2002)
docLicense("BSD revised")
*/

#include "Base.h" 

#define BYTEARRAY_C
#include "ByteArray.h"
#undef BYTEARRAY_C

#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <limits.h>

ByteArray *ByteArray_new(void)
{ 
	return ByteArray_newWithSize_(0); 
}

ByteArray *ByteArray_newWithCString_(const char *s)
{ 
	return ByteArray_newWithData_size_((const unsigned char *)s, strlen(s)); 
}

ByteArray *ByteArray_newWithCString_size_(const char *s, int size)
{ 
	return ByteArray_newWithData_size_((const unsigned char *)s, size); 
}

ByteArray *ByteArray_newWithSize_(int size)
{
	ByteArray *self = (ByteArray *)calloc(1, sizeof(ByteArray));
	self->bytes = (unsigned char *)calloc(1, size + 1);
	self->size = size;
	self->memSize = size;
	return self;
}

ByteArray *ByteArray_newWithData_size_(const unsigned char* buf, size_t size)
{
	ByteArray *self = (ByteArray *)calloc(1, sizeof(ByteArray));
	
        self->bytes = (unsigned char *)malloc(size + 1);
        self->bytes[size] = (unsigned char)0;
        memcpy(self->bytes, buf, size);
	
	self->size = size;
	
	return self;
}

ByteArray *ByteArray_clone(ByteArray *self)
{ 
	return ByteArray_newWithData_size_(self->bytes, self->size); 
}

// datum ------------------------------------------------ 

Datum ByteArray_asDatum(ByteArray *self)
{
	Datum d;
	d.data = self->bytes;
	d.size = self->size;
	return d;
}

Datum *ByteArray_asNewDatum(ByteArray *self)
{
	Datum *d = (Datum *)malloc(sizeof(Datum));
	d->data = self->bytes;
	d->size = self->size;
	return d;
}

Datum ByteArray_datumAt_(ByteArray *self, size_t i)
{
	Datum d = ByteArray_asDatum(self);
	return Datum_datumAt_(&d, i);
}

// ---------------------------------------------------------- 

void ByteArray_free(ByteArray *self)
{
	/*
	 if (self == (void *)0x5c5c990)
	 { 
		 printf("ByteArray_free(%p)\n", self); 
	 }
	 */
	
	if (self->bytes) 
	{ 
		free(self->bytes); 
	}
	
	free(self);
}

size_t ByteArray_memorySize(ByteArray *self)
{ 
	return sizeof(ByteArray) + self->memSize; 
}

void ByteArray_compact(ByteArray *self)
{ 
	size_t newSize = self->size + 1;
	
	if (self->memSize > newSize)
	{
		unsigned char *b = (unsigned char *)malloc(newSize);
		
		memcpy(b, self->bytes, newSize);
		free(self->bytes);
		self->memSize = newSize;
		self->bytes = b;
	}
}

void ByteArray_clear(ByteArray *self)
{
	memset(self->bytes, 0, self->size);
	self->size = 0;
}

void ByteArray_setAllBytesTo_(ByteArray *self, unsigned char c)
{
	memset(self->bytes, (int)c, self->size);
}

void ByteArray_sizeTo_(ByteArray *self, size_t size)
{
	if (size > self->memSize)
	{
		//if (self->memSize == 0) self->bytes = 0;
		self->bytes = (unsigned char *)realloc(self->bytes, size + 1);
		self->memSize = size;
	}
	
	self->bytes[size] = (unsigned char)0;
	
	if (self->size > size) 
	{
		self->size = size;
	}
}

void ByteArray_setSize_(ByteArray *self, size_t size)
{
	ByteArray_sizeTo_(self, size);
	self->size = size;
}

void ByteArray_copy_(ByteArray *self, ByteArray *other)
{ 
	ByteArray_setData_size_(self, other->bytes, other->size);
	//printf("self %p size %i memSize %i\n", (void *)self, (int)self->size, (int)self->memSize);
	//printf("other %p size %i memSize %i\n", (void *)other, (int)other->size, (int)other->memSize);
}

void ByteArray_setData_size_(ByteArray *self, const unsigned char *bytes, size_t size)
{
	ByteArray_setSize_(self, size);
	
	if (size) 
	{
		memcpy(self->bytes, bytes, size);
	}
}

void ByteArray_setCString_(ByteArray *self, const char *s)
{ 
	ByteArray_setData_size_(self, (unsigned char *)s, (size_t)strlen(s)); 
}

// single byte/character operations ------------------------

int ByteArray_hasDigit(ByteArray *self) 
{
	size_t i;
	
	for (i = 0; i < self->size; i ++) 
	{ 
		if (isdigit(self->bytes[i])) 
		{
			return 1;
		}
	}
	
	return 0;
}

unsigned long ByteArray_at_bytesCount_(ByteArray *self, int i, int l)
{
	int n;
	long d = 0;
	
	if (!((i >= 0) && ((size_t)(i + l) <= ByteArray_size(self))))
	{ 
		return 0; 
	}
	
	// pack bytes into a Number 
	
	for (n = i + l - 1; n >= i; n --)
	{
		d = d << 8;
		d = d | (self->bytes[n]);
	}
	
	return d;
}

int ByteArray_at_put_(ByteArray *self, int pos, unsigned char c) 
{
	int len = self->size;
	
	if (pos < - len) 
	{ 
		return 0; 
	}
	
	if (pos > len) 
	{
		ByteArray_setSize_(self, pos);
	}
	
	pos = ByteArray_wrapPos_(self, pos);
	
	if (len == -1 || pos > len - 1) 
	{ 
		return 0; 
	}
	
	self->bytes[ByteArray_wrapPos_(self, pos)] = c;
	
	return 1;
}

unsigned char ByteArray_dropLastByte(ByteArray *self) 
{ 
	unsigned char c = 0;
	
	if (self->size)
	{
		self->size --; 
		c = self->bytes[self->size];
		self->bytes[self->size] = 0;
	}
	
	return c;
}

void ByteArray_removeByteAt_(ByteArray *self, int pos) 
{ 
	ByteArray_removeSlice(self, pos, pos + 1); 
}

void ByteArray_removeCharAt_(ByteArray *self, int pos) 
{
	int csize = BYTEARRAY_BYTES_PER_CHARACTER;
	int len = self->size;
	
	if (len < 1) 
	{
		return;
	}
	
	pos = ByteArray_wrapPos_(self, pos);
	
	memmove(self->bytes + pos * csize, 
			self->bytes + (pos + 1) * csize, 
			(len - pos + 1) * csize);
}

void ByteArray_removeSlice(ByteArray *self, int from, int to)
{
	int len = self->size;
	
	if (len < 1) 
	{
		return;
	}
	
	from = ByteArray_wrapPos_(self, from);
	to   = ByteArray_wrapPos_(self, to);
	
	memmove(self->bytes + from, self->bytes + to, len - to);
	ByteArray_setSize_(self, len - (to - from));
}

// escape --------------------------------------------

void ByteArray_escape(ByteArray *self)
{
	ByteArray *ba = ByteArray_new();
	char *s = (char *)self->bytes;
	size_t i;
	
	for (i = 0; i < self->size; i ++)
	{
		char c = s[i];
		
		switch (c)
		{ 
			case '"': ByteArray_appendCString_(ba, "\\\""); break;
case '\a': ByteArray_appendCString_(ba, "\\a"); break;
case '\b': ByteArray_appendCString_(ba, "\\b"); break;
case '\f': ByteArray_appendCString_(ba, "\\f"); break;
case '\n': ByteArray_appendCString_(ba, "\\n"); break;
case '\r': ByteArray_appendCString_(ba, "\\r"); break;
case '\t': ByteArray_appendCString_(ba, "\\t"); break;
case '\v': ByteArray_appendCString_(ba, "\\v"); break;
case '\\': ByteArray_appendCString_(ba, "\\\\"); break;
default: ByteArray_appendChar_(ba, c);
		}
    }

s[i] = (char)NULL;
ByteArray_copy_(self, ba);
ByteArray_free(ba);
}

void ByteArray_unescape(ByteArray *self)
{
	int mbskip   = 0;  /* multi-byte character size */
	size_t getIndex = 0;
	size_t putIndex = 0;
	char *s = (char *)self->bytes;
	
	while (getIndex < self->size)
	{
		int c = s[getIndex];
		int nextChar = s[getIndex + 1];
		
		if (mbskip <= 0 && ismbchar(c)) 
		{ 
			mbskip = mbcharlen(c); 
		}
		
		if (c != '\\' || mbskip > 0)
		{
			mbskip --;
			
			if (getIndex != putIndex) 
			{  
				s[putIndex] = c; 
			}
			
			putIndex ++;
		}
		else
		{
			char c = nextChar;
			
			switch (c)
			{
				case  'a': c = '\a'; break;
				case  'b': c = '\b'; break;
				case  'f': c = '\f'; break;
				case  'n': c = '\n'; break;
				case  'r': c = '\r'; break;
				case  't': c = '\t'; break;
				case  'v': c = '\v'; break;
				case '\0': c = '\\'; break;
				default:
					if (isdigit(c))
					{
						c -= 48; 
					}
			}
			
			s[putIndex] = c;
			getIndex ++; 
			putIndex ++;
		}
		
		getIndex++;
	}
	
	s[putIndex] = (char)NULL;
	ByteArray_setSize_(self, putIndex);
}

void ByteArray_quote(ByteArray *self)
{
	int oldSize = self->size;
	ByteArray_setSize_(self, self->size + 2);
	memmove(self->bytes + 1, self->bytes, oldSize);
	self->bytes[0] = '"';
	self->bytes[self->size - 1] = '"';
}

void ByteArray_unquote(ByteArray *self)
{
	if (self->size == 2)
	{ 
		ByteArray_setSize_(self, 0); 
	}
	else
	{
		memmove(self->bytes, self->bytes + 1, self->size - 1);
		ByteArray_setSize_(self, self->size - 2);
	}
}

// append --------------------------------------------

void ByteArray_appendChar_(ByteArray *self, char c)
{
	ByteArray_appendBytes_size_(self, (const unsigned char *)(&c), 1);
}

void ByteArray_appendByte_(ByteArray *self, unsigned char c)
{
	ByteArray_appendBytes_size_(self, &c, 1);
}

void ByteArray_append_(ByteArray *self, ByteArray *other)
{ 
	// Don't use, ByteArray_appendBytes_size_(self, other->bytes, other->size);
	// because it can copy the wrong memory when self == other and a resize occurs.
	
	int otherSize = other->size;
	
	if(otherSize)
	{
		int selfSize = self->size;
		ByteArray_setSize_(self, selfSize + otherSize);
		memcpy(self->bytes + selfSize, other->bytes, otherSize);
	}
}

void ByteArray_appendCString_(ByteArray *self, const char *s)
{ 
	ByteArray_appendBytes_size_(self, (const unsigned char *)s, strlen(s)); 
}

void ByteArray_appendAndEscapeCString_(ByteArray *self, const char *s)
{ 
	char oneChar[2];
	
	oneChar[1] = (char)0;
	
	while (*s)
	{
		if (*s == '\n')
		{ 
			ByteArray_appendBytes_size_(self, (const unsigned char *)"\\n", 2); 
		}
		else if (*s == '\t')
		{ 
			ByteArray_appendBytes_size_(self, (const unsigned char *)"\\t", 2); 
		}
		else if (*s == '"')
		{ 
			ByteArray_appendBytes_size_(self, (const unsigned char *)"\\\"", 2); 
		}
else
{
			oneChar[0] = *s;
			ByteArray_appendBytes_size_(self, (const unsigned char *)oneChar, 1);
}

s ++;
	}

self->bytes[self->size] = (unsigned char)0;
}

void ByteArray_appendBytes_size_(ByteArray *self, const unsigned char *bytes, size_t size)
{
	int oldSize;
	
	if(size == 0)
		return;
	
	oldSize = self->size;
	ByteArray_setSize_(self, oldSize + size);
	memcpy(self->bytes + oldSize, bytes, size);
	//self->bytes[self->size] = (unsigned char)0; // not needed? 
}

// prepend --------------------------------------------

void ByteArray_prepend_(ByteArray *self, ByteArray *other)
{ 
	ByteArray_prependBytes_size_(self, other->bytes, other->size); 
}

void ByteArray_prependCString_(ByteArray *self, const char *s)
{ 
	ByteArray_prependBytes_size_(self, (const unsigned char *)s, strlen(s)); 
}

void ByteArray_prependBytes_size_(ByteArray *self, const unsigned char *bytes, size_t size)
{
	int oldSize = self->size;
	ByteArray_setSize_(self, oldSize + size);
	memmove(self->bytes + size, self->bytes, oldSize);
	memcpy(self->bytes, bytes, size);
}

// subarray --------------------------------------------

ByteArray *ByteArray_newWithBytesFrom_to_(ByteArray *self, int startpos, int endpos) 
{
	int newlen;
	
	startpos = ByteArray_wrapPos_(self, startpos);
	endpos   = ByteArray_wrapPos_(self, endpos);
	newlen = endpos - startpos;
	
	if (newlen < 1) 
	{
		return ByteArray_new();
	}
	
	return ByteArray_newWithData_size_(self->bytes+startpos, newlen);
}

// insert --------------------------------------------

void ByteArray_insert_at_(ByteArray *self, ByteArray *other, size_t pos) 
{ 
	ByteArray_insertBytes_size_at_(self, other->bytes, other->size, pos); 
}

void ByteArray_insertCString_at_(ByteArray *self, const char *s, size_t pos) 
{ 
	ByteArray_insertBytes_size_at_(self, (const unsigned char *)s, strlen(s), pos); 
}

void ByteArray_insertBytes_size_at_(ByteArray *self, const unsigned char *bytes, size_t size, size_t pos) 
{
	long oldSize = self->size;
	pos = ByteArray_wrapPos_(self, pos);
	
	if (pos == self->size) 
	{ 
		ByteArray_appendBytes_size_(self, bytes, size); 
		return; 
	}
	
	ByteArray_setSize_(self, self->size + size);
	memmove(self->bytes + pos + size, self->bytes + pos, oldSize - pos);
	memcpy(self->bytes + pos, bytes, size);
}

// clipping --------------------------------------------

char ByteArray_clipBefore_(ByteArray *self, ByteArray *other)
{
	int i = ByteArray_find_(self, other);
	if (i == -1) return 0;
	ByteArray_removeSlice(self, 0, i);
	return 1;
}

char ByteArray_clipBeforeEndOf_(ByteArray *self, ByteArray *other)
{
	int i = ByteArray_find_(self, other);
	if (i == -1) return 0;
	ByteArray_removeSlice(self, 0, i + ByteArray_size(other));
	return 1;
}

char ByteArray_clipAfter_(ByteArray *self, ByteArray *other)
{
	int i = ByteArray_find_(self, other);
	if (i == -1) return 0;
	ByteArray_removeSlice(self, i + ByteArray_size(other), ByteArray_size(self));
	return 1;
}

char ByteArray_clipAfterStartOf_(ByteArray *self, ByteArray *other)
{
	int i = ByteArray_find_(self, other);
	if (i == -1) return 0;
	ByteArray_removeSlice(self, i, ByteArray_size(self));
	return 1;
}

// strip --------------------------------------------

int ByteArray_containsByte_(ByteArray *self, unsigned char b)
{
	size_t i;
	
	for (i = 0; i < self->size; i ++)
	{
		unsigned char c = self->bytes[i];
		
		if (c == b) 
		{
			return 1;
		}
	}
	
	return 0;
}


void ByteArray_strip_(ByteArray *self, ByteArray *other)
{
	ByteArray_lstrip_(self, other);
	ByteArray_rstrip_(self, other);
}

void ByteArray_lstrip_(ByteArray *self, ByteArray *other)
{
	size_t i;
	
	for (i = 0; i < self->size; i ++)
	{
		unsigned char c = self->bytes[i];
		
		if (!ByteArray_containsByte_(other, c))
		{
			break;
		}
	}
	
	ByteArray_removeSlice(self, 0, i);
}

void ByteArray_rstrip_(ByteArray *self, ByteArray *other)
{
	size_t i = self->size;
	
	do
	{
		i --;
		
		if (!ByteArray_containsByte_(other, self->bytes[i])) 
		{
			break;
		}
		
	} while (i != 0);
	
	ByteArray_removeSlice(self, i + 1, self->size);
}

// enumerating --------------------------------------------

typedef int (ByteArrayDetectFunc)(int);

int ByteArray_detect_(ByteArray *self, ByteArrayDetectFunc *func)
{
	unsigned char *s = self->bytes;
	unsigned char *end = self->bytes + self->size;
	
	while (s < end)
	{
		if ((*func)(*s)) 
		{
			return s - self->bytes;
		}
		
		s ++;
	}
	
	return -1;
}

typedef int (ByteArrayDetectWithFunc)(void *, Datum *);

int ByteArray_detect_with_(ByteArray *self, ByteArrayDetectWithFunc *func, void *arg)
{
	unsigned char *s = self->bytes;
	unsigned char *end = self->bytes + self->size;
	
	Datum d;
	
	while (s < end)
	{
		d.data = s;
		d.size = end - s;
		
		if ((*func)(arg, &d)) 
		{
			return s - self->bytes;
		}
		
		s ++;
	}
	
	return -1;
}

//typedef unsigned char (ByteArrayCollectFunc)(unsigned char);
// tolower and toupper use this signature:

typedef int (ByteArrayCollectFunc)(int);

void ByteArray_collectInPlace_(ByteArray *self, ByteArrayCollectFunc *func)
{
	unsigned char *s = self->bytes;
	unsigned char *end = self->bytes + self->size;
	
	while (s < end)
	{
		*s = (*func)(*s);
		s ++;
	}
}

// case --------------------------------------------

int ByteArray_isLowercase(ByteArray *self)
{
	return ByteArray_detect_(self, (ByteArrayDetectFunc *)isupper) == -1;
}

int ByteArray_isUppercase(ByteArray *self)
{
	return ByteArray_detect_(self, (ByteArrayDetectFunc *)islower) == -1;
}

void ByteArray_Lowercase(ByteArray *self)
{
	ByteArray_collectInPlace_(self, (ByteArrayCollectFunc *)tolower);
}

void ByteArray_uppercase(ByteArray *self)
{
	ByteArray_collectInPlace_(self, (ByteArrayCollectFunc *)toupper);
	
}

// string comparision operations --------------------------------

int ByteArray_equals_(ByteArray *self, ByteArray *other)		
{
	// returns 1 if equal, 0 if not equal. 
	
	if (self->size != other->size) 
	{
		return 0;
	}
	
	return (memcmp(self->bytes, other->bytes, self->size) == 0);
}

int ByteArray_equalsAnyCase_(ByteArray *self, ByteArray *other)		
{
	if (self->size == other->size)
	{
		if (self->size == 0) return 1; 
		return ByteArray_containsAnyCase_(self, other);
	}
	
	return 0;
}


int ByteArray_contains_(ByteArray *self, ByteArray *other)		
{ 
	return (ByteArray_find_(self, other) != -1); 
}

int ByteArray_containsAnyCase_(ByteArray *self, ByteArray *other)		
{ 
	return (ByteArray_findAnyCase_(self, other) != -1); 
}

int ByteArray_find_(ByteArray *self, ByteArray *other)		
{ 
	return ByteArray_find_from_(self, other, 0); 
}

int ByteArray_beginsWith_(ByteArray *self, ByteArray *other)		
{ 
	if (!other->size) 
	{
		return 1;
	}
	
	if (!self->size || other->size > self->size) 
	{
		return 0;
	}
	
	return (memcmp((char *)(self->bytes), (char *)(other->bytes), other->size) == 0); 
}

int ByteArray_endsWith_(ByteArray *self, ByteArray *other)		
{ 
	int sl = self->size;
	int ol = other->size;
	
	if (!ol) 
	{
		return 1;
	}
	
	if (!sl || ol > sl) 
	{
		return 0;
	}
	
	return (memcmp((char *)(self->bytes + sl - ol), (char *)other->bytes, ol) == 0); 
}

int ByteArray_findAnyCase_(ByteArray *self, ByteArray *other)		
{ 
	return ByteArray_findAnyCase_from_(self, other, 0); 
}

static int ByteArray_contains_at_(ByteArray *self, ByteArray *other, size_t from)
{
	if (from >= self->size || self->size - from < other->size) 
	{
		return 0;
	}
	
	return (memcmp(self->bytes + from, other->bytes, other->size) == 0);
}

// return -1 for no match, starting position of match if found 

int ByteArray_find_from_(ByteArray *self, ByteArray *other, int from)		
{
	int i, max = self->size - other->size + 1;
	
	from = ByteArray_wrapPos_(self, from);
	
	//string_caseInsensitiveBoyerMooreSearch()
	
	for (i = from; i < max; i ++)
	{
		if (ByteArray_contains_at_(self, other, i)) 
		{
			return i;
		}
	}
	
	return -1;
}

int ByteArray_findCString_from_(ByteArray *self, const char *other, int from)
{
	from = ByteArray_wrapPos_(self, from);
	
	{
		char *p = strstr((char *)(self->bytes + from), other);
		ptrdiff_t rval;
		
		if (!p) 
		{
			return -1;
		}
		
		rval = (ptrdiff_t)p - (ptrdiff_t)(self->bytes);
		return rval;
	}
}

int ByteArray_rFindCString_from_(ByteArray *self, const char *other, int from)		
{
	// return -1 for no match, starting position of match if found 
	
	unsigned char *bytes = self->bytes;
	size_t otherLen = strlen(other);
	
	from = ByteArray_wrapPos_(self, from);
	bytes = bytes + from - otherLen;
	
	while (bytes >= self->bytes)
	{
		if (memcmp(bytes, other, otherLen) == 0)
		{ 
			return bytes - self->bytes; 
		}
		
		bytes --;
	}
	
	return -1;
}

int ByteArray_rFind_from_(ByteArray *self, ByteArray *other, int from)		
{
	// return -1 for no match, starting position of match if found 
	unsigned char *bytes = self->bytes;
	
	from = ByteArray_wrapPos_(self, from);
	bytes = bytes + from - other->size;
	
	while (bytes >= self->bytes)
	{
		if (memcmp(bytes, other->bytes, other->size) == 0)
		{ 
			return bytes - self->bytes; 
		}
		
		bytes --;
	}
	
	return -1;
}

int ByteArray_rFindCharacters_from_(ByteArray *self, const char *chars, int from)
{
    // return -1 for no match, starting position of match if found 
	
    unsigned char *start = self->bytes;
    unsigned char *p = self->bytes;
    const char* pChar = NULL;
	
    from = ByteArray_wrapPos_(self, from);
    p = p + from - 1;  // -1 : the from index is not included in the search
	
    // for each byte in self
    while( start <= p )
    {
        // for each character to match
        for( pChar = chars; *pChar ; ++pChar )
        {
            if( *pChar == *p )
            { 
                return p - start;
            }
        }
		
        --p;
    }
	
    return -1;
}

int ByteArray_findAnyCase_from_(ByteArray *self, ByteArray *other, int from)		
{
	// return -1 for no match 
	
	size_t n, m;
	size_t len2 = other->size;
	size_t max = self->size - other->size;
	
	from = ByteArray_wrapPos_(self, from);
	
	if (self->size < len2) return -1; // too big 
	if (len2 <= 0) return -1; // too small 
	
	for (n = from; n <= max; n++) 
	{
		m = 0;
		
		while ((tolower(self->bytes[n+m]) == tolower(other->bytes[m]) && (m < len2 + 1))) 
		{
			if (m == len2 - 1) 
			{
				return n;
			}
			
			m ++;
		}
	}
	
	return -1;
}

int ByteArray_findByteWithValue_from_(ByteArray *self, unsigned char v, int from)
{
	unsigned char *b = self->bytes;
	size_t index = from;
	size_t max = self->size;
	
	while (index < max)
	{
		if (b[index] == v) 
		{
			return index;
		}
		
		index ++; 
	}
	
	return -1;
}

int ByteArray_findByteWithoutValue_from_(ByteArray *self, unsigned char v, int from)
{
	unsigned char *b = self->bytes;
	size_t index = from;
	
	while (index < self->size)
	{
		if (b[index] != v) 
		{
			return index;
		}
		
		index ++; 
	}
	
	return -1;
}

void ByteArray_setByteWithValue_from_to_(ByteArray *self, 
										 unsigned char v, 
										 size_t from, 
										 size_t to)
{
	if (from > to)
	{
		int x = to;
		to = from;
		from = x;
	}
	
	if (to > ByteArray_size(self)) 
	{
		ByteArray_setSize_(self, to + 1);
	}
	
	if (to < from) 
	{
		return;
	}
	
	if (to == from) 
	{ 
		//printf("bytes[%i] = %i -> %i\n", from, self->bytes[from], v);
		self->bytes[from] = v; 
		return; 
	}
	
	memset(self->bytes + from, v, to - from + 1);
}

size_t ByteArray_count_(ByteArray *self, ByteArray *other)		
{
	// return number of non-overlapping occurances of other in self 
	
	size_t count = 0;
	
	if (other->size > 0)
	{
		int index = ByteArray_find_from_(self, other, 0);
		
		while (index != -1) 
		{
			index = ByteArray_find_from_(self, other, index + other->size);
			count ++;
		}
	}
	
	return count;
}

void ByteArray_replaceCString_withCString_(ByteArray *self, 
										   const char *s1, 
										   const char *s2)
{
	ByteArray *b1 = ByteArray_newWithCString_(s1);
	ByteArray *b2 = ByteArray_newWithCString_(s2);
	ByteArray_replace_with_(self, b1, b2);
	ByteArray_free(b1);
	ByteArray_free(b2);
}

void ByteArray_replace_with_output_(ByteArray *self, 
									ByteArray *substring, 
									ByteArray *other, 
									ByteArray *output)
{
	int lastGetIndex = 0;
	int getIndex = 0;
	
	while (getIndex < (int)self->size)
	{
		getIndex = ByteArray_find_from_(self, substring, getIndex);
		if (getIndex == -1) getIndex = self->size;
		
		// append the non-matching chunk 
		ByteArray_appendBytes_size_(output, self->bytes + lastGetIndex, getIndex - lastGetIndex);
		
		if (getIndex == (int)self->size) 
		{
			break;
		}
		
		// append the other string 
		ByteArray_append_(output, other);
		getIndex += substring->size;
		lastGetIndex = getIndex;
	}
}

void ByteArray_replaceFrom_size_with_(ByteArray *self, 
									  size_t index, 
									  size_t substringSize, 
									  ByteArray *other)
{
	size_t oldSize = ByteArray_size(self);
	size_t otherSize = ByteArray_size(other);
	size_t newSize = oldSize - substringSize + otherSize;
	
	if (newSize > oldSize)
	{
		ByteArray_setSize_(self, newSize);
	}
	
	// move tail to end 
	
	{
		size_t oldTailPos = index + substringSize;
		size_t newTailPos = index + otherSize;
		size_t tailSize = oldSize - oldTailPos;
		memmove(self->bytes + newTailPos, self->bytes + oldTailPos, tailSize);
	}
	
	// copy in new chunk 
	memcpy(self->bytes + index, other->bytes, otherSize);
	
	ByteArray_setSize_(self, newSize);
}

size_t ByteArray_replaceFirst_from_with_(ByteArray *self, 
										 ByteArray *substring, 
										 size_t start, 
										 ByteArray *other)
{
	size_t substringSize = ByteArray_size(substring);
	int index = ByteArray_find_from_(self, substring, start);
	
	if (index != -1) 
	{
		ByteArray_replaceFrom_size_with_(self, index, substringSize, other);
		return 1;
	}
	
	return 0;
}

size_t ByteArray_replace_with_(ByteArray *self, ByteArray *substring, ByteArray *other)
{
	ByteArray *output = ByteArray_new();
	ByteArray_replace_with_output_(self, substring, other, output);
	ByteArray_copy_(self, output);
	ByteArray_free(output);
	return 0;
}

/*
 size_t ByteArray_replace_with_2(ByteArray *self, ByteArray *substring, ByteArray *other)
 {
	 int index;
	 size_t start;
	 size_t substringSize = ByteArray_size(substring);
	 size_t otherSize = ByteArray_size(other);
	 List *indexes = List_new();
	 
	 // find match indexes 
	 while ((index = ByteArray_find_from_(self, substring, start)) != -1)
	 {
		 List_add_(indexes, (void *)index);
		 start = index += substringSize;
	 }
	 
	 size_t matchCount = List_size(indexes);
	 size_t oldSize = ByteArray_size(self);
	 size_t newSize = oldSize - ((otherSize - substringSize) * matchCount);
	 
	 if (newSize > oldSize)
	 {
		 int i;
		 
		 // walk match indexes in reverse order and on each one,
		 // copy the tail to the right and 
		 // copy the other string  
		 
		 ByteArray_setSize_(self, newSize);
		 
		 for (i = matchCount; i > -1; i --)
		 {
			 index = (int)List_at_(indexes, i);
			 
		 }
	 }
	 else
	 {
		 // "a foo b" replace("foo", "c")
		 // walk match indexes in normal order and on each copy
		 // over the other string and the next chunk
		 
		 int start = 0;
		 int index = ByteArray_find_from_(self, substring, start);
		 size_t putIndex = List_at_(indexes, i);
		 size_t gapSize;
		 int nextIndex;
		 
		 for (;;)
		 {
			 index = (int)List_at_(indexes, i);
			 nextIndex = i < matchCount ? (int)List_at_(indexes, i + 1); 
			 // copy in other: 
			 // "a coo b"
			 
			 memcpy(self->bytes + putIndex, other->bytes, otherSize);
			 putIndex += otherSize;
			 
			 // copy chunk to next match: 
			 // "a c b"
			 
			 i ++;
			 if (i == matchCount)
			 {
				 nextIndex = oldSize;
			 }
			 
			 memmove(self->bytes + putIndex, self->bytes , otherSize);
			 putIndex += gapSize;
			 
		 } while (i < matchCount)
			 
			 ByteArray_setSize_(self, newSize);
	 }
	 
	 List_free(indexes);
	 return matchCount
 }
 */

// I/O ---------------------------------------------

void ByteArray_print(ByteArray *self)
{ 
	ByteArray_writeToCStream_(self, stdout); 
}

size_t ByteArray_writeToCStream_(ByteArray *self, FILE *stream)
{ 
	return fwrite(self->bytes, 1, self->size, stream); 
}

int ByteArray_writeToFilePath_(ByteArray *self, const char *path)
{
	FILE *fp = fopen(path, "w+");
	
	if (fp) 
	{
		size_t bytesWritten = ByteArray_writeToCStream_(self, fp);
		fclose(fp);
		return bytesWritten == self->size ? 0 : -1;
	}   
	
	return -1;
}

int ByteArray_readFromCStream_(ByteArray *self, FILE *fp)
{
	size_t bytesToRead;
	size_t pos; 
	size_t bytesRead;
	
	if (!fp) 
	{
		perror("ByteArray_readFromCStream_");
		return -1; 
	}
	
	// get file size 
	pos = ftell(fp);
	fseek(fp, 0, SEEK_END);
	bytesToRead = ftell(fp) - pos;
	fseek(fp, 0, pos);
	ByteArray_setSize_(self, bytesToRead);
	
	// read whole file into buffer 
	bytesRead = fread(self->bytes, 1, bytesToRead, fp);
	
	if (bytesRead != bytesToRead) 
	{ 
		printf("WARNING: read rest of file but only found %i bytes while ftell() indicated that %i were remaining - file may be directory\n", 
			   (int)bytesRead, (int)bytesToRead);
		return -1;
	}
	
	return 1;
}

int ByteArray_readFromFilePath_(ByteArray *self, const char *path)
{
	FILE *fp = fopen(path, "rb");
	int error;
	
	if (!fp) 
	{ 
		perror("ByteArray_readFromFilePath_");
		return -1;
	}
	
	error = ByteArray_readFromCStream_(self, fp);
	fclose(fp); 
	return error;
}

/*
 
 unsigned char ByteArray_readLineFromCStream_(ByteArray *self, FILE *stream)
 {
	 unsigned char readSomething = 0;
	 
	 while(ferror(stream) == 0)
	 {
		 int b = fgetc(stream);
		 readSomething = 1;
		 
		 if ( b == '\n' || b == '\r') 
		 {
			 break;
		 }
		 
		 ByteArray_appendByte_(self, b);
	 }
	 
	 return readSomething;
 }
 */

#define CHUNK_SIZE 4096

unsigned char ByteArray_readLineFromCStream_(ByteArray *self, FILE *stream)
{
	unsigned char readSomething = 0;
	char *s = (char *)malloc(CHUNK_SIZE);
	
	while (fgets(s, CHUNK_SIZE, stream) != NULL)
	{
		char *eol1 = strchr(s, '\n');
		char *eol2 = strchr(s, '\r');
		
		readSomething = 1;
		
		if (eol1) { *eol1 = 0; } // remove the \n return character
		if (eol2) { *eol2 = 0; } // remove the \r return character
		
		if (*s)
		{
			ByteArray_appendCString_(self, s);
		}
		
		if (eol1 || eol2)
		{
			break;
		}
	}
	
	free(s);
	
	return readSomething;
}

size_t ByteArray_readNumberOfBytes_fromCStream_(ByteArray *self, size_t size, FILE *stream)
{
	size_t readSize;
	size_t oldSize = ByteArray_size(self);
	
	ByteArray_setSize_(self, oldSize + size);
	readSize = fread((void *)(self->bytes + oldSize), 1, size, stream);
	ByteArray_setSize_(self, oldSize + readSize);
	
	return readSize;
}

// private utility functions ---------------------------------

int ByteArray_wrapPos_(ByteArray *self, int pos)
{
	int len = self->size;
	
	if (pos > len - 1) 
	{
		return len;
	}
	
	if (pos < 0) 
	{
		pos = len + pos; 
		
		if (pos < 0) 
		{
			pos = 0;
		}
	}
	
	return pos;
}

ByteArray *ByteArray_newWithFormat_(const char *format, ...)
{
	ByteArray *self;
	va_list ap;
	va_start(ap, format);
	self = ByteArray_newWithVargs_(format, ap);
	va_end(ap);
	return self;
}

ByteArray *ByteArray_newWithVargs_(const char *format, va_list ap)
{
	ByteArray *self = ByteArray_new();
	ByteArray_fromVargs_(self, format,ap);
	return self;
}

ByteArray *ByteArray_fromFormat_(ByteArray *self, const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	ByteArray_fromVargs_(self, format, ap);
	va_end(ap);
	return self;
}

void ByteArray_fromVargs_(ByteArray *self, const char *format, va_list ap)
{  
	while (*format)
	{
		if (*format == '%')
		{
			format ++;
			
			if (*format == 's')
			{
				char *s = va_arg(ap, char *);
				if (!s) { printf("IoState_print missing param"); return; }
				ByteArray_appendCString_(self, s);
			}
			else if (*format == 'i' || *format == 'd')
			{
				int i = va_arg(ap, int);
				char s[100];
				
				snprintf(s, 100, "%i", i);
				ByteArray_appendCString_(self, s);
			}
			else if (*format == 'f')
			{
				double d = va_arg(ap, double);
				char s[100];
				
				snprintf(s, 100, "%f", d);
				ByteArray_appendCString_(self, s);
			}
			else if (*format == 'p')
			{
				void *p = va_arg(ap, void *);
				char s[100];
				
				snprintf(s, 100, "%p", p);
				ByteArray_appendCString_(self, s);
			}
			// new format command for a given number adding spaces
			else if (*format == '#')  
			{
				int n, i = va_arg(ap, int);
				char *s = " ";
				
				for (n = 0; n < i; n ++) 
				{
					ByteArray_appendCString_(self, s);
				}
			}
		}
		else
		{
			char s[2];
			
			snprintf(s, 2, "%c", *format);
			ByteArray_appendCString_(self, s);
		}
		
		format ++;
	}
}

ByteArray *ByteArray_asNewHexStringByteArray(ByteArray *self)
{
	size_t i, newSize = self->size * 2;
	ByteArray *ba = ByteArray_newWithSize_(newSize);
	
	for(i = 0; i < self->size; i ++)
	{
		int c = self->bytes[i];
		char *s = (char *)(ba->bytes + i * 2);
		
		if (c < 16) 
		{
			snprintf(s, newSize, "0%x", c); 
		}
		else 
		{
			snprintf(s, newSize, "%x", c);
		}
	}
	
	return ba;
}

// file paths --------------------------------------

unsigned char ByteArray_lastByte(ByteArray *self)
{
	if (!self->size) 
	{
		return 0;
	}
	
	return self->bytes[self->size - 1];
}

void ByteArray_appendPathCString_(ByteArray *self, const char *path)
{
	char lastChar = ByteArray_lastByte(self);
	int selfEndsWithSep = IsPathSeparator(lastChar);
	int pathStartsWithSep = IsPathSeparator(*path);
	//int pathStartsWithDoubleSep = IsPathSeparator(*path) && strlen(path) > 1 && IsPathSeparator(*(path + 1));
	
	/*
	 if (pathStartsWithSep)
	 {
		 ByteArray_setCString_(self, path); 
	 }
	 else 
	 */
	
	if (ByteArray_size(self) != 0)
	{
		if (!selfEndsWithSep)
		{
			ByteArray_appendCString_(self, IO_PATH_SEPARATOR); 
		}
		
		if (pathStartsWithSep)
		{
			path ++;
		}
	}
	
	ByteArray_appendCString_(self, path); 
	
	ByteArray_replaceCString_withCString_(self, IO_PATH_SEPARATOR_DOT, IO_PATH_SEPARATOR);
	//ByteArray_replaceCString_withCString_(self, "//", "/");
}

void ByteArray_removeLastPathComponent(ByteArray *self)
{
	int pos = ByteArray_rFindCharacters_from_(self, IO_PATH_SEPARATORS, self->size - 1);
	if (pos == -1) pos = 0;
	ByteArray_setSize_(self, pos);
}

void ByteArray_clipBeforeLastPathComponent(ByteArray *self)
{
	int pos = ByteArray_rFindCharacters_from_(self, IO_PATH_SEPARATORS, self->size - 1);
	
	if (pos != -1) 
	{
		ByteArray_removeSlice(self, 0, pos + 1);
	}
}

ByteArray *ByteArray_lastPathComponent(ByteArray *self)
{ 
	return ByteArray_newWithCString_(ByteArray_lastPathComponentAsCString(self)); 
}

char *ByteArray_lastPathComponentAsCString(ByteArray *self)
{
	int pos = ByteArray_rFindCharacters_from_(self, IO_PATH_SEPARATORS, self->size-1);
	char *s;
	
	if (pos == -1) 
	{ 
		return (char *)self->bytes; 
	}
	
	s = (char *)(self->bytes+pos);
	
	while ( IsPathSeparator(*s) )
	{
		s ++;
	}
	
	return s;
}

void ByteArray_removePathExtension(ByteArray *self)
{ 
	int pos = ByteArray_rFindCString_from_(self, ".", self->size - 1);
	
	if (pos != -1) 
	{
		ByteArray_setSize_(self, pos);
	}
}

ByteArray *ByteArray_pathExtension(ByteArray *self)
{
	int pos = ByteArray_rFindCString_from_(self, ".", self->size - 1);
	
	if (pos == -1) 
	{
		return ByteArray_newWithCString_("");
	}
	
	return ByteArray_newWithCString_((char *)(self->bytes + pos + 1));
}

ByteArray *ByteArray_fileName(ByteArray *self)
{
	char *s = ByteArray_lastPathComponentAsCString(self);
	char *dot = strrchr(s, '.');
	
	if (!dot) 
	{
		return ByteArray_newWithCString_(s);
	}
	
	return ByteArray_newWithCString_size_(s, dot - s);
}

// bitwise ops ------------------------------------------------ 

void ByteArray_and_(ByteArray *self, ByteArray *other)
{
	int l1 = ByteArray_size(self);
	int l2 = ByteArray_size(other);
	
	int i = l1 < l2 ? l1 : l2;
	
	unsigned char *b1 = ByteArray_bytes(self);
	unsigned char *b2 = ByteArray_bytes(other);
	
	while (i)
	{
		*b1 = *b1 & *b2;
		b1 ++; 
		b2 ++;
		i --;
	}
}

void ByteArray_or_(ByteArray *self, ByteArray *other)
{
	int l1 = ByteArray_size(self);
	int l2 = ByteArray_size(other);
	
	int i = l1 < l2 ? l1 : l2;
	
	unsigned char *b1 = ByteArray_bytes(self);
	unsigned char *b2 = ByteArray_bytes(other);
	
	while (i)
	{
		*b1 = *b1 | *b2;
		b1 ++; 
		b2 ++;
		i --;
	}
}

void ByteArray_xor_(ByteArray *self, ByteArray *other)
{
	int l1 = ByteArray_size(self);
	int l2 = ByteArray_size(other);
	int i = l1 < l2 ? l1 : l2;
	unsigned char *b1 = ByteArray_bytes(self);
	unsigned char *b2 = ByteArray_bytes(other);
	
	while (i)
	{
		*b1 = *b1 ^ *b2;
		b1 ++; 
		b2 ++;
		i --;
	}
}

void ByteArray_compliment(ByteArray *self)
{
	int i = ByteArray_size(self);
	unsigned char *b = ByteArray_bytes(self);
	
	while (i)
	{
		*b = ~ (*b);
		b ++; 
		i --;
	}
}

void ByteArray_byteShiftLeft_(ByteArray *self, int s)
{
	ByteArray *ba = ByteArray_new();
	ByteArray_setSize_(ba, s);
	ByteArray_insert_at_(self, ba, 0);
}

void ByteArray_byteShiftRight_(ByteArray *self, int s)
{
	ByteArray *ba = ByteArray_new();
	ByteArray_setSize_(ba, s);
	ByteArray_removeSlice(self, 0, s);
	ByteArray_append_(self, ba);
}

void ByteArray_bitShiftLeft_(ByteArray *self, int s)
{
	int bytes = s / 8;
	int bits  = s % 8;
	int carryBits  = 8 - bits;
	
	if (bytes > 0) 
	{ 
		ByteArray_byteShiftLeft_(self, bytes); 
	}
	else 
	{ 
		ByteArray_byteShiftRight_(self, bytes); 
	}
	
	{
		int i = ByteArray_size(self);
		unsigned char *b = ByteArray_bytes(self);
		unsigned char carry = 0;
		
		while (i)
		{
			*b = *b << s;
			*b = *b | carry;
			carry = *b >> carryBits;
			b ++; 
			i --;
		}
	}
}

unsigned char ByteArray_byteAt_(ByteArray *self, size_t i)
{
	if (i < self->size) 
	{
		return self->bytes[i];
	}
	
	return 0;
}

int ByteArray_bitAt_(ByteArray *self, size_t i)
{
	size_t byteIndex = i / 8;
	int bitIndex = i % 8;
	unsigned char byte = ByteArray_byteAt_(self, byteIndex);
	
	return ((byte >> bitIndex) & 0x1);  
}

void ByteArray_setBit_at_(ByteArray *self, int state, size_t i)
{
	size_t charPos = i / 8;
	
	if (state) 
	{ 
		state = 0x1;
	} 
	else 
	{ 
		state = 0x0;
	}
	
	if (charPos < self->size)
	{
		unsigned char bitPos = i % 8;
		unsigned char mask = 0x1 << bitPos;
		
		if (state)
		{
			self->bytes[charPos] |= mask;
		}
		else
		{
			self->bytes[charPos] &= (~mask);
		}
	}
	
	if (ByteArray_bitAt_(self, i) != state)
	{
		printf("error\n");
		ByteArray_bitAt_(self, i);
	}
}

// --------------------------------------------------- 

float ByteArray_aveAbsFloat32From_to_(ByteArray *self, size_t from, size_t to)
{
	double delta;
	double ave = 0;
	size_t max = ByteArray_size32(self);
	
	if (from > max) return 0;
	if (to > max) to = max;
	if (from >= to) return 0;
	delta = to - from;
	
	while (from != to)
	{
		double v = (double)ByteArray_float32At_(self, from);
		v = fabs(v);
		ave += v;
		from ++;
	}
	
	ave = ave ? ave / delta : 0;
	return (float)ave;
}

int ByteArray_aveAbsSignedInt32From_to_(ByteArray *self, size_t from, size_t to)
{
	double delta;
	double ave = 0;
	size_t max = ByteArray_size32(self);
	
	if (from > max) return 0;
	if (to > max)   to = max;
	if (from >= to) return 0;
	
	delta = to - from;
	
	while (from != to)
	{
		double v = (double)ByteArray_int32At_(self, from);
		v = fabs(v);
		ave += v;
		from ++;
	}
	
	ave = ave ? ave / delta : 0;
	return (int)ave;
}

void ByteArray_convertFloatArrayToInts(ByteArray *self)
{
	size_t size = self->size / sizeof(float);
	size_t n;
	int32_t *i = (int32_t *)self->bytes;
	float *f = (float *)self->bytes;
	
	for (n = 0; n < size; n ++) 
	{ 
		i[n] = (int32_t)(f[n] * INT_MAX); 
	}
}

void ByteArray_convertFloat32ArrayToInt16(ByteArray *self)
{
	size_t size = self->size / sizeof(float);
	size_t n;
	int16_t *i = (int16_t *)self->bytes;
	float *f = (float *)self->bytes;
	
	for (n = 0; n < size; n ++) 
	{ 
		i[n] = (int16_t)(f[n] * SHRT_MAX); 
	}
	
	ByteArray_setSize_(self, size * sizeof(int16_t));
}

void ByteArray_convertInt16ArrayToFloat32(ByteArray *self)
{
	size_t count = self->size / sizeof(int16_t);
	size_t n;
	int16_t *i = (int16_t *)self->bytes;
	float *f = (float *)malloc(count * sizeof(float));
	float d = (float)1.0 / (float)SHRT_MAX;
	
	for (n = 0; n < count; n ++) 
	{ 
		f[n] = i[n] * d;
	}
	
	ByteArray_setData_size_(self, (unsigned char *)f, count * sizeof(float));
	free(f);
}

void ByteArray_float32ArrayAdd_(ByteArray *self, ByteArray *other)
{
	size_t size = self->size / sizeof(float);
	size_t otherSize = other->size / sizeof(float);
	size_t count = size < otherSize ? size : otherSize;
	float *f1 = (float *)self->bytes;
	float *f2 = (float *)other->bytes;
	
	while (count --)
	{
		*f1 += *f2;
		f1 ++;
		f2 ++;
	}
}

void ByteArray_float32ArrayMultiplyScalar_(ByteArray *self, float s)
{
	size_t count = self->size / sizeof(float);
	float *f1 = (float *)self->bytes;
	
	while (count --)
	{
		*f1 *= s;
		f1 ++;
	}
}

void ByteArray_zero(ByteArray *self)
{
	memset(self->bytes, 0, self->size);
}


static inline int sameCompare(unsigned char *b1, unsigned char *b2, int size)
{
	while (size)
	{
		if (*b1 != *b2) 
		{
			return 0;
		}
		
		*b1 ++; 
		*b2 ++;
		size --;
	}
	
	return 1;
}

int ByteArray_splitCount_(ByteArray *self, List *delims)
{
	int count;
	List *r = ByteArray_split_(self, delims);
	count = List_size(r);
	List_free(r);
	return count;
}

List *ByteArray_split_(ByteArray *self, List *delims)
{
	Datum d = ByteArray_asDatum(self);
	List *datumDelims = List_map_(delims, (ListCollectCallback *)ByteArray_asNewDatum);
	List *resultDatums = (List *)Datum_split_(&d, datumDelims);
	List *results = List_map_(resultDatums, (ListCollectCallback *)Datum_asByteArray);
	
	// free result datums 
	List_do_(resultDatums, (ListDoCallback *)free);
	List_free(resultDatums);
	
	// free datum delims 
	List_do_(datumDelims, (ListDoCallback *)free);
	List_free(datumDelims);
	return results;
}

void ByteArray_printBits(ByteArray *self)
{
	int i, max = ByteArray_bitCount(self);
	
	printf("%i ", (int)(self->size));
	
	for (i = max - 1; i > - 1; i --)
	{
		printf("%i", ByteArray_bitAt_(self, i));
	}
}

unsigned int ByteArray_bitCount(ByteArray *self)
{
	return self->size * 8;
}

/*
 unsigned int ByteArray_HashData_size_(const unsigned char *bytes, unsigned int size)
 {
	 unsigned int h = 5381;
	 
	 while (size-- > 0)
	 {
		 h += (h << 5); // h(i) = (h(i-1) * 33) ^ key(i) 
		 h ^= *bytes ++;
	 }
	 return h;
 }
 
 unsigned int ByteArray_hash(ByteArray *self)
 {
	 return ByteArray_HashData_size_(self->bytes, self->size);
 }
 
 uint32_t ByteArray_orderedHash32(ByteArray *self)
 {
	 uint32_t hash;
	 memcpy(&hash, self->bytes, 4);
	 return hash;
 }
 */


void ByteArray_removeOddIndexesOfSize_(ByteArray *self, size_t typeSize)
{
	size_t di = 1;
	size_t si = 2;
	size_t max = self->size / typeSize;
	uint8_t *b = (uint8_t *)self->bytes;
	
	if (max == 0)
	{
		return;
	}
	
	while (si < max)
	{
		uint8_t *src  = b + (si * typeSize);
		uint8_t *dest = b + (di * typeSize);
		memcpy(dest, src, typeSize);
		//printf("copy %i <- %i \n", (int)di, (int)si);
		si = si + 2;
		di = di + 1;
	}
	
	ByteArray_setSize_(self, di * typeSize);
}

void ByteArray_removeEvenIndexesOfSize_(ByteArray *self, size_t typeSize)
{
	size_t di = 0;
	size_t si = 1;
	size_t max = self->size / (typeSize);
	uint8_t *b = (uint8_t *)self->bytes;
	
	while (si < max)
	{
		uint8_t *src  = b + (si * typeSize);
		uint8_t *dest = b + (di * typeSize);
		memcpy(dest, src, typeSize);
		//printf("copy %i <- %i \n", (int)di, (int)si);
		si = si + 2;
		di = di + 1;
	}
	
	ByteArray_setSize_(self, di * typeSize);
}

void ByteArray_duplicateIndexesOfSize_(ByteArray *self, size_t typeSize)
{
	size_t size = (self->size / typeSize);
	
	if (size)
	{
		size_t si = size - 1;
		size_t di = (size * 2) - 1;
		uint8_t *b;
		
		ByteArray_setSize_(self, self->size * 2);
		
		b = (uint8_t *)self->bytes;
		
		for (;;)
		{
			uint8_t *src;
			uint8_t *dest;
			
			src  = b + si * typeSize;
			dest = b + di * typeSize;
			
			//printf("copy %i -> %i, %i \n", (int)si, (int)(di - 1), (int)di);
			memcpy(dest, src, typeSize);
			memcpy(dest - typeSize, src, typeSize);
			
			if (si == 0) break;
			di = di - 2;
			si --;
		}
	}
}

int ByteArray_endsWithCString_(ByteArray *self, const char *suffix)
{
	return (strcmp((char *)(self->bytes + self->size - strlen(suffix)), suffix)== 0);
}

size_t ByteArray_matchingPrefixSizeWith_(ByteArray *self, ByteArray *other)
{
	Datum d1 = ByteArray_asDatum(self);
	Datum d2 = ByteArray_asDatum(other);
	return Datum_matchingPrefixSizeWith_(&d1, &d2);
}

// -------------------------

/*
char *string_caseInsensitiveBoyerMooreSearch(char *buffer, size_t length, char *needle, size_t needleLen)
{
        register char *p;

        switch (needleLen)
        {
                case 0:
                return NULL;

                case 1:
                for (p = buffer; *p; p++)
                {
                        if (toupper(*p) == toupper(*needle))
                                return p;
                }
                return NULL;
                break;

        }
	
		{
        register const size_t needleSafetyBouncer = needleLen - 1;
        register size_t i;
        register byte delta[256];
        register const char *end = buffer + length;
        int k;

        for (i = 0; i < 256; i++)
                delta[i] = needleLen;

        for (i = needleLen - 1, k = 0; i >=0 ; k++, i--)
        {
                if (delta[(byte)toupper(needle[i])] == needleLen)
                        delta[(byte)toupper(needle[i])] = k;
        }


        for (p = buffer; p < end; )
        {
                for (i = needleSafetyBouncer ; ;)
                {
                        if (toupper(p[i]) != toupper(needle[i]))
                        {
                                p+=delta[(byte)toupper(p[i])];
                                break;
                        }

                        if (!i--)
                                return p;
                }
        }
		}

        return NULL;
}

char *string_caseSensitiveBoyerMooreSearch(char *buffer, size_t length, char *needle, size_t needleLen)
{
	
	register char *p;
	
	switch (needleLen)
	{
		case 0:
			return NULL;
			
		case 1:
			for (p = buffer; *p; p++)
			{
				if (*p == *needle)
					return p;
			}
			return NULL;
			break;
			
	}
	
	
	{
        register const size_t needleSafetyBouncer = needleLen - 1;
        register size_t i;
        register uint8_t delta[256];
        register const char *end = buffer + length;
        int k;
		
        for (i = 0; i < 256; i++)
			delta[i] = needleLen;
		
        for (i = needleLen - 1, k = 0; i >=0 ; k++, i--)
        {
			if (delta[(uint8_t)needle[i]] == needleLen)
				delta[(uint8_t)needle[i]] = k;
        }
		
		
        for (p = buffer; p < end; )
        {
			for (i = needleSafetyBouncer ; ;)
			{
				if (p[i] != needle[i])
				{
					p+=delta[(uint8_t)p[i]];
					break;
				}
				
				if (!i--)
					return p;
			}
        }
	}
	
	return NULL;
}
*/

// cursor -------------------------------------------

/*
size_t ByteArray_bytesInCharacterAtByteOffset_(ByteArray *self, size_t offset)
{
	// utf8
	
	const char c = self->bytes[offset];

	if ((c & 0xFE) == 0xFC) return 6;
	if ((c & 0xFC) == 0xF8) return 5;
	if ((c & 0xF8) == 0xF0) return 4;
	if ((c & 0xF0) == 0xE0) return 3;
	if ((c & 0xE0) == 0xC0) return 2;
	if ((c & 0x80) == 0x00) return 1;

	return 0;
}

size_t utf8_prev(const char *s, size_t o)
{
        while (o != 0 && (s[-- o] & 0xC0) == 0x80) {}
        return o;
}

size_t utf8_next(const char *s, size_t l, size_t o)
{
        while (o != l && (s[++ o] & 0xC0) == 0x80) {}
        return o;
}

typedef struct 
{	
	ByteArray *array;
	size_t offset; // byte offset, not character offset 
} ByteArrayCursor;

ByteArrayCursor ByteArray_cursor(ByteArray *self)
{
	ByteArrayCursor lc;
	lc.array = self;
	lc.offset = 0;
	return lc;
}

void ByteArrayCursor_next(ByteArrayCursor *self)
{
	size_t offset = self->p - ByteArray_bytes(self->array);
	
	if (self->offset >= (size_t)ByteArray_sizeInBytes(self->list))
	{
		return;
	}
	
	self->offset += ByteArray_bytesInCharacterAtByteOffset_(self->array, self->offset);
}

IOINLINE size_t ByteArrayCursor_index(ByteArrayCursor *self)
{
	return self->index;
}

IOINLINE void *ByteArrayCursor_value(ByteArrayCursor *self)
{
	return List_at_(self->list, self->index);
}

*/
