 /***************************************************************************
 KFReader.c - Implementation of KFReader library for handling ADF binary file
              format, the so-called KF-files. The current version supports only 
              reading KF files. 
              Even though the binary files are system-dependent, they are 
              converted on the fly and the caller routine gets the data in the
              native format.

 Copyright (C) 2006-2023 by Software for Chemistry & Materials B.V.
 For support, contact support at scm . com
   
 This file is part of the ADF software
 For more information, see <http://www.scm.com>
   
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation version 3 of the License.
   
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 SCM owns the intellectual property right for this file and reserves the 
 right to distrbute it under a license other than LGPL
 ****************************************************************************/

/***************************************************************************
This file, originally written by another author (contact: Alexei Yakovlev, 
SCM) under the GNU Lesser General Public License (LGPL), has been modified 
in September 2023.
****************************************************************************/

 
#include <ADF_KFReader.h>

#include <stdlib.h>
//JC only for Unix system
/*#ifdef _MSC_VER
#include <io.h>
#else*/
#include <unistd.h>
//#endif
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

// JC static suppress
void ADF_getVariableFromName(const char *complexName, char *varName){
   const char *percent = strchr(complexName, '%');
   if (percent != NULL)
      strncpy(varName, percent+1, KF_SECTION_NAME_LENGTH);
   else
      strncpy(varName, complexName, KF_SECTION_NAME_LENGTH);
   
}

// JC static suppress
void ADF_getSectionFromName(const char *complexName, char *secName){

   const char *percent = strchr(complexName, '%');
   if (percent != NULL){
      int len = percent-complexName;
      strncpy(secName, complexName, len);
      secName[len] = '\0';
   }
   else {
      *secName = '\0';
   }
}


// JC static suppress
int ADF_sectionsComparator(const void *secName, const void *sec){
   int i;
   char str[KF_SECTION_NAME_LENGTH+1];
   strncpy (str, (const char*)secName, KF_SECTION_NAME_LENGTH);
   for (i = strlen((const char*)secName); i < KF_SECTION_NAME_LENGTH; i++)
   //for (i = strlen(str); i < KF_SECTION_NAME_LENGTH; i++)
      str[i] = ' ';
   str[KF_SECTION_NAME_LENGTH] = '\0';
   //printf("%s\n",str);
   return strncmp(str, ((KFSection *)sec)->name, KF_SECTION_NAME_LENGTH);
}


//JC static suppress
KFSection* ADF_findSection(KFFile *kf, const char *name){
   return (KFSection *) findArrayListElement(&(kf->sections),name, ADF_sectionsComparator);
}

// JC static suppress
int ADF_variablesNamesComparator(const void *varName, const void *var){
   int i;
   char str[KF_SECTION_NAME_LENGTH+1];
   strncpy (str, (const char*)varName, KF_SECTION_NAME_LENGTH);
   //for (i = strlen(str); i < KF_SECTION_NAME_LENGTH; i++)
   for (i = strlen((const char*)varName); i < KF_SECTION_NAME_LENGTH; i++)
      str[i] = ' ';
   str[KF_SECTION_NAME_LENGTH] = '\0';
   return strncmp(str, ((KFVariable *)var)->name, KF_SECTION_NAME_LENGTH);
}

// JC static suppress
KFVariable* ADF_findVariableInSection(const KFSection *sec, const char *name){
   return (KFVariable *)findArrayListElement (&(sec->variables), (void *)name, ADF_variablesNamesComparator);
}

// JC static suppress
KFVariable* ADF_findVariable(KFFile *kf, const char *name){
   char secName[KF_SECTION_NAME_LENGTH+1];
   char varName[KF_SECTION_NAME_LENGTH+1];
   KFSection *sec;
   KFVariable *var;

   if (strchr(name, '%') == NULL){
      fprintf (stderr, "Incomplete variable specification \"%s\" in findVariable()\n", name);
      return NULL;
   }
   ADF_getSectionFromName(name, secName);
   ADF_getVariableFromName(name, varName);
   sec = ADF_findSection (kf, secName);
   if (sec == NULL)
      return NULL;
   var = ADF_findVariableInSection(sec, varName);
   return var;
}


/* returns variable length in units of the corresponding type*/
int ADF_getKFVariableLength(KFFile *kf, const char *name){
   KFVariable *var = ADF_findVariable(kf, name);
   if (var != NULL)
      return var->length;
   else
      return -1;
}

