Esempio n. 1
0
static uint32_t
calcChecksum(const sfntDirEntry * dirEntry,
             const uint8_t * sfntData, uint32_t sfntLen)
{
  /* just returns zero on errors, they will be detected again elsewhere */
  const uint32_t * csumPtr;
  const uint32_t * csumEnd;
  uint32_t csum = 0;
  uint32_t length = LONGALIGN(READ32BE(dirEntry->length));
  uint32_t offset = READ32BE(dirEntry->offset);
  uint32_t tag;
  if ((offset & 3) != 0) {
    return csum;
  }
  if (length > sfntLen || offset > sfntLen - length) {
    return csum;
  }
  csumPtr = (const uint32_t *) (sfntData + offset);
  csumEnd = csumPtr + length / 4;
  while (csumPtr < csumEnd) {
    csum += READ32BE(*csumPtr);
    csumPtr++;
  }
  tag = READ32BE(dirEntry->tag);
  if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) {
    const sfntHeadTable * head;
    if (length < HEAD_TABLE_SIZE) {
      return 0;
    }
    head = (const sfntHeadTable *)(sfntData + offset);
    csum -= READ32BE(head->checkSumAdjustment);
  }
  return csum;
}
Esempio n. 2
0
static const uint8_t *
rebuildWoff(const uint8_t * woffData, uint32_t * woffLen,
            const uint8_t * metaData, uint32_t metaCompLen, uint32_t metaOrigLen,
            const uint8_t * privData, uint32_t privLen, uint32_t * pStatus)
{
  const woffHeader * origHeader;
  const woffDirEntry * woffDir;
  uint8_t * newData = NULL;
  uint8_t * tableData = NULL;
  woffHeader * newHeader;
  uint16_t numTables;
  uint32_t tableLimit, totalSize, offset;
  uint16_t i;
  uint32_t status = eWOFF_ok;

  if (*woffLen < sizeof(woffHeader)) {
    FAIL(eWOFF_invalid);
  }
  origHeader = (const woffHeader *) (woffData);

  if (READ32BE(origHeader->signature) != WOFF_SIGNATURE) {
    FAIL(eWOFF_bad_signature);
  }

  numTables = READ16BE(origHeader->numTables);
  woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader));
  tableLimit = 0;
  for (i = 0; i < numTables; ++i) {
    uint32_t end = READ32BE(woffDir[i].offset) + READ32BE(woffDir[i].compLen);
    if (end > tableLimit) {
      tableLimit = end;
    }
  }
  tableLimit = LONGALIGN(tableLimit);

  /* check for broken input (meta/priv data before sfnt tables) */
  offset = READ32BE(origHeader->metaOffset);
  if (offset != 0 && offset < tableLimit) {
    FAIL(eWOFF_illegal_order);
  }
  offset = READ32BE(origHeader->privOffset);
  if (offset != 0 && offset < tableLimit) {
    FAIL(eWOFF_illegal_order);
  }

  totalSize = tableLimit; /* already long-aligned */
  if (metaCompLen) {
    totalSize += metaCompLen;
  }
  if (privLen) {
    totalSize = LONGALIGN(totalSize) + privLen;
  }
  newData = malloc(totalSize);
  if (!newData) {
    FAIL(eWOFF_out_of_memory);
  }

  /* copy the header, directory, and sfnt tables */
  memcpy(newData, woffData, tableLimit);

  /* then overwrite the header fields that should be changed */
  newHeader = (woffHeader *) newData;
  newHeader->length = READ32BE(totalSize);
  newHeader->metaOffset = 0;
  newHeader->metaCompLen = 0;
  newHeader->metaOrigLen = 0;
  newHeader->privOffset = 0;
  newHeader->privLen = 0;

  offset = tableLimit;
  if (metaData && metaCompLen > 0 && metaOrigLen > 0) {
    newHeader->metaOffset = READ32BE(offset);
    newHeader->metaCompLen = READ32BE(metaCompLen);
    newHeader->metaOrigLen = READ32BE(metaOrigLen);
    memcpy(newData + offset, metaData, metaCompLen);
    offset += metaCompLen;
  }

  if (privData && privLen > 0) {
    while ((offset & 3) != 0) {
      newData[offset++] = 0;
    }
    newHeader->privOffset = READ32BE(offset);
    newHeader->privLen = READ32BE(privLen);
    memcpy(newData + offset, privData, privLen);
    offset += privLen;
  }

  *woffLen = offset;
  free((void *) woffData);

  if (pStatus) {
    *pStatus |= status;
  }
  return newData;

