windows-nt/Source/XPSP1/NT/enduser/stuff/itss/lzx/decoder/ring16.c
2020-09-26 16:20:57 +08:00

733 lines
20 KiB
C

/*
* RING16.C: Ring buffer code
*
* History:
* 30-Oct-1996 jforbes Cloned from QUANTUM\DECOMP.C
*/
#include <stdio.h> /* for NULL */
#include <stdlib.h> /* for disk ring buffer code (SEEK_SET) */
#include <fcntl.h> /* for disk ring buffer code (_O_*) */
#include <sys\stat.h> /* for disk ring buffer code (_S_I*) */
#include <io.h>
#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;i<BUFFER_SIZE;i++)
{
if (data[i] != buffer[i])
{
printf("page %3d, err@%5d: %3d vs %3d (real)\n",
page, i, data[i], buffer[i]);
}
}
}
#endif
return 1;
}
/* --- DComp_Ring_Load() -------------------------------------------------- */
/* Bring page into a buffer, return a pointer to that buffer. fWrite */
/* indicates the caller's intentions for this buffer, NZ->consider 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;
}