// JC static suppress
void ADF_addDataBlockRun (KFSection *sec, int phBlk, int logBlk, int numBlks){
   int insIndex = sec->dataBlockRuns.length;
   int i, iLogBlk;
   KFBlockRun *br;
   for (i = 0; i < sec->dataBlockRuns.length; i++) {
      br = (KFBlockRun *)getArrayListElement(&(sec->dataBlockRuns),i);
      iLogBlk = br->logBlock;
      if (logBlk < iLogBlk) {
         insIndex = i;
      }
   }
   br = ADF_createKFBlockRun(phBlk, logBlk, numBlks);
   insertArrayListElement(&(sec->dataBlockRuns), br, insIndex);
   sec->totalDataBlocks += numBlks;
}

// JC static suppress
void ADF_getDataBlockFromSuperEntry(KFFile *kf, void *buf){
   char name[KF_SECTION_NAME_LENGTH+1];
   int phBlk, logBlk, numBlks, type;
   KFSuperIndexBlockEntry32 *ent32 = (KFSuperIndexBlockEntry32*)buf;
   KFSuperIndexBlockEntry64 *ent64 = (KFSuperIndexBlockEntry64*)buf;
   KFSection *section;

   //JC for avoid maybe-uninitialized warning
   phBlk=logBlk=numBlks=type=-1;
   
   strncpy (name, (const char*)buf, KF_SECTION_NAME_LENGTH);
   name[KF_SECTION_NAME_LENGTH] = '\0';
   if (kf->integerSize == 4){
      phBlk = (int)ent32->physBlk;
      logBlk = (int)ent32->logicBlk;
      numBlks = (int)ent32->numBlks;
      type = (int)ent32->type;
   }
   else if (kf->integerSize == 8){
      phBlk = (int)ent64->physBlk;
      logBlk = (int)ent64->logicBlk;
      numBlks = (int)ent64->numBlks;
      type = (int)ent64->type;
   }
   switch (type){
      case KF_BT_EMPTY:
      case KF_BT_FREE:
      case KF_BT_SUPERINDEX:
      case KF_BT_INDEX:
         break;
      case KF_BT_DATA:
	/* JC UPDATE---------------------*/
	section = (KFSection*)ADF_findSection(kf, name);
	/*-------------------------------*/
         if (section != NULL) {
            ADF_addDataBlockRun(section, phBlk, logBlk, numBlks);
         }
         else {
            fprintf (stderr, "Could not find section %s: data block found before index block\n", name);
         }
         break;
      default:
         fprintf (stderr, "Unknown block type flag: %d\n", type);
         break;
   }
}

// JC static suppress
void ADF_parseIndexEntry(KFSection *sec, KFFile *kf, void *buf){
  /* Data in the index block must already have correct byte order */
  /* Here buf is a pointer to an entry in the index block */
  KFIndexBlockEntry32 *ent32 = (KFIndexBlockEntry32*)buf;
  KFIndexBlockEntry64 *ent64 = (KFIndexBlockEntry64*)buf;
  int firstLogBlk, firstBlkIndex, length, firstBlkLen, usedLen, type;
  /* Check that the variable does not already exist */
  KFVariable *var = ADF_findVariableInSection(sec, (const char*)buf); 

  //JC for avoid maybe-uninitialized warning
  firstLogBlk=firstBlkIndex=length=firstBlkLen=usedLen=type=-1;
  
   if (kf->integerSize == 4) {
      firstLogBlk = (int)ent32->firstLBlk;
      firstBlkIndex = (int)ent32->firstBlkIndx;
      length = (int)ent32->length;
      firstBlkLen = (int)ent32->firstBlkLen;
      usedLen = (int)ent32->usedLen;
      type = (int)ent32->type;
   }
   else if (kf->integerSize == 8) {
      firstLogBlk = (int)ent64->firstLBlk;
      firstBlkIndex = (int)ent64->firstBlkIndx;
      length = (int)ent64->length;
      firstBlkLen = (int)ent64->firstBlkLen;
      usedLen = (int)ent64->usedLen;
      type = (int)ent64->type;
   }
   if (var == NULL) {
     var = ADF_createKFVariable(sec, (const char*)buf, firstLogBlk, firstBlkIndex, length, firstBlkLen, usedLen, type);
      addArrayListElement(&(sec->variables), var);
   }
}

// JC static suppress
void ADF_parseIndexBlock(KFSection *sec, KFFile *kf, void *buf){
   if (strncmp(sec->name, (char *)buf, KF_SECTION_NAME_LENGTH) == 0){
      int off;
      for (off = kf->indexHeaderLength; off <= KF_BLOCKLENGTH - kf->indexEntryLength; off += kf->indexEntryLength) {
         if (strncmp((char*)buf+off, KF_EMPTY_SIG, KF_SECTION_NAME_LENGTH) != 0)
            ADF_parseIndexEntry(sec, kf, (char *)buf+off);
      }
   }
}


