/***************************************************************************
 *                                                                         *
 *   ALCBLOK.C                                                             *
 *                                                                         *
 *   Copyright (c) 1987-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This is the GCOMM.LIB routine for allocating memory as an array       *
 *   of structures, where the aggregate is bigger than 64K.                *
 *                                                                         *
 *   The alcblok()/ptrblok() routines are more efficient with selectors    *
 *   than alctile()/ptrtile(), but the latter assures 0-offsets on all     *
 *   pieces.  If at all possible, use alcblok()/ptrblok().                 *
 *                                                                         *
 *                                            - R. Stein 11/1/93           *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"

#define FILREV "$Revision: 6 $"

#ifdef GCDOS
#define MAXMALLOC 65524U      /* biggest blob you can alcmem() (empirical) */

struct blokhdr {         /* blocking header, used by alcblok()/ptrblok()   */
     USHORT numblk;      /* total number of bloks (qty passed to alcblok())*/
     USHORT sizblk;      /* size of each blok (size passed to alcblok())   */
     USHORT each;        /* computed number of bloks in each allocation blk*/
     CHAR *segarray[1];  /* array of pointers to globs of bloks            */
};
#endif // GCDOS

VOID *                             /*   returns ptr to allocated block     */
alcblok(                           /* allocate over 64K blocked memory rgn */
USHORT qty,                        /*   number of blocks                   */
USHORT size)                       /*   size of each block                 */
{
#ifdef GCDOS
     struct blokhdr *thptr;
     INT i;
     USHORT each,numalc,tailbloks;

     ASSERT(qty != 0);
     ASSERT(size != 0);
     if (size > MAXMALLOC) {
          catastro("ALCBLOK:  Can't allocate blocks bigger than %u bytes",
                   MAXMALLOC);
     }
     size=(size+1)&0xFFFE;         /* round up to next even boundary */
     each=MAXMALLOC/size;
#ifdef GCDOSP
#ifdef DEBUG
     if (memdbg && alcdbg) {
          each=1;
     }
#endif // DEBUG
#endif // GCDOSP
     numalc=qty/each;
     thptr=(struct blokhdr *)alczer(sizeof(struct blokhdr)+
                                    numalc*sizeof(char *));
     thptr->numblk=qty;
     thptr->sizblk=size;
     thptr->each=each;
     for (i=0 ; i < numalc ; i++) {
          thptr->segarray[i]=alczer(each*size);
     }
     if ((tailbloks=qty%each) > 0) {
          thptr->segarray[numalc]=alczer(tailbloks*size);
     }
     return((VOID *)thptr);
#else
     VOID *retptr;

     retptr=alczer(((ULONG)qty*size)+8);
     *((USHORT *)retptr)=size;
     return(retptr);
#endif // GCDOS
}

VOID
freblok(                           /* free region allocated with alcblok() */
VOID *bigptr)                      /*   alcblok() return value             */
{
#ifdef GCDOS
     struct blokhdr *thptr;
     INT i,n;

     thptr=(struct blokhdr *)bigptr;
     n=thptr->numblk/thptr->each;
     if (thptr->numblk%thptr->each != 0) {
          ++n;
     }
     for (i=0 ; i < n ; ++i) {
          free(thptr->segarray[i]);
     }
#endif // GCDOS
     free(bigptr);
}

VOID *                             /*   returns ptr to requested element   */
ptrblok(                           /* get address of a blok in blocked rgn */
VOID *bigptr,                      /*   alcblok() return value             */
USHORT idx)                        /*   index of the blok, 0..qty-1        */
{
#ifdef GCDOS
     struct blokhdr *thptr;
     USHORT glob,blokoff;

     ASSERT(bigptr != NULL);
     thptr=(struct blokhdr *)bigptr;
     if (thptr == NULL || idx >= thptr->numblk) {
          return(NULL);
     }
     glob=idx/thptr->each;
     blokoff=idx-glob*thptr->each;
     return((VOID *)(thptr->segarray[glob]+blokoff*thptr->sizblk));
#else
     USHORT size;

     size=*(USHORT *)bigptr;
     return((VOID *)(((CHAR *)bigptr+8)+size*idx));
#endif // GCDOS
}
