pbc4hs-0.1.0.0: cbits/register.c
#include "pbc.h"
#include "proto.h"
#include "alloc.h"
#include "map.h"
#include "bootstrap.h"
#include "context.h"
#include "stringpool.h"
#include <string.h>
#include <stdlib.h>
#ifdef _MSC_VER
#define strtoll _strtoi64
#endif
static const char *
_concat_name(struct _stringpool *p , const char *prefix , int prefix_sz , const char *name , int name_sz, int *sz) {
if (prefix_sz == 0) {
if (sz) {
*sz = name_sz;
}
return _pbcS_build(p , name, name_sz);
}
char * temp = (char *)alloca(name_sz + prefix_sz + 2);
memcpy(temp,prefix,prefix_sz);
temp[prefix_sz] = '.';
memcpy(temp+prefix_sz+1,name,name_sz);
temp[name_sz + prefix_sz + 1] = '\0';
if (sz) {
*sz = name_sz + prefix_sz + 1;
}
const char * ret = _pbcS_build(p , temp, name_sz + prefix_sz + 1);
return ret;
}
static void
_register_enum(struct pbc_env *p, struct _stringpool *pool, struct pbc_rmessage * enum_type, const char *prefix, int prefix_sz) {
int field_count = pbc_rmessage_size(enum_type, "value");
struct map_kv *table = (struct map_kv *)malloc(field_count * sizeof(struct map_kv));
int i;
for (i=0;i<field_count;i++) {
struct pbc_rmessage * value = pbc_rmessage_message(enum_type, "value", i);
int enum_name_sz;
const char *enum_name = pbc_rmessage_string(value , "name" , 0 , &enum_name_sz);
table[i].pointer = (void *)_pbcS_build(pool, enum_name , enum_name_sz);
table[i].id = pbc_rmessage_integer(value , "number", 0 , 0);
}
int name_sz;
const char * name = pbc_rmessage_string(enum_type, "name", 0 , &name_sz);
const char *temp = _concat_name(pool, prefix , prefix_sz , name , name_sz, NULL);
_pbcP_push_enum(p,temp,table,field_count);
free(table);
}
static void
_set_default(struct _stringpool *pool, struct _field *f , int ptype, const char *value, int sz) {
if (value == NULL || sz == 0) {
if (f->type == PTYPE_STRING || f->type == PTYPE_BYTES) {
f->default_v->s.str = "";
f->default_v->s.len = 0;
} else {
f->default_v->integer.low = 0;
f->default_v->integer.hi = 0;
}
return;
}
switch (f->type) {
case PTYPE_DOUBLE:
case PTYPE_FLOAT:
f->default_v->real = strtod(value,NULL);
break;
case PTYPE_STRING:
f->default_v->s.str = _pbcS_build(pool, value , sz);
f->default_v->s.len = sz;
break;
case PTYPE_ENUM:
// enum default value will be converted to f->default_v->e in bootstrap.c : set_field_one()
f->default_v->s.str = value;
f->default_v->s.len = sz;
break;
case PTYPE_BOOL:
if (strcmp(value,"true") == 0) {
f->default_v->integer.low = 1;
} else {
f->default_v->integer.low = 0;
}
f->default_v->integer.hi = 0;
break;
case PTYPE_UINT64:
case PTYPE_INT64:
case PTYPE_SFIXED64:
case PTYPE_SINT64: {
long long v = strtoll(value, NULL, 10);
f->default_v->integer.low = (long) v;
f->default_v->integer.hi = (long)(v >> 32);
break;
}
case PTYPE_INT32:
case PTYPE_FIXED32:
case PTYPE_SFIXED32:
case PTYPE_SINT32: {
int low = strtol(value, NULL, 10);
f->default_v->integer.low = low;
if (low < 0) {
f->default_v->integer.hi = -1;
} else {
f->default_v->integer.hi = 0;
}
break;
}
case PTYPE_UINT32:
f->default_v->integer.low = strtoul(value, NULL, 10);
f->default_v->integer.hi = 0;
break;
case PTYPE_BYTES:
case PTYPE_MESSAGE:
// bytes and message types have no default value
f->default_v->m.buffer = 0;
f->default_v->m.len = 0;
break;
default:
f->default_v->integer.low = 0;
f->default_v->integer.hi = 0;
break;
}
}
static void
_register_field(struct pbc_rmessage * field, struct _field * f, struct _stringpool *pool) {
f->id = pbc_rmessage_integer(field, "number", 0 , 0);
f->type = pbc_rmessage_integer(field, "type", 0 , 0); // enum
f->label = pbc_rmessage_integer(field, "label", 0, 0) - 1; // LABEL_OPTIONAL = 0
if (pbc_rmessage_size(field , "options") > 0) {
struct pbc_rmessage * options = pbc_rmessage_message(field, "options" , 0);
int packed = pbc_rmessage_integer(options , "packed" , 0 , NULL);
if (packed) {
f->label = LABEL_PACKED;
}
}
f->type_name.n = pbc_rmessage_string(field, "type_name", 0 , NULL) +1; // abandon prefix '.'
int vsz;
const char * default_value = pbc_rmessage_string(field, "default_value", 0 , &vsz);
_set_default(pool , f , f->type, default_value , vsz);
}
static void
_register_extension(struct pbc_env *p, struct _stringpool *pool , const char * prefix, int prefix_sz, struct pbc_rmessage * msg, pbc_array queue) {
int extension_count = pbc_rmessage_size(msg , "extension");
if (extension_count <= 0)
return;
int i;
const char * last = NULL;
for (i=0;i<extension_count;i++) {
struct pbc_rmessage * extension = pbc_rmessage_message(msg, "extension", i);
int field_name_sz = 0;
struct _field f;
const char * field_name = pbc_rmessage_string(extension , "name" , 0, &field_name_sz);
f.name = _concat_name(pool, prefix, prefix_sz, field_name, field_name_sz, NULL);
_register_field(extension, &f , pool);
const char * extendee = pbc_rmessage_string(extension , "extendee" , 0, NULL);
_pbcP_push_message(p, extendee + 1 , &f , queue);
if (last == NULL) {
last = extendee;
} else if (strcmp(extendee,last) != 0) {
_pbcP_init_message(p, last+1);
last = extendee;
}
}
_pbcP_init_message(p, last+1);
}
static void
_register_message(struct pbc_env *p, struct _stringpool *pool, struct pbc_rmessage * message_type, const char *prefix, int prefix_sz, pbc_array queue) {
int name_sz;
const char * name = pbc_rmessage_string(message_type, "name", 0 , &name_sz);
int sz = 0;
const char *temp = _concat_name(pool, prefix , prefix_sz , name , name_sz, &sz);
int field_count = pbc_rmessage_size(message_type, "field");
int i;
for (i=0;i<field_count;i++) {
struct pbc_rmessage * field = pbc_rmessage_message(message_type, "field" , i);
struct _field f;
int field_name_sz;
const char * field_name = pbc_rmessage_string(field, "name", 0 , &field_name_sz);
f.name = _pbcS_build(pool,field_name,field_name_sz);
_register_field(field, &f , pool);
_pbcP_push_message(p, temp , &f , queue);
}
_pbcP_init_message(p, temp);
_register_extension(p, pool, temp, sz,message_type, queue);
// nested enum
int enum_count = pbc_rmessage_size(message_type, "enum_type");
for (i=0;i<enum_count;i++) {
struct pbc_rmessage * enum_type = pbc_rmessage_message(message_type, "enum_type", i);
_register_enum(p, pool, enum_type, temp, sz);
}
// nested type
int message_count = pbc_rmessage_size(message_type, "nested_type");
for (i=0;i<message_count;i++) {
struct pbc_rmessage * nested_type = pbc_rmessage_message(message_type, "nested_type", i);
_register_message(p, pool, nested_type, temp, sz, queue);
}
}
static void
_register(struct pbc_env *p, struct pbc_rmessage * file, struct _stringpool *pool) {
int package_sz;
const char *package = pbc_rmessage_string(file, "package", 0, &package_sz);
pbc_array queue;
_pbcA_open(queue);
int enum_count = pbc_rmessage_size(file, "enum_type");
int i;
for (i=0;i<enum_count;i++) {
struct pbc_rmessage * enum_type = pbc_rmessage_message(file, "enum_type", i);
_register_enum(p, pool , enum_type, package, package_sz);
}
int message_count = pbc_rmessage_size(file, "message_type");
for (i=0;i<message_count;i++) {
struct pbc_rmessage * message_type = pbc_rmessage_message(file, "message_type", i);
_register_message(p, pool, message_type, package, package_sz, queue);
}
_register_extension(p, pool, package, package_sz, file , queue);
_pbcB_register_fields(p, queue);
_pbcA_close(queue);
}
#define CHECK_FILE_OK 0
#define CHECK_FILE_EXIST 1
#define CHECK_FILE_DEPENDENCY 2
static int
_check_file_name(struct pbc_env * p , struct pbc_rmessage * file, const char ** fname) {
const char * filename = pbc_rmessage_string(file, "name", 0, NULL);
// printf("reg :%s\n",filename);
if (_pbcM_sp_query(p->files, filename)) {
return CHECK_FILE_EXIST;
}
int sz = pbc_rmessage_size(file, "dependency");
int i;
for (i=0;i<sz;i++) {
const char *dname = pbc_rmessage_string(file,"dependency",i,NULL);
// printf("dependency :%s\n",dname);
if (_pbcM_sp_query(p->files, dname) == NULL) {
return CHECK_FILE_DEPENDENCY;
}
}
*fname = filename;
return CHECK_FILE_OK;
}
static int
_register_no_dependency(struct pbc_env * p,struct pbc_rmessage ** files , int n ) {
int r = 0;
int i;
for (i=0;i<n;i++) {
if (files[i] == NULL)
continue;
const char *filename = NULL;
int err = _check_file_name(p, files[i], &filename);
switch(err) {
case CHECK_FILE_EXIST:
break;
case CHECK_FILE_DEPENDENCY:
++r;
break;
case CHECK_FILE_OK: {
struct _stringpool *pool = _pbcS_new();
filename = _pbcS_build(pool, filename , strlen(filename));
_pbcM_sp_insert(p->files , filename, pool);
_register(p,files[i],pool);
files[i] = NULL;
}
break;
}
}
return r;
}
int
pbc_register(struct pbc_env * p, struct pbc_slice *slice) {
struct pbc_rmessage * message = pbc_rmessage_new(p, "google.protobuf.FileDescriptorSet", slice);
if (message == NULL) {
p->lasterror = "register open google.protobuf.FileDescriptorSet fail";
return 1;
}
int n = pbc_rmessage_size(message, "file");
struct pbc_rmessage ** files = (struct pbc_rmessage **)alloca(n * sizeof(struct pbc_rmessage *));
int i;
if (n == 0) {
p->lasterror = "register empty";
goto _error;
}
for (i=0;i<n;i++) {
files[i] = pbc_rmessage_message(message, "file", i);
if (files[i] == NULL) {
p->lasterror = "register open fail";
goto _error;
}
}
int r = n;
do {
int rr = _register_no_dependency(p,files , n);
if (rr == r) {
p->lasterror = "register dependency error";
goto _error;
}
r = rr;
} while (r>0);
pbc_rmessage_delete(message);
return 0;
_error:
pbc_rmessage_delete(message);
return 1;
}