/* No specific system
#ifdef __SUNPRO_C
static void swapBytes(char *d, int size){
#else*/
// JC static and inline suppress <=> performance to evaluate
//static void __inline swapBytes(char *d, int size){
void ADF_swapBytes(char *d, int size){
//#endif

   int i,j, halfSize = size>>1;
   for (i = 0, j = size - 1; i < halfSize ; i++, j--){
      char b = *(d+i);
      *(d+i) = *(d+j);
      *(d+j) = b;
   }
}

// JC static suppress
void ADF_swapNBytes(char *p, int size, int count){
   int cnt;
   for (cnt = 0; cnt < count; cnt++){
      ADF_swapBytes(p+cnt*size, size);
   }
}


// JC static suppress
void ADF_swapBytesIndexBlock(KFFile *kf, char *buf){
   int size = kf->integerSize;
   int headerSize = kf->indexHeaderLength;
   int entrySize = kf->indexEntryLength;
   char *p;

   /* swap bytes in the header */
   ADF_swapNBytes (buf+KF_SECTION_NAME_LENGTH, size, 3+KF_N_DATATYPES);
   /* swap bytes in the superindex entries */
   for (p = buf+headerSize; p <= buf+(KF_BLOCKLENGTH-entrySize); p+=entrySize)
      ADF_swapNBytes (p+KF_SECTION_NAME_LENGTH, size, 6);
}

// JC static suppress
void ADF_addIndexBlockRun (KFSection *sec, int phBlk, int logBlk, int numBlks){
   int insIndex = sec->indexBlockRuns.length;
   int i;
   KFBlockRun *br;
   for (i = 0; i < sec->indexBlockRuns.length; i++) {
      int iLogBlk = ((KFBlockRun *)getArrayListElement(&(sec->indexBlockRuns),i))->logBlock;
      if (logBlk < iLogBlk) {
         insIndex = i;
      }
   }
   br = ADF_createKFBlockRun(phBlk, logBlk, numBlks);
   insertArrayListElement(&(sec->indexBlockRuns), br, insIndex);
   sec->totalIndexBlocks += numBlks;
}

// JC static suppres
void ADF_getSectionFromSuperEntry(KFFile *kf, void *buf){
   char name[KF_SECTION_NAME_LENGTH+1];
   int phBlk, logBlk, numBlks, type;
   int iblk;
   KFSuperIndexBlockEntry32 *ent32 = (KFSuperIndexBlockEntry32*)buf;
   KFSuperIndexBlockEntry64 *ent64 = (KFSuperIndexBlockEntry64*)buf;
   char buf2[KF_BLOCKLENGTH];
   KFSection *section;

   // JC avoiding maybe-uninitialize warning
   phBlk=logBlk=numBlks=type=iblk=-1;
   
   strncpy (name, (const char*)buf, KF_SECTION_NAME_LENGTH);
   name[KF_SECTION_NAME_LENGTH] = '\0';
   if (kf->integerSize == 4){
      phBlk = (int)ent32->physBlk;
      logBlk = (int)ent32->logicBlk;
      numBlks = (int)ent32->numBlks;
      type = (int)ent32->type;
   }
   else if (kf->integerSize == 8){
      phBlk = (int)ent64->physBlk;
      logBlk = (int)ent64->logicBlk;
      numBlks = (int)ent64->numBlks;
      type = (int)ent64->type;
   }
   switch (type){
      case KF_BT_EMPTY:
      case KF_BT_FREE:
      case KF_BT_SUPERINDEX:
      case KF_BT_DATA:
         break;
      case KF_BT_INDEX:
         /* The entry points to an section index block run
            Add the block run to the section. */
         if (logBlk == 1) {
            /* Create a new section since it's a first index block run */
            section = ADF_createKFSection(kf, name, phBlk, logBlk, numBlks);
            addArrayListElement(&(kf->sections), section);
         }
         else {
            /* Add a new index block run to an existing section */
            section = ADF_findSection(kf, name);
            ADF_addIndexBlockRun(section, phBlk, logBlk, numBlks);
         }

         /* Now we want to read and parse the index block run
             so it will be cached in memory */
         for (iblk = 0; iblk < numBlks; iblk++) {
            int curPhysBlk = phBlk + iblk;
            ADF_readBlock(kf->fd, buf2, curPhysBlk);
            if (kf->byteOrder != ADF_getHostByteOrder())
               ADF_swapBytesIndexBlock(kf, buf2);
            ADF_parseIndexBlock(section, kf, buf2);
         }
         break;
      default:
         fprintf (stderr, "Unknown block type flag: %d\n", type);
         break;
   }
}


