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; }
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; }
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; }
/* ---------------------------------------------------------------- * 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; }
/* ---------------------------------------------------------------- * 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); }
/* ---------------------------------------------------------------- * 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); } }
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); }