diff --git a/src/decenc_jpeg2000.c b/src/decenc_jpeg2000.c index 6bed2c6b..5cd6cd2b 100644 --- a/src/decenc_jpeg2000.c +++ b/src/decenc_jpeg2000.c @@ -141,11 +141,12 @@ enc_jpeg2000(unsigned char *cin, g2int width, g2int height, g2int nbits, /* Initialize Jasper. */ #ifdef JASPER3 +#define HUNDRED_MB 100000000 jas_conf_clear(); /* static jas_std_allocator_t allocator; */ /* jas_std_allocator_init(&allocator); */ /* jas_conf_set_allocator(JAS_CAST(jas_std_allocator_t *, &allocator)); */ - jas_conf_set_max_mem_usage(10000000); + jas_conf_set_max_mem_usage(HUNDRED_MB); jas_conf_set_multithread(true); if (jas_init_library()) return G2_JASPER_INIT; diff --git a/src/g2cinq.c b/src/g2cinq.c index 0a172b7d..e3dbe65b 100644 --- a/src/g2cinq.c +++ b/src/g2cinq.c @@ -32,13 +32,14 @@ g2c_inq(int g2cid, int *num_msg) { int ret = G2C_NOERROR; - /* Is this an open GRIB2 file? */ + /* Check input parameters. */ if (g2cid < 0 || g2cid > G2C_MAX_FILES) return G2C_EBADID; /* If using threading, lock the mutex. */ MUTEX_LOCK(m); + /* Find the open file. */ if (g2c_file[g2cid].g2cid != g2cid) ret = G2C_EBADID; @@ -88,33 +89,53 @@ g2c_inq_msg(int g2cid, int msg_num, unsigned char *discipline, int *num_fields, unsigned char *local_version) { G2C_MESSAGE_INFO_T *msg; - /* Is this an open GRIB2 file? */ - if (g2cid < 0 || g2cid > G2C_MAX_FILES || g2c_file[g2cid].g2cid != g2cid) + int ret = G2C_NOERROR; + + /* Check input parameters. */ + if (g2cid < 0 || g2cid > G2C_MAX_FILES) return G2C_EBADID; + if (msg_num < 0) + return G2C_EINVAL; + + /* If using threading, lock the mutex. */ + MUTEX_LOCK(m); + + /* Find the open file. */ + if (g2c_file[g2cid].g2cid != g2cid) + ret = G2C_EBADID; /* Find the file and message. */ - for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) + if (!ret) { - if (msg->msg_num == msg_num) + ret = G2C_ENOMSG; + for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) { - if (discipline) - *discipline = msg->discipline; - if (num_fields) - *num_fields = msg->num_fields; - if (num_local) - *num_local = msg->num_local; - if (center) - *center = msg->center; - if (subcenter) - *subcenter = msg->subcenter; - if (master_version) - *master_version = msg->master_version; - if (local_version) - *local_version = msg->local_version; - return G2C_NOERROR; + if (msg->msg_num == msg_num) + { + if (discipline) + *discipline = msg->discipline; + if (num_fields) + *num_fields = msg->num_fields; + if (num_local) + *num_local = msg->num_local; + if (center) + *center = msg->center; + if (subcenter) + *subcenter = msg->subcenter; + if (master_version) + *master_version = msg->master_version; + if (local_version) + *local_version = msg->local_version; + ret = G2C_NOERROR; + break; + } } } - return G2C_ENOMSG; + + /* If using threading, unlock the mutex. */ + MUTEX_UNLOCK(m); + + return ret; } /** @@ -151,34 +172,53 @@ g2c_inq_msg_time(int g2cid, int msg_num, unsigned char *sig_ref_time, short *yea unsigned char *minute, unsigned char *second) { G2C_MESSAGE_INFO_T *msg; + int ret = G2C_NOERROR; - /* Is this an open GRIB2 file? */ - if (g2cid < 0 || g2cid > G2C_MAX_FILES || g2c_file[g2cid].g2cid != g2cid) + /* Check input parameters. */ + if (g2cid < 0 || g2cid > G2C_MAX_FILES) return G2C_EBADID; + if (msg_num < 0) + return G2C_EINVAL; - /* Find the message. */ - for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) + /* If using threading, lock the mutex. */ + MUTEX_LOCK(m); + + /* Find the open file. */ + if (g2c_file[g2cid].g2cid != g2cid) + ret = G2C_EBADID; + + /* Find the file and message. */ + if (!ret) { - if (msg->msg_num == msg_num) + ret = G2C_ENOMSG; + for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) { - if (sig_ref_time) - *sig_ref_time = msg->sig_ref_time; - if (year) - *year = msg->year; - if (month) - *month = msg->month; - if (day) - *day = msg->day; - if (hour) - *hour = msg->hour; - if (minute) - *minute = msg->minute; - if (second) - *second = msg->second; - return G2C_NOERROR; + if (msg->msg_num == msg_num) + { + if (sig_ref_time) + *sig_ref_time = msg->sig_ref_time; + if (year) + *year = msg->year; + if (month) + *month = msg->month; + if (day) + *day = msg->day; + if (hour) + *hour = msg->hour; + if (minute) + *minute = msg->minute; + if (second) + *second = msg->second; + ret = G2C_NOERROR; + break; + } } } - return G2C_ENOMSG; + + /* If using threading, unlock the mutex. */ + MUTEX_UNLOCK(m); + + return ret; } /** @@ -216,6 +256,8 @@ g2c_inq_prod(int g2cid, int msg_num, int prod_num, int *pds_template_len, /* Is this an open GRIB2 file? */ if (g2cid < 0 || g2cid > G2C_MAX_FILES) return G2C_EBADID; + if (msg_num < 0 || prod_num < 0) + return G2C_EINVAL; /* If using threading, lock the mutex. */ MUTEX_LOCK(m); @@ -302,7 +344,9 @@ g2c_inq_prod(int g2cid, int msg_num, int prod_num, int *pds_template_len, } /** - * Learn about the one of the dimensions of a GRIB2 product. + * Learn about the one of the dimensions of a GRIB2 product. This + * function will return the size, name, and values along the + * dimension. * * @param g2cid ID of the opened file, as from g2c_open(). * @param msg_num Number of the message in the file, starting with the @@ -334,51 +378,95 @@ g2c_inq_dim(int g2cid, int msg_num, int prod_num, int dim_num, size_t *len, int d; int ret = G2C_NOERROR; - /* Is this an open GRIB2 file? */ + /* Are these valid IDs? */ if (g2cid < 0 || g2cid > G2C_MAX_FILES) return G2C_EBADID; + if (msg_num < 0 || prod_num < 0 || dim_num < 0) + return G2C_EINVAL; /* If using threading, lock the mutex. */ MUTEX_LOCK(m); + /* Find the file. */ if (g2c_file[g2cid].g2cid != g2cid) - return G2C_EBADID; + ret = G2C_EBADID; /* Find the message. */ - for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) - if (msg->msg_num == msg_num) - break; - if (!msg) - return G2C_ENOMSG; + if (!ret) + { + for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) + if (msg->msg_num == msg_num) + break; + if (!msg) + ret = G2C_ENOMSG; + } /* Find the product. After this, sec4 will point to the * appropropriate section 4 G2C_SECTION_INFO_T. */ - for (sec4 = msg->sec; sec4; sec4 = sec4->next) - if (sec4->sec_num == 4 && ((G2C_SECTION4_INFO_T *)sec4->sec_info)->field_num == prod_num) - break; - if (!sec4) - return G2C_ENOPRODUCT; - /* sec4_info = (G2C_SECTION4_INFO_T *)sec4->sec_info; */ + if (!ret) + { + for (sec4 = msg->sec; sec4; sec4 = sec4->next) + if (sec4->sec_num == 4 && ((G2C_SECTION4_INFO_T *)sec4->sec_info)->field_num == prod_num) + break; + if (!sec4) + ret = G2C_ENOPRODUCT; + /* sec4_info = (G2C_SECTION4_INFO_T *)sec4->sec_info; */ + } /* Find the GDS. */ - for (sec3 = sec4->prev; sec3; sec3 = sec3->prev) - if (sec3->sec_num == 3) - break; - if (!sec3) - return G2C_ENOSECTION; - dim = &((G2C_SECTION3_INFO_T *)sec3->sec_info)->dim[dim_num]; + if (!ret) + { + for (sec3 = sec4->prev; sec3; sec3 = sec3->prev) + if (sec3->sec_num == 3) + break; + if (!sec3) + ret = G2C_ENOSECTION; + dim = &((G2C_SECTION3_INFO_T *)sec3->sec_info)->dim[dim_num]; + } /* Give the caller the info they want. */ - if (len) - *len = dim->len; - if (name) - strncpy(name, dim->name, G2C_MAX_NAME); - if (val) - for (d = 0; d < dim->len; d++) - val[d] = dim->value[d]; + if (!ret) + { + if (len) + *len = dim->len; + if (name) + strncpy(name, dim->name, G2C_MAX_NAME); + if (val) + for (d = 0; d < dim->len; d++) + val[d] = dim->value[d]; + } /* If using threading, unlock the mutex. */ MUTEX_UNLOCK(m); return ret; } + +/** + * Learn about the one of the dimensions of a GRIB2 product. This + * function will return the size and name of the dimension. + * + * @param g2cid ID of the opened file, as from g2c_open(). + * @param msg_num Number of the message in the file, starting with the + * first message as 0. + * @param prod_num Product number. + * @param dim_num Dimension number, with the first dimension as 0. + * @param len Pointer that gets the length of this dimension. Ignored if NULL. + * @param name Pointer that gets the name of this dimension. Must have + * memory of size G2C_MAX_NAME. Ignored if NULL. + * + * @return + * - ::G2C_NOERROR No error. + * - ::G2C_EBADID File ID not found. + * - ::G2C_ENOMSG Message not found. + * - ::G2C_ENOPRODUCT Product not found. + * - ::G2C_ENOSECTION GDS not found. + * + * @author Ed Hartnett @date 10/21/22 + */ +int +g2c_inq_dim_info(int g2cid, int msg_num, int prod_num, int dim_num, size_t *len, + char *name) +{ + return g2c_inq_dim(g2cid, msg_num, prod_num, dim_num, len, name, NULL); +} diff --git a/src/g2cprod.c b/src/g2cprod.c index 839d8a08..2897c6b0 100644 --- a/src/g2cprod.c +++ b/src/g2cprod.c @@ -9,6 +9,10 @@ /** Global file information. */ extern G2C_FILE_INFO_T g2c_file[G2C_MAX_FILES + 1]; +/** If pthreads are enabled, use externally-defined mutex for + * thread-safety. */ +EXTERN_MUTEX(m); + /** * Read the data for a product. * @@ -41,79 +45,108 @@ g2c_get_prod(int g2cid, int msg_num, int prod_num, int *num_data_points, float * return G2C_EBADID; if (msg_num < 0 || prod_num < 0) return G2C_EINVAL; + + /* If using threading, lock the mutex. */ + MUTEX_LOCK(m); + + /* Find the file. */ if (g2c_file[g2cid].g2cid != g2cid) - return G2C_EBADID; + ret = G2C_EBADID; /* Find the message. */ - for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) - if (msg->msg_num == msg_num) - break; - if (!msg) - return G2C_ENOMSG; + if (!ret) + { + for (msg = g2c_file[g2cid].msg; msg; msg = msg->next) + if (msg->msg_num == msg_num) + break; + if (!msg) + ret = G2C_ENOMSG; + } /* Find the product. After this, sec4 will point to the * appropropriate section 4 G2C_SECTION_INFO_T. */ - for (sec4 = msg->sec; sec4; sec4 = sec4->next) - if (sec4->sec_num == 4 && ((G2C_SECTION4_INFO_T *)sec4->sec_info)->field_num == prod_num) - break; - if (!sec4) - return G2C_ENOPRODUCT; - /* sec4_info = (G2C_SECTION4_INFO_T *)sec4->sec_info; */ + if (!ret) + { + for (sec4 = msg->sec; sec4; sec4 = sec4->next) + if (sec4->sec_num == 4 && ((G2C_SECTION4_INFO_T *)sec4->sec_info)->field_num == prod_num) + break; + if (!sec4) + ret = G2C_ENOPRODUCT; + /* sec4_info = (G2C_SECTION4_INFO_T *)sec4->sec_info; */ + } /* Find the grid definiton section, section 3. It will come * earlier in the list. */ - for (sec3 = sec4; sec3; sec3 = sec3->prev) - if (sec3->sec_num == 3) - break; - if (!sec3) - return G2C_ENOSECTION; - sec3_info = (G2C_SECTION3_INFO_T *)sec3->sec_info; + if (!ret) + { + for (sec3 = sec4; sec3; sec3 = sec3->prev) + if (sec3->sec_num == 3) + break; + if (!sec3) + ret = G2C_ENOSECTION; + sec3_info = (G2C_SECTION3_INFO_T *)sec3->sec_info; + } /* Find the section 5, data representation section, to learn how * this product is compressed. Section 5 is after section 4 in the * list. */ - for (sec5 = sec4; sec5; sec5 = sec5->next) - if (sec5->sec_num == 5) - break; - if (!sec5) - return G2C_ENOSECTION; - sec5_info = (G2C_SECTION5_INFO_T *)sec5->sec_info; + if (!ret) + { + for (sec5 = sec4; sec5; sec5 = sec5->next) + if (sec5->sec_num == 5) + break; + if (!sec5) + ret = G2C_ENOSECTION; + sec5_info = (G2C_SECTION5_INFO_T *)sec5->sec_info; + } /* Find the section 7, data section. */ - for (sec7 = sec5; sec7; sec7 = sec7->next) - if (sec7->sec_num == 7) - break; - if (!sec7) - return G2C_ENOSECTION; + if (!ret) + { + for (sec7 = sec5; sec7; sec7 = sec7->next) + if (sec7->sec_num == 7) + break; + if (!sec7) + ret = G2C_ENOSECTION; + } /* Give the caller number of data points, if desired. */ - if (num_data_points) - *num_data_points = sec5_info->num_data_points; - - /* If user doesn't want data, we need go no further. */ - if (!data) - return G2C_NOERROR; + if (!ret) + { + if (num_data_points) + *num_data_points = sec5_info->num_data_points; + } /* Allocate a char buffer to hold the packed data. */ - if (!(buf = malloc(sizeof(char) * sec7->sec_len))) - return G2C_ENOMEM; - - /* Jump to this section in the file. */ - if (fseek(g2c_file[g2cid].f, sec7->bytes_to_sec + sec7->msg->bytes_to_msg, SEEK_SET)) - return G2C_ERROR; - - /* Read the product into a char buffer. */ - if ((bytes_read = fread(buf, 1, sec7->sec_len, g2c_file[g2cid].f)) != sec7->sec_len) - return G2C_EFILE; - - /* Unpack the char buffer into a float array, which must be - * allocated by the caller. */ - ret = g2c_unpack7(buf, sec3_info->grid_def, sec3->template_len, sec3->template, - sec5_info->data_def, sec5->template_len, sec5->template, - sec5_info->num_data_points, data); - - /* Free the char buffer. */ - free(buf); + if (data) + { + if (!ret) + if (!(buf = malloc(sizeof(char) * sec7->sec_len))) + ret = G2C_ENOMEM; + + /* Jump to this section in the file. */ + if (!ret) + if (fseek(g2c_file[g2cid].f, sec7->bytes_to_sec + sec7->msg->bytes_to_msg, SEEK_SET)) + ret = G2C_ERROR; + + /* Read the product into a char buffer. */ + if (!ret) + if ((bytes_read = fread(buf, 1, sec7->sec_len, g2c_file[g2cid].f)) != sec7->sec_len) + ret = G2C_EFILE; + + /* Unpack the char buffer into a float array, which must be + * allocated by the caller. */ + if (!ret) + ret = g2c_unpack7(buf, sec3_info->grid_def, sec3->template_len, sec3->template, + sec5_info->data_def, sec5->template_len, sec5->template, + sec5_info->num_data_points, data); + + /* Free the char buffer. */ + free(buf); + } + + /* If using threading, unlock the mutex. */ + MUTEX_UNLOCK(m); return ret; } diff --git a/src/grib2.h.in b/src/grib2.h.in index e31aed7f..04f2766c 100644 --- a/src/grib2.h.in +++ b/src/grib2.h.in @@ -318,6 +318,8 @@ int g2c_inq_prod(int g2cid, int msg_num, int prod_num, int *pds_template_len, int *drs_template_len, long long int *drs_template); int g2c_inq_dim(int g2cid, int msg_num, int prod_num, int dim_num, size_t *len, char *name, float *val); +int g2c_inq_dim_info(int g2cid, int msg_num, int prod_num, int dim_num, size_t *len, + char *name); /* Getting data. */ int g2c_get_prod(int g2cid, int msg_num, int prod_num, int *num_data_points, diff --git a/tests/tst_inq.c b/tests/tst_inq.c index c53a2ec9..e2fcd211 100644 --- a/tests/tst_inq.c +++ b/tests/tst_inq.c @@ -18,6 +18,8 @@ main() printf("Testing g2c_inq()/g2c_inq_msg()/g2c_inq_prod() calls...\n"); { int op; + size_t dimlen; + char dimname[G2C_MAX_NAME]; for (op = 0; op < NUM_OPEN; op++) { @@ -68,6 +70,9 @@ main() return G2C_ERROR; /* This won't work - bad msg number. */ + if (g2c_inq_msg(g2cid, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != G2C_EINVAL) + return G2C_ERROR; + /* This won't work - msg number won't be found. */ if (g2c_inq_msg(g2cid, NUM_MSG, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != G2C_ENOMSG) return G2C_ERROR; @@ -84,17 +89,35 @@ main() return G2C_ERROR; /* This won't work - bad msg number. */ + if (g2c_inq_prod(g2cid, -1, 0, NULL, NULL, NULL, NULL, NULL, NULL) != G2C_EINVAL) + return G2C_ERROR; if (g2c_inq_prod(g2cid, NUM_MSG, 0, NULL, NULL, NULL, NULL, NULL, NULL) != G2C_ENOMSG) return G2C_ERROR; - /* This won't work - bad prod number. */ + /* These won't work - bad prod number. */ if (g2c_inq_prod(g2cid, 0, 1, NULL, NULL, NULL, NULL, NULL, NULL) != G2C_ENOPRODUCT) return G2C_ERROR; + if (g2c_inq_prod(g2cid, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL) != G2C_EINVAL) + return G2C_ERROR; /* This works but does nothing. */ if ((ret = g2c_inq_prod(g2cid, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL))) return ret; + /* Won't work, bad IDs. */ + if ((ret = g2c_inq_dim(-1, 0, 0, 0, &dimlen, dimname, NULL)) != G2C_EBADID) + return G2C_ERROR; + if ((ret = g2c_inq_dim(G2C_MAX_FILES + 1, 0, 0, 0, &dimlen, dimname, NULL)) != G2C_EBADID) + return G2C_ERROR; + if ((ret = g2c_inq_dim(10, 0, 0, 0, &dimlen, dimname, NULL)) != G2C_EBADID) + return G2C_ERROR; + if ((ret = g2c_inq_dim(g2cid, -1, 0, 0, &dimlen, dimname, NULL)) != G2C_EINVAL) + return G2C_ERROR; + if ((ret = g2c_inq_dim(g2cid, 0, -1, 0, &dimlen, dimname, NULL)) != G2C_EINVAL) + return G2C_ERROR; + if ((ret = g2c_inq_dim(g2cid, 0, 0, -1, &dimlen, dimname, NULL)) != G2C_EINVAL) + return G2C_ERROR; + /* Check each message. */ for (m = 0; m < num_msg; m++) { @@ -150,8 +173,6 @@ main() short year; short center, subcenter; unsigned char master_version, local_version; - size_t dimlen; - char dimname[G2C_MAX_NAME]; int p; printf("\t\tinquiring about message %d...\n", m); @@ -167,6 +188,27 @@ main() if (num_local || num_fields != 1 || discipline != (m < 4 ? 0 : 10)) return G2C_ERROR; + /* These will all fail due to bad inputs. */ + if ((ret = g2c_inq_msg_time(-1, m, &sig_ref_time, &year, &month, &day, &hour, + &minute, &second)) != G2C_EBADID) + return G2C_ERROR; + if ((ret = g2c_inq_msg_time(10, m, &sig_ref_time, &year, &month, &day, &hour, + &minute, &second)) != G2C_EBADID) + return G2C_ERROR; + if ((ret = g2c_inq_msg_time(G2C_MAX_FILES + 1, m, &sig_ref_time, &year, &month, &day, &hour, + &minute, &second)) != G2C_EBADID) + return G2C_ERROR; + if ((ret = g2c_inq_msg_time(g2cid, -1, &sig_ref_time, &year, &month, &day, &hour, + &minute, &second)) != G2C_EINVAL) + return G2C_ERROR; + if ((ret = g2c_inq_msg_time(g2cid, 20, &sig_ref_time, &year, &month, &day, &hour, + &minute, &second)) != G2C_ENOMSG) + return G2C_ERROR; + + /* This will work, but do nothing. */ + if ((ret = g2c_inq_msg_time(g2cid, m, NULL, NULL, NULL, NULL, NULL, NULL, NULL))) + return ret; + /* Inquire about the date/time. */ if ((ret = g2c_inq_msg_time(g2cid, m, &sig_ref_time, &year, &month, &day, &hour, &minute, &second))) @@ -205,6 +247,11 @@ main() return G2C_ERROR; } + if ((ret = g2c_inq_dim_info(g2cid, m, 0, 0, &dimlen, dimname))) + return ret; + /* printf("dimlen = %ld, dimname = %s\n", dimlen, dimname); */ + if (dimlen != 151 || strcmp(dimname, "Latitude")) + return G2C_ERROR; if ((ret = g2c_inq_dim(g2cid, m, 0, 0, &dimlen, dimname, NULL))) return ret; /* printf("dimlen = %ld, dimname = %s\n", dimlen, dimname); */