// JC static suppress
void ADF_swapBytesSuperIndexBlock(KFFile *kf, char *buf){
   int size = kf->integerSize;
   int headerSize = kf->superIndexHeaderLength;
   int entrySize = kf->superIndexEntryLength;
   char *p;

   /* swap bytes in the header */
   ADF_swapNBytes (buf+KF_SIG_LENGTH, size, 4);
   /* swap bytes in the superindex entries */
   for (p = buf+headerSize; p <= buf+(KF_BLOCKLENGTH-entrySize); p+=entrySize)
      ADF_swapNBytes (p+KF_SECTION_NAME_LENGTH, size, 4);
}

// JC static suppress
void ADF_initialize(KFFile *kf, void *buf){
   kf->integerSize = ADF_guessIntegerSize(buf);
   kf->typeSize[KF_T_INTEGER] = kf->typeSize[KF_T_LOGICAL] = kf->integerSize;
   kf->typeSize[KF_T_DOUBLE] = 8;
   kf->typeSize[KF_T_STRING] = 1;
   if (kf->integerSize == 4){
      kf->indexHeaderLength = sizeof(KFIndexBlockHeader32);
      kf->indexEntryLength = sizeof(KFIndexBlockEntry32);
      kf->superIndexHeaderLength = sizeof(KFSuperIndexBlockHeader32);
      kf->superIndexEntryLength = sizeof(KFSuperIndexBlockEntry32);
   }
   else if (kf->integerSize == 8){
      kf->indexHeaderLength = sizeof(KFIndexBlockHeader64);
      kf->indexEntryLength = sizeof(KFIndexBlockEntry64);
      kf->superIndexHeaderLength = sizeof(KFSuperIndexBlockHeader64);
      kf->superIndexEntryLength = sizeof(KFSuperIndexBlockEntry64);
   }
   kf->byteOrder = ADF_guessByteOrder(buf, kf->integerSize);

}



/* --------------------------- *
 * Function definitions follow *
 * --------------------------- */

int ADF_openKFFile(KFFile *kf, const char *name){
   char   buf[KF_BLOCKLENGTH];
   KFSuperIndexBlock32 *buf32 = (KFSuperIndexBlock32 *)buf;
   KFSuperIndexBlock64 *buf64 = (KFSuperIndexBlock64 *)buf;
   int off;

   int lastSBlock = 0;
   int firstSBlock = 1;
   int recordNum = 1;

   memset(kf, 0, sizeof(KFFile));
   kf->name=(char*)malloc(strlen(name)+1);
   strcpy(kf->name, name);
   /* Only Unix version
      #ifdef _MSC_VER
      kf->fd = open(kf->name, O_BINARY|O_RDONLY);*/
   //#else
   kf->fd = open(kf->name, O_RDONLY);
   //#endif
   if (kf->fd == -1){
      perror(kf->name);
      return -1;
   }
   kf->isOpen = 1;

   /* First pass to collect and parse only section header blocks */
   do {

      if (ADF_readBlock(kf->fd,(void *)buf,recordNum) != KF_BLOCKLENGTH) return -1; 
   
      if (firstSBlock) {
         if (ADF_verifySuperIndex(buf)) {
            /* Figure out basic file characteristics - byte order and int size. */
            ADF_initialize(kf, buf);
            firstSBlock = 0;
         }
         else {
            close (kf->fd);
            return -1;
         }
      }
      if (kf->byteOrder != ADF_getHostByteOrder())
         ADF_swapBytesSuperIndexBlock(kf, buf);
      if (kf->integerSize == 4)
         recordNum = (int)buf32->header.nextPhys;
      else if (kf->integerSize == 8)
         recordNum = (int)buf64->header.nextPhys;
      if (recordNum == 1){
         lastSBlock = 1;
      }
      for (off = kf->superIndexHeaderLength; off <= KF_BLOCKLENGTH - kf->superIndexEntryLength; \
                     off += kf->superIndexEntryLength) {
         ADF_getSectionFromSuperEntry(kf, (char*)buf+off);
      }
      
   } while (! lastSBlock);

   /* Second pass to collect data blocks for all sections */ 
   lastSBlock = 0;
   firstSBlock = 1;
   recordNum = 1;

   do {

      if (ADF_readBlock(kf->fd,(void *)buf,recordNum) != KF_BLOCKLENGTH) return -1; 
   
      if (kf->byteOrder != ADF_getHostByteOrder())
         ADF_swapBytesSuperIndexBlock(kf, buf);
      if (kf->integerSize == 4)
         recordNum = (int)buf32->header.nextPhys;
      else if (kf->integerSize == 8)
         recordNum = (int)buf64->header.nextPhys;
      if (recordNum == 1){
         lastSBlock = 1;
      }
      for (off = kf->superIndexHeaderLength; off <= KF_BLOCKLENGTH - kf->superIndexEntryLength; \
                     off += kf->superIndexEntryLength) {
         ADF_getDataBlockFromSuperEntry(kf, (char*)buf+off);
      }
      
   } while (! lastSBlock);

   return kf->fd;
}

