eccodes/src/grib_accessor_class_concept.c

534 lines
16 KiB
C

/*
* Copyright 2005-2017 ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
*/
/*******************************************************
* Enrico Fucile
******************************************************/
#include "grib_api_internal.h"
/*
This is used by make_class.pl
START_CLASS_DEF
CLASS = accessor
SUPER = grib_accessor_class_gen
IMPLEMENTS = unpack_double;pack_double
IMPLEMENTS = unpack_string;pack_string;string_length
IMPLEMENTS = unpack_long;pack_long;destroy
IMPLEMENTS = init;dump;value_count;get_native_type
IMPLEMENTS = compare
END_CLASS_DEF
*/
/* START_CLASS_IMP */
/*
Don't edit anything between START_CLASS_IMP and END_CLASS_IMP
Instead edit values between START_CLASS_DEF and END_CLASS_DEF
or edit "accessor.class" and rerun ./make_class.pl
*/
static int get_native_type(grib_accessor*);
static int pack_double(grib_accessor*, const double* val,size_t *len);
static int pack_long(grib_accessor*, const long* val,size_t *len);
static int pack_string(grib_accessor*, const char*, size_t *len);
static int unpack_double(grib_accessor*, double* val,size_t *len);
static int unpack_long(grib_accessor*, long* val,size_t *len);
static int unpack_string (grib_accessor*, char*, size_t *len);
static size_t string_length(grib_accessor*);
static int value_count(grib_accessor*,long*);
static void destroy(grib_context*,grib_accessor*);
static void dump(grib_accessor*, grib_dumper*);
static void init(grib_accessor*,const long, grib_arguments* );
static void init_class(grib_accessor_class*);
static int compare(grib_accessor*, grib_accessor*);
typedef struct grib_accessor_concept {
grib_accessor att;
/* Members defined in gen */
/* Members defined in concept */
} grib_accessor_concept;
extern grib_accessor_class* grib_accessor_class_gen;
static grib_accessor_class _grib_accessor_class_concept = {
&grib_accessor_class_gen, /* super */
"concept", /* name */
sizeof(grib_accessor_concept), /* size */
0, /* inited */
&init_class, /* init_class */
&init, /* init */
0, /* post_init */
&destroy, /* free mem */
&dump, /* describes himself */
0, /* get length of section */
&string_length, /* get length of string */
&value_count, /* get number of values */
0, /* get number of bytes */
0, /* get offset to bytes */
&get_native_type, /* get native type */
0, /* get sub_section */
0, /* grib_pack procedures long */
0, /* grib_pack procedures long */
&pack_long, /* grib_pack procedures long */
&unpack_long, /* grib_unpack procedures long */
&pack_double, /* grib_pack procedures double */
&unpack_double, /* grib_unpack procedures double */
&pack_string, /* grib_pack procedures string */
&unpack_string, /* grib_unpack procedures string */
0, /* grib_pack array procedures string */
0, /* grib_unpack array procedures string */
0, /* grib_pack procedures bytes */
0, /* grib_unpack procedures bytes */
0, /* pack_expression */
0, /* notify_change */
0, /* update_size */
0, /* preferred_size */
0, /* resize */
0, /* nearest_smaller_value */
0, /* next accessor */
&compare, /* compare vs. another accessor */
0, /* unpack only ith value */
0, /* unpack a subarray */
0, /* clear */
0, /* clone accessor */
};
grib_accessor_class* grib_accessor_class_concept = &_grib_accessor_class_concept;
static void init_class(grib_accessor_class* c)
{
c->next_offset = (*(c->super))->next_offset;
c->byte_count = (*(c->super))->byte_count;
c->byte_offset = (*(c->super))->byte_offset;
c->sub_section = (*(c->super))->sub_section;
c->pack_missing = (*(c->super))->pack_missing;
c->is_missing = (*(c->super))->is_missing;
c->pack_string_array = (*(c->super))->pack_string_array;
c->unpack_string_array = (*(c->super))->unpack_string_array;
c->pack_bytes = (*(c->super))->pack_bytes;
c->unpack_bytes = (*(c->super))->unpack_bytes;
c->pack_expression = (*(c->super))->pack_expression;
c->notify_change = (*(c->super))->notify_change;
c->update_size = (*(c->super))->update_size;
c->preferred_size = (*(c->super))->preferred_size;
c->resize = (*(c->super))->resize;
c->nearest_smaller_value = (*(c->super))->nearest_smaller_value;
c->next = (*(c->super))->next;
c->unpack_double_element = (*(c->super))->unpack_double_element;
c->unpack_double_subarray = (*(c->super))->unpack_double_subarray;
c->clear = (*(c->super))->clear;
c->make_clone = (*(c->super))->make_clone;
}
/* END_CLASS_IMP */
#define MAX_CONCEPT_STRING_LENGTH 255
/* Note: A fast cut-down version of strcmp which does NOT return -1 */
/* 0 means input strings are equal and 1 means not equal */
GRIB_INLINE static int grib_inline_strcmp(const char* a,const char* b)
{
if (*a != *b) return 1;
while((*a!=0 && *b!=0) && *(a) == *(b) ) {a++;b++;}
return (*a==0 && *b==0) ? 0 : 1;
}
static void init(grib_accessor* a, const long len , grib_arguments* args )
{
a->length = 0;
}
static void dump(grib_accessor* a, grib_dumper* dumper)
{
grib_dump_string(dumper,a,NULL);
}
static int concept_condition_expression_true(grib_handle* h,grib_concept_condition* c) {
long lval;
double dval;
long lres=0;
double dres=0.0;
const char *cval;
char buf[80];
char tmp[80];
size_t len = sizeof(buf);
size_t size=sizeof(tmp);
int ok = 0;
int err=0;
int type = grib_expression_native_type(h,c->expression);
switch(type)
{
case GRIB_TYPE_LONG:
grib_expression_evaluate_long(h,c->expression,&lres);
ok = (grib_get_long(h,c->name,&lval) == GRIB_SUCCESS) &&
(lval == lres);
break;
case GRIB_TYPE_DOUBLE:
grib_expression_evaluate_double(h,c->expression,&dres);
ok = (grib_get_double(h,c->name,&dval) == GRIB_SUCCESS) &&
(dval == dres);
break;
case GRIB_TYPE_STRING:
ok = (grib_get_string(h,c->name,buf,&len) == GRIB_SUCCESS) &&
((cval = grib_expression_evaluate_string(h,c->expression,tmp,&size,&err)) != NULL) &&
(err==0) && (grib_inline_strcmp(buf,cval) == 0);
break;
default:
/* TODO: */
break;
}
return ok;
}
static int concept_condition_iarray_true(grib_handle* h,grib_concept_condition* c) {
long *val;
size_t size=0,i;
int ret;
int err=0;
err=grib_get_size(h,c->name,&size);
if (err==0 || size!=grib_iarray_used_size(c->iarray)) return 0;
val=(long*)grib_context_malloc_clear(h->context,sizeof(long)*size);
err=grib_get_long_array(h,c->name,val,&size);
if (err==0) return 0;
ret=1;
for (i=0;i<size;i++) {
if (val[i]!=c->iarray->v[i]) {
ret=0;
break;
}
}
return ret;
}
static int concept_condition_true(grib_handle* h,grib_concept_condition* c) {
if (c->expression==NULL) return concept_condition_iarray_true(h,c);
else return concept_condition_expression_true(h,c);
}
static const char* concept_evaluate(grib_accessor* a)
{
int match = 0;
const char* best = 0;
/* const char* prev = 0; */
grib_concept_value* c = action_concept_get_concept(a);
grib_handle* h=grib_handle_of_accessor(a);
while(c)
{
grib_concept_condition* e = c->conditions;
int cnt = 0;
while(e)
{
if(!concept_condition_true(h,e)) break;
e = e->next;
cnt++;
}
if(e == NULL)
{
if(cnt >= match) {
/* prev = (cnt > match) ? NULL : best; */
match = cnt;
best = c->name;
}
}
c = c->next;
}
return best;
}
#define MAX_NUM_CONCEPT_VALUES 40
static int concept_conditions_expression_apply(grib_handle* h,grib_concept_condition* e,grib_values* values,grib_sarray* sa,int* n)
{
long lres=0;
double dres=0.0;
int count=*n;
size_t size;
int err=0;
Assert(count<1024);
values[count].name = e->name;
values[count].type = grib_expression_native_type(h,e->expression);
switch(values[count].type)
{
case GRIB_TYPE_LONG:
grib_expression_evaluate_long(h,e->expression,&lres);
values[count].long_value = lres;
break;
case GRIB_TYPE_DOUBLE:
grib_expression_evaluate_double(h,e->expression,&dres);
values[count].double_value = dres;
break;
case GRIB_TYPE_STRING:
size = sizeof(sa->v[count]);
values[count].string_value = grib_expression_evaluate_string(h,e->expression,sa->v[count],&size,&err);
break;
default:
return GRIB_NOT_IMPLEMENTED;
break;
}
(*n)++;
return err;
}
static int concept_conditions_iarray_apply(grib_handle* h,grib_concept_condition* c)
{
size_t size=grib_iarray_used_size(c->iarray);
return grib_set_long_array(h,c->name,c->iarray->v,size);
}
static int concept_conditions_apply(grib_handle* h,grib_concept_condition* c,grib_values* values,grib_sarray* sa,int* n)
{
if (c->expression==NULL) return concept_conditions_iarray_apply(h,c);
else return concept_conditions_expression_apply(h,c,values,sa,n);
}
static int cmpstringp(const void *p1, const void *p2)
{
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
static int grib_concept_apply(grib_accessor* a, const char* name)
{
int err=0;
int count = 0;
grib_concept_condition* e=NULL;
grib_values values[1024];
grib_sarray* sa=NULL;
grib_concept_value* c=NULL;
grib_concept_value* concepts = action_concept_get_concept(a);
grib_handle* h=grib_handle_of_accessor(a);
grib_action* act=a->creator;
int nofail=action_concept_get_nofail(a);
Assert(concepts!=NULL);
c=(grib_concept_value*)grib_trie_get(concepts->index,name);
if (!c) c=(grib_concept_value*)grib_trie_get(concepts->index,"default");
if (!c){
err= nofail ? GRIB_SUCCESS : GRIB_CONCEPT_NO_MATCH;
if (err) {
size_t i = 0, concept_count = 0;
char* all_concept_vals[MAX_NUM_CONCEPT_VALUES] = {NULL,}; /* sorted array containing concept values */
grib_concept_value* pCon = concepts;
grib_context_log(h->context,GRIB_LOG_ERROR, "concept: no match for %s=%s", act->name,name);
/* Create a list of all possible values for this concept and sort it */
while (pCon) {
if (i >= MAX_NUM_CONCEPT_VALUES)
break;
all_concept_vals[i++] = pCon->name;
pCon = pCon->next;
}
concept_count = i;
/* Only print out all concepts if fewer than MAX_NUM_CONCEPT_VALUES.
* Printing out all values for concepts like paramId would be silly! */
if (concept_count < MAX_NUM_CONCEPT_VALUES) {
fprintf(stderr, "Here are the possible values for concept %s:\n", act->name);
qsort(&all_concept_vals, concept_count, sizeof(char*), cmpstringp);
for(i=0; i<concept_count; ++i) {
if (all_concept_vals[i]) {
int print_it = 1;
if (i>0 && strcmp(all_concept_vals[i], all_concept_vals[i-1]) == 0) {
print_it = 0; /* skip duplicate entries */
}
if (print_it) fprintf(stderr, "\t%s\n", all_concept_vals[i]);
}
}
}
}
return err;
}
e = c->conditions;
sa=grib_sarray_new(h->context,10,10);
while(e)
{
concept_conditions_apply(h,e,values,sa,&count);
e = e->next;
}
grib_sarray_delete(h->context,sa);
if (count) err=grib_set_values(h,values,count);
return err;
}
static int pack_double(grib_accessor* a, const double* val, size_t *len)
{
return GRIB_NOT_IMPLEMENTED;
}
static int pack_long(grib_accessor* a, const long* val, size_t *len)
{
char buf[80];
size_t s;
sprintf(buf,"%ld",*val);
#if 0
if(*len > 1)
return GRIB_NOT_IMPLEMENTED;
#endif
s = strlen(buf)+1;
return pack_string(a,buf,&s);
}
static int unpack_double(grib_accessor* a, double* val, size_t *len)
{
/*
* If we want to have a condition which contains tests for paramId as well
* as a floating point key, then need to be able to evaluate paramId as a
* double. E.g.
* if (referenceValue > 0 && paramId == 129)
*/
/*return GRIB_NOT_IMPLEMENTED*/
long lval = 0;
int ret = unpack_long(a, &lval, len);
if (ret == GRIB_SUCCESS) {
*val = lval;
}
return ret;
}
static int unpack_long(grib_accessor* a, long* val, size_t *len)
{
/* TODO properly calling concept_evaluate_long ! */
const char *p = concept_evaluate(a);
if(!p) {
if (a->creator->defaultkey)
return grib_get_long_internal(grib_handle_of_accessor(a),a->creator->defaultkey,val);
return GRIB_NOT_FOUND;
}
*val = atol(p);
*len = 1;
return GRIB_SUCCESS;
}
static int get_native_type(grib_accessor* a)
{
int type=GRIB_TYPE_STRING;
if (a->flags & GRIB_ACCESSOR_FLAG_LONG_TYPE)
type=GRIB_TYPE_LONG;
return type;
}
static void destroy(grib_context* c,grib_accessor* a)
{
/*
* grib_accessor_concept *self = (grib_accessor_concept*)a;
* grib_context_free(c,self->cval);
*/
}
static int unpack_string (grib_accessor* a, char* val, size_t *len)
{
size_t slen ;
const char *p = concept_evaluate(a);
if(!p) {
if (a->creator->defaultkey)
return grib_get_string_internal(grib_handle_of_accessor(a),a->creator->defaultkey,val,len);
return GRIB_NOT_FOUND;
}
slen = strlen(p) +1;
if(*len < slen)
{
grib_context_log(a->context, GRIB_LOG_ERROR, "Variable unpack_string Wrong size for %s it is %d bytes big (len=%d)", a->name , slen ,*len);
*len = slen;
return GRIB_BUFFER_TOO_SMALL;
}
strcpy(val,p);
*len = slen;
return GRIB_SUCCESS;
}
static int pack_string(grib_accessor* a, const char* val, size_t *len)
{
return grib_concept_apply(a,val);
}
static size_t string_length(grib_accessor* a)
{
return MAX_CONCEPT_STRING_LENGTH;
}
static int value_count(grib_accessor* a,long* count)
{
*count=1;
return 0;
}
static int compare(grib_accessor* a,grib_accessor* b)
{
int retval=0;
char *aval=0;
char *bval=0;
size_t alen = 0;
size_t blen = 0;
int err=0;
long count=0;
err=grib_value_count(a,&count);
if (err) return err;
alen=count;
err=grib_value_count(b,&count);
if (err) return err;
blen=count;
if (alen != blen) return GRIB_COUNT_MISMATCH;
aval=(char*)grib_context_malloc(a->context,alen*sizeof(char));
bval=(char*)grib_context_malloc(b->context,blen*sizeof(char));
grib_unpack_string(a,aval,&alen);
grib_unpack_string(b,bval,&blen);
retval = GRIB_SUCCESS;
if (!aval || !bval || grib_inline_strcmp(aval,bval)) retval = GRIB_STRING_VALUE_MISMATCH;
grib_context_free(a->context,aval);
grib_context_free(b->context,bval);
return retval;
}