packages feed

pbc4hs-0.1.0.0: cbits/pattern.c

#include "alloc.h"
#include "context.h"
#include "varint.h"
#include "pattern.h"
#include "array.h"
#include "proto.h"
#include "map.h"

#include <stdint.h>
#ifndef _MSC_VER
#include <stdbool.h>
#endif
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdarg.h>

static void
set_default_v(void * output, int ctype, pbc_var defv) {
	switch (ctype) {
	case CTYPE_INT32:
		*(uint32_t *)output = defv->integer.low;
		break;
	case CTYPE_INT64:
		*(uint64_t *)output = (uint64_t)defv->integer.low | (uint64_t)defv->integer.hi << 32;
		break;
	case CTYPE_DOUBLE:
		*(double *)output = defv->real;
		break;
	case CTYPE_FLOAT:
		*(float *)output = (float)defv->real;
		break;
	case CTYPE_BOOL:
		*(bool *)output = (defv->integer.low != 0);
		break;
	case CTYPE_INT8:
		*(uint8_t *)output = (uint8_t)defv->integer.low;
		break;
	case CTYPE_INT16:
		*(uint16_t *)output = (uint16_t)defv->integer.low;
		break;
	case CTYPE_VAR:
		*(union _pbc_var *)output = *defv;
		break;
	}
}

static void
_pattern_set_default(struct _pattern_field *field, char *output) {
	if (field->ctype == CTYPE_ARRAY || field->ctype == CTYPE_PACKED) {
		struct _pbc_array *array = (struct _pbc_array *)(output + field->offset);
		_pbcA_open(array);
	} else if (field->ptype == PTYPE_ENUM) {
		pbc_var defv;
		defv->integer.low = field->defv->e.id;
		defv->integer.hi = 0;
		set_default_v(output + field->offset, field->ctype, defv);
	}
	set_default_v(output + field->offset, field->ctype, field->defv);
}

void
pbc_pattern_set_default(struct pbc_pattern *pat, void *output) {
	int i;
	for (i=0;i<pat->count;i++) {
		_pattern_set_default(&pat->f[i], (char *)output);
	}
}

// pattern unpack

static struct _pattern_field *
bsearch_pattern(struct pbc_pattern *pat, int id)
{
	int begin = 0;
	int end = pat->count;
	while (begin < end) {
		int mid = (begin + end)/2;
		struct _pattern_field * f = &pat->f[mid];
		if (id == f->id) {
			return f;
		}
		if (id < f->id) {
			end = mid;
		} else {
			begin = mid + 1;
		}
	}
	return NULL;
}

static inline int
write_real(int ctype, double v, void *out) {
	switch(ctype) {
	case CTYPE_DOUBLE:
		*(double *)out = v;
		return 0;
	case CTYPE_FLOAT:
		*(float *)out = (float)v;
		return 0;
	case CTYPE_VAR:
		((union _pbc_var *)out)->real = v;
		return 0;
	}
	return -1;
}

static inline int
write_longlong(int ctype, struct longlong *i, void *out) {
	switch(ctype) {
	case CTYPE_INT32:
		*(uint32_t *)out = i->low;
		return 0;
	case CTYPE_INT64:
		*(uint64_t *)out = (uint64_t)i->low | (uint64_t)i->hi << 32;
		return 0;
	case CTYPE_BOOL:
		*(bool *)out = (i->low !=0) ;
		return 0;
	case CTYPE_INT8:
		*(uint8_t *)out = (uint8_t)i->low;
		return 0;
	case CTYPE_INT16:
		*(uint8_t *)out = (uint16_t)i->low;
		return 0;
	case CTYPE_VAR:
		((union _pbc_var *)out)->integer = *i;
		return 0;
	}
	return -1;
}

static inline int
write_integer(int ctype, struct atom *a, void *out) {
	return write_longlong(ctype, &(a->v.i), out);
}

static int unpack_array(int ptype, char *buffer, struct atom *, pbc_array _array);

