/************************************************
  Team: Jon Gregory, Gor Nishanov
  Project #2 Part 2
    for RPI course 66-648 Compiler Design Theory

  utils.h:

    List<T> template class 
    Hash<Key, Data> tempalte class //used by Identifier and Type classes
    Identifier class. 

  also this class contains

    QuickNew - the class providing custom allocation. All 
      small dynamically created classes descendands of this class.

      This allows us to destroy everything by using finalize() method.

    */  

template<class T>
class List {
public:
	T* first;
	T* last;
	int count;

	List():first(0),last(0),count(0){}
	List(T* i):first(i),last(i),count(1){}
	int getSize() const {return count;}
	void add(T* i) { ++count;
		if(first == 0) last = first = i;
		else last = (last->nextInOwner = i);
           last->nextInOwner = 0; // Just to be sure
	}
	void glueList(const List<T>* t){
		count += t->getSize();
		if(first == 0) last = first = t->first;
		else last->nextInOwner = t->first;
  }
	void clear() { first = last = 0; count = 0; }
	void print(ostream& o, char* sep = " ") const {
		T* f = first; int n = count;
		while(f != 0 && n--) {
			if(f != first) o << sep;
			o << f;
			f = f->nextInOwner;
		}
	};
	friend ostream& operator << (ostream& o, const List<T>& t){
	  t.print(o); return o; }
	friend ostream& operator << (ostream& o, const List<T>* t){
          t->print(o); return o;}
};

struct QuickNew {
	static char * rawMemory;
	static char * actualEnd;
	static long Size() {return (unsigned)(actualEnd - rawMemory);}
	static long Avail() {return (unsigned)(end - avail);}
private:
	static char * avail;
	static char * end;
	static char * init(int size) {
	 assert(rawMemory == 0 /* init could be called only once */ );
		rawMemory = new char[size * 1024];
		assert(rawMemory != 0 /* failed to allocate */ );
		memset(rawMemory, 0, size * 1024);
		avail = rawMemory;
		actualEnd = end   = avail + size * 1024;
cout << "QuickNew::init = " << Size() << endl;
		return rawMemory;
	}
void operator delete(void*, size_t)   { assert(false /* DON'T CALL ME */); }
//void operator delete[](void*, size_t) { assert(false /* DON'T CALL ME */); }
//void* operator new[](void*)           { assert(false /* DON'T CALL ME */); }

public:
	void * operator new(size_t size) {
//          cout << "[new]";
		return bottomAlloc(size);
	}

	static char * topAlloc(size_t size) {
//          cout << "tAlloc = " << size << endl;
	 end -= size;
	 assert(end > avail);
	 return end;
	}

	static char * bottomAlloc(size_t size) {
	 register char * result = avail;
//          cout << "bAlloc = " << size << endl;
	 avail += size;
	 assert(avail <= end /* still in rawMemory */);
	 return result;
	}

	static void finalize() {
	 delete[] rawMemory;
  }
};

char * QuickNew::actualEnd = 0;
char * QuickNew::end = 0;
char * QuickNew::avail = 0;
char * QuickNew::rawMemory = QuickNew::init(LOCL_HEAP_SIZE);

template <class T,class D, int N>
class Hash {
	class bucket {
		bucket* next;
		friend Hash;
	public:
		T key;
		D data;
		bucket():key(T()),next(0){}
		bucket(T ky, bucket** prev, D dat ):
			key(ky),data(dat),next(0) {*prev = this;}
	};
	bucket hash[N];

public:
	bucket& get (T key) {
		unsigned int hno = (unsigned int)(key_hashNo(key) % N);
		bucket* bu = &hash[hno];
		if(key_empty(bu->key)) {
			bu->key = key;
			goto A; // It is bad, but code will be a little more efficient
		}
		for(;;) {
			if( key_equal(bu->key, key) ) break;
			if(bu->next == 0) {
				bu = (bu->next = (bucket*)QuickNew::bottomAlloc(sizeof(bucket)));
A:			bu->next = 0;
				bu->key = (T)key_make_new(key);
				break;
			}
			bu = bu->next;
		}
		return *bu;
	}
	void printSize() { cout << sizeof(hash); }
};

const char* key_make_new(const char* str, int len) {
	char* str_ = QuickNew::topAlloc(len+2)+1;
	assert(len <= 255);
	*(str_ - 1) = len;
	memcpy(str_, str, len);
	str_[len] = 0;
	return str_;
}

const char* key_make_new(const char* k) {	return key_make_new(k, strlen(k)); };
bool key_empty(const char* k) {return k == 0;}
bool key_equal(const char* a, const char* b) {return strcmp(a,b) == 0; }
unsigned long key_hashNo(const char* i) {
	unsigned long res = 0;
	char ch;
	while( (ch = *i++) != 0 ) {
		res = ((res << 4) + ch-'a'+1) % 98317l; // good prime number
	}
	return res;
}

class Nothing {};

struct IdentProxy { char* str_; }; // fighting jacc union

class Identifier {
  char * str_;
  static Hash<char*, Nothing, NAME_HASH_SIZE> hash;

friend const Identifier key_make_new(const Identifier k) { return k; };
friend bool key_equal(const Identifier a, const Identifier b) 
  {return a.str_ == b.str_; }
friend bool key_empty(const Identifier k) {return k.str_ == 0;}
friend unsigned long key_hashNo(const Identifier i)
		{ return (unsigned long)(QuickNew::actualEnd - i.str_); }

public:
  static Identifier JavaLangObject;
  static Identifier JavaLangString;
  static Identifier JavaLangCloneable;
  static Identifier crazy;
  static Identifier star, init, nullstr, clinit, length;

  void operator = (const IdentProxy a){str_ = a.str_;}
  Identifier(const IdentProxy a):str_(a.str_) {}
  Identifier(const Identifier& a):str_(a.str_){}
  operator IdentProxy() {IdentProxy tmp; tmp.str_ = str_; return tmp;}

  bool operator == (Identifier a) const {return a.str_ == str_;}
  Identifier():str_(0){}
  explicit Identifier(const char* str):str_(
    (str == 0)?nullstr.str_:hash.get((char*)str).key){
//    cout << "id[" << QuickNew::actualEnd-str_ << "]=" << str_ << endl;
  }
  const char* toString() const {return str_;}
  int len() const {return *(str_-1);}
  char* copyTo(char* a) const {memcpy(a, str_, len()+1); return a + len();}
  friend ostream& operator << (ostream& o, const Identifier i) {
     return o << i.toString(); }
};

Hash<char*, Nothing, NAME_HASH_SIZE> Identifier::hash;
Identifier Identifier::JavaLangObject("java/lang/Object");
Identifier Identifier::JavaLangString("java/lang/String");
Identifier Identifier::JavaLangCloneable("java/lang/Cloneable");
Identifier Identifier::star("*");
Identifier Identifier::init("<init>");
Identifier Identifier::clinit("<clinit>");
Identifier Identifier::nullstr("");
Identifier Identifier::crazy("!!! Crazy Ident !!!");
Identifier Identifier::length("length");