void ADF_closeKFFile (KFFile *kf){
   int i;
   for( i=0; i < kf->sections.length; i++){
     ADF_deleteKFSection((KFSection*)getArrayListElement(&(kf->sections),i));
   }
   clearArrayList(&(kf->sections));
   if (kf->fd >= 0)
      close(kf->fd);
   kf->isOpen = 0;
   free(kf->name);
}

/* returns the number of used elements in the variable */
int ADF_getKFVariableUsedLength(KFFile *kf, const char *name){
   KFVariable *var = ADF_findVariable(kf, name);
   if (var != NULL)
      return var->usedLen;
   else
      return -1;
}

/* The following function is a wrapper around getKFVariableData()
   kf - pointer to open KF file
   name - variable name in the form section%variable
   buf - pointer to the receiving buffer that must be large enough to hold all data

   returns the number of bytes read or -1 if an error has occured
*/

int ADF_getKFData(KFFile *kf, const char *name, void *buf){
   KFVariable *var = ADF_findVariable(kf, name);
   if (var != NULL)
      return ADF_getKFVariableData(var, buf);
   else
      return -1;
}

/* 

   returns variable type (one of the T_* macros) or 0 (zero) 

*/

int ADF_getKFVariableType(KFFile *kf, const char *name){
   KFVariable *var = ADF_findVariable(kf, name);
   if (var != NULL)
      return var->type;
   else
      return 0;
}

/* 
   the memory space pointed to by buf must be
   large enough to hold all data.
   IMPORTANT: the data is converted to native for this platform types

   return the number of bytes read
*/
int ADF_getKFVariableData(KFVariable *var, void *buf){
   char blk[KF_BLOCKLENGTH];
   int retValue = 0;
   KFFile *kf = var->section->file;
   int needInversion = (ADF_getHostByteOrder() != kf->byteOrder);
   int off, size, i, 
      logicalBlk = var->firstLogBlk,
      firstBlk = 1,
      done = 0,
      targetIndex = 0;

   while (!done) {
      int physBlock = ADF_getPhysicalBlockNumberForLogical(var, logicalBlk);
      if (ADF_readBlock(kf->fd, blk, physBlock) == KF_BLOCKLENGTH) {
         if (needInversion)
            ADF_swapNBytes(blk, kf->integerSize, KF_N_DATATYPES);
           /* where in the data block the data for this variable starts*/
           off = ADF_calculateDataOffset(var, blk, firstBlk); 
           size = ADF_calculateDataSize(var, blk, firstBlk);  
           if (size > var->usedLen - targetIndex)
               size = var->usedLen - targetIndex;
           /* size = number of elements of this variable in this block*/
         if (needInversion && var->type != KF_T_STRING) 
            ADF_swapNBytes(blk+off, kf->typeSize[var->type], size);
         switch (var->type){
            case KF_T_INTEGER:
            case KF_T_LOGICAL:
               if (kf->integerSize == 4){
                  INT32 *base = (INT32 *)(blk+off);
                  for (i = 0; i < size; i++) {
                     ((int*)buf)[targetIndex] = (int)base[i];
                     targetIndex++;
                  }
               }
               else if (kf->integerSize == 8){
                  INT64 *base = (INT64 *)(blk+off);
                  for (i = 0; i < size; i++) {
                     ((int*)buf)[targetIndex] = (int)base[i];
                     targetIndex++;
                  }
               }
               break;
            case KF_T_DOUBLE:
               memcpy((double*)buf+targetIndex, blk+off, size*sizeof(double));
               targetIndex += size;
               break;
            case KF_T_STRING:
               memcpy((char*)buf+targetIndex, blk+off, size);
               targetIndex += size;
               break;
         }
         if (firstBlk) {
            firstBlk = 0;
         }
         if (targetIndex > var->usedLen - 1) {
            done = 1;
            retValue = targetIndex;
         }
         logicalBlk++;
      }
      else {
         fprintf (stderr, "Error reading %s: incomplete block read. Corrupted file?\n",kf->name);
         retValue = -1;
         done = 1;
      }
   }
   return retValue;
}

int ADF_getHostByteOrder(){
   int i = 1;
   char *p = (char *)&i;
   if (*p == 0x01)
      return KF_LITTLE_ENDIAN;
   else
      return KF_BIG_ENDIAN;
}

