/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  If you do not have          *
 * access to either file, you may request a copy from help@hdfgroup.org.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <stdlib.h>

#include "H5PTprivate.h"
#include "H5TBprivate.h"

/*  Packet Table private data */

typedef struct
{
  hid_t dset_id;  /* The ID of the dataset containing this table */
  hid_t type_id;  /* The ID of the packet table's native datatype */
  hsize_t current_index;  /* The index of the packet that get_next_packet will read next */
  hsize_t size;  /* The number of packets currently contained in this table */
} htbl_t;

static hsize_t H5PT_ptable_count = 0;
static H5I_type_t H5PT_ptable_id_type = H5I_UNINIT;

#define H5PT_HASH_TABLE_SIZE 64

/* Packet Table private functions */
static herr_t H5PT_free_id(void *id);
static herr_t H5PT_close( htbl_t* table );
static herr_t H5PT_create_index(htbl_t *table_id);
static herr_t H5PT_set_index(htbl_t *table_id, hsize_t pt_index);
static herr_t H5PT_get_index(htbl_t *table_id, hsize_t *pt_index);

/*-------------------------------------------------------------------------
 *
 * Create/Open/Close functions
 *
 *-------------------------------------------------------------------------
 */

/*-------------------------------------------------------------------------
 * Function: H5PTcreate_fl
 *
 * Purpose: Creates a dataset containing a table and returns the Identifier
 *          of the table.
 *
 * Return: Success: table ID, Failure: Negative
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 12, 2004
 *
 * Comments: This function does not handle fill data
 *           currently.  Fill data is not necessary because the
 *           table is initially of size 0.
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */

hid_t H5PTcreate_fl ( hid_t loc_id,
                              const char *dset_name,
                              hid_t dtype_id,
                              hsize_t chunk_size,
                              int compression )
{
  htbl_t * table = NULL;
  hid_t dset_id = H5I_BADID;
  hid_t space_id = H5I_BADID;
  hid_t plist_id = H5I_BADID;
  hsize_t dims[1];
  hsize_t dims_chunk[1];
  hsize_t maxdims[1];
  hid_t ret_value;

  /* check the arguments */
  if (dset_name == NULL) {
    goto out;
  }

  /* Register the packet table ID type if this is the first table created */
  if(H5PT_ptable_id_type < 0)
    if((H5PT_ptable_id_type = H5Iregister_type((size_t)H5PT_HASH_TABLE_SIZE, 0, (H5I_free_t)H5PT_free_id)) < 0)
      goto out;

  /* Get memory for the table identifier */
  table = (htbl_t *)HDmalloc(sizeof(htbl_t));

  /* Create a simple data space with unlimited size */
  dims[0] = 0;
  dims_chunk[0] = chunk_size;
  maxdims[0] = H5S_UNLIMITED;
  if((space_id = H5Screate_simple(1, dims, maxdims)) < 0)
    goto out;

  /* Modify dataset creation properties to enable chunking  */
  plist_id = H5Pcreate(H5P_DATASET_CREATE);
  if(H5Pset_chunk(plist_id, 1, dims_chunk) < 0)
    goto out;
  if(compression >= 0 && compression <= 9)
    if(H5Pset_deflate(plist_id, (unsigned)compression) < 0)
        goto out;

  /* Create the dataset. */
  if((dset_id = H5Dcreate2(loc_id, dset_name, dtype_id, space_id, H5P_DEFAULT, plist_id, H5P_DEFAULT)) < 0)
    goto out;

  /* Terminate access to the data space. */
  if(H5Sclose(space_id) < 0)
    goto out;

  /* End access to the property list */
  if(H5Pclose(plist_id) < 0)
    goto out;

  /* Create the table identifier */
  table->dset_id = dset_id;

  if((table->type_id = H5Tcopy(dtype_id)) < 0)
    goto out;

  if((table->type_id = H5Tget_native_type(table->type_id, H5T_DIR_DEFAULT)) < 0)
    goto out;

  H5PT_create_index(table);
  table->size = 0;

  /* Get an ID for this table */
  ret_value = H5Iregister(H5PT_ptable_id_type, table);

  if(ret_value != H5I_INVALID_HID)
    H5PT_ptable_count++;
  else
    H5PT_close(table);

  return ret_value;

  out:
    H5E_BEGIN_TRY
    H5Sclose(space_id);
    H5Pclose(plist_id);
    H5Dclose(dset_id);
    if(table)
      HDfree(table);
    H5E_END_TRY
    return H5I_INVALID_HID;
}

