/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 "h5repack.h"
#include "h5tools.h"
#include "h5tools_utils.h"

/*-------------------------------------------------------------------------
 * Function: parse_filter
 *
 * Purpose: read filter information
 *
 * Return: a list of names, the number of names and its compression type
 *
 * <name of filter> can be:
 *  GZIP, to apply the HDF5 GZIP filter (GZIP compression)
 *  SZIP, to apply the HDF5 SZIP filter (SZIP compression)
 *  SHUF, to apply the HDF5 shuffle filter
 *  FLET, to apply the HDF5 checksum filter
 *  NBIT, to apply the HDF5 NBIT filter (NBIT compression)
 *  SOFF, to apply the HDF5 scale+offset filter (compression)
 *  UD, to apply a User Defined filter k, m, n1[,…,nm]
 *  NONE, to remove the filter
 *
 * Examples:
 * "GZIP=6"
 * "A,B:NONE"
 *
 * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
 *
 * Date: September, 23, 2003
 *
 *-------------------------------------------------------------------------
 */


obj_list_t* parse_filter(const char *str,
                         int *n_objs,
                         filter_info_t *filt,
                         pack_opt_t *options,
                         int *is_glb)
{
	unsigned    i, u;
	char        c;
	size_t      len=HDstrlen(str);
	int         j, m, n, k, l, p, r, q, end_obj=-1, no_param=0;
	char        sobj[MAX_NC_NAME];
	char        scomp[10];
	char        stype[5];
	char        smask[3];
	obj_list_t* obj_list=NULL;
	unsigned    pixels_per_block;


	/* initialize compression  info */
	HDmemset(filt,0,sizeof(filter_info_t));
	*is_glb = 0;

	/* check for the end of object list and number of objects */
	for ( i = 0, n = 0; i < len; i++)
	{
		c = str[i];
		if ( c==':' )
		{
			end_obj=i;
		}
		if ( c==',' )
		{
			n++;
		}
	}

	if (end_obj==-1) /* missing : */
	{
		/* apply to all objects */
		options->all_filter=1;
		*is_glb = 1;
	}

	n++;
	obj_list = (obj_list_t*) HDmalloc(n*sizeof(obj_list_t));
	if (obj_list==NULL)
	{
		error_msg("could not allocate object list\n");
		return NULL;
	}
	*n_objs=n;

	/* get object list */
	for ( j = 0, k = 0, n = 0; j < end_obj; j++, k++)
	{
		c = str[j];
		sobj[k] = c;
		if ( c==',' || j==end_obj-1)
		{
			if ( c==',') sobj[k]='\0'; else sobj[k+1]='\0';
			HDstrcpy(obj_list[n].obj,sobj);
			HDmemset(sobj,0,sizeof(sobj));
			n++;
			k=-1;
		}
	}
	/* nothing after : */
	if (end_obj+1==(int)len)
	{
		if (obj_list) HDfree(obj_list);
		error_msg("input Error: Invalid compression type in <%s>\n",str);
		HDexit(EXIT_FAILURE);
	}


	/* get filter additional parameters */
	m=0;
	for ( i=end_obj+1, k=0, j=0; i<len; i++,k++)
	{
		c = str[i];
		scomp[k]=c;
		if ( c=='=' || i==len-1)
		{
			if ( c=='=') /*one more parameter */
			{
				scomp[k]='\0';     /*cut space */

				/*-------------------------------------------------------------------------
				 * H5Z_FILTER_SZIP
				 * szip has the format SZIP=<pixels per block,coding>
				 * pixels per block is a even number in 2-32 and coding method is 'EC' or 'NN'
				 * example SZIP=8,NN
				 *-------------------------------------------------------------------------
				 */
				if (HDstrcmp(scomp,"SZIP")==0)
				{
					l=-1; /* mask index check */
					for ( m=0,u=i+1; u<len; u++,m++)
					{
						if (str[u]==',')
						{
							stype[m]='\0'; /* end digit of szip */
							l=0;  /* start EC or NN search */
							u++;  /* skip ',' */
						}
						c = str[u];
						if (!isdigit(c) && l==-1)
						{
							if (obj_list) HDfree(obj_list);
							error_msg("compression parameter not digit in <%s>\n",str);
							HDexit(EXIT_FAILURE);
						}
						if (l==-1)
							stype[m]=c;
						else
						{
							smask[l]=c;
							l++;
							if (l==2)
							{
								smask[l]='\0';
								i=len-1; /* end */
								(*n_objs)--; /* we counted an extra ',' */
								if (HDstrcmp(smask,"NN")==0)
									filt->cd_values[j++]=H5_SZIP_NN_OPTION_MASK;
								else if (HDstrcmp(smask,"EC")==0)
									filt->cd_values[j++]=H5_SZIP_EC_OPTION_MASK;
								else
								{
									error_msg("szip mask must be 'NN' or 'EC' \n");
									HDexit(EXIT_FAILURE);
								}


							}
						}

					}  /* u */
				} /*if */

				/*-------------------------------------------------------------------------
				 * H5Z_FILTER_SCALEOFFSET
				 * scaleoffset has the format SOFF=<scale_factor,scale_type>
				 * scale_type can be
				 *   integer datatype, H5Z_SO_INT (IN)
				 *   float datatype using D-scaling method, H5Z_SO_FLOAT_DSCALE  (DS)
				 *   float datatype using E-scaling method, H5Z_SO_FLOAT_ESCALE  (ES) , not yet implemented
				 * for integer datatypes, scale_factor denotes Minimum Bits
				 * for float datatypes, scale_factor denotes decimal scale factor
				 *  examples
				 *  SOFF=31,IN
				 *  SOFF=3,DF
				 *-------------------------------------------------------------------------
				 */

				else if (HDstrcmp(scomp,"SOFF")==0)
				{
					l=-1; /* mask index check */
					for ( m=0,u=i+1; u<len; u++,m++)
					{
						if (str[u]==',')
						{
							stype[m]='\0'; /* end digit */
							l=0;  /* start 'IN' , 'DS', or 'ES' search */
							u++;  /* skip ',' */
						}
						c = str[u];
						if (!isdigit(c) && l==-1)
						{
							if (obj_list) HDfree(obj_list);
							error_msg("compression parameter is not a digit in <%s>\n",str);
							HDexit(EXIT_FAILURE);
						}
						if (l==-1)
							stype[m]=c;
						else
						{
							smask[l]=c;
							l++;
							if (l==2)
							{
								smask[l]='\0';
								i=len-1; /* end */
								(*n_objs)--; /* we counted an extra ',' */
								if (HDstrcmp(smask,"IN")==0)
									filt->cd_values[j++]=H5Z_SO_INT;
								else if (HDstrcmp(smask,"DS")==H5Z_SO_FLOAT_DSCALE)
									filt->cd_values[j++]=H5Z_SO_FLOAT_DSCALE;
								else
								{
									error_msg("scale type must be 'IN' or 'DS' \n");
									HDexit(EXIT_FAILURE);
								}

							}
						}

					}  /* u */
				} /*if */
				/*-------------------------------------------------------------------------
				 * User Defined
				 *   has the format UD=<filter_number,cd_value_count,value_1[,value_2,...,value_N]>
				 *  BZIP2 example
				 *  UD=307,1,9
				 *-------------------------------------------------------------------------
				 */

				else if (HDstrcmp(scomp,"UD")==0)
				{
					l=-1; /* filter number index check */
					p=-1; /* CD_VAL count check */
					r=-1; /* CD_VAL check */
					for ( m=0,q=0,u=i+1; u<len; u++,m++,q++)
					{
						if (str[u]==',')
						{
							stype[q]='\0'; /* end digit */
							if (l==-1)
							{
								filt->filtn=atoi(stype);
								l=0;
							}
							else if (p==-1)
							{
								filt->cd_nelmts=atoi(stype);
								p=0;
							}
							else
								r=0;
							q=0;
							u++;  /* skip ',' */
						}
						c = str[u];
						if (!isdigit(c) && l==-1)
						{
							if (obj_list) HDfree(obj_list);
							error_msg("filter number parameter is not a digit in <%s>\n",str);
							HDexit(EXIT_FAILURE);
						}
						stype[q]=c;
						if (l==0 && p==0)
						{
							if (r==0)
								filt->cd_values[j++]=atoi(stype);
						}

					}  /* u */

					stype[q]='\0';
				} /*if */


				/*-------------------------------------------------------------------------
				 * all other filters
				 *-------------------------------------------------------------------------
				 */

				else
				{
					/* here we could have 1 or 2 digits  */
					for ( m=0,u=i+1; u<len; u++,m++)
					{
						c = str[u];
						if (!isdigit(c)){
							if (obj_list) HDfree(obj_list);
							error_msg("compression parameter is not a digit in <%s>\n",str);
							HDexit(EXIT_FAILURE);
						}
						stype[m]=c;
					} /* u */

					stype[m]='\0';
				} /*if */



				filt->cd_values[j++]=atoi(stype);
				i+=m; /* jump */
			}
			else if (i==len-1)
			{ /*no more parameters */
				scomp[k+1]='\0';
				no_param=1;
			}

			/*-------------------------------------------------------------------------
			 * translate from string to filter symbol
			 *-------------------------------------------------------------------------
			 */

			/*-------------------------------------------------------------------------
			 * H5Z_FILTER_NONE
			 *-------------------------------------------------------------------------
			 */
			if (HDstrcmp(scomp,"NONE")==0)
			{
				filt->filtn=H5Z_FILTER_NONE;
				filt->cd_nelmts = 0;
			}

			/*-------------------------------------------------------------------------
			 * H5Z_FILTER_DEFLATE
			 *-------------------------------------------------------------------------
			 */
			else if (HDstrcmp(scomp,"GZIP")==0)
			{
				filt->filtn=H5Z_FILTER_DEFLATE;
				filt->cd_nelmts = 1;
				if (no_param)
				{ /*no more parameters, GZIP must have parameter */
					if (obj_list) HDfree(obj_list);
					error_msg("missing compression parameter in <%s>\n",str);
					HDexit(EXIT_FAILURE);
				}
			}

			/*-------------------------------------------------------------------------
			 * H5Z_FILTER_SZIP
			 *-------------------------------------------------------------------------
			 */
			else if (HDstrcmp(scomp,"SZIP")==0)
			{
				filt->filtn=H5Z_FILTER_SZIP;
				filt->cd_nelmts = 2;
				if (no_param)
				{ /*no more parameters, SZIP must have parameter */
					if (obj_list) HDfree(obj_list);
					error_msg("missing compression parameter in <%s>\n",str);
					HDexit(EXIT_FAILURE);
				}
			}

			/*-------------------------------------------------------------------------
			 * H5Z_FILTER_SHUFFLE
			 *-------------------------------------------------------------------------
			 */
			else if (HDstrcmp(scomp,"SHUF")==0)
			{
				filt->filtn=H5Z_FILTER_SHUFFLE;
				filt->cd_nelmts = 0;
				if (m>0)
				{ /*shuffle does not have parameter */
					if (obj_list) HDfree(obj_list);
					error_msg("extra parameter in SHUF <%s>\n",str);
					HDexit(EXIT_FAILURE);
				}
			}
			/*-------------------------------------------------------------------------
			 * H5Z_FILTER_FLETCHER32
			 *-------------------------------------------------------------------------
			 */
			else if (HDstrcmp(scomp,"FLET")==0)
			{
				filt->filtn=H5Z_FILTER_FLETCHER32;
				filt->cd_nelmts = 0;
				if (m>0)
				{ /*shuffle does not have parameter */
					if (obj_list) HDfree(obj_list);
					error_msg("extra parameter in FLET <%s>\n",str);
					HDexit(EXIT_FAILURE);
				}
			}
			/*-------------------------------------------------------------------------
			 * H5Z_FILTER_NBIT
			 *-------------------------------------------------------------------------
			 */
			else if (HDstrcmp(scomp,"NBIT")==0)
			{
				filt->filtn=H5Z_FILTER_NBIT;
				filt->cd_nelmts = 0;
				if (m>0)
				{ /*nbit does not have parameter */
					if (obj_list) HDfree(obj_list);
					error_msg("extra parameter in NBIT <%s>\n",str);
					HDexit(EXIT_FAILURE);
				}
			}
			/*-------------------------------------------------------------------------
			 * H5Z_FILTER_SCALEOFFSET
			 *-------------------------------------------------------------------------
			 */
			else if (HDstrcmp(scomp,"SOFF")==0)
			{
				filt->filtn=H5Z_FILTER_SCALEOFFSET;
				filt->cd_nelmts = 2;
				if (no_param)
				{ /*no more parameters, SOFF must have parameter */
					if (obj_list) HDfree(obj_list);
					error_msg("missing compression parameter in <%s>\n",str);
					HDexit(EXIT_FAILURE);
				}
			}
			/*-------------------------------------------------------------------------
			 * User Defined Filter
			 *-------------------------------------------------------------------------
			 */
			else if (HDstrcmp(scomp,"UD")==0)
			{
				if (filt->cd_nelmts != j)
				{ /* parameters does not match count */
					if (obj_list) HDfree(obj_list);
					error_msg("incorrect number of compression parameters in <%s>\n",str);
					HDexit(EXIT_FAILURE);
				}
			}
			else {
				if (obj_list) HDfree(obj_list);
				error_msg("invalid filter type in <%s>\n",str);
				HDexit(EXIT_FAILURE);
			}
		}
	} /*i*/

	/*-------------------------------------------------------------------------
	 * check valid parameters
	 *-------------------------------------------------------------------------
	 */

	switch (filt->filtn)
	{

	/*-------------------------------------------------------------------------
	 * H5Z_FILTER_DEFLATE
	 *-------------------------------------------------------------------------
	 */

	case H5Z_FILTER_DEFLATE:
		if (filt->cd_values[0]>9 )
		{
			if (obj_list) HDfree(obj_list);
			error_msg("invalid compression parameter in <%s>\n",str);
			HDexit(EXIT_FAILURE);
		}
		break;

		/*-------------------------------------------------------------------------
		 * H5Z_FILTER_SZIP
		 *-------------------------------------------------------------------------
		 */

	case H5Z_FILTER_SZIP:
		pixels_per_block=filt->cd_values[0];
		if ((pixels_per_block%2)==1)
		{
			if (obj_list) HDfree(obj_list);
			error_msg("pixels_per_block is not even in <%s>\n",str);
			HDexit(EXIT_FAILURE);
		}
		if (pixels_per_block>H5_SZIP_MAX_PIXELS_PER_BLOCK)
		{
			if (obj_list) HDfree(obj_list);
			error_msg("pixels_per_block is too large in <%s>\n",str);
			HDexit(EXIT_FAILURE);
		}
		if ( (HDstrcmp(smask,"NN")!=0) && (HDstrcmp(smask,"EC")!=0) )
		{
			if (obj_list) HDfree(obj_list);
			error_msg("szip mask must be 'NN' or 'EC' \n");
			HDexit(EXIT_FAILURE);
		}
		break;
	default:
		break;


	};

	return obj_list;
}



