diff --git a/src/rest_vol_dataset.c b/src/rest_vol_dataset.c index 73317452..6b11e29d 100644 --- a/src/rest_vol_dataset.c +++ b/src/rest_vol_dataset.c @@ -18,6 +18,10 @@ #include "rest_vol_dataset.h" #include +/* Helper to serialize vlen data for writes. */ +static herr_t RV_vlen_data_to_json(const void *in, size_t nelems, hid_t dtype_id, void **out, + size_t *out_size); + /* Set of callbacks for RV_parse_response() */ static herr_t RV_parse_dataset_creation_properties_callback(char *HTTP_response, void *callback_data_in, void *callback_data_out); @@ -91,6 +95,7 @@ const char *value_keys[] = {"value", (const char *)0}; #define DATASET_CREATE_MAX_COMPACT_ATTRIBUTES_DEFAULT 8 #define DATASET_CREATE_MIN_DENSE_ATTRIBUTES_DEFAULT 6 #define OBJECT_REF_STRING_LEN 48 +#define DATASET_VLEN_JSON_BODY_DEFAULT_SIZE 512 /* Defines for multi-CURL related settings */ #define NUM_MAX_HOST_CONNS 10 @@ -850,11 +855,11 @@ herr_t RV_dataset_write(size_t count, void *dset[], hid_t mem_type_id[], hid_t _mem_space_id[], hid_t _file_space_id[], hid_t dxpl_id, const void *buf[], void **req) { - H5S_sel_type sel_type = H5S_SEL_ALL; - H5T_class_t dtype_class; - hbool_t is_transfer_binary = FALSE; - htri_t contiguous = FALSE; - htri_t is_variable_str; + H5S_sel_type sel_type = H5S_SEL_ALL; + H5T_class_t dtype_class = H5T_NO_CLASS; + hbool_t is_transfer_binary = FALSE; + htri_t contiguous = FALSE; + htri_t is_variable_str = FALSE; hssize_t mem_select_npoints = 0; hssize_t file_select_npoints = 0; hssize_t offset = 0; @@ -1036,7 +1041,7 @@ RV_dataset_write(size_t count, void *dset[], hid_t mem_type_id[], hid_t _mem_spa if ((needs_tconv = RV_need_tconv(transfer_info[i].file_type_id, transfer_info[i].mem_type_id)) < 0) FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_BADVALUE, FAIL, "unable to check if datatypes need conversion"); - if (needs_tconv) { + if (needs_tconv > 0) { #ifdef RV_CONNECTOR_DEBUG printf("-> Beginning type conversion for write\n"); @@ -1096,7 +1101,14 @@ RV_dataset_write(size_t count, void *dset[], hid_t mem_type_id[], hid_t _mem_spa } } /* end if */ else { - if (H5T_STD_REF_OBJ == transfer_info[i].file_type_id) { + if (H5T_VLEN == dtype_class) { + if (RV_vlen_data_to_json(buf[0], (size_t)file_select_npoints, mem_type_id[0], + (void **)&transfer_info[i].u.write_info.write_body, + &write_body_len) < 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, + "can't convert vlen data to a buffer"); + } + else if (H5T_STD_REF_OBJ == transfer_info[i].file_type_id) { /* Convert the buffer of rest_obj_ref_t's to a binary buffer */ if (RV_convert_obj_refs_to_buffer( (const rv_obj_ref_t *)buf_to_write, (size_t)file_select_npoints, @@ -1287,7 +1299,16 @@ RV_dataset_write(size_t count, void *dset[], hid_t mem_type_id[], hid_t _mem_spa RV_free(transfer_info[i].resp_buffer.buffer); if (transfer_info[i].tconv_buf) - RV_free(transfer_info[i].tconv_buf); + if ((dtype_class = H5Tget_class(transfer_info[i].mem_type_id)) == H5T_NO_CLASS) + FUNC_DONE_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get mem dtype class"); + + if (dtype_class == H5T_VLEN) + if (H5Treclaim(transfer_info[i].mem_type_id, transfer_info[i].mem_space_id, H5P_DEFAULT, + transfer_info[i].tconv_buf) < 0) + FUNC_DONE_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, + "can't reclaim vlen memory allocated by conversion"); + + RV_free(transfer_info[i].tconv_buf); if (transfer_info[i].bkg_buf) RV_free(transfer_info[i].bkg_buf); @@ -4635,7 +4656,14 @@ rv_dataset_read_cb(hid_t mem_type_id, hid_t mem_space_id, hid_t file_type_id, hi FUNC_GOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "can't scatter data to read buffer"); } else { - if (H5T_STD_REF_OBJ == mem_type_id) { + if (H5T_VLEN == dtype_class) { + if (RV_parse_response(resp_buffer.buffer, (void *)&mem_type_id, &json_buf, + RV_json_values_to_binary_callback) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_PARSEERROR, FAIL, "can't parse vlen data"); + + memcpy(buf, json_buf, (size_t)file_select_npoints * mem_type_size); + } + else if (H5T_STD_REF_OBJ == mem_type_id) { /* Convert the received binary buffer into a buffer of rest_obj_ref_t's */ if (RV_convert_buffer_to_obj_refs(resp_buffer.buffer, (size_t)file_select_npoints, (rv_obj_ref_t **)&obj_ref_buf, &file_data_size) < 0) @@ -4899,7 +4927,7 @@ RV_json_values_to_binary_callback(char *HTTP_response, void *callback_data_in, v { void **out_buf = (void **)callback_data_out; - hid_t dtype_id = *(hid_t *)callback_data_in; + hid_t dtype_id = H5I_INVALID_HID; yajl_val parse_tree = NULL, key_obj; char *parsed_string; herr_t ret_value = SUCCEED; @@ -4914,6 +4942,10 @@ RV_json_values_to_binary_callback(char *HTTP_response, void *callback_data_in, v FUNC_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "HTTP response buffer was NULL"); if (!out_buf) FUNC_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "output buffer was NULL"); + if (!callback_data_in) + FUNC_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "provided dtype id was NULL"); + + dtype_id = *(hid_t *)callback_data_in; if ((dtype_size = H5Tget_size(dtype_id)) <= 0) FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "can't get datatype size"); @@ -4966,75 +4998,304 @@ RV_json_values_to_binary_recursive(yajl_val value_entry, hid_t dtype_id, void *v if ((dtype_class = H5Tget_class(dtype_id)) == H5T_NO_CLASS) FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "can't get datatype class"); - if (dtype_class == H5T_INTEGER) { - if (H5Tequal(dtype_id, H5T_NATIVE_INT) != TRUE) - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, - "parsing non-native integer types is unsupported"); + switch (dtype_class) { + case (H5T_INTEGER): { + if (H5Tequal(dtype_id, H5T_NATIVE_INT) != TRUE) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "parsing non-native integer types is unsupported"); + + if (!YAJL_IS_INTEGER(value_entry)) + FUNC_GOTO_ERROR(H5E_OBJECT, H5E_PARSEERROR, FAIL, + "parsed yajl val has incorrect type; expected integer"); + + *((int *)value_buffer) = (int)YAJL_GET_INTEGER(value_entry); + } break; + + case (H5T_FLOAT): { + if (H5Tequal(dtype_id, H5T_NATIVE_FLOAT) == TRUE) { + if (!YAJL_IS_DOUBLE(value_entry)) + FUNC_GOTO_ERROR(H5E_OBJECT, H5E_PARSEERROR, FAIL, + "parsed yajl val has incorrect type; expected float-like"); + + *((float *)value_buffer) = (float)YAJL_GET_DOUBLE(value_entry); + } + else if (H5Tequal(dtype_id, H5T_NATIVE_DOUBLE) == TRUE) { + if (!YAJL_IS_DOUBLE(value_entry)) + FUNC_GOTO_ERROR(H5E_OBJECT, H5E_PARSEERROR, FAIL, + "parsed yajl val has incorrect type; expected double"); + + *((double *)value_buffer) = YAJL_GET_DOUBLE(value_entry); + } + else { + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "parsing non-native float types is unsupported"); + } + } break; + + case (H5T_COMPOUND): { + /* Recursively parse each member of the compound type */ + int nmembers = 0; + size_t offset = 0; + size_t member_size = 0; + hid_t member_dtype_id = H5I_INVALID_HID; + yajl_val member_val; + + if ((nmembers = H5Tget_nmembers(dtype_id)) < 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, + "can't get number of members in compound datatype"); + + for (int i = 0; i < nmembers; i++) { + if ((member_dtype_id = H5Tget_member_type(dtype_id, (unsigned int)i)) < 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, + "can't get datatype of member in compound datatype"); - if (!YAJL_IS_INTEGER(value_entry)) - FUNC_GOTO_ERROR(H5E_OBJECT, H5E_PARSEERROR, FAIL, - "parsed yajl val has incorrect type; expected integer"); + if ((member_size = H5Tget_size(member_dtype_id)) == 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "can't get size of member datatype"); - *((int *)value_buffer) = (int)YAJL_GET_INTEGER(value_entry); + if ((member_val = YAJL_GET_OBJECT(value_entry)->values[i]) == NULL) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_PARSEERROR, FAIL, + "failed to parse member of compound type"); + + if (RV_json_values_to_binary_recursive(member_val, member_dtype_id, + (void *)((char *)value_buffer + offset)) < 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_PARSEERROR, FAIL, "failed to parse member datatype"); + + offset += member_size; + member_val = NULL; + member_size = 0; + member_dtype_id = H5I_INVALID_HID; + } + } break; + + case (H5T_VLEN): { + yajl_val seq_elem_val = NULL; + hid_t parent_dtype_id = H5I_INVALID_HID; + hvl_t *vl = ((hvl_t *)value_buffer); + size_t parent_dtype_size = 0; + void *seq_elem_ptr = NULL; + + if (!YAJL_IS_ARRAY(value_entry)) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_PARSEERROR, FAIL, + "server returned invalid json for vlen sequence"); + + if (YAJL_GET_ARRAY(value_entry) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_PARSEERROR, FAIL, "couldn't parse returned vlen sequence"); + + if ((vl->len = YAJL_GET_ARRAY(value_entry)->len) <= 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_PARSEERROR, FAIL, + "server returned vlen sequence with invalid length"); + + if ((vl->p = calloc(vl->len, sizeof(void *))) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, + "can't allocate space for returned vlen seq data"); + + /* Recursively encode each element in the sequence and place it in the allocated buffer */ + for (size_t i = 0; i < vl->len; i++) { + if ((parent_dtype_id = H5Tget_super(dtype_id)) == H5I_INVALID_HID) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get base type of vlen sequence"); + + if ((seq_elem_val = YAJL_GET_ARRAY(value_entry)->values[i]) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_PARSEERROR, FAIL, "can't parse vlen sequence element"); + + if ((parent_dtype_size = H5Tget_size(parent_dtype_id)) == 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get size of vlen parent type"); + + seq_elem_ptr = (void *)(((char *)vl->p) + i * parent_dtype_size); + + if (RV_json_values_to_binary_recursive(seq_elem_val, parent_dtype_id, seq_elem_ptr) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_PARSEERROR, FAIL, + "can't parse element of vlen sequence"); + } + + } break; + + default: { + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unsupported datatype class for parsing"); + } break; } - else if (dtype_class == H5T_FLOAT) { - if (H5Tequal(dtype_id, H5T_NATIVE_FLOAT) == TRUE) { - if (!YAJL_IS_DOUBLE(value_entry)) - FUNC_GOTO_ERROR(H5E_OBJECT, H5E_PARSEERROR, FAIL, - "parsed yajl val has incorrect type; expected float-like"); - *((float *)value_buffer) = (float)YAJL_GET_DOUBLE(value_entry); - } - else if (H5Tequal(dtype_id, H5T_NATIVE_DOUBLE) == TRUE) { - if (!YAJL_IS_DOUBLE(value_entry)) - FUNC_GOTO_ERROR(H5E_OBJECT, H5E_PARSEERROR, FAIL, - "parsed yajl val has incorrect type; expected double"); +done: + if ((ret_value < 0) && (dtype_class == H5T_VLEN)) { + if (((hvl_t *)value_buffer)->p) + RV_free(((hvl_t *)value_buffer)->p); + } + + return ret_value; +} + +/*------------------------------------------------------------------------- + * Function: RV_vlen_data_to_json + * + * Purpose: Converts a buffer of hvl_t instances to a JSON value + * which may be sent to the server as part of a write. + * + * Allocates memory under the *out pointer which must be + * freed by the calling function. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Matthew Larson + * January, 2024 + */ +static herr_t +RV_vlen_data_to_json(const void *_in, size_t nelems, hid_t dtype_id, void **out, size_t *out_size) +{ + herr_t ret_value = H5_ITER_CONT; + hid_t parent_type = H5I_INVALID_HID; + const hvl_t *in = NULL; + H5T_class_t parent_class = H5T_NO_CLASS; + ptrdiff_t elem_buf_ptrdiff = 0; + + const char *const fmt_string = "[%s]"; + const char *elem_fmt_string = NULL; + char *out_string = NULL; + char *out_string_curr_pos = NULL; + ptrdiff_t out_diff = 0; + int bytes_printed = 0; + size_t tgt_out_size = 0; + + const char *const leading_string = "{\"value\":"; + const char *const closing_string = "]}"; + + if (!out) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_BADVALUE, H5_ITER_ERROR, + "invalid NULL pointer provided for output of vlen to JSON conversion"); - *((double *)value_buffer) = YAJL_GET_DOUBLE(value_entry); + if (!_in) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_BADVALUE, H5_ITER_ERROR, + "invalid NULL pointer provided for input of vlen to JSON conversion"); + + in = (const hvl_t *)_in; + + if ((parent_type = H5Tget_super(dtype_id)) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "can't get base type of vlen type"); + + if ((parent_class = H5Tget_class(parent_type)) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get class of parent of vlen dtype"); + + if ((out_string = (char *)calloc(DATASET_VLEN_JSON_BODY_DEFAULT_SIZE, 1)) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, "can't allocate space for vlen data JSON"); + + *out_size = DATASET_CREATION_PROPERTIES_BODY_DEFAULT_SIZE; + out_string_curr_pos = out_string; + + /* Set up leading string */ + if ((bytes_printed = snprintf(out_string_curr_pos, strlen(leading_string) + 1, "%s", leading_string)) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_SYSERRSTR, FAIL, "snprintf error"); + + if (bytes_printed > strlen(leading_string) + 1) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_SYSERRSTR, FAIL, + "leading vlen value string exceeded allocated size"); + + out_string_curr_pos += bytes_printed; + + /* Delineate start of list of sequences */ + out_diff = out_string_curr_pos - out_string; + tgt_out_size = (size_t)out_diff; + CHECKED_REALLOC(out_string, *out_size, tgt_out_size, out_string_curr_pos, H5E_DATASET, FAIL); + out_string = strcat(out_string, "["); + out_string_curr_pos += 1; + + /* The amount of space needed to serialize each element can't be known ahead of time, so serialize each + * element into a temporary fixed-size buffer before copying it to the output buffer . */ + for (size_t i = 0; i < nelems; i++) { + hvl_t seq = in[i]; + + /* Delineate start of this sequence */ + out_diff = out_string_curr_pos - out_string; + tgt_out_size = (size_t)(out_diff) + 1; + CHECKED_REALLOC(out_string, *out_size, tgt_out_size, out_string_curr_pos, H5E_DATASET, FAIL); + out_string = strcat(out_string, "["); + out_string_curr_pos += 1; + + /* Stringify each element in a second fixed-size buffer, then copy to sequence buffer */ + for (size_t j = 0; j < seq.len; j++) { +#define MAX_SEQ_ELEM_BUF_SIZE 128 + char seq_elem_buffer[MAX_SEQ_ELEM_BUF_SIZE]; + char *seq_elem_buffer_ptr = seq_elem_buffer; + + memset(seq_elem_buffer, 0, MAX_SEQ_ELEM_BUF_SIZE); + + switch (parent_class) { + case (H5T_INTEGER): + if ((bytes_printed = snprintf(seq_elem_buffer_ptr, MAX_SEQ_ELEM_BUF_SIZE, "%d", + ((int *)seq.p)[j])) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_SYSERRSTR, FAIL, "snprintf error"); + break; + case (H5T_FLOAT): + if ((bytes_printed = snprintf(seq_elem_buffer_ptr, MAX_SEQ_ELEM_BUF_SIZE, "%f", + ((float *)seq.p)[j])) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_SYSERRSTR, FAIL, "snprintf error"); + break; + case (H5T_STRING): + if ((bytes_printed = snprintf(seq_elem_buffer_ptr, MAX_SEQ_ELEM_BUF_SIZE, "%d", + ((char *)seq.p)[j])) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_SYSERRSTR, FAIL, "snprintf error"); + break; + default: + FUNC_GOTO_ERROR(H5E_DATASET, H5E_UNSUPPORTED, FAIL, "unsupported vlen parent type"); + break; + } + + seq_elem_buffer_ptr += bytes_printed; + + /* Check if room for comma */ + if (bytes_printed + 1 > MAX_SEQ_ELEM_BUF_SIZE) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "vlen seq elem JSON exceeded buffer size"); + + /* Add comma if needed */ + if (j != seq.len - 1) { + *seq_elem_buffer_ptr = ','; + seq_elem_buffer_ptr += 1; + *seq_elem_buffer_ptr = '\0'; + } + + elem_buf_ptrdiff = seq_elem_buffer_ptr - seq_elem_buffer; + + /* Check that there is room in sequence buffer for this element string*/ + out_diff = out_string_curr_pos - out_string; + tgt_out_size = (size_t)(out_diff) + (size_t)elem_buf_ptrdiff; + CHECKED_REALLOC(out_string, *out_size, tgt_out_size, out_string_curr_pos, H5E_DATASET, FAIL); + + out_string = strcat(out_string, seq_elem_buffer); + out_string_curr_pos += (size_t)elem_buf_ptrdiff; } - else { - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, - "parsing non-native float types is unsupported"); + + /* Make sure sequence buffer has room for sequence-ending bracket and potential comma */ + out_diff = out_string_curr_pos - out_string; + tgt_out_size = (size_t)out_diff + 2; + CHECKED_REALLOC(out_string, *out_size, tgt_out_size, out_string_curr_pos, H5E_DATASET, FAIL); + + out_string = strcat(out_string, "]"); + out_string_curr_pos += 1; + + /* If not the final sequence, add comma before next sequence */ + if (i != nelems - 1) { + out_string = strcat(out_string, ","); + out_string_curr_pos += 1; } } - else if (dtype_class == H5T_COMPOUND) { - /* Recursively parse each member of the compound type */ - int nmembers = 0; - size_t offset = 0; - size_t member_size = 0; - hid_t member_dtype_id = H5I_INVALID_HID; - yajl_val member_val; - - if ((nmembers = H5Tget_nmembers(dtype_id)) < 0) - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, - "can't get number of members in compound datatype"); - - for (int i = 0; i < nmembers; i++) { - if ((member_dtype_id = H5Tget_member_type(dtype_id, (unsigned int)i)) < 0) - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, - "can't get datatype of member in compound datatype"); - if ((member_size = H5Tget_size(member_dtype_id)) == 0) - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "can't get size of member datatype"); + /* Add closing bracket to all vlen data and terminating byte */ + out_diff = out_string_curr_pos - out_string; + tgt_out_size = (size_t)out_diff + strlen(closing_string) + 1; - if ((member_val = YAJL_GET_OBJECT(value_entry)->values[i]) == NULL) - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_PARSEERROR, FAIL, - "failed to parse member of compound type"); + CHECKED_REALLOC(out_string, *out_size, tgt_out_size, out_string_curr_pos, H5E_DATASET, FAIL); + out_string = strcat(out_string, closing_string); - if (RV_json_values_to_binary_recursive(member_val, member_dtype_id, - (void *)((char *)value_buffer + offset)) < 0) - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_PARSEERROR, FAIL, "failed to parse member datatype"); + /* Return length of actual string, not string buffer size */ + *out_size = strlen(out_string); - offset += member_size; - member_val = NULL; - member_size = 0; - member_dtype_id = H5I_INVALID_HID; - } +done: + + if (ret_value >= 0) { + *out = out_string; } else { - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unsupported datatype class for parsing"); + if (out_string) { + RV_free(out_string); + } } -done: return ret_value; -} +} /* end RV_vlen_data_to_json */ \ No newline at end of file diff --git a/src/rest_vol_datatype.c b/src/rest_vol_datatype.c index 615e4cf5..75706180 100644 --- a/src/rest_vol_datatype.c +++ b/src/rest_vol_datatype.c @@ -707,8 +707,9 @@ RV_parse_datatype(char *type, hbool_t need_truncate) * Purpose: Given a datatype, this function creates a JSON-formatted * string representation of the datatype. * - * Can be called recursively for the case of Array and - * Compound Datatypes. The parameter 'nested' should always be + * Can be called recursively for the case of Array, + * Compound, and Variable-length datatypes. + * The parameter 'nested' should always be * supplied as FALSE, as the function itself handles the * correct passing of the parameter when processing nested * datatypes (such as the base type for an Array datatype). @@ -744,6 +745,7 @@ RV_convert_datatype_to_JSON(hid_t type_id, char **type_body, size_t *type_body_l char *array_base_type = NULL; char **compound_member_strings = NULL; char *compound_member_name = NULL; + char *vlen_base_type = NULL; char *out_string = NULL; char *out_string_curr_pos; /* The "current position" pointer used to print to the appropriate place in the buffer and not overwrite important leading data */ @@ -1359,7 +1361,45 @@ RV_convert_datatype_to_JSON(hid_t type_id, char **type_body, size_t *type_body_l } /* H5T_REFERENCE */ case H5T_VLEN: { - FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unsupported datatype - VLEN"); + size_t vlen_base_type_len = 0; + const char *const fmt_string = "{" + "\"class\": \"H5T_VLEN\"," + "\"base\": %s" + "}"; + + /* Get the class and name of the base datatype */ + if ((type_base_class = H5Tget_super(type_id)) < 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, FAIL, "can't get base datatype for vlen sequence"); + + if (RV_convert_datatype_to_JSON(type_base_class, &vlen_base_type, &vlen_base_type_len, true, + server_version) < 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTSERIALIZE, FAIL, + "can't convert parent of vlen type to JSON"); + + /* Check whether the buffer needs to be grown */ + bytes_to_print = vlen_base_type_len + (strlen(fmt_string) - 2) + 1; + + buf_ptrdiff = out_string_curr_pos - out_string; + + if (buf_ptrdiff < 0) + FUNC_GOTO_ERROR( + H5E_INTERNAL, H5E_BADVALUE, FAIL, + "unsafe cast: datatype buffer pointer different was negative - this should not happen!"); + + CHECKED_REALLOC(out_string, out_string_len, (size_t)buf_ptrdiff + bytes_to_print, + out_string_curr_pos, H5E_DATATYPE, FAIL); + + /* Build datatype body by appending the base type */ + if ((bytes_printed = snprintf(out_string_curr_pos, out_string_len - (size_t)buf_ptrdiff, + fmt_string, vlen_base_type)) < 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_SYSERRSTR, FAIL, "snprintf error"); + + if ((size_t)bytes_printed >= out_string_len - (size_t)buf_ptrdiff) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_SYSERRSTR, FAIL, + "vlen datatype string size exceeded allocated buffer size"); + + out_string_curr_pos += bytes_printed; + break; } /* H5T_VLEN */ @@ -1419,6 +1459,8 @@ RV_convert_datatype_to_JSON(hid_t type_id, char **type_body, size_t *type_body_l H5free_memory(enum_value_name); if (enum_mapping) RV_free(enum_mapping); + if (vlen_base_type) + RV_free(vlen_base_type); return ret_value; } /* end RV_convert_datatype_to_JSON() */