int
_pbcP_unpack_packed(uint8_t *buffer, int size, int ptype, pbc_array array) {
	pbc_var var;
	var->integer.hi = 0;
	int i;
	switch(ptype) {
	case PTYPE_DOUBLE:
		if (size % 8 != 0)
			return -1;
		for (i=0;i<size;i+=8) {
			union {
				double d;
				uint64_t i64;
			} u;
			u.i64 = (uint64_t)buffer[i] |
				(uint64_t)buffer[i+1] << 8 |
				(uint64_t)buffer[i+2] << 16 |
				(uint64_t)buffer[i+3] << 24 |
				(uint64_t)buffer[i+4] << 32 |
				(uint64_t)buffer[i+5] << 40 |
				(uint64_t)buffer[i+6] << 48 |
				(uint64_t)buffer[i+7] << 56;
			var->real = u.d;
			_pbcA_push(array, var);
		}
		return size/8;
	case PTYPE_FLOAT:
		if (size % 4 != 0)
			return -1;
		for (i=0;i<size;i+=4) {
			union {
				float f;
				uint32_t i32;
			} u;
			u.i32 = (uint32_t)buffer[i] |
				(uint32_t)buffer[i+1] << 8 |
				(uint32_t)buffer[i+2] << 16 |
				(uint32_t)buffer[i+3] << 24;
			var->real = (double)u.f;
			_pbcA_push(array, var);
		}
		return size/4;
	case PTYPE_FIXED32:
	case PTYPE_SFIXED32:
		if (size % 4 != 0)
			return -1;
		for (i=0;i<size;i+=4) {
			var->integer.low = (uint32_t)buffer[i] |
				(uint32_t)buffer[i+1] << 8 |
				(uint32_t)buffer[i+2] << 16 |
				(uint32_t)buffer[i+3] << 24;
			_pbcA_push(array, var);
		}
		return size/4;
	case PTYPE_FIXED64:
	case PTYPE_SFIXED64:
		if (size % 8 != 0)
			return -1;
		for (i=0;i<size;i+=8) {
			var->integer.low = (uint32_t)buffer[i] |
				(uint32_t)buffer[i+1] << 8 |
				(uint32_t)buffer[i+2] << 16 |
				(uint32_t)buffer[i+3] << 24;
			var->integer.hi = (uint32_t)buffer[i+4] |
				(uint32_t)buffer[i+5] << 8 |
				(uint32_t)buffer[i+6] << 16 |
				(uint32_t)buffer[i+7] << 24;
			_pbcA_push(array, var);
		}
		return size/8;
	case PTYPE_INT64:
	case PTYPE_UINT64:
	case PTYPE_INT32:
	case PTYPE_UINT32:
	case PTYPE_ENUM:	// enum must be integer type in pattern mode
	case PTYPE_BOOL: {
		int n = 0;
		while (size > 0) {
			int len;
			if (size >= 10) {
				len = _pbcV_decode(buffer, &(var->integer));
			} else {
				uint8_t temp[10];
				memcpy(temp, buffer, size);
				len = _pbcV_decode(buffer, &(var->integer));
				if (len > size)
					return -1;
			}
			_pbcA_push(array, var);
			buffer += len;
			size -= len;
			++n;
		}
		return n;
	}
	case PTYPE_SINT32: {
		int n = 0;
		while (size > 0) {
			int len;
			if (size >= 10) {
				len = _pbcV_decode(buffer, &(var->integer));
				_pbcV_dezigzag32(&(var->integer));
			} else {
				uint8_t temp[10];
				memcpy(temp, buffer, size);
				len = _pbcV_decode(buffer, &(var->integer));
				if (len > size)
					return -1;
				_pbcV_dezigzag32(&(var->integer));
			}
			_pbcA_push(array, var);
			buffer += len;
			size -= len;
			++n;
		}
		return n;
	}
	case PTYPE_SINT64: {
		int n = 0;
		while (size > 0) {
			int len;
			if (size >= 10) {
				len = _pbcV_decode(buffer, &(var->integer));
				_pbcV_dezigzag64(&(var->integer));
			} else {
				uint8_t temp[10];
				memcpy(temp, buffer, size);
				len = _pbcV_decode(buffer, &(var->integer));
				if (len > size)
					return -1;
				_pbcV_dezigzag64(&(var->integer));
			}
			_pbcA_push(array, var);
			buffer += len;
			size -= len;
			++n;
		}
		return n;
	}
	}
	return -1;
}

