/************************************************************************
*                                                                       *
*                               HUGEARR.DLL                             *
*                                                                       *
*               Huge array support for Microsoft Visual Basic           *
*                                                                       *
*                             By Mike Warning                           *
*                                                                       *
*                          HugeSave and HugeLoad                        *
*                                                                       *
*            By Harry JF Wykes - End User Computing Limited             *
*                                                                       *
************************************************************************/

#include <memory.h>
#include <windows.h>
#include "hugearr.h"

HANDLE                  hLocalMem;              // handle to local memory
int                     NumArrays;              // total number of arrays

/************************************************************************
* LibMain -                                                             *
*   Standard DLL constructor.  Allocates all of local heap to store     *
* array descriptors and then set the total number of arrays possible.   *
************************************************************************/
int FAR pascal
     LibMain(HANDLE hModule, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
{
    if (cbHeapSize > 0)
        UnlockData(0);
                                        // Allocate memory for array descrips.
    hLocalMem = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
                                             LocalCompact((WORD) 65500));
    if (hLocalMem == NULL)
        return 0;                       // Something happened, bomb out
                                        // calc total number of arrays
    NumArrays = (int) (LocalSize(hLocalMem) / sizeof(struct ArrayDesc));
    return (1);
}

/************************************************************************
* WEP -                                                                 *
*   Standard DLL destructor.  Free up local memory and quit.            *
************************************************************************/
int FAR pascal WEP(int bSystemExit)
{
    LocalFree(hLocalMem);
    return (1);
}

/************************************************************************
* HugeDim -                                                             *
*   Dimensions a new array.  The size of the array is                   *
* (recsize * ubound+1). Recsize is the size in bytes of each element in *
* the array and ubound is the upper bound of the array. All arrays have *
* a lower bound of 0.                                                   *
*                                                                       *
* Note: If total size of the array is greater than 64k, recsize should  *
*       be an integer power of two (1,2,4,8,16,etc.)  Otherwise, the    *
*       huge pointers will not be added correctly.                      *
************************************************************************/
int FAR pascal HugeDim(int recsize, long ubound)
{
    int         hArray;                 // handle to array to dimension
    pDescrip    pArray;                 // pointer to array descriptor
    int         ret;                    // return value from HugeAlloc

    pArray = (pDescrip) LocalLock(hLocalMem);   // find a free array
    if ((hArray = GetFreeArray(pArray)) == HA_TOMANYARRAYS)  {
        LocalUnlock(hLocalMem);                 // couldn't find one,
        return (HA_TOMANYARRAYS);               // return error.
    }
                                        // allocate new array
    ret = HugeAlloc(pArray + hArray, recsize, ubound, FALSE);
    LocalUnlock(hLocalMem);

    if (ret < 0)                        // if an error occured during alloc
        return (ret);                   // return the error, else
    else
        return (hArray);                // return the handle to the array

}

/************************************************************************
* HugeRedim -                                                           *
*   Redimenions the given array to have the new 'ubound'.  The old      *
* recsize is kept.  All data in the array is preserved and any new data *
* (created by expanding the array) is initialized to 0.                 *
*                                                                       *
* Note: The recsize must be an integer power of two if the total array  *
*       size is greater than 64k.                                       *
************************************************************************/
int FAR pascal HugeRedim(int hArray, long ubound)
{
    pDescrip    pArray;                 // pointer to array desciptor
    register    ret;                    // return code of HugeAlloc

    if (hArray < 0 | hArray >= NumArrays)
        return (HA_BADARRAY);           // illegal array handle

    pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

    if (pArray -> handle != NULL)       // reallocate array
        ret = HugeAlloc(pArray, pArray -> recsize, ubound, TRUE);
    else
        ret = HA_BADARRAY;              // array has never been allocated

    LocalUnlock(hLocalMem);
    return (ret);
}