/* Return 1 if the file has a valid superblock. */
int ADF_isValidKFFile(const char* fname) {

   int n;
   char blk[KF_BLOCKLENGTH];
   /* Only Unix version
#ifdef _MSC_VER
   int fd = open(fname, O_BINARY|O_RDONLY);
   #else*/
   int fd = open(fname, O_RDONLY);
   //#endif

    
   if (fd < 0) return 0;

   n = ADF_readBlock(fd, blk, 1);

   if (n >=2 && (unsigned char)blk[0] == 0x1f && (unsigned char)blk[1] == 0x8b) return 1; /* gzip signature */

   if (n != KF_BLOCKLENGTH) return 0;
   
   n = ADF_verifySuperIndex(blk);
   close(fd);
   
   return n;
}


/***********************
 *  Static functions:  *
 ***********************/

// JC static suppress
int ADF_calculateDataOffset(KFVariable *var, void* data, int firstBlock) {
    /*  First read data block header ( 4 integers )
      NOTE: blkInds[0] is left empty because T_* constants have
      values from 1 to 4 */
   int blkInds[5] = {0,0,0,0,0}, i;
   KFFile *kf = var->section->file;
   int off = 0;

   if (kf->integerSize == 4) {
      KFDataBlockHeader32 *header = (KFDataBlockHeader32 *)data;
      off = sizeof(KFDataBlockHeader32);
      blkInds[1] = (int)header->index[0];
      blkInds[2] = (int)header->index[1];
      blkInds[3] = (int)header->index[2];
      blkInds[4] = (int)header->index[3];
   }
   else if (var->section->file->integerSize == 8) {
      KFDataBlockHeader64 *header = (KFDataBlockHeader64 *)data;
      off = sizeof(KFDataBlockHeader64);
      blkInds[1] = (int)header->index[0];
      blkInds[2] = (int)header->index[1];
      blkInds[3] = (int)header->index[2];
      blkInds[4] = (int)header->index[3];
   }
    for (i = 1; i < var->type; i++) {
      off += blkInds[i] * kf->typeSize[i];
    }

    if (firstBlock) {
      off += kf->typeSize[var->type] * (var->firstBlkIndex - 1);
    }
    return off;
}

// JC static suppress
int ADF_calculateDataSize(KFVariable *var, void *data, int firstBlock) {
   if (firstBlock) {
      return var->firstBlkLen;
   }
   else {
      KFFile *kf = var->section->file;
      if (kf->integerSize == 4) {
         KFDataBlockHeader32 *header = (KFDataBlockHeader32 *)data;
         return (int)header->index[var->type-1];
      }
      else if (var->section->file->integerSize == 8) {
         KFDataBlockHeader64 *header = (KFDataBlockHeader64 *)data;
         return (int)header->index[var->type-1];
      }
   }
   return 0;
}

/* return the number of read bytes
 JC static suppress*/
int ADF_readBlock(int fd, void *buf, int block){
   int lread;
/* only Unix version
#ifdef _MSC_VER
   _int64 off = (_int64) (block - 1) * KF_BLOCKLENGTH;
   _int64 off2 = _lseeki64(fd, off, SEEK_SET);
   if (off != off2) {
      fprintf (stderr, "Lseek %d, %I64d", fd, off);
      perror("lseek failed");
   }
   #else*/
   off_t off = (off_t) (block - 1) * KF_BLOCKLENGTH;
   off_t off2 = lseek(fd, off, SEEK_SET);
   if (off != off2) {
      fprintf (stderr, "lseek (%d, %ld) returned %ld", fd, (long)off, (long)off2);
      perror("");
   }
   //#endif
   lread = read(fd, buf, KF_BLOCKLENGTH);
   if (lread != KF_BLOCKLENGTH){
      perror("read failed");
   }
   return lread;
}


// JC static suppress
int ADF_guessIntegerSize(void *buf){
   KFSuperIndexBlock32 *bl32;
   KFSuperIndexBlock64 *bl64;

   bl32 = (KFSuperIndexBlock32 *)buf;
   bl64 = (KFSuperIndexBlock64 *)buf;
   if (strncmp(bl32->entries[0].name,KF_SIG,KF_SIG_LENGTH) == 0) {
      return 4;
   }
   else if (strncmp(bl64->entries[0].name,KF_SIG,KF_SIG_LENGTH) == 0) {
      return 8;
   }
   else {
      return 0;
   }
}

