netcdf-c/libcdmr/ast_runtime.c

719 lines
19 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "config.h"
#include <ast_runtime.h>
#include <ast_internal.h>
#include <ast_debug.h>
/**
* 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(i<VARINTMAX64) {
size_t count = ast_read(rt,1,byte);
if(count < 1) return rt->err;
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;i<repeater->count;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;i<repeater->count;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;
}