/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * 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 LICENSE file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <iostream>
#include <string>

#include "H5Include.h"
#include "H5Exception.h"
#include "H5IdComponent.h"
#include "H5DataSpace.h"

namespace H5 {
using std::cerr;
using std::endl;

#ifndef DOXYGEN_SHOULD_SKIP_THIS
// This DOXYGEN_SHOULD_SKIP_THIS block is a work-around approach to control
// the order of creation and deletion of the global constants.  See Design Notes
// in "H5PredType.cpp" for information.

// Initialize a pointer for the constant
DataSpace *DataSpace::ALL_ = 0;

//--------------------------------------------------------------------------
// Function:    DataSpace::getConstant
//              Creates a DataSpace object representing the HDF5 constant
//              H5S_ALL, pointed to by DataSpace::ALL_
// Exception    H5::DataSpaceIException
// Description
//              If DataSpace::ALL_ already points to an allocated object, throw
//              a DataSpaceIException.  This scenario should not happen.
//--------------------------------------------------------------------------
DataSpace *
DataSpace::getConstant()
{
    // Tell the C library not to clean up, H5Library::termH5cpp will call
    // H5close - more dependency if use H5Library::dontAtExit()
    if (!IdComponent::H5dontAtexit_called) {
        (void)H5dont_atexit();
        IdComponent::H5dontAtexit_called = true;
    }

    // If the constant pointer is not allocated, allocate it. Otherwise,
    // throw because it shouldn't be.
    if (ALL_ == 0)
        ALL_ = new DataSpace(H5S_ALL);
    else
        throw DataSpaceIException("DataSpace::getConstant",
                                  "DataSpace::getConstant is being invoked on an allocated ALL_");
    return (ALL_);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::deleteConstants
// Purpose:     Deletes the constant object that DataSpace::ALL_ points to
//--------------------------------------------------------------------------
void
DataSpace::deleteConstants()
{
    delete ALL_;
}

//--------------------------------------------------------------------------
// Purpose      Constant for default dataspace.
//--------------------------------------------------------------------------
const DataSpace &DataSpace::ALL = *getConstant();

#endif // DOXYGEN_SHOULD_SKIP_THIS

//--------------------------------------------------------------------------
// Function:    DataSpace constructor
///\brief       Creates a new dataspace given a dataspace type.
///\param       type - IN: Type of the dataspace to be created, which
///             currently can be either \c H5S_SCALAR or \c H5S_SIMPLE;
///             default to \c H5S_SCALAR.
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
DataSpace::DataSpace(H5S_class_t type) : IdComponent(), id{H5Screate(type)}
{
    if (id < 0) {
        throw DataSpaceIException("DataSpace constructor", "H5Screate failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace overloaded constructor
///\brief       Creates a new simple dataspace.
///\param       rank - IN: Number of dimensions of dataspace.
///\param       dims - IN: An array of the size of each dimension.
///\param       maxdims - IN: An array of the maximum size of each dimension.
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
DataSpace::DataSpace(int rank, const hsize_t *dims, const hsize_t *maxdims)
    : IdComponent(), id{H5Screate_simple(rank, dims, maxdims)}
{
    if (id < 0) {
        throw DataSpaceIException("DataSpace constructor", "H5Screate_simple failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace overloaded constructor
///\brief       Creates a DataSpace object using the id of an existing
///             dataspace.
///\param       existing_id - IN: Id of an existing dataspace
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
DataSpace::DataSpace(const hid_t existing_id) : IdComponent(), id(existing_id)
{
    incRefCount(); // increment number of references to this id
}

//--------------------------------------------------------------------------
// Function:    DataSpace copy constructor
///\brief       Copy constructor: same HDF5 object as \a original
///\param       original - IN: DataSpace object to copy
//--------------------------------------------------------------------------
DataSpace::DataSpace(const DataSpace &original) : IdComponent(), id(original.id)
{
    incRefCount(); // increment number of references to this id
}

//--------------------------------------------------------------------------
// Function:    DataSpace::copy
///\brief       Makes a copy of an existing dataspace.
///\param       like_space  - IN: Dataspace to be copied
///\exception   H5::DataSpaceIException
// Modification
//              - Replaced resetIdComponent() with decRefCount() to use C
//              library ID reference counting mechanism - BMR, Jun 1, 2004
//              - Replaced decRefCount with close() to let the C library
//              handle the reference counting - BMR, Jun 1, 2006
//--------------------------------------------------------------------------
void
DataSpace::copy(const DataSpace &like_space)
{
    // If this object has an hdf5 valid id, close it
    if (id != H5S_ALL) {
        try {
            close();
        }
        catch (Exception &close_error) {
            throw DataSpaceIException("DataSpace::copy", close_error.getDetailMsg());
        }
    } // end if

    // call C routine to copy the dataspace
    id = H5Scopy(like_space.getId());

    if (id < 0)
        throw DataSpaceIException("DataSpace::copy", "H5Scopy failed");
}

//--------------------------------------------------------------------------
// Function:    DataSpace::operator=
///\brief       Assignment operator.
///\param       rhs - IN: Reference to the existing dataspace
///\return      Reference to DataSpace instance
///\exception   H5::DataSpaceIException
// Description
//              Makes a copy of the type on the right hand side and stores
//              the new id in the left hand side object.
//--------------------------------------------------------------------------
DataSpace &
DataSpace::operator=(const DataSpace &rhs)
{
    if (this != &rhs)
        copy(rhs);
    return (*this);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::isSimple
///\brief       Determines whether this dataspace is a simple dataspace.
///\return      \c true if the dataspace is a simple dataspace, and \c false,
///             otherwise
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
bool
DataSpace::isSimple() const
{
    htri_t simple = H5Sis_simple(id);
    if (simple > 0)
        return true;
    else if (simple == 0)
        return false;
    else {
        throw DataSpaceIException("DataSpace::isSimple", "H5Sis_simple returns negative value");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::offsetSimple
///\brief       Sets the offset of this simple dataspace.
///\param       offset  - IN: Offset to position the selection at
///\exception   H5::DataSpaceIException
///\par Description
///             This function creates an offset for the selection within
///             an extent, allowing the same shaped selection to be moved
///             to different locations within a dataspace without requiring
///             it to be re-defined.
//--------------------------------------------------------------------------
void
DataSpace::offsetSimple(const hssize_t *offset) const
{
    herr_t ret_value = H5Soffset_simple(id, offset);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::offsetSimple", "H5Soffset_simple failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSimpleExtentDims
///\brief       Retrieves dataspace dimension size and maximum size.
///\param       dims  - IN: Name of the new member
///\param       maxdims - IN: Pointer to the value of the new member
///\return      Number of dimensions, the same value as returned by
///             \c DataSpace::getSimpleExtentNdims()
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
int
DataSpace::getSimpleExtentDims(hsize_t *dims, hsize_t *maxdims) const
{
    int ndims = H5Sget_simple_extent_dims(id, dims, maxdims);
    if (ndims < 0) {
        throw DataSpaceIException("DataSpace::getSimpleExtentDims",
                                  "H5Sget_simple_extent_dims returns negative number of dimensions");
    }
    return (ndims);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSimpleExtentNdims
///\brief       Returns the dimensionality of a dataspace.
///\return      Number of dimensions
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
int
DataSpace::getSimpleExtentNdims() const
{
    int ndims = H5Sget_simple_extent_ndims(id);
    if (ndims < 0) {
        throw DataSpaceIException(
            "DataSpace::getSimpleExtentNdims",
            "H5Sget_simple_extent_ndims returns negative value for dimensionality of the dataspace");
    }
    return (ndims);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSimpleExtentNpoints
///\brief       Returns the number of elements in a dataspace.
///\return      Number of elements
///\exception   H5::DataSpaceIException
// Modification
//              12/05/00: due to C API change
//                      return type hssize_t vs. hsize_t
//                      num_elements = -1 when failure occurs vs. 0
//--------------------------------------------------------------------------
hssize_t
DataSpace::getSimpleExtentNpoints() const
{
    hssize_t num_elements = H5Sget_simple_extent_npoints(id);
    if (num_elements > -1)
        return (num_elements);
    else {
        throw DataSpaceIException("DataSpace::getSimpleExtentNpoints",
                                  "H5Sget_simple_extent_npoints returns negative value for the number of "
                                  "elements in the dataspace");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSimpleExtentType
///\brief       Returns the current class of a dataspace.
///\return      Class of the dataspace
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
H5S_class_t
DataSpace::getSimpleExtentType() const
{
    H5S_class_t class_name = H5Sget_simple_extent_type(id);
    if (class_name == H5S_NO_CLASS) {
        throw DataSpaceIException("DataSpace::getSimpleExtentType",
                                  "H5Sget_simple_extent_type returns H5S_NO_CLASS");
    }
    return (class_name);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::extentCopy
///\brief       Copies the extent of a dataspace.
///\param       dest_space  - IN: Dataspace to copy from
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
void
DataSpace::extentCopy(const DataSpace &dest_space) const
{
    hid_t  dest_space_id = dest_space.getId();
    herr_t ret_value     = H5Sextent_copy(dest_space_id, id);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::extentCopy", "H5Sextent_copy failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::extentCopy
// Purpose      This is an overloaded member function, kept for backward
//              compatibility.  It differs from the above function in that it
//              misses const.  This wrapper will be removed in future release.
// Param        dest_space  - IN: Dataspace to copy from
// Exception    H5::DataSpaceIException
// Modification
//              Modified to call its replacement. -BMR, 2014/04/16
//              Removed from documentation. -BMR, 2016/03/07 1.8.17 and 1.10.0
//              Removed from code. -BMR, 2016/08/11 1.8.18 and 1.10.1
//--------------------------------------------------------------------------
// void DataSpace::extentCopy(DataSpace& dest_space) const
//{
//    extentCopy(dest_space);
//}

//--------------------------------------------------------------------------
// Function:    DataSpace::setExtentSimple
///\brief       Sets or resets the size of an existing dataspace.
///\param       rank  - IN: Rank of the dataspace
///\param       current_size - IN: Array containing current size of dataspace
///\param       maximum_size - IN: Array containing maximum size of dataspace
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
void
DataSpace::setExtentSimple(int rank, const hsize_t *current_size, const hsize_t *maximum_size) const
{
    herr_t ret_value;
    ret_value = H5Sset_extent_simple(id, rank, current_size, maximum_size);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::setExtentSimple", "H5Sset_extent_simple failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::setExtentNone
///\brief       Removes the extent from a dataspace.
///
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
void
DataSpace::setExtentNone() const
{
    herr_t ret_value = H5Sset_extent_none(id);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::setExtentNone", "H5Sset_extent_none failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSelectNpoints
///\brief       Returns the number of elements in a dataspace selection.
///\return      Number of elements
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
hssize_t
DataSpace::getSelectNpoints() const
{
    hssize_t num_elements = H5Sget_select_npoints(id);
    if (num_elements < 0) {
        throw DataSpaceIException(
            "DataSpace::getSelectNpoints",
            "H5Sget_select_npoints returns negative value for number of elements in the dataspace selection");
    }
    return (num_elements);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSelectHyperNblocks
///\brief       Returns number of hyperslab blocks.
///\return      Number of hyperslab blocks
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
hssize_t
DataSpace::getSelectHyperNblocks() const
{
    hssize_t num_blocks = H5Sget_select_hyper_nblocks(id);
    if (num_blocks < 0) {
        throw DataSpaceIException(
            "DataSpace::getSelectHyperNblocks",
            "H5Sget_select_hyper_nblocks returns negative value for the number of hyperslab blocks");
    }
    return (num_blocks);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSelectHyperBlocklist
///\brief       Gets the list of hyperslab blocks currently selected
///\param       startblock  - IN: Hyperslab block to start with
///\param       numblocks - IN: Number of hyperslab blocks to get
///\param       buf - IN: List of hyperslab blocks selected
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
void
DataSpace::getSelectHyperBlocklist(hsize_t startblock, hsize_t numblocks, hsize_t *buf) const
{
    herr_t ret_value;
    ret_value = H5Sget_select_hyper_blocklist(id, startblock, numblocks, buf);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::getSelectHyperBlocklist",
                                  "H5Sget_select_hyper_blocklist failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSelectElemNpoints
///\brief       Returns the number of element points in the current selection.
///\return      Number of element points
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
hssize_t
DataSpace::getSelectElemNpoints() const
{
    hssize_t num_points = H5Sget_select_elem_npoints(id);
    if (num_points < 0) {
        throw DataSpaceIException("DataSpace::getSelectElemNpoints", "H5Sget_select_elem_npoints failed");
    }
    return (num_points);
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSelectElemPointlist
///\brief       Gets the list of element points currently selected
///\param       startpoint  - IN: Element point to start with
///\param       numpoints - IN: Number of element points to get
///\param       buf - IN: List of element points selected
///\exception   H5::DataSpaceIException
///\par Description
///             For information, please refer to the C API
///             H5Sget_select_elem_pointlist in the HDF5 C Reference Manual.
//--------------------------------------------------------------------------
void
DataSpace::getSelectElemPointlist(hsize_t startpoint, hsize_t numpoints, hsize_t *buf) const
{
    herr_t ret_value;
    ret_value = H5Sget_select_elem_pointlist(id, startpoint, numpoints, buf);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::getSelectElemPointlist", "H5Sget_select_elem_pointlist failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getSelectBounds
///\brief       Gets the bounding box containing the current selection.
///\param       start  - IN: Starting coordinates of the bounding box
///\param       end - IN: Ending coordinates of the bounding box, i.e.,
///             the coordinates of the diagonally opposite corner
///\exception   H5::DataSpaceIException
///\par Description
///             For information, please refer to the H5Sget_select_bounds API in
///             the HDF5 C Reference Manual.
//--------------------------------------------------------------------------
void
DataSpace::getSelectBounds(hsize_t *start, hsize_t *end) const
{
    herr_t ret_value = H5Sget_select_bounds(id, start, end);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::getSelectBounds", "H5Sget_select_bounds failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::selectElements
///\brief       Selects array elements to be included in the selection for
///             this dataspace.
///\param       op  - IN: Operator specifying how the new selection is to be
///             combined with the existing selection for the dataspace
///\param       num_elements  - IN: Number of elements to be selected
///\param       coord  - IN: A 2-dimensional array of 0-based values
///             specifying the coordinates of the elements being selected
///\exception   H5::DataSpaceIException
///\par Description
///             For information, please refer to the H5Sselect_elements API in
///             the HDF5 C Reference Manual.
//--------------------------------------------------------------------------
void
DataSpace::selectElements(H5S_seloper_t op, const size_t num_elements, const hsize_t *coord) const
{
    herr_t ret_value;
    ret_value = H5Sselect_elements(id, op, num_elements, coord);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::selectElements", "H5Sselect_elements failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::selectAll
///\brief       Selects the entire dataspace.
///
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
void
DataSpace::selectAll() const
{
    herr_t ret_value = H5Sselect_all(id);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::selectAll", "H5Sselect_all failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::selectNone
///\brief       Resets the selection region to include no elements.
///
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
void
DataSpace::selectNone() const
{
    herr_t ret_value = H5Sselect_none(id);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::selectNone", "H5Sselect_none failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::selectValid
///\brief       Verifies that the selection is within the extent of the
///             dataspace.
///\return      \c true if the selection is within the extent of the
///             dataspace, and \c false, otherwise
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
bool
DataSpace::selectValid() const
{
    htri_t ret_value = H5Sselect_valid(id);
    if (ret_value > 0)
        return true;
    else if (ret_value == 0)
        return false;
    else {
        throw DataSpaceIException("DataSpace::selectValid", "H5Sselect_valid returns negative value");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::selectHyperslab
///\brief       Selects a hyperslab region to add to the current selected region.
///\param       op - IN: Operation to perform on current selection
///\param       count - IN: Number of blocks included in the hyperslab
///\param       start - IN: Offset of the start of hyperslab
///\param       stride - IN: Hyperslab stride - default to \c NULL
///\param       block - IN: Size of block in the hyperslab - default to \c NULL
///\exception   H5::DataSpaceIException
///\par Description
///             For information, please refer to the H5Sselect_hyperslab API in
///             the HDF5 C Reference Manual.
//--------------------------------------------------------------------------
void
DataSpace::selectHyperslab(H5S_seloper_t op, const hsize_t *count, const hsize_t *start,
                           const hsize_t *stride, const hsize_t *block) const
{
    herr_t ret_value;
    ret_value = H5Sselect_hyperslab(id, op, start, stride, count, block);
    if (ret_value < 0) {
        throw DataSpaceIException("DataSpace::selectHyperslab", "H5Sselect_hyperslab failed");
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace::getId
///\brief       Get the id of this dataspace
///\return      Dataspace identifier
// Modification:
//      May 2008 - BMR
//              Class hierarchy is revised to address bugzilla 1068.  Class
//              AbstractDS and Attribute are moved out of H5Object.  In
//              addition, member IdComponent::id is moved into subclasses, and
//              IdComponent::getId now becomes pure virtual function.
//--------------------------------------------------------------------------
hid_t
DataSpace::getId() const
{
    return (id);
}

#ifndef DOXYGEN_SHOULD_SKIP_THIS
//--------------------------------------------------------------------------
// Function:    DataSpace::p_setId
///\brief       Sets the identifier of this object to a new value.
///
///\exception   H5::IdComponentException when the attempt to close the HDF5
///             object fails
// Description:
//              The underlying reference counting in the C library ensures
//              that the current valid id of this object is properly closed.
//              Then the object's id is reset to the new id.
//--------------------------------------------------------------------------
void
DataSpace::p_setId(const hid_t new_id)
{
    // handling references to this old id
    try {
        close();
    }
    catch (Exception &close_error) {
        throw DataSpaceIException(inMemFunc("p_setId"), close_error.getDetailMsg());
    }
    // reset object's id to the given id
    id = new_id;
}
#endif // DOXYGEN_SHOULD_SKIP_THIS

//--------------------------------------------------------------------------
// Function:    DataSpace::close
///\brief       Closes this dataspace.
///
///\exception   H5::DataSpaceIException
//--------------------------------------------------------------------------
void
DataSpace::close()
{
    // check if id is a valid hdf5 object id before trying to close it
    if (p_valid_id(id)) {
        herr_t ret_value = H5Sclose(id);
        if (ret_value < 0) {
            throw DataSpaceIException("DataSpace::close", "H5Sclose failed");
        }
        // reset the id
        id = H5I_INVALID_HID;
    }
}

//--------------------------------------------------------------------------
// Function:    DataSpace destructor
///\brief       Properly terminates access to this dataspace.
// Modification
//              - Replaced resetIdComponent() with decRefCount() to use C
//              library ID reference counting mechanism - BMR, Jun 1, 2004
//              - Replaced decRefCount with close() to let the C library
//              handle the reference counting - BMR, Jun 1, 2006
//--------------------------------------------------------------------------
DataSpace::~DataSpace()
{
    try {
        close();
    }
    catch (Exception &close_error) {
        cerr << "DataSpace::~DataSpace - " << close_error.getDetailMsg() << endl;
    }
}

} // namespace H5
