/* * RING16.C: Ring buffer code * * History: * 30-Oct-1996 jforbes Cloned from QUANTUM\DECOMP.C */ #include /* for NULL */ #include /* for disk ring buffer code (SEEK_SET) */ #include /* for disk ring buffer code (_O_*) */ #include /* for disk ring buffer code (_S_I*) */ #include #include "decoder.h" /* --- local function prototypes ------------------------------------------ */ static void NEAR DComp_Internal_Literal(t_decoder_context *context, int Chr ); static void NEAR DComp_Internal_Match(t_decoder_context *context, MATCH Match ); static void NEAR DComp_Ring_Close(t_decoder_context *context); static int NEAR DComp_Ring_Init(t_decoder_context *context); static void NEAR DComp_Ring_Literal(t_decoder_context *context, int Chr ); static BYTE * NEAR DComp_Ring_Load(t_decoder_context *context, int page,int fWrite); static void NEAR DComp_Ring_Match(t_decoder_context *context, MATCH Match ); static void NEAR DComp_Ring_Reset(t_decoder_context *context); #define Disk context->Disk #define DComp context->DComp /* --- DComp_Close() ------------------------------------------------------ */ void NEAR DComp_Close(t_decoder_context *context) { if (DComp.Buf == NULL) { DComp_Ring_Close(context); /* if using a disk-based ring buffer */ } else { context->dec_free( DComp.Buf ); /* if using memory-based ring buffer */ } } /* --- DComp_Init() ------------------------------------------------------- */ int NEAR DComp_Init(t_decoder_context *context) { DComp.Cur = 0; DComp.fRingFault = 0; if( (DComp.Buf = context->dec_malloc( context->dec_window_size )) != NULL ) { DComp.BufPos = DComp.Buf; DComp.BufEnd = DComp.Buf + context->dec_window_size; context->DComp_Token_Match = DComp_Internal_Match; /* use internal buffering */ context->DComp_Token_Literal = DComp_Internal_Literal; } else if (DComp_Ring_Init(context)) /* try disk ring buffer */ { context->DComp_Token_Match = DComp_Ring_Match; /* use disk buffering */ context->DComp_Token_Literal = DComp_Ring_Literal; } else { return (1); /* if can't create ring buffer */ } return(0); } /* --- DComp_Internal_Literal() ------------------------------------------- */ static void NEAR DComp_Internal_Literal(t_decoder_context *context, int Chr) { if (DComp.NumBytes) { DComp.NumBytes--; DComp.Cur++; *context->dec_output_curpos++ = *DComp.BufPos++ = (BYTE) Chr; if (DComp.BufPos == DComp.BufEnd) DComp.BufPos = DComp.Buf; } } /* --- DComp_Internal_Match() --------------------------------------------- */ static void NEAR DComp_Internal_Match(t_decoder_context *context, MATCH Match) { BYTE HUGE *SrcPtr; if (DComp.NumBytes >= (unsigned) Match.Len) { SrcPtr = DComp.Buf + ((DComp.Cur - Match.Dist) & context->dec_window_mask); DComp.NumBytes -= Match.Len; DComp.Cur += Match.Len; while (Match.Len--) { *context->dec_output_curpos++ = *DComp.BufPos++ = *SrcPtr++; if (SrcPtr == DComp.BufEnd) SrcPtr = DComp.Buf; if (DComp.BufPos == DComp.BufEnd) DComp.BufPos = DComp.Buf; } } else /* match too large to fit */ { DComp.NumBytes = 0; DComp.fOutOverflow = 1; } } /* --- DComp_Reset() ------------------------------------------------------ */ void NEAR DComp_Reset(t_decoder_context *context) { DComp.Cur = 0; DComp.fRingFault = 0; if (DComp.Buf != NULL) DComp.BufPos = DComp.Buf; /* big buffer */ else DComp_Ring_Reset(context); /* ring buffer */ } /* --- DComp_Ring_Close() ------------------------------------------------- */ static void NEAR DComp_Ring_Close(t_decoder_context *context) { PBUFFER pBuffer, pNext; /* buffer walk pointer */ context->dec_free(Disk.PageTable); /* discard page table */ pBuffer = Disk.pNewest; while (pBuffer != NULL) /* discard buffer chain */ { pNext = pBuffer->pLinkOlder; context->dec_free(pBuffer); pBuffer = pNext; } context->dec_close(Disk.Handle); /* close that file (and delete) */ } /* --- DComp_Ring_Init() -------------------------------------------------- */ static int NEAR DComp_Ring_Init(t_decoder_context *context) { RINGNAME ringName; PBUFFER pBuffer; int cBuffers; ringName.wildName[0] = '*'; ringName.wildName[1] = '\0'; ringName.fileSize = context->dec_window_size; Disk.Handle = context->dec_open( (char FAR *) &ringName, (_O_BINARY|_O_RDWR|_O_CREAT), (_S_IREAD|_S_IWRITE) ); if (Disk.Handle == -1) { return(0); /* failed, can't make disk file */ } Disk.RingPages = (int) (context->dec_window_size / BUFFER_SIZE); if (Disk.RingPages < MIN_BUFFERS) { Disk.RingPages = MIN_BUFFERS; /* if DComp.WindowSize < BUFFER_SIZE */ } Disk.PageTable = context->dec_malloc(sizeof(PAGETABLEENTRY) * Disk.RingPages); if (Disk.PageTable == NULL) { context->dec_close(Disk.Handle); /* close the file */ return(0); /* failed, can't get page table */ } Disk.pNewest = NULL; /* DComp_Ring_Close() can be used to abort from this point on */ for (cBuffers = 0; cBuffers < Disk.RingPages; cBuffers++) { pBuffer = context->dec_malloc(sizeof(BUFFER)); if (pBuffer != NULL) { pBuffer->pLinkNewer = NULL; /* none are newer */ pBuffer->pLinkOlder = Disk.pNewest; /* all the others older now */ if (Disk.pNewest != NULL) { Disk.pNewest->pLinkNewer = pBuffer; /* old guy now knows about new */ } else /* if nobody else */ { Disk.pOldest = pBuffer; /* guess I'm the oldest too */ } Disk.pNewest = pBuffer; /* I'm the newest */ } else /* if pBuffer == NULL */ { if (cBuffers < MIN_BUFFERS) /* less than minimum? */ { DComp_Ring_Close(context); /* give it up */ return(0); /* failed, can't get min buffers */ } else /* if we got the minimum */ { break; /* got enough, quit trying */ } } } // printf("Got %d of %d ring pages\n",cBuffers,Disk.RingPages); return(1); /* ring buffer created */ } /* --- DComp_Ring_Literal() ----------------------------------------------- */ static void NEAR DComp_Ring_Literal(t_decoder_context *context, int Chr) { if (DComp.NumBytes) { DComp.NumBytes--; DComp.Cur++; *context->dec_output_curpos++ = (BYTE) Chr; } } /* * Insert output buffer contents into the page table */ static void NEAR save_page( t_decoder_context * context, int page, byte * data ) { PBUFFER pBuffer; long iPagefileOffset; pBuffer = Disk.PageTable[page].pBuffer; /* look up this page */ if (pBuffer != NULL) /* if it's in the table */ { if (pBuffer != Disk.pNewest) /* promote if not newest */ { pBuffer->pLinkNewer->pLinkOlder = pBuffer->pLinkOlder; if (pBuffer->pLinkOlder != NULL) /* if there is someone older */ { pBuffer->pLinkOlder->pLinkNewer = pBuffer->pLinkNewer; } else { Disk.pOldest = pBuffer->pLinkNewer; } /* link into head of chain */ Disk.pNewest->pLinkNewer = pBuffer; /* newest now knows one newer */ pBuffer->pLinkNewer = NULL; /* nobody's newer */ pBuffer->pLinkOlder = Disk.pNewest; /* everybody's older */ Disk.pNewest = pBuffer; /* I'm the newest */ } memcpy( pBuffer->Buffer, data, BUFFER_SIZE ); pBuffer->BufferDirty = 1; /* might already be dirty */ return; } pBuffer = Disk.pOldest; /* choose the oldest buffer */ if (pBuffer->BufferPage != -1) /* take it out of page table */ { Disk.PageTable[pBuffer->BufferPage].pBuffer = NULL; /* not here now */ if (pBuffer->BufferDirty) /* write on eject, if dirty */ { iPagefileOffset = (long) pBuffer->BufferPage * BUFFER_SIZE; if (context->dec_seek(Disk.Handle,iPagefileOffset,SEEK_SET) != iPagefileOffset) { return; } if (context->dec_write(Disk.Handle,pBuffer->Buffer,BUFFER_SIZE) != BUFFER_SIZE) { return; } Disk.PageTable[pBuffer->BufferPage].fDiskValid = 1; } } Disk.pOldest = Disk.pOldest->pLinkNewer; /* newer is now oldest */ Disk.pOldest->pLinkOlder = NULL; /* oldest knows none older */ Disk.pNewest->pLinkNewer = pBuffer; pBuffer->pLinkNewer = NULL; /* link into head of chain */ pBuffer->pLinkOlder = Disk.pNewest; Disk.pNewest = pBuffer; /* add new buffer to paging table */ Disk.PageTable[page].pBuffer = pBuffer; /* add new to paging table */ memcpy( pBuffer->Buffer, data, BUFFER_SIZE ); pBuffer->BufferDirty = 1; pBuffer->BufferPage = page; /* our new page number */ } static void NEAR init_last_chance_table(t_decoder_context *context) { int i; for (i = 0; i < NUM_OUTPUT_BUFFER_PAGES; i++) context->dec_pos_to_page[i] = -1; context->dec_last_chance_page_to_use = NUM_OUTPUT_BUFFER_PAGES; } static byte * NEAR last_chance_retrieve(t_decoder_context *context, int page) { int used_output_pages; int table_entry; /* * Where in the output buffer would our page be? */ table_entry = Disk.PageTable[page].last_chance_ptr; /* * It's not there */ if (table_entry == -1) return NULL; /* * It's now an invalid entry */ if (context->dec_pos_to_page[table_entry] != page) return NULL; context->dec_pos_to_page[table_entry] = -1; Disk.PageTable[page].last_chance_ptr = -1; used_output_pages = (int) (((context->dec_output_curpos - context->dec_output_buffer) / BUFFER_SIZE) + 1); if (table_entry <= used_output_pages) return NULL; return (context->dec_output_buffer + (BUFFER_SIZE * table_entry)); } static void NEAR last_chance_store(t_decoder_context *context, int page, byte *data) { int used_output_pages; int prev_owner; int dest; used_output_pages = (int) (((context->dec_output_curpos - context->dec_output_buffer) / BUFFER_SIZE) + 1); if (used_output_pages >= NUM_OUTPUT_BUFFER_PAGES) return; context->dec_last_chance_page_to_use--; if (context->dec_last_chance_page_to_use < used_output_pages) context->dec_last_chance_page_to_use = NUM_OUTPUT_BUFFER_PAGES-1; dest = context->dec_last_chance_page_to_use; /* * If any other page was pointing to this area of the buffer * as a last chance page, toast them. */ prev_owner = context->dec_pos_to_page[dest]; if (prev_owner != -1) { Disk.PageTable[prev_owner].last_chance_ptr = -1; } /* * Now we own this area */ Disk.PageTable[page].last_chance_ptr = dest; context->dec_pos_to_page[dest] = page; memcpy( context->dec_output_buffer + (BUFFER_SIZE*dest), data, BUFFER_SIZE ); } void NEAR DComp_Save_Output_Pages( t_decoder_context * context, uint bytes_decoded ) { uint pages_to_save; int page_num; uint i; byte * data; /* * If we managed to allocate one big buffer in the first place, then * there are no ring pages to save. */ if (DComp.Buf != NULL) return; pages_to_save = (bytes_decoded / BUFFER_SIZE); page_num = (int) ((context->dec_position_at_start & context->dec_window_mask) / (long) BUFFER_SIZE); data = context->dec_output_buffer; for (i = 0; i < pages_to_save; i++) { save_page(context, page_num, data); page_num++; if (page_num >= Disk.RingPages) page_num = 0; data += BUFFER_SIZE; } init_last_chance_table(context); } static int NEAR retrieve_page_from_disk(t_decoder_context *context, int page, byte *buffer) { long iPagefileOffset; byte *data; data = last_chance_retrieve(context, page); if (data) { memcpy(buffer, data, BUFFER_SIZE); return 1; } iPagefileOffset = (long) page * BUFFER_SIZE; if (context->dec_seek(Disk.Handle,iPagefileOffset,SEEK_SET) != iPagefileOffset) { return 0; } if (context->dec_read(Disk.Handle,buffer,BUFFER_SIZE) != BUFFER_SIZE) { return 0; } #ifdef DEBUG_VERIFY_LAST_CHANCE /* * verifies last chance data against disk page */ if (data) { int i; for (i=0;iconsider it */ /* dirty now. Returns NULL if there is a paging fault (callback */ /* failed) or if any internal assertions fail. */ static BYTE * NEAR DComp_Ring_Load( t_decoder_context * context, int page, int fWrite ) { PBUFFER pBuffer; long iPagefileOffset; pBuffer = Disk.PageTable[page].pBuffer; /* look up this page */ if (pBuffer != NULL) /* if it's in the table */ { if (pBuffer != Disk.pNewest) /* promote if not newest */ { pBuffer->pLinkNewer->pLinkOlder = pBuffer->pLinkOlder; if (pBuffer->pLinkOlder != NULL) /* if there is someone older */ { pBuffer->pLinkOlder->pLinkNewer = pBuffer->pLinkNewer; } else { Disk.pOldest = pBuffer->pLinkNewer; } /* link into head of chain */ Disk.pNewest->pLinkNewer = pBuffer; /* newest now knows one newer */ pBuffer->pLinkNewer = NULL; /* nobody's newer */ pBuffer->pLinkOlder = Disk.pNewest; /* everybody's older */ Disk.pNewest = pBuffer; /* I'm the newest */ } pBuffer->BufferDirty |= fWrite; /* might already be dirty */ return(pBuffer->Buffer); } /* desired page is not in the table; discard oldest & use it */ pBuffer = Disk.pOldest; /* choose the oldest buffer */ if (pBuffer->BufferPage != -1) /* take it out of page table */ { Disk.PageTable[pBuffer->BufferPage].pBuffer = NULL; /* not here now */ if (pBuffer->BufferDirty) /* write on eject, if dirty */ { iPagefileOffset = (long) pBuffer->BufferPage * BUFFER_SIZE; if (context->dec_seek(Disk.Handle,iPagefileOffset,SEEK_SET) != iPagefileOffset) { return(NULL); } if (context->dec_write(Disk.Handle,pBuffer->Buffer,BUFFER_SIZE) != BUFFER_SIZE) { return(NULL); } Disk.PageTable[pBuffer->BufferPage].fDiskValid = 1; } last_chance_store(context, pBuffer->BufferPage, pBuffer->Buffer); } Disk.pOldest = Disk.pOldest->pLinkNewer; /* newer is now oldest */ Disk.pOldest->pLinkOlder = NULL; /* oldest knows none older */ Disk.pNewest->pLinkNewer = pBuffer; pBuffer->pLinkNewer = NULL; /* link into head of chain */ pBuffer->pLinkOlder = Disk.pNewest; Disk.pNewest = pBuffer; /* add new buffer to paging table */ Disk.PageTable[page].pBuffer = pBuffer; /* add new to paging table */ /* if this disk page is valid, load it */ if (Disk.PageTable[page].fDiskValid) { if (retrieve_page_from_disk(context, page, pBuffer->Buffer) == 0) return NULL; } else if (!fWrite) { /* assertion failure, trying to load a never-written page from disk */ return(NULL); } pBuffer->BufferDirty = fWrite; /* might be dirty now */ pBuffer->BufferPage = page; /* our new page number */ return(pBuffer->Buffer); /* return new handle */ } /* --- DComp_Ring_Match() ------------------------------------------------- */ static void NEAR DComp_Ring_Match(t_decoder_context *context, MATCH Match) { long SrcOffset; /* offset into output ring */ int SrcPage; /* page # where that offset lies */ int Chunk; /* number of bytes this pass */ BYTE FAR *SrcPtr; /* pointer to source bytes */ BYTE *SrcBuffer; /* buffer where source data is */ int SrcBufferOffset; /* offset within the buffer */ if (DComp.NumBytes >= (unsigned) Match.Len) { SrcOffset = (DComp.Cur - Match.Dist) & context->dec_window_mask; DComp.NumBytes -= Match.Len; DComp.Cur += Match.Len; while (Match.Len) { /* Limit: number of bytes requested */ Chunk = Match.Len; /* try for everything */ /* * Match source inside current output buffer? */ if (Match.Dist <= (long) (context->dec_output_curpos - context->dec_output_buffer)) { SrcPtr = context->dec_output_curpos - Match.Dist; while (Chunk--) /* copy this chunk */ { *context->dec_output_curpos++ = *SrcPtr++; } return; } else { SrcPage = (int) (SrcOffset / BUFFER_SIZE); SrcBufferOffset = (int) (SrcOffset % BUFFER_SIZE); SrcBuffer = DComp_Ring_Load(context,SrcPage,0); /* for reading */ if (SrcBuffer == NULL) { DComp.NumBytes = 0; DComp.fRingFault = 1; return; } SrcPtr = SrcBuffer + SrcBufferOffset; /* Limit: number of source bytes on input page */ if ((BUFFER_SIZE - SrcBufferOffset) < Chunk) Chunk = (BUFFER_SIZE - SrcBufferOffset); SrcOffset += Chunk; SrcOffset &= context->dec_window_mask; Match.Len -= Chunk; while (Chunk--) /* copy this chunk */ { *context->dec_output_curpos++ = *SrcPtr++; } } } /* while Match.Len */ } /* if Match.Len size OK */ else /* match too large to fit */ { DComp.NumBytes = 0; DComp.fOutOverflow = 1; } } /* --- DComp_Ring_Reset() ------------------------------------------------- */ static void NEAR DComp_Ring_Reset(t_decoder_context *context) { PBUFFER walker; int iPage; for (walker = Disk.pNewest; walker != NULL; walker = walker->pLinkOlder) { walker->BufferPage = -1; /* buffer is not valid */ walker->BufferDirty = 0; /* and doesn't need writing */ } for (iPage = 0; iPage < Disk.RingPages; iPage++) { Disk.PageTable[iPage].pBuffer = NULL; /* not in memory */ Disk.PageTable[iPage].fDiskValid = 0; /* not on disk */ Disk.PageTable[iPage].last_chance_ptr = -1; /* not in last chance list */ } init_last_chance_table(context); context->dec_last_chance_page_to_use = NUM_OUTPUT_BUFFER_PAGES; }