home *** CD-ROM | disk | FTP | other *** search
- /* Kevo -- a prototype-based object-oriented language */
- /* (c) Antero Taivalsaari 1991-1993 */
- /* Some parts (c) Antero Taivalsaari 1986-1988 */
- /* blockfiles.c: block file management internals */
-
- /*
- Traditional Forth-style block files.
- Not really needed in Kevo, but added for
- compatibility (and fun).
-
- Note: in the current implementation block files
- are not task-specific, i.e., only one block file
- shared by all tasks can be open at each time.
- */
-
- #include "global.h"
-
- /* ----------------------------------------------------------------------- */
-
- #define BUFFERSIZE 1024
- #define BUFFERAMOUNT 64
- #define DIRTYBIT 0x80000000
-
- /*
- Structure for keeping track of blocks that are
- currently in memory.
- */
- int blockNumbers[BUFFERAMOUNT];
-
- /*
- Structure for keeping track of the addresses of buffers.
- We allocate space using dynamic allocation. Initially, no
- space is allocated, but when 'block' command is used new
- buffers are created on the fly. Once all the buffers exist,
- they will be "recycled" until deleted by 'empty-buffers'.
- */
- char* bufferAddresses[BUFFERAMOUNT];
-
- /*
- Variable that tells us how many buffers we have currently
- in memory.
- */
- int blocksInMemory = 0;
-
-
- /* Block file pointer */
- FILE* blockFile;
-
-
- /* Private operations: */
-
- /* Check if the block is in memory */
- /* Return the position + 1 */
- int blockInMemory(thisNumber)
- int thisNumber;
- {
- int i;
-
- for (i = 0; i < blocksInMemory; i++) {
- if ((blockNumbers[i] & ~DIRTYBIT) == thisNumber) return(i+1);
- }
- return(0);
- }
-
-
- /* Scroll the block numbers and buffer addresses downwards */
- /* starting from the given position+1 */
- void scrollDown(position)
- int position;
- {
- int length = blocksInMemory-position;
-
- memmove(&blockNumbers[position], &blockNumbers[position]+1, length*CELL);
- memmove(&bufferAddresses[position], &bufferAddresses[position]+1, length*CELL);
-
- /* Slower implementation:
- int i;
-
- for (i = position; i < blocksInMemory; i++) {
- blockNumbers[i] = blockNumbers[i+1];
- bufferAddresses[i] = bufferAddresses[i+1];
- }
- */
- }
-
-
- /* Mark the given block most recently used */
- void makeMostRecent(position)
- int position;
- {
- int tempNumber = blockNumbers[position];
- char* tempAddress = bufferAddresses[position];
-
- /* If most recently used is referred to again, do nothing */
- if (position == blocksInMemory-1) return;
-
- /* Scroll the data structures downwards */
- scrollDown(position);
- blockNumbers[blocksInMemory-1] = tempNumber;
- bufferAddresses[blocksInMemory-1] = tempAddress;
- }
-
-
- void writeOntoDisk(blockNumber, bufferAddress)
- int blockNumber;
- char* bufferAddress;
- {
- /* Write the contents of the given buffer to given block onto disk */
- if (fseek(blockFile, blockNumber*BUFFERSIZE, SEEK_SET) == 0) {
- if (fwrite(bufferAddress, BUFFERSIZE, 1, blockFile) == 1) return;
- }
-
- /* Something went wrong with writing */
- fprintf(confile, "== File error (cannot write block buffer)."); showTaskID();
- if (!supervisor) {
- ownPrintf("-- Cannot write block buffer #%d onto disk", blockNumber);
- execute((*up)->errorVector);
- }
- ownLongJmp();
- }
-
-
- /* Discard the least recently addressed buffer,
- returning its buffer address for future reuse
- */
- char* discardLeastRecent()
- {
- int LRUblock = blockNumbers[0] & ~DIRTYBIT;
- char* bufferAddress = bufferAddresses[0];
-
- /* If the least recent block is dirty, save it onto disk */
- if (blockNumbers[0] & DIRTYBIT) writeOntoDisk(LRUblock, bufferAddress);
-
- /* Make space in the block table */
- scrollDown(0);
- blocksInMemory--;
- return(bufferAddress);
- }
-
-
- void pOpenBlockFile()
- {
- char* fileName = (char*)popData();
- FILE* file;
-
- if ((file = fopen(fileName, "rb+")) == NIL) {
- fprintf(confile, "== File error (cannot open block file)."); showTaskID();
- if (!supervisor) {
- ownPrintf("-- Cannot open block file '%s'", fileName);
- execute((*up)->errorVector);
- }
- ownLongJmp();
- }
- else {
- /* If we have an existing block file open, close it now */
- if (blockFile) pCloseBlockFile();
- blockFile = file;
- }
- }
-
-
- void pCloseBlockFile()
- {
- /* Save and empty the buffers first */
- pSaveBuffers();
- pEmptyBuffers();
-
- if (blockFile && fclose(blockFile)) {
- fprintf(confile, "== File error (cannot close block file)."); showTaskID();
- if (!supervisor) {
- ownPrintf("-- Cannot close block file");
- execute((*up)->errorVector);
- }
- ownLongJmp();
- }
- blockFile = NIL;
- }
-
-
- void pBlock()
- {
- int blockNumber = topData; /* The block to be loaded */
- int position;
- char* blockAddress;
-
- /* Check if the desired block is already in memory */
- if (position = blockInMemory(blockNumber)) {
- /* Return the address of that buffer */
- topData = (int)bufferAddresses[position-1];
-
- /* Mark that block most recently used */
- makeMostRecent(position-1);
- yield();
- return;
- }
-
- /* Otherwise we have to load the block from file */
-
- /* If we already have the maximum number of blocks in memory */
- /* we must discard the contents of the least recently used buffer */
- if (blocksInMemory >= BUFFERAMOUNT) {
- /* Keep the address of the buffer, so we don't have to */
- /* worry amount memory management (reuse old buffer) */
- blockAddress = discardLeastRecent();
- }
- /* Otherwise we have to allocate a new buffer */
- else {
- blockAddress = mymalloc(BUFFERSIZE);
- }
-
- /* Load the block */
- if (fseek(blockFile, blockNumber*BUFFERSIZE, SEEK_SET) == 0 &&
- fread(blockAddress, BUFFERSIZE, 1, blockFile) == 1) {
-
- /* Successful reading: */
- /* Update the block file data structure */
- blockNumbers[blocksInMemory] = blockNumber;
- bufferAddresses[blocksInMemory] = blockAddress;
- blocksInMemory++;
-
- /* Return the address to data */
- topData = (int)blockAddress;
- }
- else {
- /* Something went wrong with block access */
- fprintf(confile, "== File error (cannot access block)."); showTaskID();
- if (!supervisor) {
- ownPrintf("-- Cannot access block #%d", blockNumber);
- execute((*up)->errorVector);
- }
- ownLongJmp();
- }
- yield();
- }
-
-
- void pUpdate()
- {
- /* Raise the dirty bit in the current block */
- /* Dirty bit is located in the highest bit of the block number */
- blockNumbers[blocksInMemory-1] |= DIRTYBIT;
- }
-
-
- void pDiscard()
- {
- /* Unset the dirty bit in the current block */
- blockNumbers[blocksInMemory-1] &= ~DIRTYBIT;
- }
-
-
- void pSaveBuffers()
- {
- int i;
-
- /* Write all the dirty buffers onto disk */
- /* without affecting their LRU order */
-
- for (i = 0; i < blocksInMemory; i++) {
- int thisBlock = blockNumbers[i] & ~DIRTYBIT;
-
- /* If the current block is dirty, it has to be written onto disk */
- if (blockNumbers[i] & DIRTYBIT) {
- writeOntoDisk(thisBlock, bufferAddresses[i]);
-
- /* Clean the dirty bit */
- blockNumbers[i] = thisBlock;
- }
- }
- yield();
- }
-
-
- void pEmptyBuffers()
- {
- int i;
-
- /* Empty all the buffers and release their storage space */
- /* If the buffers have not been saved, changes will be lost */
-
- /* Release the buffers */
- for (i = 0; i < blocksInMemory; i++) free(bufferAddresses[i]);
-
- /* Erase the block table */
- blocksInMemory = 0;
- }
-
-
- void pMore()
- {
- int n = popData();
- int i;
-
- /* Allocate n more blocks to the end of the file */
- /* Think C's standard function 'fseek' does not know how to expand a file */
- /* -> do the same manually using 'fputc' */
- fseek(blockFile, 0, SEEK_END);
- for (i = 0; i < n*BUFFERSIZE; i++) {
- /* Fill with zeros */
- if (fputc(0, blockFile) == EOF) {
- fprintf(confile, "== File error (cannot expand block file)."); showTaskID();
- if (!supervisor) {
- ownPrintf("-- Unable to add more blocks to block file");
- execute((*up)->errorVector);
- }
- ownLongJmp();
- }
- }
- yield();
- }
-
-
- void pCapacity()
- {
- fpos_t size;
-
- /* Return the size of the block file (in blocks) */
- fseek(blockFile, 0, SEEK_END);
- fgetpos(blockFile, &size);
- pushData(size/BUFFERSIZE);
- }
-
-