/* * Record a data block number in a logical tape's lowest indirect block, * or record an indirect block's number in the next higher indirect level. */ static void ltsRecordBlockNum(LogicalTapeSet *lts, IndirectBlock *indirect, long blocknum) { if (indirect->nextSlot >= BLOCKS_PER_INDIR_BLOCK) { /* * This indirect block is full, so dump it out and recursively save * its address in the next indirection level. Create a new * indirection level if there wasn't one before. */ long indirblock = ltsGetFreeBlock(lts); ltsWriteBlock(lts, indirblock, (void *) indirect->ptrs); if (indirect->nextup == NULL) { indirect->nextup = (IndirectBlock *) palloc(sizeof(IndirectBlock)); indirect->nextup->nextSlot = 0; indirect->nextup->nextup = NULL; } ltsRecordBlockNum(lts, indirect->nextup, indirblock); /* * Reset to fill another indirect block at this level. */ indirect->nextSlot = 0; } indirect->ptrs[indirect->nextSlot++] = blocknum; }
/* * Write to a logical tape. * * There are no error returns; we ereport() on failure. */ void LogicalTapeWrite(LogicalTapeSet *lts, LogicalTape *lt, void *ptr, size_t size) { long tmpBlkNum; size_t nthistime; Assert(lt->writing); if(lt->firstBlkNum == -1) { lt->firstBlkNum = ltsGetFreeBlock(lts); lt->currBlk.prev_blk = -1L; lt->currBlk.next_blk = -1L; lt->currBlk.payload_tail = 0; lt->currPos.blkNum = lt->firstBlkNum; lt->currPos.offset = 0; } while(size > 0) { Assert(lt->currPos.offset == lt->currBlk.payload_tail); Assert(lt->currPos.offset <= LOGTAPE_BLK_PAYLOAD_SIZE); if (lt->currPos.offset == LOGTAPE_BLK_PAYLOAD_SIZE) { Assert(lt->currBlk.payload_tail == LOGTAPE_BLK_PAYLOAD_SIZE); tmpBlkNum = ltsGetFreeBlock(lts); lt->currBlk.next_blk = tmpBlkNum; ltsWriteBlock(lts, lt->currPos.blkNum, &(lt->currBlk)); lt->currBlk.prev_blk = lt->currPos.blkNum; lt->currBlk.next_blk = -1L; lt->currBlk.payload_tail = 0; lt->currPos.blkNum = tmpBlkNum; lt->currPos.offset = 0; } nthistime = size > (LOGTAPE_BLK_PAYLOAD_SIZE - lt->currPos.offset) ? (LOGTAPE_BLK_PAYLOAD_SIZE - lt->currPos.offset) : size; memcpy(lt->currBlk.payload + lt->currBlk.payload_tail, ptr, nthistime); ptr = (void *) ((char *) ptr + nthistime); lt->currBlk.payload_tail += nthistime; lt->currPos.offset += nthistime; size -= nthistime; } }
/* * Dump the dirty buffer of a logical tape. */ static void ltsDumpBuffer(LogicalTapeSet *lts, LogicalTape *lt) { long datablock = ltsGetFreeBlock(lts); Assert(lt->dirty); ltsWriteBlock(lts, datablock, (void *) lt->buffer); ltsRecordBlockNum(lts, lt->indirect, datablock); lt->dirty = false; /* Caller must do other state update as needed */ }
/* * Reset a logical tape's indirect-block hierarchy after a write pass * to prepare for reading. We dump out partly-filled blocks except * at the top of the hierarchy, and we rewind each level to the start. * This call returns the first data block number, or -1L if the tape * is empty. * * Unless 'freezing' is true, release indirect blocks to the free pool after * reading them. */ static long ltsRewindIndirectBlock(LogicalTapeSet *lts, IndirectBlock *indirect, bool freezing) { /* Handle case of never-written-to tape */ if (indirect == NULL) return -1L; /* Insert sentinel if block is not full */ if (indirect->nextSlot < BLOCKS_PER_INDIR_BLOCK) indirect->ptrs[indirect->nextSlot] = -1L; /* * If block is not topmost, write it out, and recurse to obtain address of * first block in this hierarchy level. Read that one in. */ if (indirect->nextup != NULL) { long indirblock = ltsGetFreeBlock(lts); ltsWriteBlock(lts, indirblock, (void *) indirect->ptrs); ltsRecordBlockNum(lts, indirect->nextup, indirblock); indirblock = ltsRewindIndirectBlock(lts, indirect->nextup, freezing); Assert(indirblock != -1L); ltsReadBlock(lts, indirblock, (void *) indirect->ptrs); if (!freezing) ltsReleaseBlock(lts, indirblock); } /* * Reset my next-block pointer, and then fetch a block number if any. */ indirect->nextSlot = 0; if (indirect->ptrs[0] == -1L) return -1L; return indirect->ptrs[indirect->nextSlot++]; }
/* * Write to a logical tape. * * There are no error returns; we ereport() on failure. */ void LogicalTapeWrite(LogicalTapeSet *lts, int tapenum, void *ptr, size_t size) { LogicalTape *lt; size_t nthistime; Assert(tapenum >= 0 && tapenum < lts->nTapes); lt = <s->tapes[tapenum]; Assert(lt->writing); Assert(lt->offsetBlockNumber == 0L); /* Allocate data buffer and first block on first write */ if (lt->buffer == NULL) { lt->buffer = (char *) palloc(BLCKSZ); lt->buffer_size = BLCKSZ; } if (lt->curBlockNumber == -1) { Assert(lt->firstBlockNumber == -1); Assert(lt->pos == 0); lt->curBlockNumber = ltsGetFreeBlock(lts); lt->firstBlockNumber = lt->curBlockNumber; TapeBlockGetTrailer(lt->buffer)->prev = -1L; } Assert(lt->buffer_size == BLCKSZ); while (size > 0) { if (lt->pos >= TapeBlockPayloadSize) { /* Buffer full, dump it out */ long nextBlockNumber; if (!lt->dirty) { /* Hmm, went directly from reading to writing? */ elog(ERROR, "invalid logtape state: should be dirty"); } /* * First allocate the next block, so that we can store it in the * 'next' pointer of this block. */ nextBlockNumber = ltsGetFreeBlock(lts); /* set the next-pointer and dump the current block. */ TapeBlockGetTrailer(lt->buffer)->next = nextBlockNumber; ltsWriteBlock(lts, lt->curBlockNumber, (void *) lt->buffer); /* initialize the prev-pointer of the next block */ TapeBlockGetTrailer(lt->buffer)->prev = lt->curBlockNumber; lt->curBlockNumber = nextBlockNumber; lt->pos = 0; lt->nbytes = 0; } nthistime = TapeBlockPayloadSize - lt->pos; if (nthistime > size) nthistime = size; Assert(nthistime > 0); memcpy(lt->buffer + lt->pos, ptr, nthistime); lt->dirty = true; lt->pos += nthistime; if (lt->nbytes < lt->pos) lt->nbytes = lt->pos; ptr = (void *) ((char *) ptr + nthistime); size -= nthistime; } }