/****************************************************************************
 * NCSA HDF								                                    *
 * Software Development Group						                        *
 * National Center for Supercomputing Applications			                *
 * University of Illinois at Urbana-Champaign				                *
 * 605 E. Springfield, Champaign IL 61820				                    *
 *									                                        *
 * For conditions of distribution and use, see the accompanying		        *
 * hdf/COPYING file.							                            *
 *									                                        *
 ****************************************************************************/

/* $Id$ */

/***********************************************************
*
* Test program:	 tvlstr
*
* Test the Variable-Length String functionality
*
*************************************************************/

#ifdef WIN32
#include <stdio.h>
#endif

#include "testhdf5.h"

#include "hdf5.h"

#define DATAFILE   "tvlstr.h5"

/* 1-D dataset with fixed dimensions */
#define SPACE1_NAME  "Space1"
#define SPACE1_RANK	1
#define SPACE1_DIM1	4

/* 2-D dataset with fixed dimensions */
#define SPACE2_NAME  "Space2"
#define SPACE2_RANK	2
#define SPACE2_DIM1	10
#define SPACE2_DIM2	10

#define VLSTR_TYPE      "vl_string_type"

/* String for testing attributes */
static const char *string_att = "This is the string for the attribute";

void *test_vlstr_alloc_custom(size_t size, void *info);
void test_vlstr_free_custom(void *mem, void *info);

/****************************************************************
**
**  test_vlstr_alloc_custom(): Test VL datatype custom memory
**      allocation routines.  This routine just uses malloc to
**      allocate the memory and increments the amount of memory
**      allocated.
** 
****************************************************************/
void *test_vlstr_alloc_custom(size_t size, void *info)
{
    void *ret_value=NULL;       /* Pointer to return */
    int *mem_used=(int *)info;  /* Get the pointer to the memory used */
    size_t extra;               /* Extra space needed */

    /*
     *  This weird contortion is required on the DEC Alpha to keep the
     *  alignment correct - QAK
     */
    extra=MAX(sizeof(void *),sizeof(size_t));

    if((ret_value=HDmalloc(extra+size))!=NULL) {
        *(size_t *)ret_value=size;
        *mem_used+=size;
    } /* end if */
    ret_value=((unsigned char *)ret_value)+extra;
    return(ret_value);
}

/****************************************************************
**
**  test_vlstr_free_custom(): Test VL datatype custom memory
**      allocation routines.  This routine just uses free to
**      release the memory and decrements the amount of memory
**      allocated.
** 
****************************************************************/
void test_vlstr_free_custom(void *_mem, void *info)
{
    unsigned char *mem;
    int *mem_used=(int *)info;  /* Get the pointer to the memory used */
    size_t extra;               /* Extra space needed */

    /*
     *  This weird contortion is required on the DEC Alpha to keep the
     *  alignment correct - QAK
     */
    extra=MAX(sizeof(void *),sizeof(size_t));

    if(_mem!=NULL) {
        mem=((unsigned char *)_mem)-extra;
        *mem_used-=*(size_t *)mem;
        HDfree(mem);
    } /* end if */
}

