/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
 *
 * $Id: xdrfile.h,v 1.1 2010/10/08 11:53:37 spoel Exp spoel $
 *
 * Copyright (c) 2009-2014, Erik Lindahl & David van der Spoel
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*! \file  xdrfile.h
 *  \brief Interface to read/write portabile binary files using XDR.
 *
 * This file provides an interface to read & write portably binary files,
 * using XDR - the external data representation standard defined in RFC 1014.
 *
 * There are several advantages to the XDR approach:
 *
 * -# It is portable. And not just portable between big/small integer endian,
 *    but truly portable if you have system XDR routines. For example:
 *       - It doesn't matter if the character representation is ASCII or EBCDIC.
 *       - Some systems are small endian but use big endian order of the two
 *         dword in a double precision floating-point variable. The system XDR
 *         libraries will read/write this correctly.
 *       - Some systems (VAX...) don't use IEEE floating point. Their system
 *         XDR libraries will convert to/from this automatically.
 * -# XDR libraries are required for NFS and lots of other network functions.
 *    This means there isn't a single Unix-like system that doesn't have them.
 * -# There is NO extra metadata whatsoever, and we write plain XDR files.
 *    If you write a float, it will take exactly 4 bytes in the file.
 *    (All basic datatypes are 4 bytes, double fp 8 bytes).
 * -# You can read/write the files by calling the system XDR routines directly
 *    too - you don't have to use the routines defined in this file.
 * -# It is no problem if your system doesn't have XDR libraries (MS Windows).
 *    We have written our own versions of the necessary routines that work if
 *    your system uses ASCII for strings and IEEE floating-point. All types
 *    of byte and dword endian for integer and floating-point are supported.
 * -# You can use these routines for any type of data, but since we designed
 *    them for Gromacs we also provide a special routine to write coordinates
 *    with (adjustable) lossy compression. The default precision will give you
 *    three decimals guaranteed accuracy, and reduces the filesize to 1/10th
 *    of normal binary data.
 *
 * We do not support getting or setting positions in XDR files, since it can
 * break in horrible ways for large (64-bit) files, resulting in silent data
 * corruption. Note that it works great to open/read/write 64-bit files if
 * your system supports it; it is just the random access we cannot trust!
 *
 * We also provide wrapper routines so this module can be used from FORTRAN -
 * see the file xdrfile_fortran.txt in the Gromacs distribution for
 * documentation on the FORTRAN interface!
 */


#ifndef _XDRFILE_H_
#define _XDRFILE_H_

#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif

    /*! \brief Abstract datatype for an portable binary file handle
     *
     *  This datatype essentially works just like the standard FILE type in C.
     *  The actual contents is hidden in the implementation, so you can only
     *  define pointers to it, for use with the xdrfile routines.
     *
     *  If you \a really need to see the definition it is in xdrfile.c, but you
     *  cannot access elements of the structure outside that file.
     *
     *  \warning The implementation is completely different from the C standard
     *  library FILE, so don't even think about using an XDRFILE pointer as an
     *  argument to a routine that needs a standard FILE pointer.
     */
    typedef struct XDRFILE XDRFILE;

    enum { exdrOK, exdrHEADER, exdrSTRING, exdrDOUBLE,
           exdrINT, exdrFLOAT, exdrUINT, exdr3DX, exdrCLOSE, exdrMAGIC,
           exdrNOMEM, exdrENDOFFILE, exdrFILENOTFOUND, exdrNR };

    extern char *exdr_message[exdrNR];

