/*
 * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

struct bytes
{
	int8_t *ptr;
	size_t len;
	int8_t *limit()
	{
		return ptr + len;
	}

	void set(int8_t *ptr_, size_t len_)
	{
		ptr = ptr_;
		len = len_;
	}
	void set(const char *str)
	{
		ptr = (int8_t *)str;
		len = strlen(str);
	}
	bool inBounds(const void *p); // p in [ptr, limit)
	void malloc(size_t len_);
	void realloc(size_t len_);
	void free();
	void copyFrom(const void *ptr_, size_t len_, size_t offset = 0);
	void saveFrom(const void *ptr_, size_t len_);
	void saveFrom(const char *str)
	{
		saveFrom(str, strlen(str));
	}
	void copyFrom(bytes &other, size_t offset = 0)
	{
		copyFrom(other.ptr, other.len, offset);
	}
	void saveFrom(bytes &other)
	{
		saveFrom(other.ptr, other.len);
	}
	void clear(int fill_byte = 0)
	{
		memset(ptr, fill_byte, len);
	}
	int8_t *writeTo(int8_t *bp);
	bool equals(bytes &other)
	{
		return 0 == compareTo(other);
	}
	int compareTo(bytes &other);
	bool contains(int8_t c)
	{
		return indexOf(c) >= 0;
	}
	int indexOf(int8_t c);
	// substrings:
	static bytes of(int8_t *ptr, size_t len)
	{
		bytes res;
		res.set(ptr, len);
		return res;
	}
	bytes slice(size_t beg, size_t end)
	{
		bytes res;
		res.ptr = ptr + beg;
		res.len = end - beg;
		assert(res.len == 0 ||(inBounds(res.ptr) && inBounds(res.limit() - 1)));
		return res;
	}
	// building C strings inside byte buffers:
	bytes &strcat(const char *str)
	{
		::strcat((char *)ptr, str);
		return *this;
	}
	bytes &strcat(bytes &other)
	{
		::strncat((char *)ptr, (char *)other.ptr, other.len);
		return *this;
	}
	char *strval()
	{
		assert(strlen((char *)ptr) == len);
		return (char *)ptr;
	}
};
#define BYTES_OF(var) (bytes::of((int8_t *)&(var), sizeof(var)))

struct fillbytes
{
	bytes b;
	size_t allocated;

	int8_t *base()
	{
		return b.ptr;
	}
	size_t size()
	{
		return b.len;
	}
	int8_t *limit()
	{
		return b.limit();
	} // logical limit
	void setLimit(int8_t *lp)
	{
		assert(isAllocated(lp));
		b.len = lp - b.ptr;
	}
	int8_t *end()
	{
		return b.ptr + allocated;
	} // physical limit
	int8_t *loc(size_t o)
	{
		assert(o < b.len);
		return b.ptr + o;
	}
	void init()
	{
		allocated = 0;
		b.set(nullptr, 0);
	}
	void init(size_t s)
	{
		init();
		ensureSize(s);
	}
	void free()
	{
		if (allocated != 0)
			b.free();
		allocated = 0;
	}
	void empty()
	{
		b.len = 0;
	}
	int8_t *grow(size_t s); // grow so that limit() += s
	int getByte(uint32_t i)
	{
		return *loc(i) & 0xFF;
	}
	void addByte(int8_t x)
	{
		*grow(1) = x;
	}
	void ensureSize(size_t s); // make sure allocated >= s
	void trimToSize()
	{
		if (allocated > size())
			b.realloc(allocated = size());
	}
	bool canAppend(size_t s)
	{
		return allocated > b.len + s;
	}
	bool isAllocated(int8_t *p)
	{
		return p >= base() && p <= end();
	} // asserts
	void set(bytes &src)
	{
		set(src.ptr, src.len);
	}

	void set(int8_t *ptr, size_t len)
	{
		b.set(ptr, len);
		allocated = 0; // mark as not reallocatable
	}

	// block operations on resizing byte buffer:
	fillbytes &append(const void *ptr_, size_t len_)
	{
		memcpy(grow(len_), ptr_, len_);
		return (*this);
	}
	fillbytes &append(bytes &other)
	{
		return append(other.ptr, other.len);
	}
	fillbytes &append(const char *str)
	{
		return append(str, strlen(str));
	}
};

struct ptrlist : fillbytes
{
	typedef const void *cvptr;
	int length()
	{
		return (int)(size() / sizeof(cvptr));
	}
	cvptr *base()
	{
		return (cvptr *)fillbytes::base();
	}
	cvptr &get(int i)
	{
		return *(cvptr *)loc(i * sizeof(cvptr));
	}
	cvptr *limit()
	{
		return (cvptr *)fillbytes::limit();
	}
	void add(cvptr x)
	{
		*(cvptr *)grow(sizeof(x)) = x;
	}
	void popTo(int l)
	{
		assert(l <= length());
		b.len = l * sizeof(cvptr);
	}
	int indexOf(cvptr x);
	bool contains(cvptr x)
	{
		return indexOf(x) >= 0;
	}
	void freeAll(); // frees every ptr on the list, plus the list itself
};
// Use a macro rather than mess with subtle mismatches
// between member and non-member function pointers.
#define PTRLIST_QSORT(ptrls, fn) ::qsort((ptrls).base(), (ptrls).length(), sizeof(void *), fn)

struct intlist : fillbytes
{
	int length()
	{
		return (int)(size() / sizeof(int));
	}
	int *base()
	{
		return (int *)fillbytes::base();
	}
	int &get(int i)
	{
		return *(int *)loc(i * sizeof(int));
	}
	int *limit()
	{
		return (int *)fillbytes::limit();
	}
	void add(int x)
	{
		*(int *)grow(sizeof(x)) = x;
	}
	void popTo(int l)
	{
		assert(l <= length());
		b.len = l * sizeof(int);
	}
	int indexOf(int x);
	bool contains(int x)
	{
		return indexOf(x) >= 0;
	}
};