#ifdef H5_VLPT_ENABLED
/*-------------------------------------------------------------------------
 * Function: H5PTcreate_vl
 *
 * Purpose: Creates a dataset containing a table of variable length records
 *          and returns the Identifier of the table.
 *
 * Return: Success: table ID, Failure: Negative
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: April 12, 2004
 *
 * Comments: This function does not handle compression or fill data
 *           currently.  Fill data is not necessary because the
 *           table is initially of size 0.
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
hid_t H5PTcreate_vl ( hid_t loc_id,
                                 const char*dset_name,
                                 hsize_t chunk_size)
{
  hid_t ret_value=H5I_BADID;
  hid_t vltype;

  /* check the arguments */
  if (dset_name == NULL) {
    goto out;
  }

  /* Create a variable length type that uses single bytes as its base type */
  vltype = H5Tvlen_create(H5T_NATIVE_UCHAR);
  if(vltype < 0)
    goto out;

  if((ret_value=H5PTcreate_fl(loc_id, dset_name, vltype, chunk_size, 0)) < 0)
    goto out;

  /* close the vltype */
  if(H5Tclose(vltype) < 0)
    goto out;

  return ret_value;

out:
  if(ret_value != H5I_BADID)
    H5PTclose(ret_value);
  return H5I_BADID;
}
#endif /* H%_VLPT_ENABLED */

/*-------------------------------------------------------------------------
 * Function: H5PTopen
 *
 * Purpose: Opens a dataset containing a table and returns the Identifier
 *          of the table.
 *
 * Return: Success: table ID, Failure: Negative
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 10, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 * 		John Mainzer -- 4/23/08
 * 		Added error check on malloc of table, initialized fields
 * 		in table to keep lower level code from choking on bogus
 * 		data in error cases.
 *
 *-------------------------------------------------------------------------
 */
hid_t H5PTopen( hid_t loc_id,
                             const char *dset_name )
{
  hid_t type_id=H5I_BADID;
  hid_t space_id=H5I_BADID;
  htbl_t * table = NULL;
  hid_t ret_value;
  hsize_t dims[1];

  /* check the arguments */
  if (dset_name == NULL) {
    goto out;
  }

  /* Register the packet table ID type if this is the first table created */
  if( H5PT_ptable_id_type < 0)
    if((H5PT_ptable_id_type = H5Iregister_type((size_t)H5PT_HASH_TABLE_SIZE, 0, (H5I_free_t)H5PT_free_id)) < 0)
      goto out;

  table = (htbl_t *)HDmalloc(sizeof(htbl_t));

  if ( table == NULL ) {
    goto out;
  }
  table->dset_id = H5I_BADID;
  table->type_id = H5I_BADID;

  /* Open the dataset */
  if((table->dset_id = H5Dopen2(loc_id, dset_name, H5P_DEFAULT)) < 0)
      goto out;
  if(table->dset_id < 0)
    goto out;

  /* Get the dataset's disk datatype */
  if((type_id = H5Dget_type(table->dset_id)) < 0)
    goto out;

  /* Get the table's native datatype */
  if((table->type_id = H5Tget_native_type(type_id, H5T_DIR_ASCEND)) < 0)
    goto out;

  if(H5Tclose(type_id) < 0)
    goto out;

  /* Initialize the current record pointer */
  if((H5PT_create_index(table)) < 0)
    goto out;

  /* Get number of records in table */
  if((space_id=H5Dget_space(table->dset_id)) < 0)
    goto out;
  if( H5Sget_simple_extent_dims( space_id, dims, NULL) < 0)
    goto out;
  if(H5Sclose(space_id) < 0)
    goto out;
  table->size = dims[0];

  /* Get an ID for this table */
  ret_value = H5Iregister(H5PT_ptable_id_type, table);

  if(ret_value != H5I_INVALID_HID)
    H5PT_ptable_count++;
  else
    H5PT_close(table);

  return ret_value;

out:
  H5E_BEGIN_TRY
  H5Tclose(type_id);
  H5Sclose(space_id);
  if(table)
  {
    H5Dclose(table->dset_id);
    H5Tclose(table->type_id);
    HDfree(table);
  }
  H5E_END_TRY
  return H5I_INVALID_HID;
}

/*-------------------------------------------------------------------------
 * Function: H5PT_free_id
 *
 * Purpose: Free an id.  Callback for H5Iregister_type.
 *
 * Return: Success: 0, Failure: N/A
 *-------------------------------------------------------------------------
 */
static herr_t
H5PT_free_id(void *id)
{
    HDfree(id);
    return 0;
}