#define DIM 3
    typedef float matrix[DIM][DIM];
    typedef float rvec[DIM];
    typedef int   mybool;


    /*! \brief Open a portable binary file, just like fopen()
     *
     *  Use this routine much like calls to the standard library function
     *  fopen(). The only difference is that the returned pointer should only
     *  be used with routines defined in this header.
     *
     *  \param path  Full or relative path (including name) of the file
     *  \param mode  "r" for reading, "w" for writing, "a" for append.
     *
     *  \return Pointer to abstract xdr file datatype, or NULL if an error occurs.
     *
     */
    XDRFILE *
    xdrfile_open    (const char *    path,
                     const char *    mode);


    /*! \brief Close a previously opened portable binary file, just like fclose()
     *
     *  Use this routine much like calls to the standard library function
     *  fopen(). The only difference is that it is used for an XDRFILE handle
     *  instead of a FILE handle.
     *
     *  \param xfp  Pointer to an abstract XDRFILE datatype
     *
     *  \return     0 on success, non-zero on error.
     */
    int
    xdrfile_close   (XDRFILE *       xfp);




    /*! \brief Read one or more \a char type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of characters to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of characters read
     */
    int
    xdrfile_read_char(char *      ptr,
                      int         ndata,
                      XDRFILE *   xfp);



    /*! \brief Write one or more \a characters type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of characters to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of characters written
     */
    int
    xdrfile_write_char(char *      ptr,
                       int         ndata,
                       XDRFILE *   xfp);



    /*! \brief Read one or more \a unsigned \a char type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of unsigned characters to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of unsigned characters read
     */
    int
    xdrfile_read_uchar(unsigned char *    ptr,
                       int                ndata,
                       XDRFILE *          xfp);



    /*! \brief Write one or more \a unsigned \a characters type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of unsigned characters to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of unsigned characters written
     */
    int
    xdrfile_write_uchar(unsigned char *   ptr,
                        int               ndata,
                        XDRFILE *         xfp);



    /*! \brief Read one or more \a short type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of shorts to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of shorts read
     */
    int
    xdrfile_read_short(short *             ptr,
                       int                 ndata,
                       XDRFILE *           xfp);



    /*! \brief Write one or more \a short type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of shorts to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of shorts written
     */
    int
    xdrfile_write_short(short *            ptr,
                        int                ndata,
                        XDRFILE *          xfp);



    /*! \brief Read one or more \a unsigned \a short type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of unsigned shorts to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of unsigned shorts read
     */
    int
    xdrfile_read_ushort(unsigned short *   ptr,
                        int                ndata,
                        XDRFILE *          xfp);



    /*! \brief Write one or more \a unsigned \a short type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of unsigned shorts to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of unsigned shorts written
     */
    int
    xdrfile_write_ushort(unsigned short *     ptr,
                         int                  ndata,
                         XDRFILE *            xfp);


    /*! \brief Read one or more \a integer type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of integers to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of integers read
     *
     *  The integer data type is assumed to be less than or equal to 32 bits.
     *
     *  We do not provide any routines for reading/writing 64-bit integers, since
     *  - Not all XDR implementations support it
     *  - Not all machines have 64-bit integers
     *
     *  Split your 64-bit data into two 32-bit integers for portability!
     */
    int
    xdrfile_read_int(int *         ptr,
                     int           ndata,
                     XDRFILE *     xfp);



    /*! \brief Write one or more \a integer type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of integers to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of integers written
     *
     *  The integer data type is assumed to be less than or equal to 32 bits.
     *
     *  We do not provide any routines for reading/writing 64-bit integers, since
     *  - Not all XDR implementations support it
     *  - Not all machines have 64-bit integers
     *
     *  Split your 64-bit data into two 32-bit integers for portability!
     */
    int
    xdrfile_write_int(int *        ptr,
                      int          ndata,
                      XDRFILE *    xfp);

    /*! \brief Read one or more \a unsigned \a integers type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of unsigned integers to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of unsigned integers read
     *
     *  The integer data type is assumed to be less than or equal to 32 bits.
     *
     *  We do not provide any routines for reading/writing 64-bit integers, since
     *  - Not all XDR implementations support it
     *  - Not all machines have 64-bit integers
     *
     *  Split your 64-bit data into two 32-bit integers for portability!
     */
    int
    xdrfile_read_uint(unsigned int *    ptr,
                      int               ndata,
                      XDRFILE *         xfp);



    /*! \brief Write one or more \a unsigned \a integer type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of unsigned integers to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of unsigned integers written
     *
     *  The integer data type is assumed to be less than or equal to 32 bits.
     *
     *  We do not provide any routines for reading/writing 64-bit integers, since
     *  - Not all XDR implementations support it
     *  - Not all machines have 64-bit integers
     *
     *  Split your 64-bit data into two 32-bit integers for portability!
     */
    int
    xdrfile_write_uint(unsigned int *    ptr,
                       int               ndata,
                       XDRFILE *         xfp);



    /*! \brief Read one or more \a float type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of floats to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of floats read
     */
    int
    xdrfile_read_float(float *           ptr,
                       int               ndata,
                       XDRFILE *         xfp);



    /*! \brief Write one or more \a float type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of floats to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of floats written
     */
    int
    xdrfile_write_float(float *          ptr,
                        int              ndata,
                        XDRFILE *        xfp);



    /*! \brief Read one or more \a double type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be written
     *  \param ndata  Number of doubles to read
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of doubles read
     */
    int
    xdrfile_read_double(double *          ptr,
                        int               ndata,
                        XDRFILE *         xfp);



    /*! \brief Write one or more \a double type variable(s)
     *
     *  \param ptr    Pointer to memory where data should be read
     *  \param ndata  Number of double to write.
     *  \param xfp    Handle to portable binary file, created with xdrfile_open()
     *
     *  \return       Number of doubles written
     */
    int
    xdrfile_write_double(double *        ptr,
                         int             ndata,
                         XDRFILE *       xfp);



    /*! \brief Read a string (array of characters)
     *
     *  \param ptr     Pointer to memory where data should be written
     *  \param maxlen  Maximum length of string. If no end-of-string is encountered,
     *                 one byte less than this is read and end-of-string appended.
     *  \param xfp     Handle to portable binary file, created with xdrfile_open()
     *
     *  \return        Number of characters read, including end-of-string
     */
    int
    xdrfile_read_string(char *          ptr,
                        int             maxlen,
                        XDRFILE *       xfp);



    /*! \brief Write a string (array of characters)
     *
     *  \param ptr     Pointer to memory where data should be read
     *  \param xfp     Handle to portable binary file, created with xdrfile_open()
     *
     *  \return        Number of characters written, including end-of-string
     */
    int
    xdrfile_write_string(char *          ptr,
                         XDRFILE *       xfp);



    /*! \brief Read raw bytes from file (unknown datatype)
     *
     *  \param ptr     Pointer to memory where data should be written
     *  \param nbytes  Number of bytes to read. No conversion whatsoever is done.
     *  \param xfp     Handle to portable binary file, created with xdrfile_open()
     *
     *  \return        Number of bytes read from file
     */
    int
    xdrfile_read_opaque(char *             ptr,
                        int                nbytes,
                        XDRFILE *          xfp);




    /*! \brief Write raw bytes to file (unknown datatype)
     *
     *  \param ptr     Pointer to memory where data should be read
     *  \param nbytes  Number of bytes to write. No conversion whatsoever is done.
     *  \param xfp     Handle to portable binary file, created with xdrfile_open()
     *
     *  \return        Number of bytes written to file
     */
    int
    xdrfile_write_opaque(char *            ptr,
                         int               nbytes,
                         XDRFILE *         xfp);






    /*! \brief Compress coordiates in a float array to XDR file
     *
     *  This routine will perform \a lossy compression on the three-dimensional
     *  coordinate data data specified and store it in the XDR file.
     *
     *  The lossy part of the compression consists of multiplying each
     *  coordinate with the precision argument and then rounding to integers.
     *  We suggest a default value of 1000.0, which means you are guaranteed
     *  three decimals of accuracy. The only limitation is that scaled coordinates
     *  must still fit in an integer variable, so if the precision is 1000.0 the
     *  coordinate magnitudes must be less than +-2e6.
     *
     *  \param ptr        Pointer to coordinates to compress (length 3*ncoord)
     *  \param ncoord     Number of coordinate triplets in data
     *  \param precision  Scaling factor for lossy compression. If it is <=0,
     *                    the default value of 1000.0 is used.
     *  \param xfp        Handle to portably binary file
     *
     *  \return           Number of coordinate triplets written.
     *                    IMPORTANT: Check that this is equal to ncoord - if it is
     *                    negative, an error occured. This should not happen with
     *                    normal data, but if your coordinates are NaN or very
     *                    large (>1e6) it is not possible to use the compression.
     *
     *  \warning          The compression algorithm is not part of the XDR standard,
     *                    and very complicated, so you will need this xdrfile module
     *                    to read it later.
     */
    int
    xdrfile_compress_coord_float(float *     ptr,
                                 int         ncoord,
                                 float       precision,
                                 XDRFILE *   xfp);




    /*! \brief Decompress coordiates from XDR file to array of floats
     *
     *  This routine will decompress three-dimensional coordinate data previously
     *  stored in an XDR file and store it in the specified array of floats.
     *
     *  The precision used during the earlier compression is read from the file
     *  and returned - you cannot adjust the accuracy at this stage.
     *
     *  \param ptr        Pointer to coordinates to compress (length>= 3*ncoord)
     *  \param ncoord     Max number of coordinate triplets to read on input, actual
     *                    number of coordinate triplets read on return. If this
     *                    is smaller than the number of coordinates in the frame an
     *                    error will occur.
     *  \param precision  The precision used in the previous compression will be
     *                    written to this variable on return.
     *  \param xfp        Handle to portably binary file
     *
     *  \return           Number of coordinate triplets read. If this is negative,
     *                    an error occured.
     *
     *  \warning          Since we cannot count on being able to set/get the
     *                    position of large files (>2Gb), it is not possible to
     *                    recover from errors by re-reading the frame if the
     *                    storage area you provided was too small. To avoid this
     *                    from happening, we recommend that you store the number of
     *                    coordinates triplet as an integer either in a header or
     *                    just before the compressed coordinate data, so you can
     *                    read it first and allocated enough memory.
     */
    int
    xdrfile_decompress_coord_float(float *     ptr,
                                   int *       ncoord,
                                   float *     precision,
                                   XDRFILE *   xfp);




    /*! \brief Compress coordiates in a double array to XDR file
     *
     *  This routine will perform \a lossy compression on the three-dimensional
     *  coordinate data data specified and store it in the XDR file. Double will
     *  NOT give you any extra precision since the coordinates are compressed. This
     *  routine just avoids allocating a temporary array of floats.
     *
     *  The lossy part of the compression consists of multiplying each
     *  coordinate with the precision argument and then rounding to integers.
     *  We suggest a default value of 1000.0, which means you are guaranteed
     *  three decimals of accuracy. The only limitation is that scaled coordinates
     *  must still fit in an integer variable, so if the precision is 1000.0 the
     *  coordinate magnitudes must be less than +-2e6.
     *
     *  \param ptr        Pointer to coordinates to compress (length 3*ncoord)
     *  \param ncoord     Number of coordinate triplets in data
     *  \param precision  Scaling factor for lossy compression. If it is <=0, the
     *                    default value of 1000.0 is used.
     *  \param xfp        Handle to portably binary file
     *
     *  \return           Number of coordinate triplets written.
     *                    IMPORTANT: Check that this is equal to ncoord - if it is
     *                    negative, an error occured. This should not happen with
     *                    normal data, but if your coordinates are NaN or very
     *                    large (>1e6) it is not possible to use the compression.
     *
     *  \warning          The compression algorithm is not part of the XDR standard,
     *                    and very complicated, so you will need this xdrfile module
     *                    to read it later.
     */
    int
    xdrfile_compress_coord_double(double *     ptr,
                                  int          ncoord,
                                  double       precision,
                                  XDRFILE *    xfp);




    /*! \brief Decompress coordiates from XDR file to array of doubles
     *
     *  This routine will decompress three-dimensional coordinate data previously
     *  stored in an XDR file and store it in the specified array of doubles.
     *  Double will NOT give you any extra precision since the coordinates are
     *  compressed. This routine just avoids allocating a temporary array of floats.
     *
     *  The precision used during the earlier compression is read from the file
     *  and returned - you cannot adjust the accuracy at this stage.
     *
     *  \param ptr        Pointer to coordinates to compress (length>= 3*ncoord)
     *  \param ncoord     Max number of coordinate triplets to read on input, actual
     *                    number of coordinate triplets read on return. If this
     *                    is smaller than the number of coordinates in the frame an
     *                    error will occur.
     *  \param precision  The precision used in the previous compression will be
     *                    written to this variable on return.
     *  \param xfp        Handle to portably binary file
     *
     *  \return           Number of coordinate triplets read. If this is negative,
     *                    an error occured.
     *
     *  \warning          Since we cannot count on being able to set/get the
     *                    position of large files (>2Gb), it is not possible to
     *                    recover from errors by re-reading the frame if the
     *                    storage area you provided was too small. To avoid this
     *                    from happening, we recommend that you store the number of
     *                    coordinates triplet as an integer either in a header or
     *                    just before the compressed coordinate data, so you can
     *                    read it first and allocated enough memory.
     */
    int
    xdrfile_decompress_coord_double(double *     ptr,
                                    int *        ncoord,
                                    double *     precision,
                                    XDRFILE *    xfp);

    int64_t xdr_tell(XDRFILE *xd);
    int xdr_seek(XDRFILE *xd, int64_t pos, int whence);

#ifdef __cplusplus
}
#endif

#endif /* _XDRFILE_H_ */
