Skip to content

Commit

Permalink
Optimized zipentry searching uses a binary search
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Jun 3, 2003
1 parent d2aea7a commit 3f4593a
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 33 deletions.
4 changes: 3 additions & 1 deletion doomsday/Include/dd_zip.h
Expand Up @@ -6,13 +6,15 @@

#include "sys_file.h"

// Zip entry indices are invalidated when a new Zip file is read.
typedef int zipindex_t;

void Zip_Init(void);
void Zip_Shutdown(void);

boolean Zip_Open(const char *fileName, DFILE *prevOpened);
zipindex_t Zip_Find(int (*finder)(const char*, void*), void *parm);
zipindex_t Zip_Find(const char *fileName);
zipindex_t Zip_Iterate(int (*iterator)(const char*, void*), void *parm);
uint Zip_GetSize(zipindex_t index);
uint Zip_Read(zipindex_t index, void *buffer);

Expand Down
92 changes: 82 additions & 10 deletions doomsday/Src/dd_zip.c
Expand Up @@ -12,6 +12,9 @@

#include "de_base.h"
#include "de_console.h"
#include "de_misc.h"

#include "sys_direc.h"

// MACROS ------------------------------------------------------------------

Expand Down Expand Up @@ -164,6 +167,16 @@ zipentry_t* Zip_NewFiles(unsigned int count)
return gFiles + oldCount;
}

/*
* Sorts all the zip entries alphabetically. All the paths are absolute.
*/
int C_DECL Zip_EntrySorter(const void *a, const void *b)
{
// Compare the names.
return stricmp( ((const zipentry_t*)a)->name,
((const zipentry_t*)b)->name );
}

/*
* Adds a new package to the list of packages.
*/
Expand All @@ -186,6 +199,7 @@ boolean Zip_Open(const char *fileName, DFILE *prevOpened)
localfileheader_t header;
zipentry_t *entry;
char *entryName;
char fullPath[256];

if(prevOpened == NULL)
{
Expand Down Expand Up @@ -231,7 +245,7 @@ boolean Zip_Open(const char *fileName, DFILE *prevOpened)
// This must be a local file data segment. Read the header.
F_Read(&header, sizeof(header), file);

// Read the file name. This memory is freed in Zip_Shutdown().
// Read the file name. This memory is freed below.
entryName = calloc(header.fileNameSize + 1, 1);
F_Read(entryName, header.fileNameSize, file);

Expand All @@ -253,9 +267,21 @@ boolean Zip_Open(const char *fileName, DFILE *prevOpened)

if(header.size)
{
// Convert all slashes to backslashes, for compatibility with
// the sys_filein routines.
for(i = 0; entryName[i]; i++)
if(entryName[i] == '/') entryName[i] = '\\';

// Make it absolute.
M_PrependBasePath(entryName, fullPath);

// We can add this file to the zipentry list.
entry = Zip_NewFiles(1);
entry->name = entryName;

// This memory is freed in Zip_Shutdown().
entry->name = malloc(strlen(fullPath) + 1);
strcpy(entry->name, fullPath);

entry->package = pack;
entry->size = header.size;

Expand All @@ -264,17 +290,17 @@ boolean Zip_Open(const char *fileName, DFILE *prevOpened)

// Skip the data.
F_Seek(file, header.size, SEEK_CUR);

// Convert all slashes to backslashes, for compatibility with
// the sys_filein routines.
for(i = 0; entry->name[i]; i++)
if(entry->name[i] == '/')
entry->name[i] = '\\';
}

free(entryName);
}

pack->file = file;

// Sort all the zipentries by name. (Note: When lots of files loaded,
// most of list is already sorted. Quicksort becomes slow...)
qsort(gFiles, gNumFiles, sizeof(zipentry_t), Zip_EntrySorter);

// File successfully opened!
return true;
}
Expand All @@ -285,18 +311,64 @@ boolean Zip_Open(const char *fileName, DFILE *prevOpened)
* returned. Parm is passed to the finder func. Returns zero if nothing
* is found.
*/
zipindex_t Zip_Find(int (*finder)(const char*, void*), void *parm)
zipindex_t Zip_Iterate(int (*iterator)(const char*, void*), void *parm)
{
int i;

for(i = 0; i < gNumFiles; i++)
if(finder(gFiles[i].name, parm))
if(iterator(gFiles[i].name, parm))
return i + 1;

// Nothing was accepted.
return 0;
}

/*
* Find a specific path in the zipentry list. Relative paths are converted
* to absolute ones. A binary search is used (the entries have been sorted).
* Good performance: O(log n). Returns zero if nothing is found.
*/
zipindex_t Zip_Find(const char *fileName)
{
zipindex_t begin, end, mid;
int relation;
char fullPath[256];

// Convert to an absolute path.
strcpy(fullPath, fileName);
Dir_MakeAbsolute(fullPath);

// Init the search.
begin = 0;
end = gNumFiles - 1;

while(begin <= end)
{
mid = (begin + end) / 2;

// How does this compare?
relation = strcmp(fullPath, gFiles[mid].name);
if(!relation)
{
// Got it! We return a 1-based index.
return mid + 1;
}
if(relation < 0)
{
// What we are searching must be in the first half.
end = mid - 1;
}
else
{
// Then it must be in the second half.
begin = mid + 1;
}
}

// It wasn't found.
return 0;
}

/*
* Returns the size of a zipentry.
*/
Expand Down
26 changes: 4 additions & 22 deletions doomsday/Src/sys_filein.c
Expand Up @@ -374,20 +374,6 @@ void F_TranslateZipFileName(const char *zipFileName, char *translated)
M_PrependBasePath(zipFileName, translated);
}

//===========================================================================
// F_ZipFinder
// Returns true if the names match.
//===========================================================================
int F_ZipFinder(const char *zipFileName, void *path)
{
char fullZipFn[256];

F_TranslateZipFileName(zipFileName, fullZipFn);

// Are they the same?
return !stricmp(path, fullZipFn);
}

//===========================================================================
// F_OpenZip
// Zip data is buffered like lump data.
Expand Down Expand Up @@ -436,7 +422,7 @@ DFILE *F_Open(const char *path, const char *mode)
if(!strchr(mode, 'f')) // Doesn't need to be a real file?
{
// First check the Zip directory.
zipindex_t foundZip = Zip_Find(F_ZipFinder, full);
zipindex_t foundZip = Zip_Find(full);
if(foundZip)
return F_OpenZip(foundZip, dontBuffer);

Expand Down Expand Up @@ -577,13 +563,9 @@ int F_Length(DFILE *file)
int F_ZipFinderForAll(const char *zipFileName, void *parm)
{
zipforall_t *info = parm;
char fullZipFn[256];

// Convert the zip file name into a real path.
F_TranslateZipFileName(zipFileName, fullZipFn);

if(F_MatchName(fullZipFn, info->pattern))
if(!info->func(fullZipFn, info->parm))
if(F_MatchName(zipFileName, info->pattern))
if(!info->func(zipFileName, info->parm))
return true; // Stop searching.

// Continue searching.
Expand Down Expand Up @@ -612,7 +594,7 @@ int F_ForAll(const char *filespec, int parm, f_forall_func_t func)
zipFindInfo.func = func;
zipFindInfo.parm = parm;
zipFindInfo.pattern = fn;
if(Zip_Find(F_ZipFinderForAll, &zipFindInfo))
if(Zip_Iterate(F_ZipFinderForAll, &zipFindInfo))
{
// Find didn't finish.
return false;
Expand Down

0 comments on commit 3f4593a

Please sign in to comment.