/* capn-list.inc
 *
 * Copyright (C) 2013 James McKaskill
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#define CAT2(A,B) A ## B
#define CAT(A,B) CAT2(A, B)
#define UINT_T CAT(CAT(uint, SZ), _t)
#define LIST_T CAT(capn_list, SZ)
#define FLIP CAT(capn_flip, SZ)

UINT_T CAT(capn_get,SZ) (LIST_T l, int off) {
	char *d;
	capn_ptr p = l.p;
	if (off >= p.len) {
		return 0;
	}

	switch (p.type) {
	case CAPN_LIST:
		if (p.datasz < SZ/8)
			return 0;
		d = p.data + off * (p.datasz + 8*p.ptrs);
		return FLIP(*(UINT_T*)d);

	case CAPN_PTR_LIST:
		d = struct_ptr(p.seg, p.data + 8*off, SZ/8);
		if (d) {
			return FLIP(*(UINT_T*)d);
		} else {
			return 0;
		}

	default:
		return 0;
	}
}

int CAT(capn_getv,SZ) (LIST_T l, int off, UINT_T *to, int sz) {
	int i;
	capn_ptr p;
	capn_resolve(&l.p);
	p = l.p;
	if (off + sz > p.len) {
		sz = p.len - off;
	}

	switch (p.type) {
	case CAPN_LIST:
		if (p.datasz == SZ/8 && !p.ptrs && (SZ == 8 || CAPN_LITTLE)) {
			memcpy(to, p.data + off, sz * (SZ/8));
			return sz;
		} else if (p.datasz < SZ/8) {
			return -1;
		}

		for (i = 0; i < sz; i++) {
			char *d = p.data + (i + off) * (p.datasz + 8*p.ptrs);
			to[i] = FLIP(*(UINT_T*)d);
		}
		return sz;

	case CAPN_PTR_LIST:
		for (i = 0; i < sz; i++) {
			char *d = struct_ptr(p.seg, p.data + 8*(i+off), SZ/8);
			if (d) {
				to[i] = FLIP(*(UINT_T*)d);
			} else {
				return -1;
			}
		}
		return sz;

	default:
		return -1;
	}
}

int CAT(capn_set,SZ) (LIST_T l, int off, UINT_T v) {
	char *d;
	capn_ptr p = l.p;
	if (off >= p.len) {
		return -1;
	}

	switch (p.type) {
	case CAPN_LIST:
		if (p.datasz < SZ/8)
			return -1;
		d = p.data + off * (p.datasz + 8*p.ptrs);
		*(UINT_T*) d = FLIP(v);
		return 0;

	case CAPN_PTR_LIST:
		d = struct_ptr(p.seg, p.data + 8*off, SZ/8);
		if (!d) {
			return -1;
		}
		*(UINT_T*) d = FLIP(v);
		return 0;

	default:
		return -1;
	}
}

int CAT(capn_setv,SZ) (LIST_T l, int off, const UINT_T *from, int sz) {
	int i;
	capn_ptr p = l.p;
	if (off + sz > p.len) {
		sz = p.len - off;
	}

	switch (p.type) {
	case CAPN_LIST:
		if (p.datasz == SZ/8 && !p.ptrs && (SZ == 8 || CAPN_LITTLE)) {
			memcpy(p.data + off, from, sz * (SZ/8));
			return sz;
		} else if (p.datasz < SZ/8) {
			return -1;
		}

		for (i = 0; i < sz; i++) {
			char *d = p.data + (i + off) * (p.datasz + 8*p.ptrs);
			*(UINT_T*) d = FLIP(from[i]);
		}
		return sz;

	case CAPN_PTR_LIST:
		for (i = 0; i < sz; i++) {
			char *d = struct_ptr(p.seg, p.data + 8*(i+off), SZ/8);
			if (d) {
				*(UINT_T*) d = FLIP(from[i]);
			} else {
				return -1;
			}
		}
		return sz;

	default:
		return -1;
	}
}

LIST_T CAT(capn_new_list,SZ) (struct capn_segment *seg, int sz) {
	LIST_T l = {{CAPN_LIST}};
	l.p.seg = seg;
	l.p.len = sz;
	l.p.datasz = SZ/8;
	new_object(&l.p, sz*(SZ/8));
	return l;
}

#undef CAT2
#undef CAT
#undef UINT_T
#undef LIST_T
#undef FLIP