/********************************************************************
* GetFreeArray -                                                    *
*   Searches the array descriptor table looking for a free entry.   *
* It returns the index into the table if an entry is free or        *
* HA_TOMANYARRAYS otherwise.  pArray is the pointer to the start of *
* the table.                                                        *
********************************************************************/
int GetFreeArray(pDescrip pArray)
{
    int i = 0;
                                        // loop until found or out of entries
    while ((i < NumArrays) && (pArray -> handle != NULL))  {
        ++pArray;
        ++i;
    }

    if (i == NumArrays)                 // didn't find a spot
        return (HA_TOMANYARRAYS);

    return (i);                         // found one, return index to it
}

/********************************************************************
* GetHugeEl -                                                       *
*   Retrieves an element of the array storing it into the buffer    *
* pointed to by 'buffer'.  hArray is the index into the descriptor  *
* table of the array, element is the element to get.                *
*                                                                   *
* NOTE: there is absolutely no type checking done on the buffer.    *
*       It is up to the programmer to make sure it points to the    *
*       correct data type.                                          *
********************************************************************/
int FAR pascal GetHugeEl(int hArray, long element, BYTE FAR *buffer)
{
    BYTE _huge          *ptr;               // pointer to array element
    pDescrip            pArray;             // pointer to array descriptor

    if (hArray < 0 || hArray >= NumArrays)
        return (HA_BADARRAY);               // illegal array handle

                                            // point to proper descriptor
    pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

    if (pArray -> handle == NULL)  {
        LocalUnlock(hLocalMem);
        return (HA_BADARRAY);               // array hasn't been allocated
    }
    if ((pArray -> ubound < element) || (element < 0))  {
        LocalUnlock(hLocalMem);
        return (HA_SUBSCRIPT);              // subscript out of range
    }
                                            // calculate pointer to element
    ptr = (BYTE _huge *) GlobalLock(pArray -> handle);
    ptr = ptr + element * pArray->recsize;
    _fmemcpy(buffer, ptr, pArray -> recsize);   // copy data

    GlobalUnlock(pArray -> handle);
    LocalUnlock(hLocalMem);
    return (HA_OK);
}

/********************************************************************
* SetHugeEl -                                                       *
*   Sets the value of an array element.  This routine is exactly    *
* the same as 'GetHugeEl' except that the memory copy is resversed. *
********************************************************************/
int FAR pascal SetHugeEl(int hArray, long element, BYTE FAR *buffer)
{
    BYTE _huge          *ptr;               // pointer to array element
    pDescrip            pArray;             // pointer to array descriptor

    if (hArray < 0 || hArray >= NumArrays)
        return (HA_BADARRAY);               // illegal array handle

                                            // point to proper descriptor
    pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

    if (pArray -> handle == NULL)  {
        LocalUnlock(hLocalMem);
        return (HA_BADARRAY);               // array hasn't been allocated
    }
    if ((pArray -> ubound < element) || (element < 0))  {
        LocalUnlock(hLocalMem);
        return (HA_SUBSCRIPT);              // subscript out of range
    }
                                            // calculate pointer to element
    ptr = (BYTE _huge *) GlobalLock(pArray -> handle);
    ptr = ptr + element * pArray->recsize;
    _fmemcpy(ptr, buffer, pArray -> recsize);   // copy data

    GlobalUnlock(pArray -> handle);
    LocalUnlock(hLocalMem);
    return (HA_OK);
}

/********************************************************************
* HugeErase -                                                       *
*   Deletes an array and marks it as free in the descriptor table.  *
* 'hArray' is the array to erase.                                   *
********************************************************************/
int FAR pascal HugeErase(int hArray)
{
    pDescrip    pArray;                     // pointer to array descriptor

    if (hArray < 0 || hArray >= NumArrays)
        return (HA_BADARRAY);               // illegal array handle

    pArray = (pDescrip) LocalLock(hLocalMem) + hArray;
    if (pArray -> handle == NULL)  {
        LocalUnlock(hLocalMem);
        return (HA_BADARRAY);               // array hasn't been allocated yet
    }

    GlobalFree(pArray -> handle);           // free the memory
    pArray -> handle = NULL;

    LocalUnlock(hLocalMem);
    return (HA_OK);
}