// JC static suppress
int ADF_guessByteOrder(void *buf, int size){
   KFSuperIndexBlock32 *bl32 = (KFSuperIndexBlock32 *)buf;
   KFSuperIndexBlock64 *bl64 = (KFSuperIndexBlock64 *)buf;
   // JC avoiding maybe-uninitialize warning
   char *c=NULL;
   if (size == 4) 
      c = (char *)&(bl32->entries[0].physBlk);
   else if (size == 8)
      c = (char *)&(bl64->entries[0].physBlk);

   if (*c == '\0') 
      return KF_BIG_ENDIAN;
   else if (*c == '\001') 
      return KF_LITTLE_ENDIAN;
   else {
      fprintf (stderr, "Corrupted file header\n");
      return 0;
   }
}


/*************** Functions for KFVariable **************/
// JC static suppress
KFVariable* ADF_createKFVariable (KFSection *sec, const char *name, int lbl, int dex, int len, int fln, int usd, int typ){
  KFVariable *var = (KFVariable*)malloc (sizeof(KFVariable));
  if (var != NULL) {
    var->section = sec;
    strncpy(var->name, name, KF_SECTION_NAME_LENGTH);
    var->name[KF_SECTION_NAME_LENGTH] = '\0';
    var->firstLogBlk = lbl;
    var->firstBlkIndex = dex;
    var->length = len;
    var->firstBlkLen = fln;
    var->usedLen = usd;
    var->type = typ;
    var->multiBlock = ((usd > fln)? 1 : 0);
  }
  return var;
}

// JC static suppress
void ADF_deleteKFVariable (KFVariable *var){
   free (var);
}
/** Calculates physical block number (record) where the variable starts
 * First block number has number 1.
 *
 * @return block (record) number, one-based.
 */
// JC static suppress
int ADF_getPhysicalBlockNumber(KFVariable *var){
   int record = 0, i;
   for (i = var->section->dataBlockRuns.length - 1; i >= 0; i--) {
     KFBlockRun *br = (KFBlockRun*)getArrayListElement(&(var->section->dataBlockRuns),i);
      if (var->firstLogBlk >= br->logBlock) {
         record = br->physBlock + (var->firstLogBlk - br->logBlock);
         break;
      }
   }
   return record;
}

// JC static suppress
int ADF_getPhysicalBlockNumberForLogical(KFVariable *var, int logBlock){
   int i;
   for (i = 0; i < var->section->dataBlockRuns.length; i++) {
     KFBlockRun *br = (KFBlockRun*)getArrayListElement(&(var->section->dataBlockRuns),i);
      int delta = logBlock - br->logBlock;
      if ( delta >=0 && delta < br->count ) {
         return br->physBlock + delta;
      }
   }
   return 0;
}

/* Functions for KFSection */
// JC static suppress
KFSection* ADF_createKFSection (KFFile *kf, const char *name, int phBlk, int logBlk, int numBlks){
  KFSection *sec = (KFSection*)malloc (sizeof(KFSection));
  memset(sec, 0, sizeof(KFSection));
  if (sec != NULL) {
    strncpy(sec->name, name, KF_SECTION_NAME_LENGTH);
    sec->name[KF_SECTION_NAME_LENGTH] = '\0';
    ADF_addIndexBlockRun (sec, phBlk, logBlk, numBlks);
  }
  sec->file = kf;
  return sec;
}

// JC static suppress
void ADF_deleteKFSection(KFSection *sec){
   int i;
   for (i=0; i < sec->dataBlockRuns.length; i++){
     ADF_deleteKFBlockRun((KFBlockRun*)getArrayListElement(&(sec->dataBlockRuns),i));
   }
   clearArrayList(&(sec->dataBlockRuns));
   for (i=0; i < sec->indexBlockRuns.length; i++){
     ADF_deleteKFBlockRun((KFBlockRun*)getArrayListElement(&(sec->indexBlockRuns),i));
   }
   clearArrayList(&(sec->indexBlockRuns));
   for (i=0; i < sec->variables.length; i++){
     ADF_deleteKFVariable((KFVariable*)getArrayListElement(&(sec->variables),i));
   }
   clearArrayList(&(sec->variables));
   free(sec);
}

// JC static suppress
KFBlockRun* ADF_createKFBlockRun(int phBlk, int logBlk, int numBlks){
  KFBlockRun *br = (KFBlockRun*)malloc (sizeof(KFBlockRun));
  br->count = numBlks;
  br->logBlock = logBlk;
  br->physBlock = phBlk;
  return br;
}

// JC static suppress
void ADF_deleteKFBlockRun(KFBlockRun *br){
   free(br);
}

/* Functions for KFFile */
// JC static suppress
int ADF_verifySuperIndex(void *buf){
   /* return 1 if superindex signature is valid */
   int ret = strncmp((char *)buf, KF_SIG, KF_SIG_LENGTH);

   if ( ret == 0){
      return 1;
   }
   else {
      return 0;
   }
}