/*-------------------------------------------------------------------------
 * Function: parse_layout
 *
 * Purpose: read layout info
 *
 * Return: a list of names, the number of names and its chunking info for
 *  chunked. NULL, on error
 * the layout type can be:
 *  CHUNK, to apply chunking layout
 *  CONTI, to apply contiguous layout
 *  COMPA, to apply compact layout
 *
 * Example:
 * "AA,B,CDE:CHUNK=10X10"
 *
 * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
 *
 * Date: December 30, 2003
 *
 *-------------------------------------------------------------------------
 */
obj_list_t* parse_layout(const char *str,
                         int *n_objs,
                         pack_info_t *pack,    /* info about layout needed */
                         pack_opt_t *options)
{
    obj_list_t* obj_list=NULL;
    unsigned    i;
    char        c;
    size_t      len=HDstrlen(str);
    int         j, n, k, end_obj=-1, c_index;
    char        sobj[MAX_NC_NAME];
    char        sdim[10];
    char        slayout[10];


    HDmemset(sdim, '\0', sizeof(sdim));
    HDmemset(sobj, '\0', sizeof(sobj));
    HDmemset(slayout, '\0', sizeof(slayout));

    /* check for the end of object list and number of objects */
    for ( i=0, n=0; i<len; i++)
    {
        c = str[i];
        if ( c==':' )
        {
            end_obj=i;
        }
        if ( c==',' )
        {
            n++;
        }
    }

    if (end_obj==-1) { /* missing : chunk all */
        options->all_layout=1;
    }

    n++;
    obj_list = (obj_list_t*) HDmalloc(n*sizeof(obj_list_t));
    if (obj_list==NULL)
    {
        error_msg("could not allocate object list\n");
        return NULL;
    }
    *n_objs=n;

    /* get object list */
    for ( j=0, k=0, n=0; j<end_obj; j++,k++)
    {
        c = str[j];
        sobj[k]=c;
        if ( c==',' || j==end_obj-1)
        {
            if ( c==',') sobj[k]='\0'; else sobj[k+1]='\0';
            HDstrcpy(obj_list[n].obj,sobj);
            HDmemset(sobj,0,sizeof(sobj));
            n++;
            k=-1;
        }
    }

    /* nothing after : */
    if (end_obj+1==(int)len)
    {
        if (obj_list) HDfree(obj_list);
        error_msg("in parse layout, no characters after : in <%s>\n",str);
        HDexit(EXIT_FAILURE);
    }

    /* get layout info */
    for ( j=end_obj+1, n=0; n<=5; j++,n++)
    {
        if (n==5)
        {
            slayout[n]='\0';  /*cut string */
            if (HDstrcmp(slayout,"COMPA")==0)
                pack->layout=H5D_COMPACT;
            else if (HDstrcmp(slayout,"CONTI")==0)
                pack->layout=H5D_CONTIGUOUS;
            else if (HDstrcmp(slayout,"CHUNK")==0)
                pack->layout=H5D_CHUNKED;
            else {
                error_msg("in parse layout, not a valid layout in <%s>\n",str);
                HDexit(EXIT_FAILURE);
            }
        }
        else
        {
            c = str[j];
            slayout[n]=c;
        }
    } /* j */


    if ( pack->layout==H5D_CHUNKED )
    {

    /*-------------------------------------------------------------------------
    * get chunk info
    *-------------------------------------------------------------------------
        */
        k=0;

        if (j>(int)len)
        {
            if (obj_list) HDfree(obj_list);
            error_msg("in parse layout,  <%s> Chunk dimensions missing\n",str);
            HDexit(EXIT_FAILURE);
        }

        for ( i=j, c_index=0; i<len; i++)
        {
            c = str[i];
            sdim[k]=c;
            k++; /*increment sdim index */

            if (!isdigit(c) && c!='x'
                && c!='N' && c!='O' && c!='N' && c!='E'
                ){
                if (obj_list) HDfree(obj_list);
                error_msg("in parse layout, <%s> Not a valid character in <%s>\n",
                    sdim,str);
                HDexit(EXIT_FAILURE);
            }

            if ( c=='x' || i==len-1)
            {
                if ( c=='x') {
                    sdim[k-1]='\0';
                    k=0;
                    pack->chunk.chunk_lengths[c_index]=atoi(sdim);
                    if (pack->chunk.chunk_lengths[c_index]==0) {
                        if (obj_list) HDfree(obj_list);
                        error_msg("in parse layout, <%s> conversion to number in <%s>\n",
                            sdim,str);
                        HDexit(EXIT_FAILURE);
                    }
                    c_index++;
                }
                else if (i==len-1) { /*no more parameters */
                    sdim[k]='\0';
                    k=0;
                    if (HDstrcmp(sdim,"NONE")==0)
                    {
                        pack->chunk.rank=-2;
                    }
                    else
                    {
                        pack->chunk.chunk_lengths[c_index]=atoi(sdim);
                        if (pack->chunk.chunk_lengths[c_index]==0){
                            if (obj_list) HDfree(obj_list);
                            error_msg("in parse layout, <%s> conversion to number in <%s>\n",
                                sdim,str);
                            HDexit(EXIT_FAILURE);
                        }
                        pack->chunk.rank=c_index+1;
                    }
                } /*if */
            } /*if c=='x' || i==len-1 */
        } /*i*/


    } /*H5D_CHUNKED*/


    return obj_list;
}