failure:
  if (newData) {
    free(newData);
  }
  if (pStatus) {
    *pStatus = status;
  }
  return NULL;
}
Esempio n. 3
0
const uint8_t *
woffEncode(const uint8_t * sfntData, uint32_t sfntLen,
           uint16_t majorVersion, uint16_t minorVersion,
           uint32_t * woffLen, uint32_t * pStatus)
{
  uint8_t * woffData = NULL;
  tableOrderRec * tableOrder = NULL;

  uint32_t tableOffset;
  uint32_t totalSfntSize;

  uint16_t numOrigTables;
  uint16_t numTables;
  uint16_t tableIndex;
  uint16_t order;
  const sfntDirEntry * sfntDir;
  uint32_t tableBase;
  uint32_t checkSumAdjustment = 0;
  woffHeader * newHeader;
  uint32_t tag = 0;
  uint32_t removedDsigSize = 0;
  uint32_t status = eWOFF_ok;

  const sfntHeader * header = (const sfntHeader *) (sfntData);
  const sfntHeadTable * head = NULL;

  if (pStatus && WOFF_FAILURE(*pStatus)) {
    return NULL;
  }

  if (READ32BE(header->version) != SFNT_VERSION_TT &&
      READ32BE(header->version) != SFNT_VERSION_CFF &&
      READ32BE(header->version) != SFNT_VERSION_true) {
    status |= eWOFF_warn_unknown_version;
  }

  numOrigTables = READ16BE(header->numTables);
  sfntDir = (const sfntDirEntry *) (sfntData + sizeof(sfntHeader));

  for (tableIndex = 0; tableIndex < numOrigTables; ++tableIndex) {
    /* validate table checksums, to figure out if we need to drop DSIG;
       also check that table directory is correctly sorted */
    uint32_t prevTag = tag;
    uint32_t csum = calcChecksum(&sfntDir[tableIndex], sfntData, sfntLen);
    if (csum != READ32BE(sfntDir[tableIndex].checksum)) {
      status |= eWOFF_warn_checksum_mismatch;
    }
    checkSumAdjustment += csum;
    tag = READ32BE(sfntDir[tableIndex].tag);
    if (tag <= prevTag) {
      FAIL(eWOFF_invalid);
    }
    if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) {
      if (READ32BE(sfntDir[tableIndex].length) < HEAD_TABLE_SIZE) {
        FAIL(eWOFF_invalid);
      }
      head = (const sfntHeadTable *)(sfntData +
                                     READ32BE(sfntDir[tableIndex].offset));
    }
  }
  if (!head) {
    FAIL(eWOFF_invalid);
  }
  if ((status & eWOFF_warn_checksum_mismatch) == 0) {
    /* no point even checking if we already have an error,
       as fixing that will change the overall checksum too */
    const uint32_t * csumPtr = (const uint32_t *) sfntData;
    const uint32_t * csumEnd = csumPtr + 3 + 4 * numOrigTables;
    while (csumPtr < csumEnd) {
      checkSumAdjustment += READ32BE(*csumPtr);
      ++csumPtr;
    }
    checkSumAdjustment = 0xB1B0AFBA - checkSumAdjustment;
    if (checkSumAdjustment != READ32BE(head->checkSumAdjustment)) {
      status |= eWOFF_warn_checksum_mismatch;
    }
  }

  /* Fixing checkSumAdjustment is tricky, because if there's a DSIG table,
     we're going to have to remove that, which in turn means that table
     offsets in the directory will all change.
     And recalculating checkSumAdjustment requires taking account of any
     individual table checksum corrections, but they have not actually been
     applied to the sfnt data at this point.
     And finally, we'd need to get the corrected checkSumAdjustment into the
     encoded head table (but we can't modify the original sfnt data).
     An easier way out seems to be to go ahead and encode the font, knowing
     that checkSumAdjustment will be wrong; then (if the status flag
     eWOFF_warn_checksum_mismatch is set) we'll decode the font back to
     sfnt format. This will fix up the checkSumAdjustment (and return a
     warning status). We'll ignore that warning, and then re-encode the
     new, cleaned-up sfnt to get the final WOFF data. Perhaps not the most
     efficient approach, but it seems simpler than trying to predict the
     correct final checkSumAdjustment and incorporate it into the head
     table on the fly. */

  tableOrder = (tableOrderRec *) malloc(numOrigTables * sizeof(tableOrderRec));
  if (!tableOrder) {
    FAIL(eWOFF_out_of_memory);
  }
  for (tableIndex = 0, numTables = 0;
       tableIndex < numOrigTables; ++tableIndex) {
    if ((status & eWOFF_warn_checksum_mismatch) != 0) {
      /* check for DSIG table that we must drop if we're fixing checksums */
      tag = READ32BE(sfntDir[tableIndex].tag);
      if (tag == TABLE_TAG_DSIG) {
        status |= eWOFF_warn_removed_DSIG;
        removedDsigSize = READ32BE(sfntDir[tableIndex].length);
        continue;
      }
    }
    tableOrder[numTables].offset = READ32BE(sfntDir[tableIndex].offset);
    tableOrder[numTables].oldIndex = tableIndex;
    tableOrder[numTables].newIndex = numTables;
    ++numTables;
  }
  qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets);

  /* initially, allocate space for header and directory */
  tableOffset = sizeof(woffHeader) + numTables * sizeof(woffDirEntry);
  woffData = (uint8_t *) malloc(tableOffset);
  if (!woffData) {
    FAIL(eWOFF_out_of_memory);
  }

  /* accumulator for total expected size of decoded font */
  totalSfntSize = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry);