/* Function returning a type for a given triplet (kx,ky,kz) */
// JC static remove
int ADF_primtype(int kx, int ky, int kz){
  /* kx: input --> power of x in STO
     ky: input --> power of y in STO
     kz: input --> power of z in STO
     returns an integer in the range [1:35] = assignment type (the same as LI array in IGMPLOT) */

  /*CASE 1 */
  if ( (kx == 0) && (ky == 0) && (kz == 0) ) 
    return 1;
  
  /*CASE 2 */
  if ( (kx == 1) && (ky == 0) && (kz == 0) ) 
    return 2;
  
  /*CASE 3 */
  if ( (kx == 0) && (ky == 1) && (kz == 0) ) 
    return 3;
  
  /*CASE 4 */
  if ( (kx == 0) && (ky == 0) && (kz == 1) ) 
    return 4;
  
  /*CASE 5 */
  if ( (kx == 2) && (ky == 0) && (kz == 0) ) 
    return 5;
  
  /*CASE 6 */
  if ( (kx == 0) && (ky == 2) && (kz == 0) )
    return 6;
  
  /* CASE 7*/
  if ( (kx == 0) && (ky == 0) && (kz == 2) )
    return 7;

  /* CASE 8 */
  if ( (kx == 1) && (ky == 1) && (kz == 0) )
    return 8;
  
  /* CASE 9 */
  if ( (kx == 1) && (ky == 0) && (kz == 1) ) 
    return 9;
  
  /* CASE 10 */ 
  if ( (kx == 0) && (ky == 1) && (kz == 1) ) 
    return 10;
  
  /* CASE 11 */
  if ( (kx == 3) && (ky == 0) && (kz == 0) ) 
    return 11;
  
  /* CASE 12 */
  if ( (kx == 0) && (ky == 3) && (kz == 0) ) 
    return 12;
  
  /* CASE 13 */
  if ( (kx == 0) && (ky == 0) && (kz == 3) ) 
    return 13;
  
  /* CASE 14 */
  if ( (kx == 2) && (ky == 1) && (kz == 0) )
    return 14;
  
  /* CASE 15 */
  if ( (kx == 2) && (ky == 0) && (kz == 1) )
    return 15;
  
  /* CASE 16 */
  if ( (kx == 0) && (ky == 2) && (kz == 1) )
    return 16;
  
  /* CASE 17 */
  if ( (kx == 1) && (ky == 2) && (kz == 0) ) 
    return 17;
  
  /* CASE 18 */
  if ( (kx == 1) && (ky == 0) && (kz == 2) ) 
    return 18;
  
  /* CASE 19 */
  if ( (kx == 0) && (ky == 1) && (kz == 2) ) 
    return 19;
  
  /* CASE 20 */
  if ( (kx == 1) && (ky == 1) && (kz == 1) ) 
    return 20;
  
  /* CASE 21 */
  if ( (kx == 0) && (ky == 0) && (kz == 4) ) 
    return 21;
  
  /* CASE 22 */
  if ( (kx == 0) && (ky == 1) && (kz == 3) )
    return 22;
  
  /* CASE 23 */
  if ( (kx == 0) && (ky == 2) && (kz == 2) )
    return 23;
  
  /* CASE 24 */
  if ( (kx == 0) && (ky == 3) && (kz == 1) )
    return 24;
  
  /* CASE 25 */
  if ( (kx == 0) && (ky == 4) && (kz == 0) ) 
    return 25;
  
  /* CASE 26 */
  if ( (kx == 1) && (ky == 0) && (kz == 3) ) 
    return 26;
  
  /* CASE 27 */
  if ( (kx == 1) && (ky == 1) && (kz == 2) ) 
    return 27;
  
  /* CASE 28 */
  if ( (kx == 1) && (ky == 2) && (kz == 1) ) 
    return 28;
  
  /* CASE 29 */
  if ( (kx == 1) && (ky == 3) && (kz == 0) ) 
    return 29;

  /* CASE 30 */
  if ( (kx == 2) && (ky == 0) && (kz == 2) )
    return 30;
  
  /* CASE 31 */
  if ( (kx == 2) && (ky == 1) && (kz == 1) )
    return 31;
  
  /* CASE 32 */
  if ( (kx == 2) && (ky == 2) && (kz == 0) )
    return 32;
  
  /* CASE 33 */
  if ( (kx == 3) && (ky == 0) && (kz == 1) )
    return 33;
  
  /* CASE 34 */
  if ( (kx == 3) && (ky == 1) && (kz == 0) )
    return 34;
  
  /* CASE 35 */
  if ( (kx == 4) && (ky == 0) && (kz == 0) )
    return 35;

  /* CASE 666 */
  return -1;
  
} /* end of function primtype */