static int
unpack_field(int ctype, int ptype, char * buffer, struct atom * a, void *out) {
	if (ctype == CTYPE_ARRAY) {
		return unpack_array(ptype, buffer, a , (struct _pbc_array *)out);
	}
	if (ctype == CTYPE_PACKED) {
		return _pbcP_unpack_packed((uint8_t *)buffer + a->v.s.start, a->v.s.end - a->v.s.start,	ptype, (struct _pbc_array *)out);
	}
	switch(ptype) {
	case PTYPE_DOUBLE:
		CHECK_BIT64(a, -1);
		return write_real(ctype, read_double(a), out);
	case PTYPE_FLOAT:
		CHECK_BIT32(a, -1);
		return write_real(ctype, read_float(a), out);
	case PTYPE_INT64:
	case PTYPE_UINT64:
	case PTYPE_INT32:
	case PTYPE_UINT32:
	case PTYPE_ENUM:	// enum must be integer type in pattern mode
	case PTYPE_BOOL:
		CHECK_VARINT(a, -1);
		return write_integer(ctype, a , out);
	case PTYPE_FIXED32:
	case PTYPE_SFIXED32:
		CHECK_BIT32(a, -1);
		return write_integer(ctype, a , out);
	case PTYPE_FIXED64:
	case PTYPE_SFIXED64:
		CHECK_BIT64(a, -1);
		return write_integer(ctype, a , out);
	case PTYPE_SINT32: {
		CHECK_VARINT(a, -1);
		struct longlong temp = a->v.i;
		_pbcV_dezigzag32(&temp);
		return write_longlong(ctype, &temp , out);
	}
	case PTYPE_SINT64: {
		CHECK_LEND(a, -1);
		struct longlong temp = a->v.i;
		_pbcV_dezigzag64(&temp);
		return write_longlong(ctype, &temp , out);
	}
	case PTYPE_MESSAGE: 
		CHECK_LEND(a, -1);
		((union _pbc_var *)out)->m.buffer = buffer + a->v.s.start;
		((union _pbc_var *)out)->m.len = a->v.s.end - a->v.s.start;
		return 0;
	case PTYPE_STRING:
	case PTYPE_BYTES:
		CHECK_LEND(a, -1);
		((struct pbc_slice *)out)->buffer = buffer + a->v.s.start;
		((struct pbc_slice *)out)->len = a->v.s.end - a->v.s.start;
		return 0;
	}
	return -1;
}

static int 
unpack_array(int ptype, char *buffer, struct atom * a, pbc_array _array) {
	pbc_var var;
	int r = unpack_field(CTYPE_VAR, ptype, buffer, a , var);
	if (r !=0 )
		return r;
	_pbcA_push(_array , var);

	return 0;
}

void 
pbc_pattern_close_arrays(struct pbc_pattern *pat, void * data) {
	int i;
	for (i=0;i<pat->count;i++) {
		if (pat->f[i].ctype == CTYPE_ARRAY || pat->f[i].ctype == CTYPE_PACKED) {
			void *array = (char *)data + pat->f[i].offset;
			_pbcA_close((struct _pbc_array *)array);
		}
	}
}

static inline int
_pack_wiretype(uint32_t wt, struct pbc_slice *s) {
	int len;
	if (s->len < 10) {
		uint8_t temp[10];
		len = _pbcV_encode32(wt, temp);
		if (len > s->len)
			return -1;
		memcpy(s->buffer, temp, len);
	} else {
		len = _pbcV_encode32(wt, (uint8_t *)s->buffer);
	}
	s->buffer = (char *)s->buffer + len;
	s->len -= len;
	return len;
}

static inline int
_pack_varint64(uint64_t i64, struct pbc_slice *s) {
	int len;
	if (s->len < 10) {
		uint8_t temp[10];
		len = _pbcV_encode(i64, temp);
		if (len > s->len)
			return -1;
		memcpy(s->buffer, temp, len);
	} else {
		len = _pbcV_encode(i64, (uint8_t *)s->buffer);
	}
	s->buffer = (char *)s->buffer + len;
	s->len -= len;
	return len;
}

static inline int
_pack_sint32(uint32_t v, struct pbc_slice *s) {
	int len;
	if (s->len < 10) {
		uint8_t temp[10];
		len = _pbcV_zigzag32(v, temp);
		if (len > s->len)
			return -1;
		memcpy(s->buffer, temp, len);
	} else {
		len = _pbcV_zigzag32(v, (uint8_t *)s->buffer);
	}
	s->buffer = (char *)s->buffer + len;
	s->len -= len;
	return len;
}