/*
 * We use a macro for this rather than creating a variable because woffData
 * will get reallocated during encoding. The macro avoids the risk of using a
 * stale pointer, and the compiler should optimize multiple successive uses.
 */
#define WOFFDIR ((woffDirEntry *) (woffData + sizeof(woffHeader)))

  for (order = 0; order < numTables; ++order) {
    uLong sourceLen, destLen;
    uint32_t sourceOffset;

    uint16_t oldIndex = tableOrder[order].oldIndex;
    uint16_t newIndex = tableOrder[order].newIndex;

    WOFFDIR[newIndex].tag = sfntDir[oldIndex].tag;
    if ((status & eWOFF_warn_checksum_mismatch) != 0) {
      uint32_t csum = calcChecksum(&sfntDir[oldIndex], sfntData, sfntLen);
      WOFFDIR[newIndex].checksum = READ32BE(csum);
    } else {
      WOFFDIR[newIndex].checksum = sfntDir[oldIndex].checksum;
    }
    WOFFDIR[newIndex].origLen = sfntDir[oldIndex].length;
    WOFFDIR[newIndex].offset = READ32BE(tableOffset);

    /* allocate enough space for upper bound of compressed size */
    sourceOffset = READ32BE(sfntDir[oldIndex].offset);
    if ((sourceOffset & 3) != 0) {
      status |= eWOFF_warn_misaligned_table;
    }
    sourceLen = READ32BE(sfntDir[oldIndex].length);
    if (sourceLen > sfntLen || sourceOffset > sfntLen - sourceLen) {
      FAIL(eWOFF_invalid);
    }
    destLen = LONGALIGN(compressBound(sourceLen));
    woffData = (uint8_t *) realloc(woffData, tableOffset + destLen);
    if (!woffData) {
      FAIL(eWOFF_out_of_memory);
    }

    /* do the compression directly into the WOFF data block */
    if (compress2((Bytef *) (woffData + tableOffset), &destLen,
                  (const Bytef *) (sfntData + sourceOffset),
                  sourceLen, 9) != Z_OK) {
      FAIL(eWOFF_compression_failure);
    }
    if (destLen < sourceLen) {
      /* compressed table was smaller */
      tableOffset += destLen;
      WOFFDIR[newIndex].compLen = READ32BE(destLen);
    } else {
      /* compression didn't make it smaller, so store original data instead */
      destLen = sourceLen;
      /* reallocate to ensure enough space for the table,
         plus potential padding after it */
      woffData = (uint8_t *) realloc(woffData,
                                     tableOffset + LONGALIGN(sourceLen));
      if (!woffData) {
        FAIL(eWOFF_out_of_memory);
      }
      /* copy the original data into place */
      memcpy(woffData + tableOffset,
             sfntData + READ32BE(sfntDir[oldIndex].offset), sourceLen);
      tableOffset += sourceLen;
      WOFFDIR[newIndex].compLen = WOFFDIR[newIndex].origLen;
    }

    /* we always realloc woffData to a long-aligned size, so this is safe */
    while ((tableOffset & 3) != 0) {
      woffData[tableOffset++] = 0;
    }

    /* update total size of uncompressed OpenType with table size */
    totalSfntSize += sourceLen;
    totalSfntSize = LONGALIGN(totalSfntSize);
  }

  if (totalSfntSize > sfntLen) {
    if (totalSfntSize > LONGALIGN(sfntLen)) {
      FAIL(eWOFF_invalid);
    } else {
      status |= eWOFF_warn_unpadded_table;
    }
  } else if (totalSfntSize < sfntLen) {
    /* check if the remaining data is a DSIG we're removing;
       if so, we're already warning about that */
    if ((status & eWOFF_warn_removed_DSIG) != 0 ||
        sfntLen - totalSfntSize >
          LONGALIGN(removedDsigSize) + sizeof(sfntDirEntry)) {
      status |= eWOFF_warn_trailing_data;
    }
  }

  /* write the header */
  newHeader = (woffHeader *) (woffData);
  newHeader->signature = WOFF_SIGNATURE;
  newHeader->signature = READ32BE(newHeader->signature);
  newHeader->flavor = header->version;
  newHeader->length = READ32BE(tableOffset);
  newHeader->numTables = READ16BE(numTables);
  newHeader->reserved = 0;
  newHeader->totalSfntSize = READ32BE(totalSfntSize);
  newHeader->majorVersion = READ16BE(majorVersion);
  newHeader->minorVersion = READ16BE(minorVersion);
  newHeader->metaOffset = 0;
  newHeader->metaCompLen = 0;
  newHeader->metaOrigLen = 0;
  newHeader->privOffset = 0;
  newHeader->privLen = 0;

  free(tableOrder);

  if ((status & eWOFF_warn_checksum_mismatch) != 0) {
    /* The original font had checksum errors, so we now decode our WOFF data
       back to sfnt format (which fixes checkSumAdjustment), then re-encode
       to get a clean copy. */
    const uint8_t * cleanSfnt = woffDecode(woffData, tableOffset,
                                           &sfntLen, &status);
    if (WOFF_FAILURE(status)) {
      FAIL(status);
    }
    free(woffData);
    woffData = (uint8_t *) woffEncode(cleanSfnt, sfntLen,
                                      majorVersion, minorVersion,
                                      &tableOffset, &status);
    free((void *) cleanSfnt);
    if (WOFF_FAILURE(status)) {
      FAIL(status);
    }
  }

  if (woffLen) {
    *woffLen = tableOffset;
  }
  if (pStatus) {
    *pStatus |= status;
  }
  return woffData;