/****************************************************************
**
**  test_vlstrings_basic(): Test basic VL string code.
**      Tests simple VL string I/O
** 
****************************************************************/
static void
test_vlstrings_basic(void)
{
    const char *wdata[SPACE1_DIM1]= {
        "Four score and seven years ago our forefathers brought forth on this continent a new nation,",
        "conceived in liberty and dedicated to the proposition that all men are created equal.",
        "Now we are engaged in a great civil war,",
        "testing whether that nation or any nation so conceived and so dedicated can long endure."
        };   /* Information to write */
    char *rdata[SPACE1_DIM1];   /* Information read in */
    hid_t		fid1;		/* HDF5 File IDs		*/
    hid_t		dataset;	/* Dataset ID			*/
    hid_t		sid1;       /* Dataspace ID			*/
    hid_t		tid1;       /* Datatype ID			*/
    hid_t       xfer_pid;   /* Dataset transfer property list ID */
    hsize_t		dims1[] = {SPACE1_DIM1};
    hsize_t     size;       /* Number of bytes which will be used */
    unsigned       i;          /* counting variable */
    int         str_used;   /* String data in memory */
    int         mem_used=0; /* Memory used during allocation */
    herr_t		ret;		/* Generic return value		*/

    /* Output message about test being performed */
    MESSAGE(5, ("Testing Basic VL String Functionality\n"));

    /* Create file */
    fid1 = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
    CHECK(fid1, FAIL, "H5Fcreate");

    /* Create dataspace for datasets */
    sid1 = H5Screate_simple(SPACE1_RANK, dims1, NULL);
    CHECK(sid1, FAIL, "H5Screate_simple");

    /* Create a datatype to refer to */
    tid1 = H5Tcopy (H5T_C_S1);
    CHECK(tid1, FAIL, "H5Tcopy");

    ret = H5Tset_size (tid1,H5T_VARIABLE);
    CHECK(ret, FAIL, "H5Tset_size");

    /* Create a dataset */
    dataset=H5Dcreate(fid1,"Dataset1",tid1,sid1,H5P_DEFAULT);
    CHECK(dataset, FAIL, "H5Dcreate");

    /* Write dataset to disk */
    ret=H5Dwrite(dataset,tid1,H5S_ALL,H5S_ALL,H5P_DEFAULT,wdata);
    CHECK(ret, FAIL, "H5Dwrite");

    /* Change to the custom memory allocation routines for reading VL string */
    xfer_pid=H5Pcreate(H5P_DATASET_XFER);
    CHECK(xfer_pid, FAIL, "H5Pcreate");

    ret=H5Pset_vlen_mem_manager(xfer_pid,test_vlstr_alloc_custom,&mem_used,test_vlstr_free_custom,&mem_used);
    CHECK(ret, FAIL, "H5Pset_vlen_mem_manager");

    /* Make certain the correct amount of memory will be used */
    ret=H5Dvlen_get_buf_size(dataset,tid1,sid1,&size);
    CHECK(ret, FAIL, "H5Dvlen_get_buf_size");

    /* Count the actual number of bytes used by the strings */
    for(i=0,str_used=0; i<SPACE1_DIM1; i++)
        str_used+=strlen(wdata[i])+1;

    /* Compare against the strings actually written */
    VERIFY(size,(hsize_t)str_used,"H5Dvlen_get_buf_size");

    /* Read dataset from disk */
    ret=H5Dread(dataset,tid1,H5S_ALL,H5S_ALL,xfer_pid,rdata);
    CHECK(ret, FAIL, "H5Dread");

    /* Make certain the correct amount of memory has been used */
    VERIFY(mem_used,str_used,"H5Dread");

    /* Compare data read in */
    for(i=0; i<SPACE1_DIM1; i++) {
        if(strlen(wdata[i])!=strlen(rdata[i])) {
            num_errs++;
            printf("VL data length don't match!, strlen(wdata[%d])=%d, strlen(rdata[%d])=%d\n",(int)i,(int)strlen(wdata[i]),(int)i,(int)strlen(rdata[i]));
            continue;
        } /* end if */
        if( strcmp(wdata[i],rdata[i]) != 0 ) {
            num_errs++;
            printf("VL data values don't match!, wdata[%d]=%s, rdata[%d]=%s\n",(int)i,wdata[i],(int)i,rdata[i]);
            continue;
        } /* end if */
    } /* end for */

    /* Reclaim the read VL data */
    ret=H5Dvlen_reclaim(tid1,sid1,xfer_pid,rdata);
    CHECK(ret, FAIL, "H5Dvlen_reclaim");

    /* Make certain the VL memory has been freed */
    VERIFY(mem_used,0,"H5Dvlen_reclaim");

    /* Close Dataset */
    ret = H5Dclose(dataset);
    CHECK(ret, FAIL, "H5Dclose");

    /* Close datatype */
    ret = H5Tclose(tid1);
    CHECK(ret, FAIL, "H5Tclose");

    /* Close disk dataspace */
    ret = H5Sclose(sid1);
    CHECK(ret, FAIL, "H5Sclose");
    
    /* Close dataset transfer property list */
    ret = H5Pclose(xfer_pid);
    CHECK(ret, FAIL, "H5Pclose");
    
    /* Close file */
    ret = H5Fclose(fid1);
    CHECK(ret, FAIL, "H5Fclose");

} /* end test_vlstrings_basic() */

