#include #include #include #include #include "config.h" #include #include #include /** * Define MAXWIRELEN to be at least big enough * to hold instance of any possible primitives * except string and byte and as encoded using any * of the possible encodings (e.g. int32, int64, varint, etc) * => Must hold at least lub(64/7) = 10 */ #define MAXWIRELEN 16 /* max length of a 32 varint Must hold at least lub(32/7) = 5 */ #define VARINTMAX32 5 /* max length of a 64 varint Must hold at least lub(64/7) = 10 */ #define VARINTMAX64 10 #define VARINTMAX VARINTMAX64 /* Define the maximum c-type size; should be max(sizeof(bytes_t),sizeof(uint64_t) */ #define MAXCTYPESIZE (sizeof(bytes_t)>sizeof(uint64_t)?sizeof(bytes_t):sizeof(uint64_t)) /* Map the primitives (ast_sort) to the associated wiretype */ static int sort_wiretype_map[] = { ast_64bit, /*ast_double*/ ast_32bit, /*ast_float*/ ast_varint, /*ast_int32*/ ast_varint, /*ast_int64*/ ast_varint, /*ast_uint32*/ ast_varint, /*ast_uint64*/ ast_varint, /*ast_sint32*/ ast_varint, /*ast_sint64*/ ast_32bit, /*ast_fixed32*/ ast_64bit, /*ast_fixed64*/ ast_32bit, /*ast_sfixed32*/ ast_64bit, /*ast_sfixed64*/ ast_counted, /*ast_string*/ ast_counted, /*ast_bytes*/ ast_varint, /*ast_bool*/ ast_varint, /*ast_enum*/ }; enum Sortsize { sizecount=0, size32=32, size64=64 }; /* Map sorts to their value sizes; 0=>counted */ #ifdef IGNORE static enum Sortsize sort_size[] = { size64, /*ast_double*/ size32, /*ast_float*/ size32, /*ast_int32*/ size64, /*ast_int64*/ size32, /*ast_uint32*/ size64, /*ast_uint64*/ size32, /*ast_sint32*/ size64, /*ast_sint64*/ size32, /*ast_fixed32*/ size64, /*ast_fixed64*/ size32, /*ast_sfixed32*/ size32, /*ast_sfixed64*/ sizecount, /*ast_string*/ sizecount, /*ast_bytes*/ size32, /*ast_bool*/ size32, /*ast_enum*/ }; #endif /* Define a null value for bytes_t */ bytes_t bytes_t_null = {0,NULL}; static int ast_readvarint(ast_runtime* rt, uint8_t* buffer, size_t* countp); /* Invoke the ast_runtime operators */ static size_t ast_write(ast_runtime* rt, size_t count, uint8_t* data) { return rt->ops->write(rt,count,data); } static size_t ast_read(ast_runtime* rt, size_t count, uint8_t* buffer) { return rt->ops->read(rt,count,buffer); } ast_err ast_mark(ast_runtime* rt, size_t avail) { return rt->ops->mark(rt,avail); } ast_err ast_unmark(ast_runtime* rt) { return rt->ops->unmark(rt); } ast_err ast_reclaim(ast_runtime* rt) { return rt->ops->reclaim(rt); } void* ast_alloc(ast_runtime* rt, size_t len) { if(len == 0) return NULL; return rt->ops->alloc(rt,len); } void ast_free(ast_runtime* rt, void* mem) { if(mem == NULL) return; return rt->ops->free(rt,mem); } /* Define the primitive W/R/F functions */ /* Read the tag and return the relevant info */ ast_err ast_read_tag(ast_runtime* rt, uint32_t* wiretypep, uint32_t* fieldnop) { ast_err status = AST_NOERR; uint8_t buffer[MAXWIRELEN]; size_t count; uint32_t key, wiretype, fieldno; /* Extract the wiretype + index */ status = ast_readvarint(rt,buffer,&count); if(status != AST_NOERR) ATHROW(status,done); /* convert from varint */ key = uint32_decode(count,buffer); /* Extract the wiretype and fieldno */ wiretype = (key & 0x7); fieldno = (key >> 3); /* return the fieldno and wiretype */ if(fieldnop) *fieldnop = fieldno; if(wiretypep) *wiretypep = wiretype; done: return ACATCH(status); } static ast_err ast_read_primitive_data(ast_runtime* rt, const ast_sort sort, void* valuep) { ast_err status = AST_NOERR; uint8_t buffer[MAXWIRELEN]; size_t count,len; uint32_t wiretype; if(valuep == NULL) AERR(status,AST_EFAIL,done); /* compute the wiretype from the sort */ wiretype = sort_wiretype_map[sort]; /* Based on the wiretype, extract the proper number of bytes */ switch (wiretype) { case ast_varint: status = ast_readvarint(rt,buffer,&len); if(status != AST_NOERR) ATHROW(status,done); break; case ast_32bit: len = 4; if(ast_read(rt,len,buffer) != len) AERR(status,AST_EOF,done); break; case ast_64bit: len = 8; if(ast_read(rt,len,buffer) != len) AERR(status,AST_EOF,done); break; case ast_counted: /* get the count */ status = ast_readvarint(rt,buffer,&len); if(status != AST_NOERR) ATHROW(status,done); count = (size_t)uint64_decode(len,buffer); break; default: ATHROW(status,done); } /* Now extract the value */ switch (sort) { case ast_enum: /* fall thru */ case ast_int32: *((int32_t*)valuep) = int32_decode(len,buffer); break; case ast_int64: *((int64_t*)valuep) = int64_decode(len,buffer); break; case ast_uint32: *((uint32_t*)valuep) = uint32_decode(len,buffer); break; case ast_uint64: *((uint64_t*)valuep) = uint64_decode(len,buffer); break; case ast_sint32: *((int32_t*)valuep) = unzigzag32(uint32_decode(len,buffer)); break; case ast_sint64: *((int64_t*)valuep) = unzigzag64(uint64_decode(len,buffer)); break; case ast_bool: *((bool_t*)valuep) = boolen_decode(len,buffer); break; case ast_fixed32: *((uint32_t*)valuep) = fixed32_decode(buffer); break; case ast_sfixed32: *((int32_t*)valuep) = fixed32_decode(buffer); break; case ast_fixed64: *((uint64_t*)valuep) = fixed64_decode(buffer); break; case ast_sfixed64: *((int64_t*)valuep) = fixed64_decode(buffer); break; case ast_float: { uint32_t value = fixed32_decode(buffer); memcpy(valuep,&value,sizeof(float)); } break; case ast_double: { uint64_t value = fixed64_decode(buffer); memcpy(valuep,&value,sizeof(double)); } break; case ast_string: { /* Count already holds the length of the string */ char* stringvalue = ast_alloc(rt,count+1); if(stringvalue == NULL) AERR(status,AST_ENOMEM,done); if(ast_read(rt,count,(uint8_t*)stringvalue) != count) AERR(status,AST_EOF,done); stringvalue[count] = '\0'; *((char**)valuep) = stringvalue; } break; case ast_bytes: { /* Count already holds the length of the byte string */ uint8_t* bytestring = ast_alloc(rt,count); if(bytestring == NULL) AERR(status,AST_ENOMEM,done); if(ast_read(rt,count,bytestring) != count) AERR(status,AST_EOF,done); ((bytes_t*)valuep)->nbytes = count; ((bytes_t*)valuep)->bytes = bytestring; } break; default: ATHROW(status,done); break; } done: return ACATCH(status); } ast_err ast_read_primitive(ast_runtime* rt, const ast_sort sort, const int fieldno,void* valuep) { ast_err status = AST_NOERR; uint32_t wiretype; if(valuep == NULL) AERR(status,AST_EFAIL,done); /* compute the wiretype from the sort */ wiretype = sort_wiretype_map[sort]; status = ast_read_primitive_data(rt,sort,valuep); if(status != AST_NOERR) goto done; done: return ACATCH(status); } static ast_err ast_write_primitive_data(ast_runtime* rt, const ast_sort sort, const int fieldno, const void* valuep) { ast_err status = AST_NOERR; uint8_t buffer[MAXWIRELEN]; size_t count; /* Write the data in proper wiretype format using the sort */ switch (sort) { case ast_int32: count = int32_encode(*(int32_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done)}; break; case ast_int64: count = int64_encode(*(int64_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_uint32: count = uint32_encode(*(uint32_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_uint64: count = uint64_encode(*(uint64_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_sint32: count = sint32_encode(*(int32_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_sint64: count = sint64_encode(*(int64_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_bool: count = boolen_encode(*(bool_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_enum: count = int32_encode(*(int32_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_fixed32: /* fall thru */ case ast_sfixed32: /* fall thru */ case ast_float: count = fixed32_encode(*(uint32_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_fixed64: /* fall thru */ case ast_sfixed64: /* fall thru */ case ast_double: count = fixed64_encode(*(uint64_t*)valuep,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} break; case ast_string: { char* stringvalue = *(char**)valuep; size_t len = strlen(stringvalue); count = uint32_encode(len,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} if(ast_write(rt,len,(uint8_t*)stringvalue) != count) {status = AST_EIO; ATHROW(status,done);} } break; case ast_bytes: { bytes_t* bytevalue = (bytes_t*)valuep; count = uint32_encode(bytevalue->nbytes,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; ATHROW(status,done);} if(ast_write(rt,bytevalue->nbytes,bytevalue->bytes) != bytevalue->nbytes) {status = AST_EIO; ATHROW(status,done);} } break; default: ATHROW(status,done); break; } done: return ACATCH(status); } static size_t ast_encode_tag(ast_runtime* rt, const uint32_t wiretype, const uint32_t fieldno, uint8_t* buffer/*[MAXWIRELEN]*/) { int key; size_t count; key = (wiretype | (fieldno << 3)); /* convert key to varint */ count = uint32_encode(key,(uint8_t*)buffer); return count; } ast_err ast_write_tag(ast_runtime* rt, const uint32_t wiretype, const uint32_t fieldno) { size_t count; uint8_t buffer[MAXWIRELEN]; count = ast_encode_tag(rt,wiretype,fieldno,buffer); /* Write it out */ if(ast_write(rt,count,buffer) != count) return AST_EIO; return AST_NOERR; } /* Procedure to write out count */ ast_err ast_write_size(ast_runtime* rt, const size_t size) { uint8_t buffer[VARINTMAX64]; /* write size as varint */ size_t len = uint64_encode((uint64_t)size,buffer); if(ast_write(rt,len,buffer) != len) return AST_EIO; return AST_NOERR; } ast_err ast_write_primitive(ast_runtime* rt, const ast_sort sort, const int fieldno, const void* valuep) { ast_err status = AST_NOERR; ast_wiretype wiretype; wiretype = sort_wiretype_map[sort]; status = ast_write_tag(rt,wiretype,fieldno); if(status != AST_NOERR) goto done; /* write the data part */ status = ast_write_primitive_data(rt,sort,fieldno,valuep); if(status != AST_NOERR) goto done; done: return status; } ast_err ast_read_size(ast_runtime* rt, size_t* sizep) { uint64_t size = 0; uint8_t buffer[VARINTMAX64]; size_t len; ast_err status = ast_readvarint(rt,buffer,&len); if(status != AST_NOERR) ATHROW(status,done); size = uint64_decode(len,buffer); if(sizep) *sizep = (size_t)size; done: return ACATCH(status); } /* Append + extend a REPEAT field */ ast_err ast_repeat_append(ast_runtime* rt, ast_sort sort,void* repeater0, void* value) { size_t sortsize = ast_ctypesize(rt,sort); ast_repeated_field* repeater = (ast_repeated_field*)repeater0; char* target; if(repeater->count == 0 || repeater->values == NULL) { repeater->count = 0; repeater->values = ast_alloc(rt,sortsize); if(repeater->values == NULL) return ACATCH(AST_ENOMEM); } else { void* newmem = ast_alloc(rt,(repeater->count+1)*sortsize); if(newmem == NULL) return ACATCH(AST_ENOMEM); memcpy(newmem,repeater->values,(repeater->count)*sortsize); repeater->values = newmem; } target = ((char*)repeater->values) + (repeater->count * sortsize); memcpy((void*)target,value,sortsize); repeater->count++; return AST_NOERR; } size_t ast_ctypesize(ast_runtime* rt, ast_sort sort) { switch(sort) { case ast_double: return sizeof(double); case ast_float: return sizeof(float); case ast_enum: case ast_bool: case ast_int32: case ast_sint32: case ast_fixed32: case ast_sfixed32: case ast_uint32: return sizeof(uint32_t); case ast_int64: case ast_sint64: case ast_fixed64: case ast_sfixed64: case ast_uint64: return sizeof(uint64_t); case ast_string: return sizeof(char*); case ast_bytes: return sizeof(bytes_t); case ast_message:return sizeof(void*); default: assert(0); } return 0; } size_t ast_get_tagsize(ast_runtime* rt, ast_sort sort, int fieldno) { size_t count; int wiretype; uint8_t buffer[MAXWIRELEN]; wiretype = sort_wiretype_map[sort]; count = ast_encode_tag(rt,wiretype,fieldno,buffer); return count; } /* Return on the wire # of bytes for this value */ size_t ast_get_size(ast_runtime* rt, ast_sort sort, void* valuep) { size_t count = 0; switch (sort) { case ast_enum: /* fall thru */ case ast_int32: count = int32_size(*(int32_t*)valuep); break; case ast_int64: count = int64_size(*(int64_t*)valuep); break; case ast_uint32: count = uint32_size(*(uint32_t*)valuep); break; case ast_uint64: count = uint64_size(*(uint64_t*)valuep); break; case ast_sint32: count = sint32_size(*(int32_t*)valuep); break; case ast_sint64: count = sint64_size(*(int64_t*)valuep); break; case ast_bool: count = 1; break; case ast_fixed32: count = 4; break; case ast_sfixed32: count = 4; break; case ast_fixed64: count = 8; break; case ast_sfixed64: count = 8; break; case ast_float: count = 4; break; case ast_double: count = 8; break; case ast_string: /* string count is size for length counter + strlen(string) */ count = 0; if(valuep != NULL) { char* stringvalue = (char*)valuep; uint32_t slen = strlen(stringvalue); count = uint32_size(slen); count += slen; } break; case ast_bytes: count = 0; if(valuep != NULL) { bytes_t* bytedata = (bytes_t*)valuep; count = uint32_size(bytedata->nbytes); count += bytedata->nbytes; } break; default: break; } return count; } /* Procedure to extract arbitrary varint bytes from input stream */ static ast_err ast_readvarint(ast_runtime* rt, uint8_t* buffer, size_t* countp) { size_t i=0; uint8_t byte[1]; while(ierr; buffer[i++] = 0x7f & byte[0]; if((0x80 & byte[0]) == 0) break; } if(countp) *countp = i; return AST_NOERR; } /* Given an unknown field, skip past it */ ast_err ast_skip_field(ast_runtime* rt, int wiretype, int fieldno) { ast_err status = AST_NOERR; uint8_t buffer[MAXWIRELEN]; size_t len; switch (wiretype) { case ast_varint: status = ast_readvarint(rt,buffer,&len); if(status != AST_NOERR) ATHROW(status,done); break; case ast_32bit: len = 4; status = ast_read(rt,len,buffer); if(status != AST_NOERR) ATHROW(status,done); break; case ast_64bit: len = 8; status = ast_read(rt,len,buffer); if(status != AST_NOERR) ATHROW(status,done); break; case ast_counted: status = ast_readvarint(rt,buffer,&len); if(status != AST_NOERR) ATHROW(status,done); /* get the count */ len = (size_t)uint64_decode(len,buffer); /* Now skip "count" bytes */ while(len > 0) { size_t n = (len > sizeof(buffer) ? sizeof(buffer) : len); size_t actual = ast_read(rt,n,buffer); if(actual < n) {status = rt->err; ATHROW(status,done);} } break; default: status = AST_EFAIL; break; } done: return ACATCH(status); } ast_err ast_write_primitive_packed(ast_runtime* rt, const ast_sort sort, const int fieldno, const void* valuep) { ast_err status = AST_NOERR; int i; uint8_t buffer[MAXWIRELEN]; size_t count,size; const ast_repeated_field* repeater = (const ast_repeated_field*)valuep; char* p; int typesize; if(repeater->count == 0) goto done; typesize = ast_ctypesize(rt,sort); /* wiretype is always ast_counted */ status = ast_write_tag(rt,ast_counted,fieldno); if(status != AST_NOERR) goto done; /* Compute the size of what is to be written */ for(p=repeater->values,size=0,i=0;icount;i++,p+=typesize) { size += ast_get_size(rt,sort,(void*)p); } /* convert size to varint and write it out */ count = uint32_encode(size,buffer); if(ast_write(rt,count,buffer) != count) {status = AST_EIO; goto done;} /* Now write each value */ for(p=repeater->values,i=0;icount;i++,p+=typesize) { status = ast_write_primitive_data(rt,sort,fieldno,(void*)p); if(status != AST_NOERR) goto done; } done: return ACATCH(status); } ast_err ast_read_primitive_packed(ast_runtime* rt, const ast_sort sort, const int fieldno, void* valuep) { ast_err status = AST_NOERR; size_t count,len; const ast_repeated_field* repeater = (const ast_repeated_field*)valuep; int typesize = ast_ctypesize(rt,sort); char* p = NULL; uint8_t varintbuf[VARINTMAX64]; uint8_t cbuffer[MAXCTYPESIZE]; if(valuep == NULL) return ACATCH(AST_EFAIL); /* wire type should always be ast_counted */ status = ast_readvarint(rt,varintbuf,&len); if(status != AST_NOERR) ATHROW(status,done); /* extract the count */ count = (size_t)uint64_decode(len,varintbuf); /* mark the runtime */ status = ast_mark(rt,count); if(status != AST_NOERR) ATHROW(status,done); /* Read until we hit the end of the mark */ for(count=0,p=(char*)valuep;status == AST_NOERR;count++,p+=typesize) { status = ast_read_primitive_data(rt,sort,cbuffer); if(status != AST_NOERR) break; status = ast_repeat_append(rt,sort,(void*)repeater,(void*)cbuffer); } if(status == AST_EOF) status = AST_NOERR; if(status != AST_NOERR) ATHROW(status,done); status = ast_unmark(rt); done: return ACATCH(status); } ast_err ast_reclaim_string(ast_runtime* rt, char* value) { ast_free(rt,value); return AST_NOERR; } ast_err ast_reclaim_bytes(ast_runtime* rt, bytes_t* value) { ast_free(rt,value->bytes); return AST_NOERR; } ast_runtime_ops* ast_byteio_getopts(ast_runtime* rt) { return rt->ops; } ast_err ast_byteio_setopts(ast_runtime* rt, ast_runtime_ops* ops) { rt->ops = ops; return AST_NOERR; }