failure:
  if (tableOrder) {
    free(tableOrder);
  }
  if (woffData) {
    free(woffData);
  }
  if (pStatus) {
    *pStatus = status;
  }
  return NULL;
}
Esempio n. 4
0
/* ----------------------------------------------------------------
 *	ExecScanHashBucket
 *
 *	scan a hash bucket of matches
 * ----------------------------------------------------------------
 */
HeapTuple
ExecScanHashBucket(HashJoinState *hjstate,
		   HashBucket bucket,
		   HeapTuple curtuple,
		   List *hjclauses,
		   ExprContext *econtext)
{
    HeapTuple 		heapTuple;
    bool 		qualResult;
    OverflowTuple 	otuple = NULL;
    OverflowTuple 	curotuple;
    TupleTableSlot	*inntuple;
    OverflowTuple	firstotuple;
    OverflowTuple	lastotuple;
    HashJoinTable	hashtable;
    
    hashtable = hjstate->hj_HashTable;
    firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
    lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
    
    /* ----------------
     *	search the hash bucket
     * ----------------
     */
    if (curtuple == NULL || curtuple < (HeapTuple)ABSADDR(bucket->bottom)) {
        if (curtuple == NULL)
	    heapTuple = (HeapTuple)
		LONGALIGN(ABSADDR(bucket->top));
	else 
	    heapTuple = (HeapTuple)
		LONGALIGN(((char*)curtuple+curtuple->t_len));
	
        while (heapTuple < (HeapTuple)ABSADDR(bucket->bottom)) {
	    
	    inntuple = 	ExecStoreTuple(heapTuple,	/* tuple to store */
				       hjstate->hj_HashTupleSlot,   /* slot */
				       InvalidBuffer,/* tuple has no buffer */
				       false);	/* do not pfree this tuple */
	    
	    econtext->ecxt_innertuple = inntuple;
	    qualResult = ExecQual((List*)hjclauses, econtext);
	    
	    if (qualResult)
		return heapTuple;
	    
	    heapTuple = (HeapTuple)
		LONGALIGN(((char*)heapTuple+heapTuple->t_len));
	}
	
	if (firstotuple == NULL)
	    return NULL;
	otuple = firstotuple;
    }
    
    /* ----------------
     *	search the overflow area of the hash bucket
     * ----------------
     */
    if (otuple == NULL) {
	curotuple = hjstate->hj_CurOTuple;
	otuple = (OverflowTuple)ABSADDR(curotuple->next);
    }
    
    while (otuple != NULL) {
	heapTuple = (HeapTuple)ABSADDR(otuple->tuple);
	
	inntuple =  ExecStoreTuple(heapTuple,	  /* tuple to store */
			   hjstate->hj_HashTupleSlot, /* slot */
			   InvalidBuffer, /* SP?? this tuple has no buffer */
			   false);	  /* do not pfree this tuple */
	
	econtext->ecxt_innertuple = inntuple;
	qualResult = ExecQual((List*)hjclauses, econtext);
	
	if (qualResult) {
	    hjstate->hj_CurOTuple = otuple;
	    return heapTuple;
	}
	
	otuple = (OverflowTuple)ABSADDR(otuple->next);
    }
    
    /* ----------------
     *	no match
     * ----------------
     */
    return NULL;
}
Esempio n. 5
0
/* ----------------------------------------------------------------
 *	ExecHashOverflowInsert
 *
 *	insert into the overflow area of a hash bucket
 * ----------------------------------------------------------------
 */