/****************************************************************
**
**  test_vlstring_type(): Test VL string type.
**      Tests if VL string is treated as string. 
**
****************************************************************/
static void test_vlstring_type(void)
{
    hid_t               fid;           /* HDF5 File IDs                */
    hid_t               tid_vlstr;
    H5T_cset_t          cset;
    H5T_str_t           pad;
    herr_t              ret;

    /* Output message about test being performed */
    MESSAGE(5, ("Testing VL String type\n"));

    /* Create file */
    fid = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
    CHECK(fid, FAIL, "H5Fcreate");

    /* Create a datatype to refer to */
    tid_vlstr = H5Tcopy(H5T_C_S1);
    CHECK(tid_vlstr, FAIL, "H5Tcopy");

    /* Change padding and verify it */ 
    ret = H5Tset_strpad(tid_vlstr, H5T_STR_NULLPAD);
    CHECK(ret, FAIL, "H5Tset_strpad");
    pad = H5Tget_strpad(tid_vlstr);
    VERIFY(pad, H5T_STR_NULLPAD, "H5Tget_strpad");

    /* Convert to variable-length string */
    ret = H5Tset_size(tid_vlstr, H5T_VARIABLE);
    CHECK(ret, FAIL, "H5Tset_size");

    /* Check if datatype is VL string */
    ret = H5Tget_class(tid_vlstr);
    VERIFY(ret, H5T_STRING, "H5Tget_class");
    ret = H5Tis_variable_str(tid_vlstr);
    VERIFY(ret, TRUE, "H5Tis_variable_str");
    
    /* Check default character set and padding */
    cset = H5Tget_cset(tid_vlstr);
    VERIFY(cset, H5T_CSET_ASCII, "H5Tget_cset");
    pad = H5Tget_strpad(tid_vlstr);
    VERIFY(pad, H5T_STR_NULLPAD, "H5Tget_strpad");

    /* Commit variable-length string datatype to storage */
    ret = H5Tcommit(fid, VLSTR_TYPE, tid_vlstr);
    CHECK(ret, FAIL, "H5Tcommit");

    /* Close datatype */
    ret = H5Tclose(tid_vlstr);
    CHECK(ret, FAIL, "H5Tclose");


    /* Open the variable-length string datatype just created */ 
    tid_vlstr = H5Topen(fid, VLSTR_TYPE);
    CHECK(tid_vlstr, FAIL, "H5Topen");

    /* Verify character set and padding */ 
    cset = H5Tget_cset(tid_vlstr);
    VERIFY(cset, H5T_CSET_ASCII, "H5Tget_cset");
    pad = H5Tget_strpad(tid_vlstr);
    VERIFY(pad, H5T_STR_NULLPAD, "H5Tget_strpad");

    /* Close datatype and file */
    ret = H5Tclose(tid_vlstr);
    CHECK(ret, FAIL, "H5Tclose");
    ret = H5Fclose(fid);
    CHECK(ret, FAIL, "H5Fclose");    

} /* end test_vlstring_type() */