/********************************************************************
* NumHugeArrays -                                                   *
*   Returns the number of free entries in the array descriptor table*
********************************************************************/
int FAR pascal NumHugeArrays(void)
{
    pDescrip            pArray;             // pointer to current descriptor
    int                 num, i;             // number free so far

    pArray = (pDescrip) LocalLock(hLocalMem);
    for (i = 0, num = 0; i < NumArrays; i++, pArray++)
        if (pArray -> handle == NULL)
            ++num;

    LocalUnlock(hLocalMem);
    return (num);
}


/********************************************************************
* HugeUbound -                                                      *
*   Returns the upper bound of a given array                        *
********************************************************************/
long FAR pascal HugeUbound(int hArray)
{
    pDescrip    pArray;                     // pointer to array descriptor
    long    ubound;                     // upper bound of array

    if (hArray < 0 || hArray >= NumArrays)
        return (HA_BADARRAY);               // illegal array handle

    pArray = (pDescrip) LocalLock(hLocalMem) + hArray;
    if (pArray -> handle == NULL)  {
        LocalUnlock(hLocalMem);
        return (HA_BADARRAY);               // array hasn't been allocated yet
    }

    ubound = pArray -> ubound;
    LocalUnlock(hLocalMem);

    return (ubound);
}


/********************************************************************
* HugeInt -                                                         *
*   Same as GetHugeEl except that it explicitly returns an integer. *
* Use this function when you are in an expression such as:          *
*           i% = 5 * HugeInt(4, 5)                                  *
*                                                                   *
* NOTE: Because the user could store anything in the array element, *
*       they will not know if the value returned is a real value or *
*       if an error occured.  Use GetHugeEl if the error code must  *
*       be checked.                                                 *
********************************************************************/
int FAR pascal HugeInt(int hArray, long element)
{
    int     retval;

    GetHugeEl(hArray, element, (BYTE FAR *) &retval);
    return (retval);
}


/********************************************************************
* HugeLong -                                                        *
*   Returns an element of a long integer array.  (re. HugeInt)      *
********************************************************************/
long FAR pascal HugeLong(int hArray, long element)
{
    long    retval;

    GetHugeEl(hArray, element, (BYTE FAR *) &retval);
    return (retval);
}



/********************************************************************
* HugeSingle -                                                      *
*   Returns an element of a single precesion array.  (re. HugeInt)  *
********************************************************************/
float FAR pascal HugeSingle(int hArray, long element)
{
    float  retval;

    GetHugeEl(hArray, element, (BYTE FAR *) &retval);
    return (retval);
}


/********************************************************************
* HugeDouble -                                                      *
*   Returns an element of a double precesion array.  (re. HugeInt)  *
********************************************************************/
double FAR pascal HugeDouble(int hArray, long element)
{
    double  retval;

    GetHugeEl(hArray, element, (BYTE FAR *) &retval);
    return (retval);
}


/********************************************************************
* HugeCurrency -                                                    *
*   Returns an element of a currency array.  (re. HugeInt)          *
********************************************************************/
currency FAR pascal HugeCurrency(int hArray, long element)
{
    return((currency) HugeDouble(hArray, element));
}

/********************************************************************
*   HugeAlloc -                                                     *
* Allocates (or reallocates) global memory for an array.  The size  *
* of the array is recsize * (ubound + 1) bytes.  The information    *
* for the array is stored in the array descriptor pointed to by     *
* 'pArray'.                                                         *
*                                                                   *
* Note:  See HugeDim for a discussion on array size limitations.    *
********************************************************************/
int HugeAlloc(pDescrip pArray,int recsize,long ubound, BOOL realloc)
{
    HANDLE              handle;             // temp handle for alloc

    ++ubound;                               // ubound = #elements - 1
    if (recsize * ubound > 0xffff)          // is size integer power two?
        if ((recsize <= 0) || (((recsize - 1) & recsize) != 0))
            return (HA_BADELEMENTSIZE);

    if (realloc)                            // allocate array
        handle = GlobalReAlloc(pArray -> handle, recsize * ubound,
                                               GMEM_MOVEABLE || GMEM_ZEROINIT);
    else
        handle = GlobalAlloc(GMEM_MOVEABLE || GMEM_ZEROINIT, recsize * ubound);

    if (handle == NULL)
        return (HA_OUTOFMEMORY);            // out of memory

    pArray -> handle = handle;              // save new handle
    pArray -> recsize = recsize;            // record element size
    pArray -> ubound = ubound - 1;

    return (HA_OK);
}