void
ExecHashOverflowInsert(HashJoinTable hashtable,
		       HashBucket bucket,
		       HeapTuple heapTuple)
{
    OverflowTuple otuple;
    RelativeAddr  newend;
    OverflowTuple firstotuple;
    OverflowTuple lastotuple;
    
    firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
    lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
    /* ----------------
     *	see if we run out of overflow space
     * ----------------
     */
    newend = (RelativeAddr)LONGALIGN(hashtable->overflownext + sizeof(*otuple)
				     + heapTuple->t_len);
    if (newend > hashtable->bottom) {
#if 0
	elog(DEBUG, "hash table out of memory. expanding.");
	/* ------------------
	 * XXX this is a temporary hack
	 * eventually, recursive hash partitioning will be 
	 * implemented
	 * ------------------
	 */
	hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom;
	hashtable =
	    (HashJoinTable)repalloc(hashtable, hashtable->bottom+BLCKSZ);
	if (hashtable == NULL) {
	    perror("repalloc");
	    elog(WARN, "can't expand hashtable.");
	}
#else
      /* ------------------
       * XXX the temporary hack above doesn't work because things
       * above us don't know that we've moved the hash table!
       *  - Chris Dunlop, <*****@*****.**>
       * ------------------
       */
      elog(WARN, "hash table out of memory. Use -B parameter to increase buffers.");
#endif

    }
    
    /* ----------------
     *	establish the overflow chain
     * ----------------
     */
    otuple = (OverflowTuple)ABSADDR(hashtable->overflownext);
    hashtable->overflownext = newend;
    if (firstotuple == NULL)
	bucket->firstotuple = bucket->lastotuple = RELADDR(otuple);
    else {
	lastotuple->next = RELADDR(otuple);
	bucket->lastotuple = RELADDR(otuple);
    }
    
    /* ----------------
     *	copy the tuple into the overflow area
     * ----------------
     */
    otuple->next = -1;
    otuple->tuple = RELADDR(LONGALIGN(((char*)otuple + sizeof(*otuple))));
    memmove(ABSADDR(otuple->tuple),
	    heapTuple,
	    heapTuple->t_len);
}
Esempio n. 6
0
/* ----------------------------------------------------------------
 *	ExecHashTableInsert
 *
 *	insert a tuple into the hash table depending on the hash value
 *	it may just go to a tmp file for other batches
 * ----------------------------------------------------------------
 */
