/* * Copyright © 1998 NCSA * All rights reserved. * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Purpose: A library for displaying the values of a dataset in a human * readable format. */ #include #include #include #include #include #include "h5tools.h" #include "hdf5.h" #include "H5private.h" #include "h5dump.h" /* taken from h5dumputil.c */ int indent; int compound_data; int nCols = 80; FILE *rawdatastream; /* should initialize to stdout but gcc moans about it */ static int h5tools_init_g = 0; /* if h5tools lib has been initialized */ int print_data(hid_t oid, hid_t _p_type, int obj_data); /* * If REPEAT_VERBOSE is defined then character strings will be printed so * that repeated character sequences like "AAAAAAAAAA" are displayed as * * 'A' repeates 9 times * * Otherwise the format is more Perl-like * * 'A'*10 * */ #define REPEAT_VERBOSE /* * The output functions need a temporary buffer to hold a piece of the * dataset while it's being printed. This constant sets the limit on the * size of that temporary buffer in bytes. For efficiency's sake, choose the * largest value suitable for your machine (for testing use a small value). */ #if 1 #define H5DUMP_BUFSIZE (1024 * 1024) #else #define H5DUMP_BUFSIZE (1024) #endif #define OPT(X,S) ((X) ? (X) : (S)) #define ALIGN(A,Z) ((((A) + (Z) - 1) / (Z)) * (Z)) #define START_OF_DATA 0x0001 #define END_OF_DATA 0x0002 /* Special strings embedded in the output */ #define OPTIONAL_LINE_BREAK "\001" /* Variable length string datatype */ #define STR_INIT_LEN 4096 /*initial length */ typedef struct h5dump_str_t { char *s; /*allocate string */ size_t len; /*length of actual value */ size_t nalloc; /*allocated size of string */ } h5dump_str_t; /* Output variables */ typedef struct h5dump_context_t { size_t cur_column; /*current column for output */ size_t cur_elmt; /*current element/output line */ int need_prefix; /*is line prefix needed? */ int ndims; /*dimensionality */ hsize_t p_min_idx[H5S_MAX_RANK]; /*min selected index */ hsize_t p_max_idx[H5S_MAX_RANK]; /*max selected index */ int prev_multiline; /*was prev datum multiline? */ size_t prev_prefix_len;/*length of previous prefix */ int continuation; /*continuation of previous data?*/ int size_last_dim; /*the size of the last dimension, *needed so we can break after each *row */ int indent_level; /*the number of times we need some *extra indentation */ int default_indent_level; /*this is used when the indent *level gets changed */ } h5dump_context_t; typedef herr_t (*H5G_operator_t)(hid_t, const char*, void*); extern void init_prefix(char **temp, int length); extern void init_table(table_t **table); extern void free_table(table_t **table); extern void dump_table(char *name, table_t* table); extern herr_t find_objs(hid_t group, const char *name, void *op_data); extern int search_obj (table_t *temp, unsigned long *); extern int get_table_idx(table_t *table, unsigned long *); extern int get_tableflag(table_t*, int); extern int set_tableflag(table_t*, int); extern char *get_objectname(table_t*, int); /* local functions */ static int h5dump_vlen_dset(FILE *, const h5dump_t *, hid_t, hid_t, int); /*------------------------------------------------------------------------- * Function: h5tools_init * * Purpose: Initialize the H5 Tools library. * This should be called before any other h5tools function is * called. Effect of any h5tools function called before this * has been called is undetermined. * * Return: None * * Programmer: Albert Cheng, 2000-10-31 * * Modifications: *------------------------------------------------------------------------- */ void h5tools_init(void) { if (!h5tools_init_g) { if (!rawdatastream) rawdatastream = stdout; h5tools_init_g++; } } /*------------------------------------------------------------------------- * Function: h5tools_close * * Purpose: Close the H5 Tools library by closing or releasing resources * such as files opened by the library. * This should be called after all other h5tools functions have * been called. Effect of any h5tools function called after this * has been called is undetermined. * * Return: None * * Programmer: Albert Cheng, 2000-10-31 * * Modifications: *------------------------------------------------------------------------- */ void h5tools_close(void) { if (h5tools_init_g) { if (rawdatastream && rawdatastream != stdout) { if (fclose(rawdatastream)) perror("closing rawdatastream"); else rawdatastream = NULL; } h5tools_init_g = 0; } } /*------------------------------------------------------------------------- * Function: h5dump_str_close * * Purpose: Closes a string by releasing it's memory and setting the size * information to zero. * * Return: void * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static void h5dump_str_close(h5dump_str_t *str) { if (str && str->nalloc) { free(str->s); memset(str, 0, sizeof(h5dump_str_t)); } } /*------------------------------------------------------------------------- * Function: h5dump_str_len * * Purpose: Returns the length of the string, not counting the null * terminator. * * Return: Success: Length of string * * Failure: 0 * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static size_t h5dump_str_len(h5dump_str_t *str) { return str->len; } /*------------------------------------------------------------------------- * Function: h5dump_str_append * * Purpose: Formats variable arguments according to printf() format * string and appends the result to variable length string STR. * * Return: Success: Pointer to buffer containing result. * * Failure: NULL * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static char * h5dump_str_append(h5dump_str_t *str/*in,out*/, const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* Make sure we have some memory into which to print */ if (!str->s || str->nalloc <= 0) { str->nalloc = STR_INIT_LEN; str->s = malloc(str->nalloc); assert(str->s); str->s[0] = '\0'; str->len = 0; } while (1) { size_t avail = str->nalloc - str->len; size_t nchars = (size_t)HDvsnprintf(str->s+str->len, avail, fmt, ap); if (nchars < avail) { /* success */ str->len += nchars; break; } /* Try again with twice as much space */ str->nalloc *= 2; str->s = realloc(str->s, str->nalloc); assert(str->s); } va_end(ap); return str->s; } /*------------------------------------------------------------------------- * Function: h5dump_str_reset * * Purpose: Reset the string to the empty value. If no memory is * allocated yet then initialize the h5dump_str_t struct. * * Return: Success: Ptr to the buffer which contains a null * character as the first element. * * Failure: NULL * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static char * h5dump_str_reset(h5dump_str_t *str/*in,out*/) { if (!str->s || str->nalloc <= 0) { str->nalloc = STR_INIT_LEN; str->s = malloc(str->nalloc); assert(str->s); } str->s[0] = '\0'; str->len = 0; return str->s; } /*------------------------------------------------------------------------- * Function: h5dump_str_trunc * * Purpose: Truncate a string to be at most SIZE characters. * * Return: Success: Pointer to the string * * Failure: NULL * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static char * h5dump_str_trunc(h5dump_str_t *str/*in,out*/, size_t size) { if (size < str->len) { str->len = size; str->s[size] = '\0'; } return str->s; } /*------------------------------------------------------------------------- * Function: h5dump_str_fmt * * Purpose: Reformat a string contents beginning at character START * according to printf format FMT. FMT should contain no format * specifiers except possibly the `%s' variety. For example, if * the input string is `hello' and the format is "<<%s>>" then * the output value will be "<>". * * Return: Success: A pointer to the resulting string. * * Failure: NULL * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static char * h5dump_str_fmt(h5dump_str_t *str/*in,out*/, size_t start, const char *fmt) { char _temp[1024], *temp = _temp; /* If the format string is simply "%s" then don't bother doing anything */ if (!strcmp(fmt, "%s")) return str->s; /* * Save the input value if there is a `%' anywhere in FMT. Otherwise * don't bother because we don't need a temporary copy. */ if (strchr(fmt, '%')) { if (str->len - start + 1 > sizeof(_temp)) { temp = malloc(str->len-start + 1); assert(temp); } strcpy(temp, str->s + start); } /* Reset the output string and append a formatted version */ h5dump_str_trunc(str, start); h5dump_str_append(str, fmt, temp); /* Free the temp buffer if we allocated one */ if (temp != _temp) free(temp); return str->s; } /*------------------------------------------------------------------------- * Function: h5dump_prefix * * Purpose: Renders the line prefix value into string STR. * * Return: Success: Pointer to the prefix. * * Failure: NULL * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static char * h5dump_prefix(h5dump_str_t *str/*in,out*/, const h5dump_t *info, hsize_t elmtno, int ndims, hsize_t min_idx[], hsize_t max_idx[]) { hsize_t p_prod[H5S_MAX_RANK], p_idx[H5S_MAX_RANK]; hsize_t n, i = 0; h5dump_str_reset(str); if (ndims > 0) { /* * Calculate the number of elements represented by a unit change in a * certain index position. */ for (i = ndims - 1, p_prod[ndims - 1] = 1; i > 0; --i) { p_prod[i - 1] = (max_idx[i] - min_idx[i]) * p_prod[i]; } /* * Calculate the index values from the element number. */ for (i = 0, n = elmtno; i < (hsize_t)ndims; i++) { p_idx[i] = n / p_prod[i] + min_idx[i]; n %= p_prod[i]; } /* * Print the index values. */ for (i = 0; i < (hsize_t)ndims; i++) { if (i) h5dump_str_append(str, "%s", OPT(info->idx_sep, ",")); h5dump_str_append(str, OPT(info->idx_n_fmt, "%lu"), (unsigned long)p_idx[i]); } } else { /* Scalar */ h5dump_str_append(str, OPT(info->idx_n_fmt, "%lu"), (unsigned long)0); } /* * Add prefix and suffix to the index. */ return h5dump_str_fmt(str, 0, OPT(info->idx_fmt, "%s: ")); } /*------------------------------------------------------------------------- * Function: h5dump_escape * * Purpose: Changes all "funny" characters in S into standard C escape * sequences. If ESCAPE_SPACES is non-zero then spaces are * escaped by prepending a backslash. * * Return: Success: S * * Failure: NULL if the buffer would overflow. The * buffer has as many left-to-right escapes as * possible before overflow would have happened. * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static char * h5dump_escape(char *s/*in,out*/, size_t size, int escape_spaces) { size_t n = strlen(s); size_t i; const char *escape; char octal[8]; for (i = 0; i < n; i++) { switch (s[i]) { case '"': escape = "\\\""; break; case '\\': escape = "\\\\"; break; case '\b': escape = "\\b"; break; case '\f': escape = "\\f"; break; case '\n': escape = "\\n"; break; case '\r': escape = "\\r"; break; case '\t': escape = "\\t"; break; case ' ': escape = escape_spaces ? "\\ " : NULL; break; default: if (!isprint((int)*s)) { sprintf(octal, "\\%03o", (unsigned char)s[i]); escape = octal; } else { escape = NULL; } break; } if (escape) { size_t esc_size = strlen(escape); if (n + esc_size + 1 > size) /*would overflow*/ return NULL; memmove(s + i + esc_size, s + i, (n - i) + 1); /*make room*/ memcpy(s + i, escape, esc_size); /*insert*/ n += esc_size; i += esc_size - 1; } } return s; } /*------------------------------------------------------------------------- * Function: h5dump_is_zero * * Purpose: Determines if memory is initialized to all zero bytes. * * Return: TRUE if all bytes are zero; FALSE otherwise * * Programmer: Robb Matzke * Monday, June 7, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static hbool_t h5dump_is_zero(const void *_mem, size_t size) { const unsigned char *mem = (const unsigned char *)_mem; while (size-- > 0) if (mem[size]) return FALSE; return TRUE; } /*------------------------------------------------------------------------- * Function: h5dump_region * * Purpose: Prints information about a dataspace region by appending * the information to the specified string. * * Return: Success: 0 * * Failure: NULL * * Programmer: Robb Matzke * Monday, June 7, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static int h5dump_region(hid_t region, h5dump_str_t *str/*in,out*/, const h5dump_t *info) { hssize_t nblocks, npoints; hsize_t *ptdata; int ndims = H5Sget_simple_extent_ndims(region); /* * These two functions fail if the region does not have blocks or points, * respectively. They do not currently know how to translate from one to * the other. */ H5E_BEGIN_TRY { nblocks = H5Sget_select_hyper_nblocks(region); npoints = H5Sget_select_elem_npoints(region); } H5E_END_TRY; h5dump_str_append(str, "{"); /* Print block information */ if (nblocks > 0) { int i; ptdata = malloc(nblocks * ndims * 2 * sizeof(ptdata[0])); H5Sget_select_hyper_blocklist(region, 0, nblocks, ptdata); for (i = 0; i < nblocks; i++) { int j; h5dump_str_append(str, info->dset_blockformat_pre, i ? "," OPTIONAL_LINE_BREAK " " : "", (unsigned long)i); /* Start coordinates and opposite corner */ for (j = 0; j < ndims; j++) h5dump_str_append(str, "%s%lu", j ? "," : "(", (unsigned long)ptdata[i * 2 * ndims + j]); for (j = 0; j < ndims; j++) h5dump_str_append(str, "%s%lu", j ? "," : ")-(", (unsigned long)ptdata[i * 2 * ndims + j + ndims]); h5dump_str_append(str, ")"); } free(ptdata); } /* Print point information */ if (npoints > 0) { int i; ptdata = malloc(npoints * ndims * sizeof(ptdata[0])); H5Sget_select_elem_pointlist(region, 0, npoints, ptdata); for (i = 0; i < npoints; i++) { int j; h5dump_str_append(str, info->dset_ptformat_pre , i ? "," OPTIONAL_LINE_BREAK " " : "", (unsigned long)i); for (j = 0; j < ndims; j++) h5dump_str_append(str, "%s%lu", j ? "," : "(", (unsigned long)(ptdata[i * ndims + j])); h5dump_str_append(str, ")"); } free(ptdata); } h5dump_str_append(str, "}"); return 0; } /*------------------------------------------------------------------------- * Function: h5dump_sprint * * Purpose: Renders the value pointed to by VP of type TYPE into variable * length string STR. * * Return: A pointer to memory containing the result or NULL on error. * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Modifications: * Robb Matzke, 1999-04-26 * Made this function safe from overflow problems by allowing it * to reallocate the output string. * * Robb Matzke, 1999-06-04 * Added support for object references. The new `container' * argument is the dataset where the reference came from. * * Robb Matzke, 1999-06-07 * Added support for printing raw data. If info->raw is non-zero * then data is printed in hexadecimal format. * *------------------------------------------------------------------------- */ static char * h5dump_sprint(h5dump_str_t *str/*in,out*/, const h5dump_t *info, hid_t container, hid_t type, void *vp, h5dump_context_t *ctx) { size_t n, offset, size, nelmts, start; hsize_t dims[H5S_MAX_RANK]; char *name, quote='\0'; unsigned char *ucp_vp = (unsigned char *)vp; char *cp_vp = (char *)vp; hid_t memb, obj, region; int nmembs, ndims, otype; static char fmt_llong[8], fmt_ullong[8]; H5T_str_t pad; H5G_stat_t sb; /*some tempvars to store the value before we append it to the string to get rid of the memory alignment problem*/ float tempfloat; double tempdouble; int tempint; unsigned int tempuint; short tempshort; unsigned short tempushort; long templong; unsigned long tempulong; unsigned long_long tempullong; long_long templlong; /* Build default formats for long long types */ if (!fmt_llong[0]) { sprintf(fmt_llong, "%%%sd", PRINTF_LL_WIDTH); sprintf(fmt_ullong, "%%%su", PRINTF_LL_WIDTH); } /* Append value depending on data type */ start = h5dump_str_len(str); if (info->raw) { unsigned int i; h5dump_str_append(str, "0x"); n = H5Tget_size(type); for (i = 0; i < n; i++) h5dump_str_append(str, OPT(info->fmt_raw, "%02x"), ucp_vp[i]); } else if (H5Tequal(type, H5T_NATIVE_DOUBLE)) { memcpy(&tempdouble, vp, sizeof(double)); h5dump_str_append(str, OPT(info->fmt_double, "%g"), tempdouble); } else if (H5Tequal(type, H5T_NATIVE_FLOAT)) { memcpy(&tempfloat, vp, sizeof(float)); h5dump_str_append(str, OPT(info->fmt_double, "%g"), tempfloat); } else if (info->ascii && (H5Tequal(type, H5T_NATIVE_SCHAR) || H5Tequal(type, H5T_NATIVE_UCHAR))) { if (ESCAPE_HTML == info->str_locale) { if (*cp_vp <= ' ' || *cp_vp > '~') h5dump_str_append(str, "%%%02X", *ucp_vp); else h5dump_str_append(str, "%c", *cp_vp); } else { switch (*cp_vp) { case '"': h5dump_str_append(str, "\\\""); break; case '\\': h5dump_str_append(str, "\\\\"); break; case '\b': h5dump_str_append(str, "\\b"); break; case '\f': h5dump_str_append(str, "\\f"); break; case '\n': h5dump_str_append(str, "\\n"); break; case '\r': h5dump_str_append(str, "\\r"); break; case '\t': h5dump_str_append(str, "\\t"); break; default: if (isprint(*cp_vp)) { h5dump_str_append(str, "%c", *cp_vp); } else { h5dump_str_append(str, "\\%03o", *ucp_vp); } break; } } } else if (H5T_STRING==H5Tget_class(type)) { unsigned int i; size = H5Tget_size(type); quote = '\0'; pad = H5Tget_strpad(type); for (i=0; i < size && ((pad == H5T_STR_NULLPAD) ? 1 : (cp_vp[i] != '\0')); i++) { int j = 1; /* * Count how many times the next character repeats. If the * threshold is zero then that means it can repeat any number * of times. */ if (info->str_repeat > 0) while (i + j < size && cp_vp[i] == cp_vp[i + j]) j++; /* * Print the opening quote. If the repeat count is high enough to * warrant printing the number of repeats instead of enumerating * the characters, then make sure the character to be repeated is * in it's own quote. */ if (info->str_repeat > 0 && j > info->str_repeat) { if (quote) h5dump_str_append(str, "%c", quote); quote = '\''; h5dump_str_append(str, "%s%c", i ? " " : "", quote); } else if (!quote) { quote = '"'; h5dump_str_append(str, "%s%c", i ? " " : "", quote); } /* Print the character */ if (ESCAPE_HTML == info->str_locale) { if (cp_vp[i] <= ' ' || cp_vp[i] > '~') { h5dump_str_append(str, "%%%02X", ucp_vp[i]); } else { h5dump_str_append(str, "%c", cp_vp[i]); } } else { switch (cp_vp[i]) { case '"': h5dump_str_append(str, "\\\""); break; case '\\': h5dump_str_append(str, "\\\\"); break; case '\b': h5dump_str_append(str, "\\b"); break; case '\f': h5dump_str_append(str, "\\f"); break; case '\n': h5dump_str_append(str, "\\n"); break; case '\r': h5dump_str_append(str, "\\r"); break; case '\t': h5dump_str_append(str, "\\t"); break; default: if (isprint(cp_vp[i])) h5dump_str_append(str, "%c", cp_vp[i]); else h5dump_str_append(str, "\\%03o", ucp_vp[i]); break; } } /* Print the repeat count */ if (info->str_repeat && j > info->str_repeat) { #ifdef REPEAT_VERBOSE h5dump_str_append(str, "%c repeats %d times", quote, j - 1); #else h5dump_str_append(str, "%c*%d", quote, j - 1); #endif quote = '\0'; i += j - 1; } } if (quote) h5dump_str_append(str, "%c", quote); if (0 == i) h5dump_str_append(str, "\"\""); /*empty string*/ } else if (H5Tequal(type, H5T_NATIVE_INT)) { memcpy(&tempint, vp, sizeof(int)); h5dump_str_append(str, OPT(info->fmt_int, "%d"), tempint); } else if (H5Tequal(type, H5T_NATIVE_UINT)) { memcpy(&tempuint, vp, sizeof(unsigned int)); h5dump_str_append(str, OPT(info->fmt_uint, "%u"), tempuint); } else if (H5Tequal(type, H5T_NATIVE_SCHAR)) { h5dump_str_append(str, OPT(info->fmt_schar, "%d"), *((signed char*)vp)); } else if (H5Tequal(type, H5T_NATIVE_UCHAR)) { h5dump_str_append(str, OPT(info->fmt_uchar, "%u"), *((unsigned char*)vp)); } else if (H5Tequal(type, H5T_NATIVE_SHORT)) { memcpy(&tempshort, vp, sizeof(short)); h5dump_str_append(str, OPT(info->fmt_short, "%d"), tempshort); } else if (H5Tequal(type, H5T_NATIVE_USHORT)) { memcpy(&tempushort, vp, sizeof(unsigned short)); h5dump_str_append(str, OPT(info->fmt_ushort, "%u"), tempushort); } else if (H5Tequal(type, H5T_NATIVE_LONG)) { memcpy(&templong, vp, sizeof(long)); h5dump_str_append(str, OPT(info->fmt_long, "%ld"), templong); } else if (H5Tequal(type, H5T_NATIVE_ULONG)) { memcpy(&tempulong, vp, sizeof(unsigned long)); h5dump_str_append(str, OPT(info->fmt_ulong, "%lu"), tempulong); } else if (H5Tequal(type, H5T_NATIVE_LLONG)) { memcpy(&templlong, vp, sizeof(long_long)); h5dump_str_append(str, OPT(info->fmt_llong, fmt_llong), templlong); } else if (H5Tequal(type, H5T_NATIVE_ULLONG)) { memcpy(&tempullong, vp, sizeof(unsigned long_long)); h5dump_str_append(str, OPT(info->fmt_ullong, fmt_ullong), tempullong); } else if (H5Tequal(type, H5T_NATIVE_HSSIZE)) { if (sizeof(hssize_t) == sizeof(int)) { memcpy(&tempint, vp, sizeof(int)); h5dump_str_append(str, OPT(info->fmt_int, "%d"), tempint); } else if (sizeof(hssize_t) == sizeof(long)) { memcpy(&templong, vp, sizeof(long)); h5dump_str_append(str, OPT(info->fmt_long, "%ld"), templong); } else { memcpy(&templlong, vp, sizeof(long_long)); h5dump_str_append(str, OPT(info->fmt_llong, fmt_llong), templlong); } } else if (H5Tequal(type, H5T_NATIVE_HSIZE)) { if (sizeof(hsize_t) == sizeof(int)) { memcpy(&tempuint, vp, sizeof(unsigned int)); h5dump_str_append(str, OPT(info->fmt_uint, "%u"), tempuint); } else if (sizeof(hsize_t) == sizeof(long)) { memcpy(&tempulong, vp, sizeof(long)); h5dump_str_append(str, OPT(info->fmt_ulong, "%lu"), tempulong); } else { memcpy(&tempullong, vp, sizeof(unsigned long_long)); h5dump_str_append(str, OPT(info->fmt_ullong, fmt_ullong), tempullong); } } else if (H5T_COMPOUND==H5Tget_class(type)) { int j, x; nmembs = H5Tget_nmembers(type); h5dump_str_append(str, "%s", OPT(info->cmpd_pre, "{")); for (j = 0; j < nmembs; j++) { if (j) h5dump_str_append(str, "%s", OPT(info->cmpd_sep, ", " OPTIONAL_LINE_BREAK)); /* RPM 2000-10-31 * If the previous character is a line-feed (which is true when * h5dump is running) then insert some white space for * indentation. Be warned that column number calculations will be * incorrect and that object indices at the beginning of the line * will be missing (h5dump doesn't display them anyway). */ if (ctx->indent_level >= 0 && str->len && str->s[str->len - 1] == '\n') { h5dump_str_append(str, OPT(info->line_pre, ""), ""); for (x = 0; x < ctx->indent_level + 1; x++) h5dump_str_append(str, "%s", OPT(info->line_indent, "")); } /* The name */ name = H5Tget_member_name(type, j); h5dump_str_append(str, OPT(info->cmpd_name, ""), name); free(name); /* The value */ offset = H5Tget_member_offset(type, j); memb = H5Tget_member_type(type, j); ctx->indent_level++; h5dump_sprint(str, info, container, memb, cp_vp + offset , ctx); ctx->indent_level--; H5Tclose(memb); } /* RPM 2000-10-31 * If the previous character is a line feed (which is true when * h5dump is running) then insert some white space for indentation. * Be warned that column number calculations will be incorrect and * that object indices at the beginning of the line will be missing * (h5dump doesn't display them anyway). */ h5dump_str_append(str, "%s", OPT(info->cmpd_end, "")); if (ctx->indent_level >= 0 && str->len && str->s[str->len - 1] == '\n') { h5dump_str_append(str, OPT(info->line_pre, ""), ""); for (x = 0; x < ctx->indent_level; x++) h5dump_str_append(str, "%s", OPT(info->line_indent, "")); } h5dump_str_append(str, "%s", OPT(info->cmpd_suf, "}")); } else if (H5T_ENUM==H5Tget_class(type)) { char enum_name[1024]; if (H5Tenum_nameof(type, vp, enum_name, sizeof enum_name) >= 0) { h5dump_str_append(str, h5dump_escape(enum_name, sizeof(enum_name), TRUE)); } else { unsigned int i; h5dump_str_append(str, "0x"); n = H5Tget_size(type); for (i = 0; i < n; i++) h5dump_str_append(str, "%02x", ucp_vp[i]); } } else if (H5Tequal(type, H5T_STD_REF_DSETREG)) { /* * Dataset region reference -- show the type and OID of the referenced * object, but we are unable to show the region yet because there * isn't enough support in the data space layer. - rpm 19990604 */ if (h5dump_is_zero(vp, H5Tget_size(type))) { h5dump_str_append(str, "NULL"); } else { obj = H5Rdereference(container, H5R_DATASET_REGION, vp); region = H5Rget_region(container, H5R_DATASET_REGION, vp); H5Gget_objinfo(obj, ".", FALSE, &sb); if (info->dset_hidefileno) h5dump_str_append(str, info->dset_format, sb.objno[1], sb.objno[0]); else h5dump_str_append(str, info->dset_format, sb.fileno[1], sb.fileno[0], sb.objno[1], sb.objno[0]); h5dump_region(region, str, info); H5Sclose(region); H5Dclose(obj); } } else if (H5Tequal(type, H5T_STD_REF_OBJ)) { /* * Object references -- show the type and OID of the referenced * object. */ if (h5dump_is_zero(vp, H5Tget_size(type))) { h5dump_str_append(str, "NULL"); } else { otype = H5Rget_object_type(container, vp); obj = H5Rdereference(container, H5R_OBJECT, vp); H5Gget_objinfo(obj, ".", FALSE, &sb); /* Print object type and close object */ switch (otype) { case H5G_GROUP: h5dump_str_append(str, GROUPNAME); H5Gclose(obj); break; case H5G_DATASET: h5dump_str_append(str, DATASET); H5Dclose(obj); break; case H5G_TYPE: h5dump_str_append(str, DATATYPE); H5Tclose(obj); break; default: h5dump_str_append(str, "%u-", otype); break; } /* Print OID */ if (info->obj_hidefileno){ h5dump_str_append(str, info->obj_format, sb.objno[1], sb.objno[0]); } else { h5dump_str_append(str, info->obj_format, sb.fileno[1], sb.fileno[0], sb.objno[1], sb.objno[0]); } } } else if (H5T_ARRAY==H5Tget_class(type)) { int x; unsigned int i; int k; /* Get the array's base datatype for each element */ memb=H5Tget_super(type); size = H5Tget_size(memb); ndims = H5Tget_array_ndims(type); H5Tget_array_dims(type, dims, NULL); assert(ndims>=1 && ndims<=H5S_MAX_RANK); /* Calculate the number of array elements */ for (k = 0, nelmts = 1; k < ndims; k++) nelmts *= dims[k]; /* Print the opening bracket */ h5dump_str_append(str, "%s", OPT(info->arr_pre, "[")); for (i = 0; i < nelmts; i++) { if (i) h5dump_str_append(str, "%s", OPT(info->arr_sep, "," OPTIONAL_LINE_BREAK)); if (info->arr_linebreak && i && 0 == i % dims[ndims - 1]) { h5dump_str_append(str, "%s", "\n"); /*need to indent some more here*/ if (ctx->indent_level >= 0) h5dump_str_append(str, "%s", OPT(info->line_pre, "")); for (x = 0; x < ctx->indent_level + 1; x++) h5dump_str_append(str,"%s",OPT(info->line_indent,"")); } /* end if */ ctx->indent_level++; /* Dump the array element */ h5dump_sprint(str, info, container, memb, cp_vp + i * size, ctx); ctx->indent_level--; } /* end for */ /* Print the closing bracket */ h5dump_str_append(str, "%s", OPT(info->arr_suf, "]")); H5Tclose(memb); } else { /* All other types get printed as hexadecimal */ unsigned int i; h5dump_str_append(str, "0x"); n = H5Tget_size(type); for (i = 0; i < n; i++) h5dump_str_append(str, "%02x", ((unsigned char *)vp)[i]); } return h5dump_str_fmt(str, start, OPT(info->elmt_fmt, "%s")); } /*------------------------------------------------------------------------- * Function: h5dump_ncols * * Purpose: Count the number of columns in a string. This is the number * of characters in the string not counting line-control * characters. * * Return: Success: Width of string. * * Failure: 0 * * Programmer: Robb Matzke * Tuesday, April 27, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static size_t h5dump_ncols(const char *s) { size_t i; for (i = 0; *s; s++) if (*s >= ' ') i++; return i; } /*------------------------------------------------------------------------- * Function: h5dump_simple_prefix * * Purpose: If ctx->need_prefix is set then terminate the current line * (if applicable), calculate the prefix string, and display it * at the start of a line. * * Return: void * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * Robb Matzke, 1999-09-29 * If a new prefix is printed then the current element number is * set back to zero. *------------------------------------------------------------------------- */ static void h5dump_simple_prefix(FILE *stream, const h5dump_t *info, h5dump_context_t *ctx, hsize_t elmtno, int secnum) { h5dump_str_t prefix; size_t templength = 0; int i, indentlevel = 0; memset(&prefix, 0, sizeof(h5dump_str_t)); if (!ctx->need_prefix) return; /* Terminate previous line, if any */ if (ctx->cur_column) { fputs(OPT(info->line_suf, ""), stream); #if 0 /*why?*/ if (info->line_ncols != ctx->cur_column) { putc('\n', stream); } #endif putc('\n',stream); fputs(OPT(info->line_sep, ""), stream); } /* Calculate new prefix */ h5dump_prefix(&prefix, info, elmtno, ctx->ndims, ctx->p_min_idx, ctx->p_max_idx); /* Write new prefix to output */ if (ctx->indent_level >= 0) { indentlevel = ctx->indent_level; } else { /* this is because sometimes we dont print out all the header * info for the data(like the tattr-2.ddl example. if that happens * the ctx->indent_level a negative so we need to skip the above * and just print out the default indent levels. */ indentlevel = ctx->default_indent_level; } if (elmtno == 0 && secnum == 0 && info->line_1st) fputs(h5dump_str_fmt(&prefix, 0, info->line_1st), stream); else if (secnum && info->line_cont) fputs(h5dump_str_fmt(&prefix, 0, info->line_cont), stream); else fputs(h5dump_str_fmt(&prefix, 0, info->line_pre), stream); templength = h5dump_str_len(&prefix); for (i = 0; i < indentlevel; i++){ fputs(h5dump_str_fmt(&prefix, 0, info->line_indent), stream); templength += h5dump_str_len(&prefix); } ctx->cur_column = ctx->prev_prefix_len = templength; ctx->cur_elmt = 0; ctx->need_prefix = 0; /* Free string */ h5dump_str_close(&prefix); } /*------------------------------------------------------------------------- * Function: h5dump_simple_data * * Purpose: Prints some (NELMTS) data elements to output STREAM. The * elements are stored in _MEM as type TYPE and are printed * according to the format described in INFO. The CTX struct * contains context information shared between calls to this * function. The FLAGS is a bit field that indicates whether * the data supplied in this call falls at the beginning or end * of the total data to be printed (START_OF_DATA and * END_OF_DATA). * * Return: void * * Programmer: Robb Matzke * Monday, April 26, 1999 * * Modifications: * Robb Matzke, 1999-06-04 * The `container' argument is the optional dataset for * reference types. * * Robb Matzke, 1999-09-29 * Understands the `per_line' property which indicates that * every Nth element should begin a new line. * *------------------------------------------------------------------------- */ static void h5dump_simple_data(FILE *stream, const h5dump_t *info, hid_t container, h5dump_context_t *ctx/*in,out*/, unsigned flags, hsize_t nelmts, hid_t type, void *_mem) { unsigned char *mem = (unsigned char*)_mem; hsize_t i; /*element counter */ char *s, *section; /*a section of output */ int secnum; /*section sequence number */ size_t size; /*size of each datum */ size_t ncols=80; /*available output width */ h5dump_str_t buffer; /*string into which to render */ int multiline; /*datum was multiline */ int elmt_counter=0; /*counts the # elements printed. * I (ptl?) needed something that * isnt going to get reset when a new * line is formed. I'm going to use * this var to count elements and * break after we see a number equal * to the ctx->size_last_dim. */ /* Setup */ memset(&buffer, 0, sizeof(h5dump_str_t)); size = H5Tget_size(type); if (info->line_ncols > 0) ncols = info->line_ncols; h5dump_simple_prefix(stream, info, ctx, 0, 0); for (i = 0; i < nelmts; i++, ctx->cur_elmt++, elmt_counter++){ /* Render the element */ h5dump_str_reset(&buffer); h5dump_sprint(&buffer, info, container, type, mem + i * size, ctx); if (i + 1 < nelmts || 0 == (flags & END_OF_DATA)) h5dump_str_append(&buffer, "%s", OPT(info->elmt_suf1, ",")); s = h5dump_str_fmt(&buffer, 0, "%s"); /* * If the element would split on multiple lines if printed at our * current location... */ if (info->line_multi_new == 1 && (ctx->cur_column + h5dump_ncols(s) + strlen(OPT(info->elmt_suf2, " ")) + strlen(OPT(info->line_suf, ""))) > ncols) { if (ctx->prev_multiline) { /* * ... and the previous element also occupied more than one * line, then start this element at the beginning of a line. */ ctx->need_prefix = TRUE; } else if ((ctx->prev_prefix_len + h5dump_ncols(s) + strlen(OPT(info->elmt_suf2, " ")) + strlen(OPT(info->line_suf, ""))) <= ncols) { /* * ...but *could* fit on one line otherwise, then we * should end the current line and start this element on its * own line. */ ctx->need_prefix = TRUE; } } /* * We need to break after each row of a dimension---> we should * break at the end of the each last dimension well that is the * way the dumper did it before */ if (info->arr_linebreak && ctx->cur_elmt){ if ((ctx->cur_elmt % ctx->size_last_dim) == 0) ctx->need_prefix = TRUE; if (elmt_counter == ctx->size_last_dim) { ctx->need_prefix = TRUE; elmt_counter = 0; } } /* * If the previous element occupied multiple lines and this element * is too long to fit on a line then start this element at the * beginning of the line. */ if (info->line_multi_new == 1 && ctx->prev_multiline && (ctx->cur_column + h5dump_ncols(s) + strlen(OPT(info->elmt_suf2, " ")) + strlen(OPT(info->line_suf, ""))) > ncols) ctx->need_prefix = TRUE; /* * If too many elements have already been printed then we need to * start a new line. */ if (info->line_per_line > 0 && ctx->cur_elmt >= info->line_per_line) ctx->need_prefix = TRUE; /* * Each OPTIONAL_LINE_BREAK embedded in the rendered string can cause * the data to split across multiple lines. We display the sections * one-at a time. */ for (secnum = 0, multiline = 0; (section = strtok(secnum ? NULL : s, OPTIONAL_LINE_BREAK)); secnum++) { /* * If the current section plus possible suffix and end-of-line * information would cause the output to wrap then we need to * start a new line. */ /* * Added the info->skip_first because the dumper does not want * this check to happen for the first line */ if ((!info->skip_first || i) && (ctx->cur_column + strlen(section) + strlen(OPT(info->elmt_suf2, " ")) + strlen(OPT(info->line_suf, ""))) > ncols) ctx->need_prefix = 1; /* * Print the prefix or separate the beginning of this element * from the previous element. */ if (ctx->need_prefix) { if (secnum) multiline++; h5dump_simple_prefix(stream, info, ctx, i, secnum); } else if ((i || ctx->continuation) && secnum == 0) { fputs(OPT(info->elmt_suf2, " "), stream); ctx->cur_column += strlen(OPT(info->elmt_suf2, " ")); } /* Print the section */ fputs(section, stream); ctx->cur_column += strlen(section); } ctx->prev_multiline = multiline; } h5dump_str_close(&buffer); } /*------------------------------------------------------------------------- * Function: h5dump_simple_dset * * Purpose: Print some values from a dataset with a simple data space. * This is a special case of h5dump_dset(). This function only * intended for dumping datasets -- it does strip mining and * some other things which are unnecessary for smaller objects * such as attributes (to print small objects like attributes * simply read the attribute and call h5dump_simple_mem()). * * Return: Success: 0 * * Failure: -1 * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static int h5dump_simple_dset(FILE *stream, const h5dump_t *info, hid_t dset, hid_t p_type, int indentlevel) { hid_t f_space; /*file data space */ hsize_t elmtno, i; /*counters */ int carry; /*counter carry value */ hssize_t zero[8]; /*vector of zeros */ unsigned flags; /*buffer extent flags */ hsize_t total_size[H5S_MAX_RANK];/*total size of dataset*/ /* Print info */ h5dump_context_t ctx; /*print context */ size_t p_type_nbytes; /*size of memory type */ hsize_t p_nelmts; /*total selected elmts */ /* Stripmine info */ hsize_t sm_size[H5S_MAX_RANK]; /*stripmine size */ hsize_t sm_nbytes; /*bytes per stripmine */ hsize_t sm_nelmts; /*elements per stripmine*/ unsigned char *sm_buf=NULL; /*buffer for raw data */ hid_t sm_space; /*stripmine data space */ /* Hyperslab info */ hssize_t hs_offset[H5S_MAX_RANK];/*starting offset */ hsize_t hs_size[H5S_MAX_RANK]; /*size this pass */ hsize_t hs_nelmts; /*elements in request */ #if 0 hsize_t dim_n_size; #endif /* * Check that everything looks okay. The dimensionality must not be too * great and the dimensionality of the items selected for printing must * match the dimensionality of the dataset. */ memset(&ctx, 0, sizeof ctx); ctx.indent_level = indentlevel; ctx.need_prefix = 1; f_space = H5Dget_space(dset); ctx.ndims = H5Sget_simple_extent_ndims(f_space); if ((size_t)ctx.ndims > NELMTS(sm_size)) return -1; /* Assume entire data space to be printed */ for (i = 0; i < (hsize_t)ctx.ndims; i++) ctx.p_min_idx[i] = 0; H5Sget_simple_extent_dims(f_space, total_size, NULL); for (i = 0, p_nelmts = 1; i < (hsize_t)ctx.ndims; i++) p_nelmts *= total_size[i]; if (p_nelmts == 0) return 0; /*nothing to print*/ ctx.size_last_dim = total_size[ctx.ndims - 1]; /* * Determine the strip mine size and allocate a buffer. The strip mine is * a hyperslab whose size is manageable. */ p_type_nbytes = H5Tget_size(p_type); for (i = ctx.ndims, sm_nbytes = p_type_nbytes; i > 0; --i) { sm_size[i - 1] = MIN(total_size[i - 1], H5DUMP_BUFSIZE / sm_nbytes); sm_nbytes *= sm_size[i - 1]; assert(sm_nbytes > 0); } sm_buf = malloc(sm_nbytes); sm_nelmts = sm_nbytes / p_type_nbytes; sm_space = H5Screate_simple(1, &sm_nelmts, NULL); /* The stripmine loop */ memset(hs_offset, 0, sizeof hs_offset); memset(zero, 0, sizeof zero); for (elmtno = 0; elmtno < p_nelmts; elmtno += hs_nelmts) { /* Calculate the hyperslab size */ if (ctx.ndims > 0) { for (i = 0, hs_nelmts = 1; i < (hsize_t)ctx.ndims; i++) { hs_size[i] = MIN(total_size[i] - hs_offset[i], sm_size[i]); ctx.p_max_idx[i] = ctx.p_min_idx[i] + hs_size[i]; hs_nelmts *= hs_size[i]; } H5Sselect_hyperslab(f_space, H5S_SELECT_SET, hs_offset, NULL, hs_size, NULL); H5Sselect_hyperslab(sm_space, H5S_SELECT_SET, zero, NULL, &hs_nelmts, NULL); } else { H5Sselect_all(f_space); H5Sselect_all(sm_space); hs_nelmts = 1; } /* Read the data */ if (H5Dread(dset, p_type, sm_space, f_space, H5P_DEFAULT, sm_buf) < 0) return -1; /* Print the data */ flags = (((elmtno == 0) ? START_OF_DATA : 0) | (((elmtno + hs_nelmts) >= p_nelmts) ? END_OF_DATA : 0)); h5dump_simple_data(stream, info, dset, &ctx, flags, hs_nelmts, p_type, sm_buf); /* Calculate the next hyperslab offset */ for (i = ctx.ndims, carry = 1; i > 0 && carry; --i) { ctx.p_min_idx[i - 1] = ctx.p_max_idx[i - 1]; hs_offset[i - 1] += hs_size[i - 1]; if (hs_offset[i - 1] == (hssize_t)total_size[i - 1]) hs_offset[i-1] = 0; else carry = 0; } ctx.continuation++; } /* Terminate the output */ if (ctx.cur_column) { fputs(OPT(info->line_suf, ""), stream); putc('\n', stream); fputs(OPT(info->line_sep, ""), stream); } H5Sclose(sm_space); H5Sclose(f_space); free(sm_buf); return 0; } /*------------------------------------------------------------------------- * Function: h5dump_simple_mem * * Purpose: Print some values from memory with a simple data space. * This is a special case of h5dump_mem(). * * Return: Success: 0 * * Failure: -1 * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static int h5dump_simple_mem(FILE *stream, const h5dump_t *info, hid_t type, hid_t space, void *mem, int indentlevel) { hsize_t i; /*counters */ hsize_t nelmts; /*total selected elmts */ h5dump_context_t ctx; /*printing context */ /* * Check that everything looks okay. The dimensionality must not be too * great and the dimensionality of the items selected for printing must * match the dimensionality of the dataset. */ memset(&ctx, 0, sizeof(ctx)); ctx.ndims = H5Sget_simple_extent_ndims(space); if ((size_t)ctx.ndims > NELMTS(ctx.p_min_idx)) return -1; ctx.indent_level = indentlevel; ctx.need_prefix = 1; /* Assume entire data space to be printed */ for (i = 0; i < (hsize_t)ctx.ndims; i++) ctx.p_min_idx[i] = 0; H5Sget_simple_extent_dims(space, ctx.p_max_idx, NULL); for (i = 0, nelmts = 1; ctx.ndims != 0 && i < (hsize_t)ctx.ndims; i++) nelmts *= ctx.p_max_idx[i] - ctx.p_min_idx[i]; if (nelmts == 0) return 0; /*nothing to print*/ ctx.size_last_dim = ctx.p_max_idx[ctx.ndims - 1]; /* Print it */ h5dump_simple_data(stream, info, -1/*no dataset*/, &ctx, START_OF_DATA|END_OF_DATA, nelmts, type, mem); /* Terminate the output */ if (ctx.cur_column) { fputs(OPT(info->line_suf, ""), stream); putc('\n', stream); fputs(OPT(info->line_sep, ""), stream); } return 0; } /*------------------------------------------------------------------------- * Function: h5dump_fixtype * * Purpose: Given a file data type choose a memory data type which is * appropriate for printing the data. * * Return: Success: Memory data type * * Failure: FAIL * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Modifications: * Robb Matzke, 1999-06-04 * Added support for references. * *------------------------------------------------------------------------- */ hid_t h5dump_fixtype(hid_t f_type) { hid_t m_type = FAIL, f_memb; hid_t *memb = NULL; char **name = NULL; int nmembs = 0, i; int ndims; hsize_t dim[H5S_MAX_RANK]; size_t size, offset; hid_t array_base; /* H5T_str_t strpad; */ size = H5Tget_size(f_type); switch (H5Tget_class(f_type)) { case H5T_INTEGER: /* * Use the smallest native integer type of the same sign as the file * such that the memory type is at least as large as the file type. * If there is no memory type large enough then use the largest * memory type available. */ if (size <= sizeof(char)) { m_type = H5Tcopy(H5T_NATIVE_SCHAR); } else if (size <= sizeof(short)) { m_type = H5Tcopy(H5T_NATIVE_SHORT); } else if (size <= sizeof(int)) { m_type = H5Tcopy(H5T_NATIVE_INT); } else if (size <= sizeof(long)) { m_type = H5Tcopy(H5T_NATIVE_LONG); } else { m_type = H5Tcopy(H5T_NATIVE_LLONG); } H5Tset_sign(m_type, H5Tget_sign(f_type)); break; case H5T_FLOAT: /* * Use the smallest native floating point type available such that * its size is at least as large as the file type. If there is not * native type large enough then use the largest native type. */ if (size <= sizeof(float)) { m_type = H5Tcopy(H5T_NATIVE_FLOAT); } else if (size <= sizeof(double)) { m_type = H5Tcopy(H5T_NATIVE_DOUBLE); } else { m_type = H5Tcopy(H5T_NATIVE_LDOUBLE); } break; case H5T_STRING: /* * This is needed because the function in dumputil.c is the case where * strDUAction == TRUE. if it is false we will do the original action * here. */ m_type = H5Tcopy(f_type); H5Tset_cset(m_type, H5T_CSET_ASCII); break; case H5T_COMPOUND: /* * We have to do this in two steps. The first step scans the file * type and converts the members to native types and remembers all * their names and sizes, computing the size of the memory compound * type at the same time. Then we create the memory compound type * and add the members. */ nmembs = H5Tget_nmembers(f_type); memb = calloc(nmembs, sizeof(hid_t)); name = calloc(nmembs, sizeof(char *)); for (i = 0, size = 0; i < nmembs; i++) { /* Get the member type and fix it */ f_memb = H5Tget_member_type(f_type, i); memb[i] = h5dump_fixtype(f_memb); H5Tclose(f_memb); if (memb[i] < 0) goto done; /* Get the member name */ name[i] = H5Tget_member_name(f_type, i); if (name[i] == NULL) goto done; /* * Compute the new offset so each member is aligned on a byte * boundary which is the same as the member size. */ size = ALIGN(size, H5Tget_size(memb[i])) + H5Tget_size(memb[i]); } m_type = H5Tcreate(H5T_COMPOUND, size); for (i = 0, offset = 0; i < nmembs; i++) { if (offset) offset = ALIGN(offset, H5Tget_size(memb[i])); H5Tinsert(m_type, name[i], offset, memb[i]); offset += H5Tget_size(memb[i]); } break; case H5T_ARRAY: /* Get the array information */ ndims=H5Tget_array_ndims(f_type); H5Tget_array_dims(f_type,dim,NULL); /* Get the array's base type and convert it to the printable version */ f_memb=H5Tget_super(f_type); array_base = h5dump_fixtype(f_memb); /* Copy the array */ m_type=H5Tarray_create(array_base,ndims,dim,NULL); /* Close the temporary datatypes */ H5Tclose(array_base); H5Tclose(f_memb); break; case H5T_ENUM: case H5T_REFERENCE: case H5T_OPAQUE: case H5T_VLEN: /* Same as file type */ m_type = H5Tcopy(f_type); break; case H5T_BITFIELD: /* * Same as the file except the offset is set to zero and the byte * order is set to little endian. */ m_type = H5Tcopy(f_type); H5Tset_offset(m_type, 0); H5Tset_order(m_type, H5T_ORDER_LE); break; case H5T_TIME: /* * These type classes are not implemented yet. */ break; default: /* What the heck? */ break; } done: /* Clean up temp buffers */ if (memb && name) { int j; for (j = 0; j < nmembs; j++) { if (memb[j] >= 0) H5Tclose(memb[j]); if (name[j]) free(name[j]); } free(memb); free(name); } return m_type; } /*------------------------------------------------------------------------- * Function: h5dump_dset * * Purpose: Print some values from a dataset DSET to the file STREAM * after converting all types to P_TYPE (which should be a * native type). If P_TYPE is a negative value then it will be * computed from the dataset type using only native types. * * Note: This function is intended only for datasets since it does * some things like strip mining which are unnecessary for * smaller objects such as attributes. The easiest way to print * small objects is to read the object into memory and call * h5dump_mem(). * * Return: Success: 0 * * Failure: -1 * * Programmer: Robb Matzke * Thursday, July 23, 1998 * * Modifications: * Robb Matzke, 1999-06-07 * If info->raw is set then the memory datatype will be the same * as the file datatype. * *------------------------------------------------------------------------- */ int h5dump_dset(FILE *stream, const h5dump_t *info, hid_t dset, hid_t _p_type, int indentlevel) { hid_t f_space; hid_t p_type = _p_type; hid_t f_type; int status = -1; h5dump_t info_dflt; /* Use default values */ if (!stream) stream = stdout; if (!info) { memset(&info_dflt, 0, sizeof info_dflt); info = &info_dflt; } if (p_type < 0) { f_type = H5Dget_type(dset); if (info->raw) p_type = H5Tcopy(f_type); else p_type = h5dump_fixtype(f_type); H5Tclose(f_type); if (p_type < 0) goto done; } /* Check the data space */ f_space = H5Dget_space(dset); /* Print the data */ if (H5Sis_simple(f_space) > 0) { /* Probably a compound datatype or something... */ if (H5Tget_class(p_type) == H5T_VLEN) { /* Nope...variable length. Try to dump it */ status = h5dump_vlen_dset(rawdatastream, info, dset, p_type, indentlevel); } else { status = h5dump_simple_dset(rawdatastream, info, dset, p_type, indentlevel); } } done: if (p_type != _p_type) H5Tclose(p_type); return status; } static void *vlcustom_alloc(size_t size, void *info) { void *ret_value = NULL; int *mem_used = (int *)info; size_t extra = MAX(sizeof(void *), sizeof(size_t)); if ((ret_value = HDmalloc(extra + size)) != NULL) { *(size_t *)ret_value = size; *mem_used += size; } ret_value = ((unsigned char *)ret_value) + extra; return ret_value; } /*------------------------------------------------------------------------- * Function: h5dump_vlen_dset * * Purpose: Dump variable length datatypes to the STREAM. We can only * support atomic, 1-dimensional variable length data since other * types would be way too complicated to dump. * * Return: Success: 0 * * Failure: FAIL * * Programmer: Bill Wendling * Wednesday, November 8, 2000 * * Modifications: * *------------------------------------------------------------------------- */ static int h5dump_vlen_dset(FILE *stream, const h5dump_t *info, hid_t dset, hid_t type, int indentlevel) { h5dump_context_t ctx; /*print context */ hid_t base_type; /*the base type of the VL data */ hid_t xfer_pid; /*dataset transfer property list id */ hid_t f_space; /*file data space */ hsize_t dims[H5S_MAX_RANK]; /*size of the dimensions */ hid_t mem_space; const char *bad_type; herr_t ret; hssize_t start = 0; hsize_t count = 1; unsigned int i; memset(&ctx, 0, sizeof(h5dump_context_t)); ctx.indent_level = indentlevel; ctx.need_prefix = 1; f_space = H5Dget_space(dset); ctx.ndims = H5Sget_simple_extent_ndims(f_space); if (ctx.ndims > 1) { ret = FAIL; goto free_space; } base_type = H5Tget_super(type); xfer_pid = H5Pcreate(H5P_DATA_XFER); if (xfer_pid == FAIL) { ret = FAIL; goto free_xfer; } H5Sget_simple_extent_dims(f_space, dims, NULL); ctx.size_last_dim = dims[ctx.ndims - 1]; /* Assume entire data space to be printed */ for (i = 0; i < (hsize_t)ctx.ndims; i++) ctx.p_min_idx[i] = 0; recheck: switch (H5Tget_class(base_type)) { case H5T_INTEGER: case H5T_FLOAT: /* These are the types we can actually handle */ break; case H5T_VLEN: { hid_t tmp_type = base_type; base_type = H5Tget_super(base_type); H5Tclose(tmp_type); goto recheck; } case H5T_STRING: bad_type = "H5T_STRING"; case H5T_COMPOUND: if (!bad_type) bad_type = "H5T_COMPOUND"; case H5T_ENUM: if (!bad_type) bad_type = "H5T_ENUM"; case H5T_REFERENCE: if (!bad_type) bad_type = "H5T_REFERENCE"; case H5T_OPAQUE: if (!bad_type) bad_type = "H5T_OPAQUE"; case H5T_BITFIELD: if (!bad_type) bad_type = "H5T_BITFIELD"; case H5T_TIME: if (!bad_type) bad_type = "H5T_TIME"; case H5T_ARRAY: if (!bad_type) bad_type = "H5T_ARRAY"; default: fprintf(stream, "Dumper doesn't support %s variable length datatype at this time.\n", OPT(bad_type, "(null)")); goto free_xfer; } /* Create a simple memory space so that we can read in the hvl_t object */ mem_space = H5Screate_simple(0, NULL, NULL); for (i = 0; i < dims[0]; i++) { unsigned char *buffer; hsize_t mem_needed; herr_t ret; hvl_t *vldata; h5dump_context_t tmp; start = i; ret = H5Sselect_hyperslab(f_space, H5S_SELECT_SET, &start, NULL, &count, NULL); if (ret == FAIL) goto free_mem; ret = H5Dvlen_get_buf_size(dset, type, f_space, &mem_needed); if (ret == FAIL) goto free_mem; mem_needed += sizeof(hvl_t); buffer = calloc(mem_needed, 1); if (!buffer) { indentation(indentlevel); fprintf(stream, "Unable to allocate %ld bytes for variable length data.\n", (long)mem_needed); continue; } ret = H5Dread(dset, type, mem_space, f_space, H5P_DEFAULT, buffer); if (ret == FAIL) goto free_mem; vldata = (hvl_t *)buffer; /* I cast, therefore, I think I know what I am doing */ tmp = ctx; h5dump_simple_data(stream, info, dset, &ctx, START_OF_DATA | END_OF_DATA, vldata->len, base_type, (void *)vldata->p); ctx = tmp; fputs("\n", stream); free(buffer); } free_mem: H5Sclose(mem_space); free_xfer: H5Pclose(xfer_pid); free_space: H5Sclose(f_space); return ret; } /*------------------------------------------------------------------------- * Function: h5dump_mem * * Purpose: Displays the data contained in MEM. MEM must have the * specified data TYPE and SPACE. Currently only simple data * spaces are allowed and only the `all' selection. * * Return: Success: 0 * * Failure: -1 * * Programmer: Robb Matzke * Wednesday, January 20, 1999 * * Modifications: * *------------------------------------------------------------------------- */ int h5dump_mem(FILE *stream, const h5dump_t *info, hid_t type, hid_t space, void *mem,int indentlevel) { h5dump_t info_dflt; /* Use default values */ if (!stream) stream = stdout; if (!info) { memset(&info_dflt, 0, sizeof info_dflt); info = &info_dflt; } /* Check the data space */ if (H5Sis_simple(space) <= 0) return -1; return h5dump_simple_mem(stream, info, type, space, mem, indentlevel); } /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*from h5dumputil.c*/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*************************************************************************/ /*------------------------------------------------------------------------- * Function: indentation * * Purpose: Print spaces for indentation * * Return: void * * Programmer: Ruey-Hsia Li * * Modifications: * *-----------------------------------------------------------------------*/ void indentation(int x) { if (x < nCols) { while (x-- > 0) printf(" "); } else { printf("The indentation exceeds the number of cols. Exiting....\n"); exit(1); } } /*------------------------------------------------------------------------- * Function: print_version * * Purpose: Print the program name and the version information which is * defined the same as the HDF5 library version. * * Return: void * * Programmer: unknown * * Modifications: * *-----------------------------------------------------------------------*/ void print_version(const char *program_name) { printf("%s: Version %u.%u.%u%s%s\n", program_name, H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE, H5_VERS_SUBRELEASE[0] ? "-" : "", H5_VERS_SUBRELEASE); } /* * * THE FUNCTIONS BELOW ARE FROM THE H5FINSHD.C FILE * */ /*------------------------------------------------------------------------- * Function: init_table * * Purpose: allocate and initialize tables for shared groups, datasets, * and committed types * * Return: void * * Programmer: Ruey-Hsia Li * * Modifications: * *-----------------------------------------------------------------------*/ void init_table(table_t** temp) { int i; table_t *table = malloc(sizeof(table_t)); table->size = 20; table->nobjs = 0; table->objs = (obj_t*) malloc(table->size*sizeof(obj_t)); for (i = 0; i < table->size; i++) { table->objs[i].objno[0] = table->objs[i].objno[1] = 0; table->objs[i].displayed = 0; table->objs[i].recorded = 0; table->objs[i].objflag = 0; } *temp = table; } /*------------------------------------------------------------------------- * Function: init_prefix * * Purpose: allocate and initialize prefix * * Return: void * * Modifications: * *-----------------------------------------------------------------------*/ void init_prefix(char **prefix, int prefix_len) { char *temp = malloc(prefix_len); *temp = '\0'; *prefix = temp; } /*------------------------------------------------------------------------- * Function: free_table * * Purpose: free tables for shared groups, datasets, * and committed types * * Return: void * * Programmer: Paul Harten * * Modifications: * *-----------------------------------------------------------------------*/ void free_table(table_t **table) { HDfree((*table)->objs); } /*------------------------------------------------------------------------- * Function: search_obj * * Purpose: search the object specified by objno in the table * * Return: an integer, the location of the object * -1 if object is not found * * * Programmer: Ruey-Hsia Li * * Modifications: * *-----------------------------------------------------------------------*/ int search_obj(table_t *table, unsigned long *objno) { int i; for (i = 0; i < table->nobjs; i++) if (table->objs[i].objno[0] == *objno && table->objs[i].objno[1] == *(objno + 1)) return i; return -1; } /*------------------------------------------------------------------------- * Function: add_obj * * Purpose: add a shared object to the table * realloc the table if necessary * * Return: void * * Programmer: Ruey-Hsia Li * * Modifications: * *-----------------------------------------------------------------------*/ static void add_obj (table_t *table, unsigned long *objno, char *objname) { int i; if (table->nobjs == table->size) { table->size *= 2; table->objs = realloc(table->objs, table->size*sizeof(obj_t)); for (i = table->nobjs; i < table->size; i++) { table->objs[i].objno[0] = table->objs[i].objno[1] = 0; table->objs[i].displayed = 0; table->objs[i].recorded = 0; table->objs[i].objflag = 0; } } i = table->nobjs++; table->objs[i].objno[0] = *objno; table->objs[i].objno[1] = *(objno + 1); strcpy(table->objs[i].objname, objname); } /*------------------------------------------------------------------------- * Function: Find_objs * * Purpose: Find objects, committed types and store them in tables * * Return: Success: SUCCEED * * Failure: FAIL * * Programmer: Ruey-Hsia Li * * Modifications: * *-----------------------------------------------------------------------*/ herr_t find_objs(hid_t group, const char *name, void *op_data) { hid_t obj, type; H5G_stat_t statbuf; char *tmp; int i; find_objs_t *info = (find_objs_t*)op_data; if (info->threshold > 1) /*will get an infinite loop if greater than 1*/ return FAIL; H5Gget_objinfo(group, name, TRUE, &statbuf); tmp = malloc(strlen(info->prefix) + strlen(name) + 2); strcpy(tmp, info->prefix); switch (statbuf.type) { case H5G_GROUP: if ((obj = H5Gopen(group, name)) >= 0) { if (info->prefix_len < (int)(strlen(info->prefix) + strlen(name) + 2)) { info->prefix_len *= 2; info->prefix = realloc(info->prefix, info->prefix_len * sizeof(char)); } strcat(strcat(info->prefix,"/"), name); if (statbuf.nlink > info->threshold) { if (search_obj(info->group_table, statbuf.objno) < 0) { add_obj(info->group_table, statbuf.objno, info->prefix); H5Giterate(obj, ".", NULL, find_objs, (void *)info); } } else { H5Giterate (obj, ".", NULL, find_objs, (void *)info); } strcpy(info->prefix, tmp); H5Gclose (obj); } else { info->status = 1; } break; case H5G_DATASET: strcat(tmp,"/"); strcat(tmp,name); /* absolute name of the data set */ if (statbuf.nlink > info->threshold && search_obj(info->dset_table, statbuf.objno) < 0) add_obj(info->dset_table, statbuf.objno, tmp); if ((obj = H5Dopen (group, name)) >= 0) { type = H5Dget_type(obj); if (H5Tcommitted(type) > 0) { H5Gget_objinfo(type, ".", TRUE, &statbuf); if (search_obj (info->type_table, statbuf.objno) < 0) { add_obj(info->type_table, statbuf.objno, tmp); info->type_table->objs[info->type_table->nobjs - 1].objflag = 0; } } H5Tclose(type); H5Dclose (obj); } else { info->status = 1; } break; case H5G_TYPE: strcat(tmp,"/"); strcat(tmp,name); /* absolute name of the type */ i = search_obj(info->type_table, statbuf.objno); if (i < 0) { add_obj(info->type_table, statbuf.objno, tmp) ; /* named data type */ info->type_table->objs[info->type_table->nobjs-1].recorded = 1; /* named data type */ info->type_table->objs[info->type_table->nobjs-1].objflag = 1; } else { strcpy (info->type_table->objs[i].objname, tmp); info->type_table->objs[i].recorded = 1; /* named data type */ info->type_table->objs[info->type_table->nobjs-1].objflag = 1; } break; default: break; } free(tmp); return SUCCEED; } /*------------------------------------------------------------------------- * Function: dump_tables * * Purpose: display the contents of tables for debugging purposes * * Return: void * * Programmer: Ruey-Hsia Li * * Modifications: * *-----------------------------------------------------------------------*/ void dump_table(char* tablename, table_t *table) { int i; printf("%s: # of entries = %d\n", tablename,table->nobjs); for ( i = 0; i < table->nobjs; i++) printf("%lu %lu %s %d\n", table->objs[i].objno[0], table->objs[i].objno[1], table->objs[i].objname, table->objs[i].objflag); } /*------------------------------------------------------------------------- * Function: get_table_idx * * Purpose: Determine if objects are in a link loop * * Return: Success: table index of object detected to be in loop * * Failure: FAIL * * Programmer: Paul Harten * *-----------------------------------------------------------------------*/ int get_table_idx(table_t *table, unsigned long *objno) { return search_obj(table, objno); } /*------------------------------------------------------------------------- * Function: Get table flag setting * * Purpose: Keep the structures and variables used private to * this file. * * Return: Success: Boolean setting of the i'th element of the * object table flag * * Failure: FAIL * * Programmer: Paul Harten * *-----------------------------------------------------------------------*/ int get_tableflag(table_t *table, int idx) { return table->objs[idx].objflag; } /*------------------------------------------------------------------------- * Function: Set table flag setting * * Purpose: Keep the structures and variables used private to * this file. * * Return: Success: Boolean setting of the i'th element of the * object table flag * * Failure: FAIL * * Programmer: Paul Harten * *-----------------------------------------------------------------------*/ int set_tableflag(table_t *table, int idx) { table->objs[idx].objflag = TRUE; return SUCCEED; } /*------------------------------------------------------------------------- * Function: Get name of i'th object in table * * Purpose: * * Return: Success: strdup() of object name character string * * Failure: NULL * * Programmer: Paul Harten * *-----------------------------------------------------------------------*/ char * get_objectname(table_t* table, int idx) { return strdup(table->objs[idx].objname); } /*------------------------------------------------------------------------- * Function: opens a file using the list of drivers * * Purpose: Attempts to open a file with various VFL drivers. * * Return: Success: a file id for the opened file. If * DRIVERNAME is non-null then the first * DRIVERNAME_SIZE-1 characters of the driver * name are copied into the DRIVERNAME array * and null terminated. * * Failure: -1. If DRIVERNAME is non-null then the * first byte is set to the null terminator. * * Modifications: * Robb Matzke, 2000-06-23 * We only have to initialize driver[] on the first call, * thereby preventing memory leaks from repeated calls to * H5Pcreate(). * * Robb Matzke, 2000-06-23 * Added DRIVERNAME_SIZE arg to prevent overflows when * writing to DRIVERNAME. * * Robb Matzke, 2000-06-23 * Added test to prevent coredump when the file could not be * opened by any driver. * * Robb Matzke, 2000-06-23 * Changed name from H5ToolsFopen() so it jives better with * the names we already have at the top of this source file. * * Thomas Radke, 2000-09-12 * Added Stream VFD to the driver[] array. *-----------------------------------------------------------------------*/ hid_t h5dump_fopen(const char *fname, char *drivername, size_t drivername_size) { static struct { const char *name; hid_t fapl; } driver[16]; static int ndrivers = 0; hid_t fid, fapl = H5P_DEFAULT; int drivernum; if (!ndrivers) { /* Build a list of file access property lists which we should try * when opening the file. Eventually we'd like some way for the * user to augment/replace this list interactively. */ driver[ndrivers].name = "sec2"; driver[ndrivers].fapl = H5P_DEFAULT; ndrivers++; #ifdef VERSION13 driver[ndrivers].name = "family"; driver[ndrivers].fapl = fapl = H5Pcreate(H5P_FILE_ACCESS); H5Pset_fapl_family(fapl, 0, H5P_DEFAULT); ndrivers++; driver[ndrivers].name = "split"; driver[ndrivers].fapl = fapl = H5Pcreate(H5P_FILE_ACCESS); H5Pset_fapl_split(fapl, "-m.h5", H5P_DEFAULT, "-r.h5", H5P_DEFAULT); ndrivers++; driver[ndrivers].name = "multi"; driver[ndrivers].fapl = fapl = H5Pcreate(H5P_FILE_ACCESS); H5Pset_fapl_multi(fapl, NULL, NULL, NULL, NULL, TRUE); ndrivers++; #ifdef H5_HAVE_STREAM driver[ndrivers].name = "stream"; driver[ndrivers].fapl = fapl = H5Pcreate(H5P_FILE_ACCESS); H5Pset_fapl_stream(fapl, NULL); ndrivers++; #endif /* H5_HAVE_STREAM */ #endif /* VERSION13 */ } /* Try to open the file using each of the drivers */ for (drivernum = 0; drivernum < ndrivers; drivernum++) { H5E_BEGIN_TRY { fid = H5Fopen(fname, H5F_ACC_RDONLY, driver[drivernum].fapl); } H5E_END_TRY; if (fid >= 0) break; } /* Save the driver name */ if (drivername && drivername_size){ if (fid >= 0) { strncpy(drivername, driver[drivernum].name, drivername_size); drivername[drivername_size - 1] = '\0'; } else { drivername[0] = '\0'; /*no file opened*/ } } return fid; }