/*-------------------------------------------------------------------------
 * Function: H5PT_close
 *
 * Purpose: Closes a table (i.e. cleans up all open resources used by a
 *          table).
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 10, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5PT_close( htbl_t* table)
{
  if(table == NULL)
    goto out;

  /* Close the dataset */
  if(H5Dclose(table->dset_id) < 0)
    goto out;

  /* Close the memory datatype */
  if(H5Tclose(table->type_id) < 0)
    goto out;

  HDfree(table);

  return 0;

out:
  if(table)
  {
    H5E_BEGIN_TRY
    H5Dclose(table->dset_id);
    H5Tclose(table->type_id);
    H5E_END_TRY
    HDfree(table);
  }
  return -1;
}

/*-------------------------------------------------------------------------
 * Function: H5PTclose
 *
 * Purpose: Closes a table (i.e. cleans up all open resources used by a
 *          table).
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: April 21, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTclose( hid_t table_id )
{
  htbl_t * table;

  /* Remove the ID from the library */
  if((table = (htbl_t *)H5Iremove_verify(table_id, H5PT_ptable_id_type)) ==NULL)
    goto out;

  /* If the library found the table, remove it */
  if( H5PT_close(table) < 0)
    goto out;

  /* One less packet table open */
  H5PT_ptable_count--;

  /* Remove the packet table type ID if no more packet */
  /* tables are open                                   */
  if(H5PT_ptable_count == 0)
  {
    H5Idestroy_type(H5PT_ptable_id_type);
    H5PT_ptable_id_type = H5I_UNINIT;
  }

  return 0;

out:
  return -1;
}


/*-------------------------------------------------------------------------
 *
 * Write functions
 *
 *-------------------------------------------------------------------------
 */

/*-------------------------------------------------------------------------
 * Function: H5PTappend
 *
 * Purpose: Appends packets to the end of a packet table
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 12, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTappend( hid_t table_id,
                           size_t nrecords,
                           const void * data )
{
  htbl_t * table;

  /* Find the table struct from its ID */
  if((table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    goto out;

  /* If we are asked to write 0 records, just do nothing */
  if(nrecords == 0)
    return 0;

  if((H5TB_common_append_records(table->dset_id, table->type_id,
  			nrecords, table->size, data)) < 0)
    goto out;

  /* Update table size */
  table->size += nrecords;
  return 0;

out:
  return -1;
}

/*-------------------------------------------------------------------------
 *
 * Read functions
 *
 *-------------------------------------------------------------------------
 */


/*-------------------------------------------------------------------------
 * Function: H5PTget_next
 *
 * Purpose: Reads packets starting at the current index and updates
 *          that index
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 10, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTget_next( hid_t table_id,
                             size_t nrecords,
                             void * data)
{
  htbl_t * table;

  /* Find the table struct from its ID */
  if((table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    goto out;

  /* If nrecords == 0, do nothing */
  if(nrecords == 0)
    return 0;

  if((H5TB_common_read_records(table->dset_id, table->type_id,
                              table->current_index, nrecords, table->size, data)) < 0)
    goto out;

  /* Update the current index */
  table->current_index += nrecords;
  return 0;

out:
  return -1;
}

/*-------------------------------------------------------------------------
 * Function: H5PTread_packets
 *
 * Purpose: Reads packets from anywhere in a packet table
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 12, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTread_packets( hid_t table_id,
                         hsize_t start,
                         size_t nrecords,
                         void *data)
{
  htbl_t * table;

  /* find the table struct from its ID */
  table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type);
  if(table == NULL)
    goto out;

  /* If nrecords == 0, do nothing */
  if(nrecords == 0)
    return 0;

  if( H5TB_common_read_records(table->dset_id, table->type_id,
                              start, nrecords, table->size, data) < 0)
    goto out;

  return 0;

out:
  return -1;
}

/*-------------------------------------------------------------------------
 *
 * Table attribute functions
 *
 *-------------------------------------------------------------------------
 */

/*-------------------------------------------------------------------------
 * Function: H5PT_create_index, H5PT_set_index, H5PT_get_index
 *
 * Purpose: Resets, sets, and gets the current record index for a packet table
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *  		   James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 12, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5PT_create_index(htbl_t *table)
{
  if( table != NULL)
  {
    table->current_index = 0;
    return 0;
  }
  return -1;
}

static herr_t
H5PT_set_index(htbl_t *table, hsize_t index)
{
  /* Ensure index is valid */
  if( table != NULL )
  {
    if( index < table->size )
    {
      table->current_index = index;
      return 0;
    }
  }
  return -1;
}