void
ExecHashTableInsert(HashJoinTable hashtable,
		    ExprContext *econtext,
		    Var *hashkey,
		    File *batches)
{
    TupleTableSlot	*slot;
    HeapTuple 		heapTuple;
    HashBucket 		bucket;
    int			bucketno;
    int			nbatch;
    int			batchno;
    char		*buffer;
    RelativeAddr	*batchPos;
    int			*batchSizes;
    char		*pos;
    
    nbatch = 		hashtable->nbatch;
    batchPos = 		(RelativeAddr*)ABSADDR(hashtable->innerbatchPos);
    batchSizes = 	(int*)ABSADDR(hashtable->innerbatchSizes);
    
    slot = econtext->ecxt_innertuple;
    heapTuple = slot->val;
    
#ifdef HJDEBUG
    printf("Inserting ");
#endif
    
    bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
    
    /* ----------------
     *	decide whether to put the tuple in the hash table or a tmp file
     * ----------------
     */
    if (bucketno < hashtable->nbuckets) {
	/* ---------------
	 *  put the tuple in hash table
	 * ---------------
	 */
	bucket = (HashBucket)
	    (ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize);
	if ((char*)LONGALIGN(ABSADDR(bucket->bottom))
	    -(char*)bucket+heapTuple->t_len > hashtable->bucketsize)
	    ExecHashOverflowInsert(hashtable, bucket, heapTuple);
	else {
	    memmove((char*)LONGALIGN(ABSADDR(bucket->bottom)),
		    heapTuple,
		    heapTuple->t_len); 
	    bucket->bottom = 
		((RelativeAddr)LONGALIGN(bucket->bottom) + heapTuple->t_len);
	}
    }
    else {
	/* -----------------
	 * put the tuple into a tmp file for other batches
	 * -----------------
	 */
	batchno = (float)(bucketno - hashtable->nbuckets)/
	    (float)(hashtable->totalbuckets - hashtable->nbuckets)
		* nbatch;
	buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ;
	batchSizes[batchno]++;
	pos= (char *)
	    ExecHashJoinSaveTuple(heapTuple,
				  buffer,
				  batches[batchno],
				  (char*)ABSADDR(batchPos[batchno]));
	batchPos[batchno] = RELADDR(pos);
    }
}
Esempio n. 7
0
HashJoinTable
ExecHashTableCreate(Hash *node)
{
    Plan	  *outerNode;
    int		  nbatch;
    int 	  ntuples;
    int 	  tupsize;
    IpcMemoryId   shmid;
    HashJoinTable hashtable;
    HashBucket 	  bucket;
    int 	  nbuckets;
    int		  totalbuckets;
    int 	  bucketsize;
    int 	  i;
    RelativeAddr  *outerbatchNames;
    RelativeAddr  *outerbatchPos;
    RelativeAddr  *innerbatchNames;
    RelativeAddr  *innerbatchPos;
    int		  *innerbatchSizes;
    RelativeAddr  tempname;
    
    nbatch = -1;
    HashTBSize = NBuffers/2;
    while (nbatch < 0) {
	/*
	 * determine number of batches for the hashjoin
	 */
	HashTBSize *= 2;
	nbatch = ExecHashPartition(node);
    }
    /* ----------------
     *	get information about the size of the relation
     * ----------------
     */
    outerNode = outerPlan(node);
    ntuples = outerNode->plan_size;
    if (ntuples <= 0)
	ntuples = 1000;  /* XXX just a hack */
    tupsize = outerNode->plan_width + sizeof(HeapTupleData);
    
    /*
     * totalbuckets is the total number of hash buckets needed for
     * the entire relation
     */
    totalbuckets = ceil((double)ntuples/NTUP_PER_BUCKET);
    bucketsize = LONGALIGN (NTUP_PER_BUCKET * tupsize + sizeof(*bucket));
    
    /*
     * nbuckets is the number of hash buckets for the first pass
     * of hybrid hashjoin
     */
    nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC);
    if (totalbuckets < nbuckets)
	totalbuckets = nbuckets;
    if (nbatch == 0)
	nbuckets = totalbuckets;