/****************************************************************
**
**  test_write_vl_string_attribute(): Test basic VL string code.
**      Tests writing VL strings as attributes
** 
****************************************************************/
static void test_write_vl_string_attribute(void)
{
    hid_t file, root, dataspace, att;
    hid_t type;
    herr_t ret;
    char *string_att_check;

    file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
    CHECK(file, FAIL, "H5Fcreate");

    /* Create a datatype to refer to. */
    type = H5Tcopy (H5T_C_S1);
    CHECK(type, FAIL, "H5Tcopy");

    ret = H5Tset_size (type, H5T_VARIABLE);
    CHECK(ret, FAIL, "H5Tset_size");

    root = H5Gopen(file, "/");
    CHECK(root, FAIL, "H5Gopen");

    dataspace = H5Screate(H5S_SCALAR);
    CHECK(dataspace, FAIL, "H5Screate");

    att = H5Acreate(root, "test_scalar", type, dataspace, H5P_DEFAULT);
    CHECK(att, FAIL, "H5Acreate");

    ret = H5Awrite(att, type, &string_att);
    CHECK(ret, FAIL, "H5Awrite");

    ret = H5Aread(att, type, &string_att_check);
    CHECK(ret, FAIL, "H5Aread");

    if(HDstrcmp(string_att_check,string_att)!=0) {
        num_errs++;
        printf("VL string attributes don't match!, string_att=%s, string_att_check=%s\n",string_att,string_att_check);
    } /* end if */

    free(string_att_check);

    ret = H5Aclose(att);
    CHECK(ret, FAIL, "HAclose");

    ret = H5Gclose(root);
    CHECK(ret, FAIL, "H5Gclose");

    ret = H5Tclose(type);
    CHECK(ret, FAIL, "H5Tclose");

    ret = H5Sclose(dataspace);
    CHECK(ret, FAIL, "H5Sclose");

    ret = H5Fclose(file);
    CHECK(ret, FAIL, "H5Fclose");

    return;
}

/****************************************************************
**
**  test_read_vl_string_attribute(): Test basic VL string code.
**      Tests reading VL strings from attributes
** 
****************************************************************/
static void test_read_vl_string_attribute(void)
{
    hid_t file, root, att;
    hid_t type;
    herr_t ret;
    char *string_att_check;

    file = H5Fopen(DATAFILE, H5F_ACC_RDONLY, H5P_DEFAULT);
    CHECK(file, FAIL, "H5Fopen");

    /* Create a datatype to refer to. */
    type = H5Tcopy (H5T_C_S1);
    CHECK(type, FAIL, "H5Tcopy");

    ret = H5Tset_size (type, H5T_VARIABLE);
    CHECK(ret, FAIL, "H5Tset_size");

    root = H5Gopen(file, "/");
    CHECK(root, FAIL, "H5Gopen");

    att = H5Aopen_name(root, "test_scalar");
    CHECK(att, FAIL, "H5Aopen_name");

    ret = H5Aread(att, type, &string_att_check);
    CHECK(ret, FAIL, "H5Aread");

    if(HDstrcmp(string_att_check,string_att)!=0) {
        num_errs++;
        printf("VL string attributes don't match!, string_att=%s, string_att_check=%s\n",string_att,string_att_check);
    } /* end if */

    free(string_att_check);

    ret = H5Aclose(att);
    CHECK(ret, FAIL, "HAclose");

    ret = H5Tclose(type);
    CHECK(ret, FAIL, "H5Tclose");

    ret = H5Gclose(root);
    CHECK(ret, FAIL, "H5Gclose");

    ret = H5Fclose(file);
    CHECK(ret, FAIL, "H5Fclose");

    return;
}

/****************************************************************
**
**  test_vlstrings(): Main VL string testing routine.
** 
****************************************************************/
void 
test_vlstrings(void)
{
    /* Output message about test being performed */
    MESSAGE(5, ("Testing Variable-Length Strings\n"));

    /* These next tests use the same file */
    /* Test basic VL string datatype */
    test_vlstrings_basic();
    test_vlstring_type();         

    /* Test using VL strings in attributes */
    test_write_vl_string_attribute();
    test_read_vl_string_attribute();
}   /* test_vlstrings() */


/*-------------------------------------------------------------------------
 * Function:	cleanup_vlstrings
 *
 * Purpose:	Cleanup temporary test files
 *
 * Return:	none
 *
 * Programmer:	Quincey Koziol
 *              September 10, 1999
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
void
cleanup_vlstrings(void)
{
    remove(DATAFILE);
}