text: /**
   * @date Wed Jun 22 17:50:08 2011 +0200
   * @author Andre Anjos <andre.anjos@idiap.ch>
   *
   * @brief A few helpers to handle HDF5 datasets in a more abstract way.
   *
   * Copyright (C) Idiap Research Institute, Martigny, Switzerland
   */
  
  #ifndef BOB_IO_BASE_HDF5TYPES_H
  #define BOB_IO_BASE_HDF5TYPES_H
  
  #include <vector>
  #include <string>
  #include <boost/shared_ptr.hpp>
  #include <blitz/array.h>
  #include <hdf5.h>
  
  /**
   * Checks if the version of HDF5 installed is greater or equal to some set of
   * values. (extracted from hdf5-1.8.7)
   */
  #ifndef H5_VERSION_GE
  #define H5_VERSION_GE(Maj,Min,Rel) \
   (((H5_VERS_MAJOR==Maj) && (H5_VERS_MINOR==Min) && (H5_VERS_RELEASE>=Rel)) || \
    ((H5_VERS_MAJOR==Maj) && (H5_VERS_MINOR>Min)) || \
    (H5_VERS_MAJOR>Maj))
  #endif
  
  #include <bob.io.base/array.h>
  
  namespace bob { namespace io { namespace base {
  
    /**
     * Supported types
     */
    typedef enum hdf5type {
      s=0, //std::string
      b, //bool
      i8, //int8_t
      i16, //int16_t
      i32, //int32_t
      i64, //int64_t
      u8, //uint8_t
      u16, //uint16_t
      u32, //uint32_t
      u64, //uint64_t
      f32, //float
      f64, //double
      f128, //long double
      c64, //std::complex<float>
      c128, //std::complex<double>
      c256, //std::complex<long double>
      unsupported //this must be last
    } hdf5type;
  
    /**
     * Converts a hdf5type enumeration into its string representation
     */
    const char* stringize (hdf5type t);
  
    /**
     * A wrapper to handle the HDF5 C-API error printing in a nicer way...
     */
    class HDF5ErrorStack {
  
      public: //api
  
        /**
         * Binds the HDF5Error to the current default error stack.
         */
        HDF5ErrorStack ();
  
        /**
         * Binds to a specific HDF5 error stack
         */
        HDF5ErrorStack (hid_t stack);
  
        /**
         * Destructor virtualization.
         */
        virtual ~HDF5ErrorStack();
  
        /**
         * Returns the currently captured error stack
         */
        inline std::vector<std::string>& get() { return m_err; }
  
        /**
         * Clears the current error stack
         */
        inline void clear() { m_err.clear(); }
  
        /**
         * Sets muting
         */
        inline void mute () { m_muted = true; }
        inline void unmute () { m_muted = false; }
        inline bool muted () const { return m_muted; }
  
      private: //not implemented
  
        HDF5ErrorStack(const HDF5ErrorStack& other);
  
        HDF5ErrorStack& operator= (const HDF5ErrorStack& other);
  
      private: //representation
        hid_t m_stack; ///< the stack I'm observing
        bool m_muted; ///< if I'm currently muted
        std::vector<std::string> m_err; ///< the current captured stack
        herr_t (*m_func)(hid_t, void*); ///< temporary cache
        void* m_client_data; ///< temporary cache
  
    };
  
    // Global default HDF5 error stack
    extern const boost::shared_ptr<HDF5ErrorStack> DefaultHDF5ErrorStack;
  
    /**
     * This class defines the shape type: a counter and a variable-size hsize_t
     * array that contains the dimensionality of a certain array. Internally, we
     * always allocate a fixed size vector with 12 positions (after the maximum
     * number of dimensions of a blitz::Array<T,N> + 1).
     */
    class HDF5Shape {
  
  # define MAX_HDF5SHAPE_SIZE 12
  
      public: //api
  
        /**
         * Builds a new shape with a certain size and values. The size has to be
         * smaller than the maximum number of supported dimensions (12).
         */
        template <typename T> HDF5Shape(const size_t n, const T* values):
          m_n(n), m_shape() {
            if (n > MAX_HDF5SHAPE_SIZE)
              throw std::length_error("maximum number of dimensions exceeded");
            for (size_t i=0; i<n; ++i) m_shape[i] = values[i];
          }
  
        /**
         * Builds a new shape with data from a blitz::TinyVector
         */
        template <int N> HDF5Shape(const blitz::TinyVector<int,N>& vec):
          m_n(N), m_shape() {
            if (N > MAX_HDF5SHAPE_SIZE)
              throw std::length_error("maximum number of dimensions exceeded");
            for (size_t i=0; i<N; ++i) m_shape[i] = vec[i];
          }
  
        /**
         * Allocates the shape space, but fills all with zeros
         */
        HDF5Shape (size_t n);
  
        /**
         * Default constructor (m_n = 0, no shape)
         */
        HDF5Shape ();
  
        /**
         * Copy construct the shape
         */
        HDF5Shape (const HDF5Shape& other);
  
        /**
         * Virtual destructor
         */
        virtual ~HDF5Shape();
  
        /**
         * Resets this new shape
         */
        HDF5Shape& operator= (const HDF5Shape& other);
  
        /**
         * Returns the current size of shape. If values are less than zero, the
         * shape is not valid.
         */
        inline size_t n () const { return m_n; }
  
        /**
         * Returs a pointer to the first element of the shape
         */
        inline const hsize_t* get() const { return m_shape; }
        inline hsize_t* get() { return m_shape; }
  
        /**
         * Copies the data from the other HDF5Shape. If the other shape is
         * smaller, will copy up to the number of positions in the other shape,
         * if it is bigger, will copy up to my number of positions.
         */
        void copy(const HDF5Shape& other);
  
        /**
         * Sets a TinyVector with the contents of this shape. If the tinyvector
         * shape is smaller, will copy up to the number of positions in the
         * current shape. If that is bigger, will copy up to my number of
         * positions
         */
        template <int N> void set (blitz::TinyVector<int,N>& v) const {
          if (N >= m_n) for (size_t i=0; i<m_n; ++i) v[i] = m_shape[i];
          else for (size_t i=0; i<N; ++i) v[i] = m_shape[i];
        }
  
        /**
         * Resets the current shape so it becomes invalid.
         */
        void reset();
  
        /**
         * Accesses a certain position of this shape (unchecked!)
         */
        inline const hsize_t& operator[] (size_t pos) const { return m_shape[pos]; }
        inline hsize_t& operator[] (size_t pos) { return m_shape[pos]; }
  
        /**
         * Left-shift a number of positions, decreases the total size.
         */
        HDF5Shape& operator <<= (size_t pos);
  
        /**
         * Right-shift a number of positions, increases the total size. New
         * positions are filled with 1's (ones).
         */
        HDF5Shape& operator >>= (size_t pos);
  
        /**
         * Returns the product of all dimensions
         */
        hsize_t product() const;
  
        /**
         * Compares two shapes for equality
         */
        bool operator== (const HDF5Shape& other) const;
        bool operator!= (const HDF5Shape& other) const;
  
        /**
         * Compares a shape with a TinyVector for equality
         */
        template <int N>
        bool operator== (const blitz::TinyVector<int,N>& other) const {
          if (N != m_n) return false;
          for (size_t i=0; i<m_n; ++i) if (m_shape[i] != other[i]) return false;
          return true;
        }
  
        template <int N>
        bool operator!= (const blitz::TinyVector<int,N>& other) const {
          return !(*this == other);
        }
  
        /**
         * Tells if this shape is invalid
         */
        inline bool operator! () const { return m_n == 0; }
  
        /**
         * Returns a tuple-like string representation for this shape
         */
        std::string str() const;
  
      private: //representation
        size_t m_n; ///< The number of valid hsize_t's in this shape
        hsize_t m_shape[MAX_HDF5SHAPE_SIZE]; ///< The actual shape values
  
    };
  
    /**
     * Support to compare data types, convert types into runtime equivalents and
     * make our life easier when deciding what to input and output.
     */
    class HDF5Type {
  
      public:
  
        /**
         * Specific implementations bind the type T to the support_t enum
         */
  #     define DECLARE_SUPPORT(T) HDF5Type(const T& value);
        DECLARE_SUPPORT(bool)
        DECLARE_SUPPORT(int8_t)
        DECLARE_SUPPORT(int16_t)
        DECLARE_SUPPORT(int32_t)
        DECLARE_SUPPORT(int64_t)
        DECLARE_SUPPORT(uint8_t)
        DECLARE_SUPPORT(uint16_t)
        DECLARE_SUPPORT(uint32_t)
        DECLARE_SUPPORT(uint64_t)
        DECLARE_SUPPORT(float)
        DECLARE_SUPPORT(double)
        DECLARE_SUPPORT(long double)
        DECLARE_SUPPORT(std::complex<float>)
        DECLARE_SUPPORT(std::complex<double>)
        DECLARE_SUPPORT(std::complex<long double>)
  #     undef DECLARE_SUPPORT
  
        /**
         * Builds the type from a string (special HDF5 handling)
         */
        HDF5Type(const char* value);
        HDF5Type(const std::string& value);
  
  #     define DECLARE_SUPPORT(T,N) HDF5Type(const blitz::Array<T,N>& value);
  
  #     define DECLARE_BZ_SUPPORT(T) \
        DECLARE_SUPPORT(T,1) \
        DECLARE_SUPPORT(T,2) \
        DECLARE_SUPPORT(T,3) \
        DECLARE_SUPPORT(T,4)
  
        DECLARE_BZ_SUPPORT(bool)
        DECLARE_BZ_SUPPORT(int8_t)
        DECLARE_BZ_SUPPORT(int16_t)
        DECLARE_BZ_SUPPORT(int32_t)
        DECLARE_BZ_SUPPORT(int64_t)
        DECLARE_BZ_SUPPORT(uint8_t)
        DECLARE_BZ_SUPPORT(uint16_t)
        DECLARE_BZ_SUPPORT(uint32_t)
        DECLARE_BZ_SUPPORT(uint64_t)
        DECLARE_BZ_SUPPORT(float)
        DECLARE_BZ_SUPPORT(double)
        DECLARE_BZ_SUPPORT(long double)
        DECLARE_BZ_SUPPORT(std::complex<float>)
        DECLARE_BZ_SUPPORT(std::complex<double>)
        DECLARE_BZ_SUPPORT(std::complex<long double>)
  #     undef DECLARE_BZ_SUPPORT
  #     undef DECLARE_SUPPORT
  
        /**
         * Default constructor, results in an unsupported type with invalid shape
         */
        HDF5Type();
  
        /**
         * Creates a HDF5Type from a type enumeration, assumes it is a scalar
         */
        HDF5Type(hdf5type type);
  
        /**
         * Creates a HDF5Type from an bob::io::base::array::typeinfo
         */
        HDF5Type(const bob::io::base::array::typeinfo& ti);
  
        /**
         * Creates a HDF5Type from a type enumeration and an explicit shape
         */
        HDF5Type(bob::io::base::array::ElementType eltype, const HDF5Shape& extents);
  
        /**
         * Creates a HDF5Type from a type enumeration and an explicit shape
         */
        HDF5Type(hdf5type type, const HDF5Shape& extents);
  
        /**
         * Creates a HDF5Type from a HDF5 Dataset, Datatype and Dataspace
         */
        HDF5Type(const boost::shared_ptr<hid_t>& type,
            const HDF5Shape& extents);
  
        /**
         * Scalar of a certain type
         */
        HDF5Type(const boost::shared_ptr<hid_t>& type);
  
        /**
         * Copy construction
         */
        HDF5Type(const HDF5Type& other);
  
        /**
         * Virtual destructor
         */
        virtual ~HDF5Type();
  
        /**
         * Assignment
         */
        HDF5Type& operator= (const HDF5Type& other);
  
        /**
         * Checks if two types are the same
         */
        bool operator== (const HDF5Type& other) const;
  
        /**
         * Checks if two types are *not* the same
         */
        bool operator!= (const HDF5Type& other) const;
  
        /**
         * Checks if an existing object is compatible with my type
         */
        template <typename T> bool compatible (const T& value) const {
          return *this == HDF5Type(value);
        }
  
        /**
         * Checks if an existing object is compatible with my type
         */
        bool compatible (const bob::io::base::array::typeinfo& value) const;
  
        /**
         * Returns the HDF5Shape of this type
         */
        const HDF5Shape& shape() const { return m_shape; }
  
        /**
         * Returns the HDF5Shape of this type
         */
        HDF5Shape& shape() { return m_shape; }
  
        /**
         * Returns the equivalent HDF5 type info object for this type.
         */
        boost::shared_ptr<hid_t> htype() const;
  
        /**
         * Returns a string representation of this supported type.
         */
        std::string str() const;
  
        /**
         * Returns a string representation of the element type.
         */
        std::string type_str() const { return stringize(m_type); }
  
        /**
         * Returns the current enumeration for the type
         */
        inline hdf5type type() const { return m_type; }
  
        /**
         * Returns a mapping between the current type and the supported element
         * types in bob::io::base::array
         */
        bob::io::base::array::ElementType element_type() const;
  
        /**
         * Copies this type information to a stock bob::io::base::array::typeinfo
         */
        void copy_to (bob::io::base::array::typeinfo& ti) const;
  
      private: //representation
  
        hdf5type m_type; ///< the precise supported type
        HDF5Shape m_shape; ///< what is the shape of the type (scalar)
  
    };
  
    /**
     * Describes ways to read a Dataset.
     */
    struct HDF5Descriptor {
  
      public: //api
  
        /**
         * Constructor
         */
        HDF5Descriptor(const HDF5Type& type, size_t size = 0, bool expand = true);
  
        /**
         * Copy constructor
         */
        HDF5Descriptor(const HDF5Descriptor& other);
  
        /**
         * Virtual destructor
         */
        virtual ~HDF5Descriptor();
  
        /**
         * Assignment
         */
        HDF5Descriptor& operator= (const HDF5Descriptor& other);
  
        /**
         * Setup myself as I was supposed to be read from a space with N+1
         * dimensions.
         */
        HDF5Descriptor& subselect();
  
      public: //representation
  
        HDF5Type type; ///< base type for read/write operations
        size_t size; ///< number of objects of this type stored at dataset
        bool expandable; ///< is this dataset expandable using this type?
  
        /**
         * Variables required for fast read/write operations.
         */
        HDF5Shape hyperslab_start; ///< offset to read/write operations
        HDF5Shape hyperslab_count; ///< count for read/write operations
  
    };
  
    /**
     * Format and returns the current HDF5 error stack. It also clears the stack
     * before returning.
     */
    std::string format_hdf5_error();
  
  }}}
  
  #endif /* BOB_IO_BASE_HDF5TYPES_H */
  