#ifdef HJDEBUG
    printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets);
#endif
    
    /* ----------------
     *  in non-parallel machines, we don't need to put the hash table
     *  in the shared memory.  We just palloc it.
     * ----------------
     */
    hashtable = (HashJoinTable)palloc((HashTBSize+1)*BLCKSZ);
    shmid = 0;
    
    if (hashtable == NULL) {
	elog(WARN, "not enough memory for hashjoin.");
    }
    /* ----------------
     *	initialize the hash table header
     * ----------------
     */
    hashtable->nbuckets = nbuckets;
    hashtable->totalbuckets = totalbuckets;
    hashtable->bucketsize = bucketsize;
    hashtable->shmid = shmid;
    hashtable->top = sizeof(HashTableData);
    hashtable->bottom = HashTBSize * BLCKSZ;
    /*
     *  hashtable->readbuf has to be long aligned!!!
     */
    hashtable->readbuf = hashtable->bottom;
    hashtable->nbatch = nbatch;
    hashtable->curbatch = 0;
    hashtable->pcount = hashtable->nprocess = 0;
    if (nbatch > 0) {
	/* ---------------
	 *  allocate and initialize the outer batches
	 * ---------------
	 */
	outerbatchNames = (RelativeAddr*)ABSADDR(
						 hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
	outerbatchPos = (RelativeAddr*)ABSADDR(
					       hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
	for (i=0; i<nbatch; i++) {
	    tempname = hashTableAlloc(12, hashtable);
	    mk_hj_temp(ABSADDR(tempname));
	    outerbatchNames[i] = tempname;
	    outerbatchPos[i] = -1;
	}
	hashtable->outerbatchNames = RELADDR(outerbatchNames);
	hashtable->outerbatchPos = RELADDR(outerbatchPos);
	/* ---------------
	 *  allocate and initialize the inner batches
	 * ---------------
	 */
	innerbatchNames = (RelativeAddr*)ABSADDR(
						 hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
	innerbatchPos = (RelativeAddr*)ABSADDR(
					       hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
	innerbatchSizes = (int*)ABSADDR(
					hashTableAlloc(nbatch * sizeof(int), hashtable));
	for (i=0; i<nbatch; i++) {
	    tempname = hashTableAlloc(12, hashtable);
	    mk_hj_temp(ABSADDR(tempname));
	    innerbatchNames[i] = tempname;
	    innerbatchPos[i] = -1;
	    innerbatchSizes[i] = 0;
	}
	hashtable->innerbatchNames = RELADDR(innerbatchNames);
	hashtable->innerbatchPos = RELADDR(innerbatchPos);
	hashtable->innerbatchSizes = RELADDR(innerbatchSizes);
    }
    else {
	hashtable->outerbatchNames = (RelativeAddr)NULL;
	hashtable->outerbatchPos = (RelativeAddr)NULL;
	hashtable->innerbatchNames = (RelativeAddr)NULL;
	hashtable->innerbatchPos = (RelativeAddr)NULL;
	hashtable->innerbatchSizes = (RelativeAddr)NULL;
    }
    
    hashtable->batch = (RelativeAddr)LONGALIGN(hashtable->top + 
					       bucketsize * nbuckets);
    hashtable->overflownext=hashtable->batch + nbatch * BLCKSZ;
    /* ----------------
     *	initialize each hash bucket
     * ----------------
     */
    bucket = (HashBucket)ABSADDR(hashtable->top);
    for (i=0; i<nbuckets; i++) {
	bucket->top = RELADDR((char*)bucket + sizeof(*bucket));
	bucket->bottom = bucket->top;
	bucket->firstotuple = bucket->lastotuple = -1;
	bucket = (HashBucket)LONGALIGN(((char*)bucket + bucketsize));
    }
    return(hashtable);
}