static herr_t
H5PT_get_index(htbl_t *table, hsize_t *index)
{
  /* Ensure index is valid */
  if( table != NULL )
  {
    if(index)
      *index = table->current_index;
    return 0;
  }
  return -1;
}

/*-------------------------------------------------------------------------
 * Function: H5PTcreate_index, H5PTset_index, H5PTget_index
 *
 * Purpose: Resets, sets, and gets the current record index for a packet table
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: April 23, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTcreate_index(hid_t table_id)
{
  htbl_t * table;

  /* find the table struct from its ID */
  if((table = (htbl_t *) (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    return -1;

  return H5PT_create_index(table);
}

herr_t H5PTset_index(hid_t table_id, hsize_t pt_index)
{
  htbl_t * table;

  /* find the table struct from its ID */
  if((table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    return -1;

  return H5PT_set_index(table, pt_index);
}

herr_t H5PTget_index(hid_t table_id, hsize_t *pt_index)
{
  htbl_t * table;

  /* find the table struct from its ID */
  if((table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    return -1;

  return H5PT_get_index(table, pt_index);
}

/*-------------------------------------------------------------------------
 *
 * Inquiry functions
 *
 *-------------------------------------------------------------------------
 */

/*-------------------------------------------------------------------------
 * Function: H5PTget_num_packets
 *
 * Purpose: Returns by reference the number of packets in the dataset
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 12, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTget_num_packets( hid_t table_id, hsize_t *nrecords)
{
  htbl_t * table;

  /* find the table struct from its ID */
  if((table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    goto out;

  if(nrecords)
    *nrecords = table->size;

  return 0;
out:
  return -1;
}


/*-------------------------------------------------------------------------
 * Function: H5PTis_valid
 *
 * Purpose: Validates a table identifier
 *
 * Return: Success: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: March 12, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTis_valid(hid_t table_id)
{
  /* find the table struct from its ID */
  if(H5Iobject_verify(table_id, H5PT_ptable_id_type) ==NULL)
    return -1;

  return 0;
}

#ifdef H5_VLPT_ENABLED
/*-------------------------------------------------------------------------
 * Function: H5PTis_varlen
 *
 * Purpose: Returns 1 if a table_id corresponds to a packet table of variable-
 *          length records or 0 for fixed-length records.
 *
 * Return: True: 1, False: 0, Failure: -1
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: April 14, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *
 *-------------------------------------------------------------------------
 */
herr_t H5PTis_varlen(hid_t table_id)
{
  H5T_class_t type;
  htbl_t * table;

  /* find the table struct from its ID */
  if((table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    goto out;

  if((type = H5Tget_class( table->type_id )) == H5T_NO_CLASS)
    goto out;

  if( type == H5T_VLEN )
    return 1;
  else
    return 0;
out:
  return -1;
}

/*-------------------------------------------------------------------------
 *
 * Memory Management functions
 *
 *-------------------------------------------------------------------------
 */

/*-------------------------------------------------------------------------
 * Function: H5PTfree_vlen_readbuff
 *
 * Purpose: Frees memory used when reading from a variable length packet
 *          table.
 *
 * Return: Success: 0, Failure: -1
 *          -2 if memory was reclaimed but another error occurred
 *
 * Programmer: Nat Furrer, nfurrer@ncsa.uiuc.edu
 *             James Laird, jlaird@ncsa.uiuc.edu
 *
 * Date: April 12, 2004
 *
 * Comments:
 *
 * Modifications:
 *
 *
 *-------------------------------------------------------------------------
 */

herr_t H5PTfree_vlen_readbuff( hid_t table_id,
                               size_t _bufflen,
                               void * buff )
{
  hid_t space_id = H5I_BADID;
  htbl_t * table;
  hsize_t bufflen = _bufflen;
  herr_t ret_value;

  /* find the table struct from its ID */
  if((table = (htbl_t *) H5Iobject_verify(table_id, H5PT_ptable_id_type)) == NULL)
    goto out;

  if((space_id = H5Screate_simple(1, &bufflen, NULL)) < 0)
    goto out;

  /* Free the memory.  If this succeeds, ret_value should be 0. */
  if((ret_value = H5Dvlen_reclaim(table->type_id, space_id, H5P_DEFAULT, buff)) < 0)
    goto out;

  /* If the dataspace cannot be closed, return -2 to indicate that memory */
  /* was freed successfully but an error still occurred. */
  if(H5Sclose(space_id) < 0)
    return -2;

  return ret_value;

out:
  H5E_BEGIN_TRY
    H5Sclose(space_id);
  H5E_END_TRY
  return -1;
}

#endif /* H5_VLPT_ENABLED */
