/*
 * Dwarf data format parsing routines.
 */

#include <u.h>
#include <libc.h>
#include <bio.h>
#include "elf.h"
#include "dwarf.h"

ulong
dwarfget1(DwarfBuf *b)
{
	if(b->p==nil || b->p+1 > b->ep){
		b->p = nil;
		return 0;
	}
	return *b->p++;
}

int
dwarfgetn(DwarfBuf *b, uchar *a, int n)
{
	if(b->p==nil || b->p+n > b->ep){
		b->p = nil;
		memset(a, 0, n);
		return -1;
	}
	memmove(a, b->p, n);
	b->p += n;
	return 0;
}

uchar*
dwarfgetnref(DwarfBuf *b, ulong n)
{
	uchar *p;

	if(b->p==nil || b->p+n > b->ep){
		b->p = nil;
		return nil;
	}
	p = b->p;
	b->p += n;
	return p;
}

char*
dwarfgetstring(DwarfBuf *b)
{
	char *s;

	if(b->p == nil)
		return nil;
	s = (char*)b->p;
	while(b->p < b->ep && *b->p)
		b->p++;
	if(b->p >= b->ep){
		b->p = nil;
		return nil;
	}
	b->p++;
	return s;
}

void
dwarfskip(DwarfBuf *b, int n)
{
	if(b->p==nil || b->p+n > b->ep)
		b->p = nil;
	else
		b->p += n;
}

ulong
dwarfget2(DwarfBuf *b)
{
	ulong v;

	if(b->p==nil || b->p+2 > b->ep){
		b->p = nil;
		return 0;
	}
	v = b->d->elf->hdr.e2(b->p);
	b->p += 2;
	return v;
}

ulong
dwarfget4(DwarfBuf *b)
{
	ulong v;

	if(b->p==nil || b->p+4 > b->ep){
		b->p = nil;
		return 0;
	}
	v = b->d->elf->hdr.e4(b->p);
	b->p += 4;
	return v;
}

uvlong
dwarfget8(DwarfBuf *b)
{
	uvlong v;

	if(b->p==nil || b->p+8 > b->ep){
		b->p = nil;
		return 0;
	}
	v = b->d->elf->hdr.e8(b->p);
	b->p += 8;
	return v;
}

ulong
dwarfgetaddr(DwarfBuf *b)
{
	static int nbad;

	if(b->addrsize == 0)
		b->addrsize = b->d->addrsize;

	switch(b->addrsize){
	case 1:
		return dwarfget1(b);
	case 2:
		return dwarfget2(b);
	case 4:
		return dwarfget4(b);
	case 8:
		return dwarfget8(b);
	default:
		if(++nbad == 1)
			fprint(2, "dwarf: unexpected address size %lud in dwarfgetaddr\n", b->addrsize);
		b->p = nil;
		return 0;
	}
}

int n1, n2, n3, n4, n5;

/* An inline function picks off the calls to dwarfget128 for 1-byte encodings,
 * more than by far the common case (99.999% on most binaries!). */
ulong
dwarfget128(DwarfBuf *b)
{
	static int nbad;
	ulong c, d;

	if(b->p == nil)
		return 0;
	c = *b->p++;
	if(!(c&0x80))
{n1++;
		return c;
}
	c &= ~0x80;
	d = *b->p++;
	c |= (d&0x7F)<<7;
	if(!(d&0x80))
{n2++;
		return c;
}
	d = *b->p++;
	c |= (d&0x7F)<<14;
	if(!(d&0x80))
{n3++;
		return c;
}
	d = *b->p++;
	c |= (d&0x7F)<<21;
	if(!(d&0x80))
{n4++;
		return c;
}
	d = *b->p++;
	c |= (d&0x7F)<<28;
	if(!(d&0x80))
{n5++;
		return c;
}
	while(b->p<b->ep && *b->p&0x80)
		b->p++;
	if(++nbad == 1)
		fprint(2, "dwarf: overflow during parsing of uleb128 integer\n");
	return c;
}

long
dwarfget128s(DwarfBuf *b)
{
	int nb, c;
	ulong v;
	static int nbad;

	v = 0;
	nb = 0;
	if(b->p==nil)
		return 0;
	while(b->p<b->ep){
		c = *b->p++;
		v |= (c & 0x7F)<<nb;
		nb += 7;
		if(!(c&0x80))
			break;
	}
	if(v&(1<<(nb-1)))
		v |= ~(((ulong)1<<nb)-1);
	if(nb > 8*sizeof(ulong)){
		if(0)
		if(++nbad == 1)
			fprint(2, "dwarf: overflow during parsing of sleb128 integer: got %d bits\n", nb);
	}
	return v;
}