/********************************************************************
*   HugeSave - Saves the specified number of elements to a file     *
*              Returns the number of items saved.                   *
*                                                                   *
*   NB!   HugeSave does not Erase the Huge Array.                   *
********************************************************************/
long FAR pascal HugeSave(int hArray, long nelements, int OutLen, LPSTR FileSpec)
 {
   int                 hSaveFile;          // handle to the save file
   BYTE _huge          *ptr;               // pointer to array element
   long                element;            // element in the huge array
   pDescrip            pArray;             // pointer to array descriptor

   if (hArray < 0 || hArray >= NumArrays)
       return (HA_BADARRAY);               // illegal array handle

   pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

   if (pArray -> handle == NULL)
    {
       LocalUnlock(hLocalMem);
       return (HA_BADARRAY);               // array hasn't been allocated yet
    }

    if ((pArray -> ubound < nelements) || (nelements < 0))
     {
        LocalUnlock(hLocalMem);
        return (HA_SUBSCRIPT);              // subscript out of range
     }

   // Open the file
   hSaveFile = _lopen(FileSpec, OF_WRITE);
   if(hSaveFile < 0)
    {
        LocalUnlock(hLocalMem);
        return HA_FILEOPENERROR;
    }

   for(element = 0L; element < nelements; element++)
    {

      // calculate pointer to element
      ptr = (BYTE _huge *) GlobalLock(pArray -> handle);
      ptr = ptr + element * pArray->recsize;

      // Write out the record
      if(_lwrite(hSaveFile, ptr, OutLen) < 0)
       {
         GlobalUnlock(pArray -> handle);
         LocalUnlock(hLocalMem);
         _lclose(hSaveFile);
         return HA_FILEWRITEERROR;
       }

      GlobalUnlock(pArray -> handle);

    }

   // Wrap up and return
   _lclose(hSaveFile);

   LocalUnlock(hLocalMem);
   return(element);

 }

/********************************************************************
*   HugeLoad - Loads a Huge Array from the specified file           *
*              Returns the number of elements loaded                *
*                                                                   *
*   NB!  The array must have been allocated and be of sufficient    *
*   NB!  size to hold the array.                                    *
********************************************************************/
long FAR pascal HugeLoad(int hArray, int InLen, LPSTR FileSpec)
 {
   int                 BytesRead;          // number of bytes read from the file
   int                 hLoadFile;          // handle to the save file
   BYTE _huge          *ptr;               // pointer to array element
   long                element;            // element in the huge array
   pDescrip            pArray;             // pointer to array descriptor

   if (hArray < 0 || hArray >= NumArrays)
       return (HA_BADARRAY);               // illegal array handle

   pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

   if (pArray -> handle == NULL)
    {
       LocalUnlock(hLocalMem);
       return (HA_BADARRAY);               // array hasn't been allocated yet
    }

   // Open the file
   if((hLoadFile = _lopen(FileSpec, OF_READ)) < 0)
    {
        LocalUnlock(hLocalMem);
        return HA_FILEOPENERROR;
    }

   for(element = 0L; element < pArray -> ubound ; element++)
    {

      // calculate pointer to element
      ptr = (BYTE _huge *) GlobalLock(pArray -> handle);
      ptr = ptr + element * pArray->recsize;

      // Read the record
      BytesRead = _lread(hLoadFile, ptr, InLen);
      if(BytesRead < 0)
       {
         GlobalUnlock(pArray -> handle);
         LocalUnlock(hLocalMem);
         _lclose(hLoadFile);
         return HA_FILEREADERROR;
       }
      if(BytesRead == 0)
       {
         GlobalUnlock(pArray -> handle);
         break;
       }

      GlobalUnlock(pArray -> handle);

    }

   // Wrap up and return
   _lclose(hLoadFile);

   LocalUnlock(hLocalMem);
   return(element);

 }