static inline int
_pack_sint64(uint64_t v, struct pbc_slice *s) {
	int len;
	if (s->len < 10) {
		uint8_t temp[10];
		len = _pbcV_zigzag(v, temp);
		if (len > s->len)
			return -1;
		memcpy(s->buffer, temp, len);
	} else {
		len = _pbcV_zigzag(v, (uint8_t *)s->buffer);
	}
	s->buffer = (char *)s->buffer + len;
	s->len -= len;
	return len;
}

static inline void
_fix32_encode(uint32_t v , uint8_t *buffer) {
	buffer[0] = (uint8_t) v;
	buffer[1] = (uint8_t) (v >> 8);
	buffer[2] = (uint8_t) (v >> 16);
	buffer[3] = (uint8_t) (v >> 24);
}

static inline void
_fix64_encode(struct longlong *v , uint8_t *buffer) {
	_fix32_encode(v->low , buffer);
	_fix32_encode(v->hi, buffer + 4);
}

static int
_pack_number(int ptype , int ctype , struct pbc_slice *s, void *input) {
	pbc_var var;
	if (ctype == CTYPE_VAR) {
		memcpy(var, input, sizeof(var));
	} else {
		switch (ctype) {
		case CTYPE_INT32:
			var->integer.low = *(uint32_t *)input;
			var->integer.hi = 0;
			break;
		case CTYPE_INT64: {
			uint64_t v = *(uint64_t *)input;
			var->integer.low = (uint32_t) (v & 0xffffffff);
			var->integer.hi = (uint32_t) (v >> 32);
			break;
		}
		case CTYPE_INT16:
			var->integer.low = *(uint16_t *)input;
			var->integer.hi = 0;
			break;
		case CTYPE_INT8:
			var->integer.low = *(uint8_t *)input;
			var->integer.hi = 0;
			break;
		case CTYPE_BOOL:
			var->integer.low = *(bool *)input;
			var->integer.hi = 0;
			break;
		case CTYPE_DOUBLE:
			var->real = *(double *)input;
			break;
		case CTYPE_FLOAT:
			var->real = *(float *)input;
			break;
		}
	}

	switch(ptype) {
	case PTYPE_FIXED64:
	case PTYPE_SFIXED64:
		if (s->len < 8)
			return -1;
		_fix64_encode(&(var->integer), (uint8_t *)s->buffer);
		s->buffer = (char *)s->buffer + 8;
		s->len -= 8;
		return 8;
	case PTYPE_DOUBLE:
		if (s->len < 8)
			return -1;
		double_encode(var->real , (uint8_t *)s->buffer);
		s->buffer = (char *)s->buffer + 8;
		s->len -= 8;
		return 8;
	case PTYPE_FLOAT:
		if (s->len < 4)
			return -1;
		float_encode((float)var->real , (uint8_t *)s->buffer);
		s->buffer = (char *)s->buffer + 4;
		s->len -= 4;
		return 4;
	case PTYPE_FIXED32:
	case PTYPE_SFIXED32:
		if (s->len < 4)
			return -1;
		_fix32_encode(var->integer.low, (uint8_t *)s->buffer);
		s->buffer = (char *)s->buffer + 4;
		s->len -= 4;
		return 4;
	case PTYPE_UINT64:
	case PTYPE_INT64:
	case PTYPE_INT32:
		return _pack_varint64((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32, s);
	case PTYPE_UINT32:
	case PTYPE_BOOL:
	case PTYPE_ENUM:
		return _pack_wiretype(var->integer.low , s);
	case PTYPE_SINT32: 
		return _pack_sint32(var->integer.low , s);
	case PTYPE_SINT64:
		return _pack_sint64((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32 , s);
	default:
		return -1;
	}
}

static int
_pack_field(struct _pattern_field *pf , int ctype, struct pbc_slice *s, void *input) {
	int wiretype;
	int ret = 0;
	int len;
	struct pbc_slice * input_slice;
	struct pbc_slice string_slice;

	switch(pf->ptype) {
	case PTYPE_FIXED64:
	case PTYPE_SFIXED64:
	case PTYPE_DOUBLE:
		wiretype = WT_BIT64;
		goto _number;
	case PTYPE_FIXED32:
	case PTYPE_SFIXED32:
	case PTYPE_FLOAT:
		wiretype = WT_BIT32;
		goto _number;
	case PTYPE_UINT64:
	case PTYPE_INT64:
	case PTYPE_INT32:
	case PTYPE_BOOL:
	case PTYPE_UINT32:
	case PTYPE_ENUM:
	case PTYPE_SINT32:
	case PTYPE_SINT64:
		wiretype = WT_VARINT;
		goto _number;
	case PTYPE_STRING:
		wiretype = WT_LEND;
		input_slice = (struct pbc_slice *)input;
		if (input_slice->len >= 0)
			goto _string;
		string_slice.buffer = input_slice->buffer;
		string_slice.len = strlen((const char *)string_slice.buffer) - input_slice->len;
		input_slice = &string_slice;
	
		goto _string;
	case PTYPE_MESSAGE:
	case PTYPE_BYTES:
		wiretype = WT_LEND;
		goto _bytes;
	default:
		break;
	}

	return 0;
_bytes:
	input_slice = (struct pbc_slice *)input;
_string:
	len = _pack_wiretype(pf->id << 3 | WT_LEND , s);
	if (len < 0) {
		return len;
	}
	ret += len;
	len = _pack_wiretype(input_slice->len , s);
	if (len < 0) {
		return len;
	}
	ret += len;
	if (input_slice->len > s->len)
		return -1;
	memcpy(s->buffer , input_slice->buffer, input_slice->len);
	ret += input_slice->len;
	s->buffer = (char *)s->buffer + input_slice->len;
	s->len -= input_slice->len;

	return ret;
_number:
	len = _pack_wiretype(pf->id << 3 | wiretype , s);
	if (len < 0) {
		return len;
	}
	ret += len;
	len = _pack_number(pf->ptype, ctype , s, input);
	if (len < 0) {
		return len;
	}
	ret += len;

	return ret;
}

static int 
_pack_repeated(struct _pattern_field *pf , struct pbc_slice *s, pbc_array array) {
	int n = pbc_array_size(array);
	int ret = 0;
	if (n>0) {
		int i;
		for (i=0;i<n;i++) {
			int len = _pack_field(pf , CTYPE_VAR , s , _pbcA_index_p(array , i));
			if (len < 0)
				return len;
			ret += len;
		}
	}
	return ret;
}

static int
_pack_packed_fixed(struct _pattern_field *pf , int width, struct pbc_slice *s, pbc_array array) {
	int len;
	int n = pbc_array_size(array);
	len = _pack_wiretype(n * width , s);
	if (len < 0) {
		return len;
	}
	if (s->len - len <  n * width)
		return -1;
	int i;
	for (i=0;i<n;i++) {
		_pack_number(pf->ptype, CTYPE_VAR , s, _pbcA_index_p(array, i));
	}

	return len + n * width;
}

static int
_pack_packed_varint(struct _pattern_field *pf , struct pbc_slice *slice, pbc_array array) {
	struct pbc_slice s = * slice;
	int n = pbc_array_size(array);
	int estimate = n; 
	int estimate_len = _pack_wiretype(estimate , &s);
	if (estimate_len < 0) {
		return -1;
	}
	int i;
	int packed_len = 0;
	for (i=0;i<n;i++) {
		int len	= _pack_number(pf->ptype, CTYPE_VAR , &s, _pbcA_index_p(array, i));
		if (len < 0)
			return -1;
		packed_len += len;
	}
	if (packed_len == estimate) {
		*slice = s;
		return packed_len + estimate_len;
	}
	uint8_t temp[10];
	struct pbc_slice header_slice = { temp , 10 };
	int header_len = _pack_wiretype(packed_len , &header_slice);
	if (header_len == estimate_len) {
		memcpy(slice->buffer , temp , header_len);
		*slice = s;
		return packed_len + estimate_len;
	}
	if (header_len + packed_len > slice->len)
		return -1;
	memmove((char *)slice->buffer + header_len , (char *)slice->buffer + estimate_len, packed_len);
	memcpy(slice->buffer , temp , header_len);
	slice->buffer = (char *)slice->buffer + packed_len + header_len;
	slice->len -= packed_len + header_len;
	return packed_len + header_len;
}

static int 
_pack_packed(struct _pattern_field *pf , struct pbc_slice *s, pbc_array array) {
	int n = pbc_array_size(array);
	if (n == 0)
		return 0;

	int ret = 0;
	int len;
	len = _pack_wiretype(pf->id << 3 | WT_LEND , s);
	if (len < 0) {
		return len;
	}
	ret += len;

	switch (pf->ptype) {
	case PTYPE_FIXED64:
	case PTYPE_SFIXED64:
	case PTYPE_DOUBLE:
		len = _pack_packed_fixed(pf, 8, s , array);
		if (len < 0)
			return len;
		break;
	case PTYPE_FIXED32:
	case PTYPE_SFIXED32:
	case PTYPE_FLOAT:
		len = _pack_packed_fixed(pf, 4, s , array);
		if (len < 0)
			return len;
		break;
	case PTYPE_UINT64:
	case PTYPE_INT64:
	case PTYPE_INT32:
	case PTYPE_BOOL:
	case PTYPE_UINT32:
	case PTYPE_ENUM:
	case PTYPE_SINT32:
	case PTYPE_SINT64:
		len = _pack_packed_varint(pf, s, array);
		if (len < 0)
			return len;
		break;
	}
	ret += len;

	return ret;
}

static bool
_is_default(struct _pattern_field * pf, void * in) {
	switch (pf->ctype) {
	case CTYPE_INT64: {
		struct longlong * d64 = &pf->defv->integer;
		return ((uint64_t)d64->low | (uint64_t)d64->hi << 32) == *(uint64_t *)in;
	}
	case CTYPE_DOUBLE: 
		return pf->defv->real == *(double *)in;
	case CTYPE_FLOAT:
		return (float)(pf->defv->real) == *(float *)in;
	case CTYPE_INT32:
		return pf->defv->integer.low == *(uint32_t *)in;
	case CTYPE_INT16:
		return (uint16_t)(pf->defv->integer.low) == *(uint16_t *)in;
	case CTYPE_INT8:
		return (uint8_t)(pf->defv->integer.low) == *(uint8_t *)in;
	case CTYPE_BOOL:
		if (pf->defv->integer.low)
			return *(bool *)in == true;
		else
			return *(bool *)in == false;
	}
	if (pf->ptype == PTYPE_STRING) {
		struct pbc_slice *slice = (struct pbc_slice *)in;
		if (slice->buffer == NULL) {
			return pf->defv->s.str[0] == '\0';
		}
		int len = slice->len;
		if (len <= 0) {
			return strcmp(pf->defv->s.str, (const char *)slice->buffer) == 0;
		}
		return len == pf->defv->s.len && memcmp(pf->defv->s.str, slice->buffer, len)==0;
	}

	return false;
}

int 
pbc_pattern_pack(struct pbc_pattern *pat, void *input, struct pbc_slice * s)
{
	struct pbc_slice slice = *s;
	int i;
	for (i=0;i<pat->count;i++) {
		struct _pattern_field * pf = &pat->f[i];
		void * in = (char *)input + pf->offset;
		int len = 0;
		switch(pf->label) {
		case LABEL_OPTIONAL:
			if (_is_default(pf , in)) {
				break;
			}
		case LABEL_REQUIRED:
			len = _pack_field(pf, pf->ctype, &slice, in);
			break;
		case LABEL_REPEATED:
			len = _pack_repeated(pf, &slice , (struct _pbc_array *)in);
			break;
		case LABEL_PACKED:
			len = _pack_packed(pf, &slice , (struct _pbc_array *)in);
			break;
		}
		if (len < 0) {
			return len;
		}
	}
	int len = (char *)slice.buffer - (char *)s->buffer;
	int ret = s->len - len;
	s->len = len;
	return ret;
}

int 
pbc_pattern_unpack(struct pbc_pattern *pat, struct pbc_slice *s, void * output) {
	if (s->len == 0) {
		pbc_pattern_set_default(pat, output);
		return 0;
	}
	pbc_ctx _ctx;
	int r = _pbcC_open(_ctx, s->buffer, s->len);
	if (r <= 0) {
		pat->env->lasterror = "Pattern unpack open context error";
		_pbcC_close(_ctx);
		return r-1;
	}

	struct context * ctx = (struct context *)_ctx;
	bool * field = (bool *)alloca(pat->count * sizeof(bool));
	memset(field, 0, pat->count * sizeof(bool));

	int i;
	int fc = 0;

	for (i=0;i<ctx->number;i++) {
		struct _pattern_field * f = bsearch_pattern(pat, ctx->a[i].wire_id >> 3);
		if (f) {
			int index = f - pat->f;
			if (field[index] == false) {
				field[index] = true;
				++fc;
				if ((f->ctype == CTYPE_ARRAY || f->ctype == CTYPE_PACKED)) {
					struct _pbc_array *array = (struct _pbc_array *)((char *)output + f->offset);
					_pbcA_open(array);
				}
			}
			char * out = (char *)output + f->offset;
			if (unpack_field(f->ctype , f->ptype , ctx->buffer , &ctx->a[i], out) < 0) {
				int j;
				for (j=0;j<pat->count;j++) {
					if (field[j] == true && (pat->f[j].ctype == CTYPE_ARRAY || pat->f[j].ctype == CTYPE_PACKED)) {
						void *array = (char *)output + pat->f[j].offset;
						_pbcA_close((struct _pbc_array *)array);
					}
				}
				_pbcC_close(_ctx);
				pat->env->lasterror = "Pattern unpack field error";
				return -i-1;
			}
		}
	}
	_pbcC_close(_ctx);
	if (fc != pat->count) {
		for (i=0;i<pat->count;i++) {
			if (field[i] == false) {
				_pattern_set_default(&pat->f[i], (char *)output);
			}
		}
	}
	return 0;
}

/* 
	format : key %type
	%f float
	%F double
	%d int32
	%D int64
	%b bool
	%h int16
	%c int8
	%s slice
	%a array
*/

static int
_ctype(const char * ctype) {
	if (ctype[0]!='%')
		return -1;
	switch (ctype[1]) {
	case 'f':
		return CTYPE_FLOAT;
	case 'F':
		return CTYPE_DOUBLE;
	case 'd':
		return CTYPE_INT32;
	case 'D':
		return CTYPE_INT64;
	case 'b':
		return CTYPE_BOOL;
	case 'h':
		return CTYPE_INT16;
	case 'c':
		return CTYPE_INT8;
	case 's':
		return CTYPE_VAR;
	case 'a':
		return CTYPE_ARRAY;
	default:
		return -1;
	}
}

static int
_ctype_size(const char *ctype) {
	switch (ctype[1]) {
	case 'f':
		return sizeof(float);
	case 'F':
		return sizeof(double);
	case 'd':
		return sizeof(int32_t);
	case 'D':
		return sizeof(int64_t);
	case 'b':
		return sizeof(bool);
	case 'h':
		return sizeof(int16_t);
	case 'c':
		return sizeof(int8_t);
	case 's':
		return sizeof(struct pbc_slice);
	case 'a':
		return sizeof(pbc_array);
	default:
		return 0;
	}
}

static const char *
_copy_string(const char *format , char ** temp) {
	char * output = *temp;
	while (*format == ' ' || *format == '\t' || *format == '\n' || *format == '\r') {
		++format;
	}
	while (*format != '\0' &&
		*format != ' ' &&
		*format != '\t' &&
		*format != '\n' &&
		*format != '\r') {
		*output = *format;
		++output;
		++format;
	}
	*output = '\0';
	++output;
	*temp = output;

	return format;
}

static int
_scan_pattern(const char * format , char * temp) {
	int n = 0;
	for(;;) {
		format = _copy_string(format , &temp);
		if (format[0] == '\0')
			return 0;
		++n;
		format = _copy_string(format , &temp);
		if (format[0] == '\0')
			return n;
	} 
}

static int 
_comp_field(const void * a, const void * b) {
	const struct _pattern_field * fa = (const struct _pattern_field *)a;
	const struct _pattern_field * fb = (const struct _pattern_field *)b;

	return fa->id - fb->id;
}

struct pbc_pattern *
_pbcP_new(struct pbc_env * env, int n) {
	size_t sz = sizeof(struct pbc_pattern) + (sizeof(struct _pattern_field)) * (n-1);
	struct pbc_pattern * ret = (struct pbc_pattern *)malloc(sz);
	memset(ret, 0 , sz);
	ret->count = n;
	ret->env = env;
	return ret;
}

static int
_check_ctype(struct _field * field, struct _pattern_field *f) {
	if (field->label == LABEL_REPEATED) {
		return f->ctype != CTYPE_ARRAY;
	} 
	if (field->label == LABEL_PACKED) {
		return f->ctype != CTYPE_PACKED;
	}
	if (field->type == PTYPE_STRING || field->type == PTYPE_MESSAGE || field->type == PTYPE_BYTES) {
		return f->ctype != CTYPE_VAR;
	}
	if (field->type == PTYPE_FLOAT || field->type == PTYPE_DOUBLE) {
		return !(f->ctype == CTYPE_DOUBLE || f->ctype == CTYPE_FLOAT);
	}
	if (field->type == PTYPE_ENUM) {
		return !(f->ctype == CTYPE_INT8 || 
			f->ctype == CTYPE_INT8 || 
			f->ctype == CTYPE_INT16 ||
			f->ctype == CTYPE_INT32 ||
			f->ctype == CTYPE_INT64);
	}

	return f->ctype == CTYPE_VAR || f->ctype == CTYPE_ARRAY || f->ctype == CTYPE_PACKED ||
		f->ctype == CTYPE_DOUBLE || f->ctype == CTYPE_FLOAT;
}

struct pbc_pattern *
_pattern_new(struct _message *m, const char *format) {
	int len = strlen(format);
	char * temp = (char *)alloca(len+1);
	int n = _scan_pattern(format, temp);
	struct pbc_pattern * pat = _pbcP_new(m->env, n);
	int i;

	const char *ptr = temp;

	int offset = 0;

	for (i=0;i<n;i++) {
		struct _pattern_field * f = &(pat->f[i]);
		struct _field * field = (struct _field *)_pbcM_sp_query(m->name, ptr);
		if (field == NULL) {
			m->env->lasterror = "Pattern @new query none exist field";
			goto _error;
		}
		f->id = field->id;
		f->ptype = field->type;
		*f->defv = *field->default_v;
		f->offset = offset;
		f->label = field->label;
		ptr += strlen(ptr) + 1;
		f->ctype = _ctype(ptr);
		if (f->ctype < 0) {
			m->env->lasterror = "Pattern @new use an invalid ctype";
			goto _error;
		}
		
		if (f->ctype == CTYPE_ARRAY && field->label == LABEL_PACKED) {
			f->ctype = CTYPE_PACKED;
		}
		if (_check_ctype(field, f)) {
			m->env->lasterror = "Pattern @new ctype check error";
			goto _error;
		}

		offset += _ctype_size(ptr);
		ptr += strlen(ptr) + 1;
	}

	pat->count = n;

	qsort(pat->f , n , sizeof(struct _pattern_field), _comp_field);
	return pat;
_error:
	free(pat);
	return NULL;
}

struct pbc_pattern * 
pbc_pattern_new(struct pbc_env * env , const char * message, const char * format, ... ) {
	struct _message *m = _pbcP_get_message(env, message);
	if (m==NULL) {
		env->lasterror = "Pattern new can't find proto";
		return NULL;
	}
	if (format[0]=='@') {
		return _pattern_new(m , format+1);
	}

	int len = strlen(format);
	char * temp = (char *)alloca(len+1);
	int n = _scan_pattern(format, temp);
	struct pbc_pattern * pat = _pbcP_new(env, n);
	int i;
	va_list ap;
	va_start(ap , format);

	const char *ptr = temp;

	for (i=0;i<n;i++) {
		struct _pattern_field * f = &(pat->f[i]);
		struct _field * field = (struct _field *)_pbcM_sp_query(m->name, ptr);
		if (field == NULL) {
			env->lasterror = "Pattern new query none exist field";
			goto _error;
		}
		f->id = field->id;
		f->ptype = field->type;
		*f->defv = *field->default_v;
		f->offset = va_arg(ap, int);
		f->label = field->label;

		ptr += strlen(ptr) + 1;

		f->ctype = _ctype(ptr);
		if (f->ctype < 0) {
			env->lasterror = "Pattern new use an invalid ctype";
			goto _error;
		}
		if (f->ctype == CTYPE_ARRAY && field->label == LABEL_PACKED) {
			f->ctype = CTYPE_PACKED;
		}
		if (_check_ctype(field, f)) {
			env->lasterror = "Pattern new ctype check error";
			goto _error;
		}

		ptr += strlen(ptr) + 1;
	}

	va_end(ap);

	pat->count = n;

	qsort(pat->f , n , sizeof(struct _pattern_field), _comp_field);
	return pat;
_error:
	free(pat);
	return NULL;
}

void 
pbc_pattern_delete(struct pbc_pattern * pat) {
	free(pat);
}