/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright 1993, University Corporation for Atmospheric Research           *
 * See netcdf/COPYRIGHT file for copying and redistribution conditions.      *
 *                                                                           *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF.  The full HDF copyright notice, including       *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 *
 *	This file supports netCDF variable I/O for generalized hyperslabs.
 *	A generalized hyperslab is one in which the locations of the
 *	memory-resident data values may be arbitrary, though they are
 *	constrained to have a regular structure.  In addition, the values
 *	of the netCDF variable may be accessed using non-unity strides.
 *
 */

#include "nc_priv.h"

/*
 * Perform I/O on a generalized hyperslab.  The efficiency of this
 * implementation is dependent upon caching in the lower layers.
 */
/* start  - NULL => first corner */
/* count  - NULL => everything following start[] */
/* stride - NULL => unity strides */
/* imap   - NULL => same structure as netCDF variable */
int
NCgenio(NC *handle, int varid, const long *start, const long *count, const long *stride, const long *imap,
        void *values)
{
    int     maxidim; /* maximum dimensional index */
    NC_var *vp = NC_hlookupvar(handle, varid);

    if (vp == NULL)
        return -1;

    maxidim = vp->assoc->count - 1;

    if (maxidim < 0) {
        /*
         * The variable is a scalar; consequently, there's only one thing
         * to get and only one place to put it.  (Why was I called?)
         */
        return NCvario(handle, varid, start, count, values);
    }
    else {
        /*
         * The variable is an array.
         */
        int   idim;
        char *valp = values;
        long  mycount[H4_MAX_VAR_DIMS];
        long  mystart[H4_MAX_VAR_DIMS];
        long  mystride[H4_MAX_VAR_DIMS];
        long  myimap[H4_MAX_VAR_DIMS];
        long  iocount[H4_MAX_VAR_DIMS]; /* count vector for NCvario() */
        long  stop[H4_MAX_VAR_DIMS];    /* stop indexes */
        long  length[H4_MAX_VAR_DIMS];  /* edge lengths in bytes */

        /*
         * Verify stride argument.
         */
        for (idim = 0; idim <= maxidim; ++idim) {
            if (stride != NULL && stride[idim] < 1) {
                NCadvise(NC_EINVAL, "Non-positive stride");
                return -1;
            }
        }

        /*
         * Initialize I/O parameters.
         */
        for (idim = maxidim; idim >= 0; --idim) {
            mystart[idim]  = start != NULL ? start[idim] : 0;
            mycount[idim]  = count != NULL                ? count[idim]
                             : idim == 0 && IS_RECVAR(vp) ? handle->numrecs - mystart[idim]
                                                          : vp->shape[idim] - mystart[idim];
            mystride[idim] = stride != NULL ? stride[idim] : 1;
            myimap[idim]   = imap != NULL      ? imap[idim]
                             : idim == maxidim ? vp->szof
                                               : myimap[idim + 1] * mycount[idim + 1];

            iocount[idim] = 1;
            length[idim]  = myimap[idim] * mycount[idim];
            stop[idim]    = mystart[idim] + mycount[idim] * mystride[idim];
        }

        /*
         * As an optimization, adjust I/O parameters when the fastest
         * dimension has unity stride both externally and internally.
         * In this case, the user could have called a simpler routine
         * (i.e. ncvarget() or ncvarput()).
         */
        if (mystride[maxidim] == 1 && myimap[maxidim] == vp->szof) {
            iocount[maxidim]  = mycount[maxidim];
            mystride[maxidim] = mycount[maxidim];
            myimap[maxidim]   = length[maxidim];
        }

        /*
         * Perform I/O.  Exit when done.
         */
        for (;;) {
            int iostat = NCvario(handle, varid, mystart, iocount, (uint8_t *)valp);

            if (iostat != 0)
                return iostat;

            /*
             * The following code permutes through the variable's external
             * start-index space and it's internal address space.  At the
             * UPC, this algorithm is commonly called `odometer code'.
             */
            idim = maxidim;
carry:
            valp += myimap[idim];
            mystart[idim] += mystride[idim];
            if (mystart[idim] >= stop[idim]) {
                mystart[idim] = start[idim];
                valp -= length[idim];
                if (--idim < 0)
                    return 0;
                goto carry;
            }
        } /* I/O loop */
    }     /* variable is array */
}

/*
 * Generalized hyperslab output.
 */
int
ncvarputg(int cdfid, int varid, const long *start, const long *count, const long *stride, const long *imap,
          void *values)
{
    NC *handle;

    cdf_routine_name = "ncvarputg";

    handle = NC_check_id(cdfid);
    if (handle == NULL)
        return -1;

    if (!(handle->flags & NC_RDWR)) {
        NCadvise(NC_EPERM, "%s: NC_NOWRITE", handle->path);
        return -1;
    }
    handle->xdrs->x_op = XDR_ENCODE;

    return NCgenio(handle, varid, start, count, stride, imap, values);
}

/*
 * Generalized hyperslab input.
 */
int
ncvargetg(int cdfid, int varid, const long *start, const long *count, const long *stride, const long *imap,
          void *values)
{
    NC *handle;

    cdf_routine_name = "ncvargetg";

    handle = NC_check_id(cdfid);
    if (handle == NULL)
        return -1;

    handle->xdrs->x_op = XDR_DECODE;

    return NCgenio(handle, varid, start, count, stride, imap, (uint8_t *)values);
}

/*
 * Stride-oriented hyperslab output.
 */
int
ncvarputs(int cdfid, int varid, const long *start, const long *count, const long *stride, void *values)
{
    NC *handle;

    cdf_routine_name = "ncvarputs";

    handle = NC_check_id(cdfid);
    if (handle == NULL)
        return -1;

    if (!(handle->flags & NC_RDWR)) {
        NCadvise(NC_EPERM, "%s: NC_NOWRITE", handle->path);
        return -1;
    }
    handle->xdrs->x_op = XDR_ENCODE;

    return NCgenio(handle, varid, start, count, stride, NULL, values);
}

/*
 * Stride-oriented hyperslab input.
 */
int
ncvargets(int cdfid, int varid, const long *start, const long *count, const long *stride, void *values)
{
    NC *handle;

    cdf_routine_name = "ncvargets";

    handle = NC_check_id(cdfid);
    if (handle == NULL)
        return -1;

    handle->xdrs->x_op = XDR_DECODE;

    return NCgenio(handle, varid, start, count, stride, (long *)0, (uint8_t *)values);
}
