cinera.c: Binary search the .metadata

Also optimise out superfluous searches, and relieve IndexToBuffer() of
the need to string-search the .index

Rewrite the table of contents page after deleting an entry

This commit also retains profiling, as a reminder to me how I used it,
and the old linear search code. The timing blocks and the old code may
be deleted in a future commit
This commit is contained in:
Matt Mascarenhas 2018-03-06 20:40:12 +00:00
parent ab598e37e6
commit de2c632806
1 changed files with 242 additions and 74 deletions

View File

@ -14,20 +14,21 @@ typedef struct
version CINERA_APP_VERSION = {
.Major = 0,
.Minor = 5,
.Patch = 37
.Patch = 38
};
// TODO(matt): Copy in the DB 3 stuff from cinera_working.c
#define CINERA_DB_VERSION 3
#define DEBUG 0
#define DEBUG_MEM 0
typedef unsigned int bool;
#define TRUE 1
#define FALSE 0
#define DEBUG 0
#define DEBUG_MEM 0
#define BINARY_SEARCH 1
bool PROFILING = 0;
#include <stdarg.h> // NOTE(matt): varargs
#include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror
#include <stdlib.h> // NOTE(matt): calloc, malloc, free
@ -44,6 +45,10 @@ typedef unsigned int bool;
#include <sys/inotify.h> // NOTE(matt): inotify
#include <unistd.h> // NOTE(matt): sleep()
clock_t TIMING_START;
#define START_TIMING_BLOCK(...) if(PROFILING) { printf(__VA_ARGS__); TIMING_START = clock(); }
#define END_TIMING_BLOCK() if(PROFILING) { printf("\e[1;34m%ld\e[0m\n", clock() - TIMING_START);}
#define Kilobytes(Bytes) Bytes << 10
#define Megabytes(Bytes) Bytes << 20
@ -880,11 +885,17 @@ DeclaimBuffer(buffer *Buffer)
MemoryArena.Ptr - MemoryArena.Location);
#endif
LogUsage(Buffer);
if(PercentageUsed >= 80.0f)
if(PercentageUsed >= 95.0f)
{
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org
LogError(LOG_ERROR, "%s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
fprintf(stderr, "Warning: %s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
fprintf(stderr, "\e[1;31mWarning\e[0m: %s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
}
else if(PercentageUsed >= 80.0f)
{
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org
LogError(LOG_ERROR, "%s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
fprintf(stderr, "\e[0;33mWarning\e[0m: %s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed);
}
Buffer->Size = 0;
}
@ -2916,7 +2927,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen
if(ClaimBuffer(&AnnotationClass, "AnnotationClass", 256) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
if(ClaimBuffer(&AnnotationData, "AnnotationData", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
if(ClaimBuffer(&Text, "Text", Kilobytes(4)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
if(ClaimBuffer(&CategoryIcons, "CategoryIcons", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
if(ClaimBuffer(&CategoryIcons, "CategoryIcons", Kilobytes(1)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; };
CopyStringToBuffer(&AnnotationHeader,
" <div data-timestamp=\"%d\"",
@ -4373,7 +4384,61 @@ SeekBufferForString(buffer *Buffer, char *String,
}
int
InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename)
BinarySearchForMetadataEntry(index *Index, index_metadata **Test, char *SearchTerm)
{
int Lower = 0;
index_metadata *LowerEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Lower);
if(StringsDiffer(SearchTerm, LowerEntry->BaseFilename) < 0 ) { return Lower; }
int Upper = Index->Header.EntryCount - 1;
int Pivot = Upper - ((Upper - Lower) >> 1);
index_metadata *UpperEntry;
index_metadata *PivotEntry;
do {
LowerEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Lower);
PivotEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Pivot);
UpperEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Upper);
if(!StringsDiffer(SearchTerm, LowerEntry->BaseFilename)) { *Test = LowerEntry; return Lower; }
if(!StringsDiffer(SearchTerm, PivotEntry->BaseFilename)) { *Test = PivotEntry; return Pivot; }
if(!StringsDiffer(SearchTerm, UpperEntry->BaseFilename)) { *Test = UpperEntry; return Upper; }
if((StringsDiffer(SearchTerm, PivotEntry->BaseFilename) < 0)) { Upper = Pivot; }
else { Lower = Pivot; }
Pivot = Upper - ((Upper - Lower) >> 1);
} while(Upper > Pivot);
return Upper;
}
int
AccumulateIndexEntryInsertionOffset(index *Index, int EntryIndex)
{
int Result = 0;
index_metadata *AccEntry = { 0 };
if(EntryIndex < Index->Header.EntryCount >> 1)
{
for(; EntryIndex > 0; --EntryIndex)
{
AccEntry = (index_metadata*)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * (EntryIndex - 1));
Result += AccEntry->Size;
}
Result += StringLength("---\n");
}
else
{
for(; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
{
AccEntry = (index_metadata*)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex);
Result += AccEntry->Size;
}
Result = Index->File.FileSize - Result;
}
return Result;
}
int
InsertIntoIndex(index *Index, int *EntryIndex, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename)
{
int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0);
switch(IndexMetadataFileReadCode)
@ -4399,12 +4464,11 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
int IndexEntryInsertionStart = -1;
int IndexEntryInsertionEnd = -1;
Index->Header.EntryCount = 0;
char *IndexEntryStart;
bool Found = FALSE;
int EntryIndex;
neighbours Neighbours = { 0 };
index_metadata *This, *Next;
index_metadata *This = { 0 };
index_metadata *Next = { 0 };
if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS)
{
// TODO(matt): Index validation?
@ -4421,15 +4485,70 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
Index->File.Buffer.Ptr += StringLength("---\n");
IndexEntryStart = Index->File.Buffer.Ptr;
for(EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
#if BINARY_SEARCH
START_TIMING_BLOCK(" InsertIntoIndex(%s) BinarySearch: ", BaseFilename);
index_metadata *Prev = { 0 };
*EntryIndex = BinarySearchForMetadataEntry(Index, &This, BaseFilename);
if(This)
{
// Reinsert
Found = TRUE;
MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * *EntryIndex;
IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex);
IndexEntryInsertionEnd = IndexEntryInsertionStart + This->Size;
if(*EntryIndex > 0)
{
Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex - 1));
Neighbours.Prev.BaseFilename = Prev->BaseFilename;
Neighbours.Prev.Title = Prev->Title;
}
if(*EntryIndex < Index->Header.EntryCount - 1)
{
Next = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex + 1));
Neighbours.Next.BaseFilename = Next->BaseFilename;
Neighbours.Next.Title = Next->Title;
}
}
else
{
This = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * *EntryIndex);
if(StringsDiffer(BaseFilename, This->BaseFilename) < 0)
{
// Insert
MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * *EntryIndex;
IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex);
if(*EntryIndex > 0)
{
Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex - 1));
Neighbours.Prev.BaseFilename = Prev->BaseFilename;
Neighbours.Prev.Title = Prev->Title;
}
Neighbours.Next.BaseFilename = This->BaseFilename;
Neighbours.Next.Title = This->Title;
}
else
{
// Append
if(*EntryIndex > 0)
{
Neighbours.Prev.BaseFilename = This->BaseFilename;
Neighbours.Prev.Title = This->Title;
}
++*EntryIndex;
}
}
#else
START_TIMING_BLOCK(" InsertIntoIndex(%s) LinearSearch: ", BaseFilename);
char *IndexEntryStart;
IndexEntryStart = Index->File.Buffer.Ptr;
for(*EntryIndex = 0; *EntryIndex < Index->Header.EntryCount; ++*EntryIndex)
{
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
if(!StringsDiffer(This->BaseFilename, BaseFilename))
{
// Reinsert
if(EntryIndex < (Index->Header.EntryCount - 1))
if(*EntryIndex < (Index->Header.EntryCount - 1))
{
Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry));
Neighbours.Next.BaseFilename = Next->BaseFilename;
@ -4463,9 +4582,11 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
Neighbours.Prev.Title = This->Title;
}
}
#endif
}
else
{
START_TIMING_BLOCK(" InsertIntoIndex(%s) NoSearch: ", BaseFilename);
// NOTE(matt): Initialising new index_header
Index->Header.CurrentDBVersion = CINERA_DB_VERSION;
Index->Header.CurrentAppVersion = CINERA_APP_VERSION;
@ -4507,6 +4628,7 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
}
closedir(OutputDirectoryHandle);
}
END_TIMING_BLOCK();
char InputFile[StringLength(BaseFilename) + StringLength(".hmml")];
CopyString(InputFile, "%s.hmml", BaseFilename);
@ -4783,7 +4905,7 @@ int DeleteNeighbourLinks(file_buffer *File, index_metadata *Metadata)
}
}
int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
int LinkNeighbours(index *Index, int EntryIndex, char *BaseFilename, int LinkType)
{
switch(ReadFileIntoBuffer(&Index->Metadata, 0))
{
@ -4801,8 +4923,24 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
index_metadata *Prev = { 0 };
index_metadata *This = { 0 };
index_metadata *Next = { 0 };
int EntryIndex = 0;
#if BINARY_SEARCH
START_TIMING_BLOCK(" LinkNeighbours(%s) [passing in EntryIndex]: ", BaseFilename);
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
if(Index->Header.EntryCount != 1)
{
This = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * EntryIndex);
if(EntryIndex > 0)
{
Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (EntryIndex - 1));
}
if(EntryIndex < Index->Header.EntryCount - 1)
{
Next = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (EntryIndex + 1));
}
}
#else
START_TIMING_BLOCK(" LinkNeighbours(%s) LinearSearch: ", BaseFilename);
switch(LinkType)
{
case LINK_INCLUDE:
@ -4848,6 +4986,8 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
}
} break;
}
#endif
END_TIMING_BLOCK();
if(!Prev && !Next)
{
@ -4953,7 +5093,7 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
}
int
DeleteFromIndex(index *Index, char *BaseFilename)
DeleteFromIndex(index *Index, int *EntryIndex, char *BaseFilename)
{
// TODO(matt): LogError()
switch(ReadFileIntoBuffer(&Index->Metadata, 0))
@ -4985,9 +5125,23 @@ DeleteFromIndex(index *Index, char *BaseFilename)
int DeleteMetadataFrom = -1;
int DeleteFileFrom = -1;
int DeleteFileTo = -1;
int SizeAcc = 0;
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata))
#if BINARY_SEARCH
START_TIMING_BLOCK(" DeleteFromIndex(%s) BinarySearch: ", BaseFilename);
index_metadata *This = { 0 };
*EntryIndex = BinarySearchForMetadataEntry(Index, &This, BaseFilename);
if(This)
{
Found = TRUE;
DeleteMetadataFrom = (char *)This - Index->Metadata.Buffer.Location;
DeleteFileFrom = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex);
DeleteFileTo = DeleteFileFrom + This->Size;
--Index->Header.EntryCount;
}
#else
START_TIMING_BLOCK(" DeleteFromIndex(%s) LinearSearch: ", BaseFilename);
int SizeAcc = 0;
for(*EntryIndex = 0; *EntryIndex < Index->Header.EntryCount; ++*EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata))
{
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
if(!StringsDiffer(This.BaseFilename, BaseFilename))
@ -5001,6 +5155,9 @@ DeleteFromIndex(index *Index, char *BaseFilename)
}
SizeAcc += This.Size;
}
#endif
END_TIMING_BLOCK();
if(Found)
{
@ -5019,6 +5176,11 @@ DeleteFromIndex(index *Index, char *BaseFilename)
}
else
{
if(*EntryIndex == Index->Header.EntryCount) // NOTE(matt): LinkNeighbours() requires this
{
--*EntryIndex;
}
if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; }
if(!(Index->File.Handle = fopen(Index->File.Path, "w"))) { FreeBuffer(&Index->File.Buffer); return RC_ERROR_FILE; }
@ -5043,18 +5205,10 @@ DeleteFromIndex(index *Index, char *BaseFilename)
int
IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy malloc's CollationBuffers->Index
{
// TODO(matt): Consider parsing the index into a linked / skip list, or do something to save us having to iterate through
// the index file multiple times
int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0);
int IndexFileReadCode = ReadFileIntoBuffer(&Index->File, 0);
if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS)
if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_SUCCESS)
{
Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr;
Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
Index->File.Buffer.Ptr += StringLength("---\n");
char *IndexEntryStart = Index->File.Buffer.Ptr;
bool ProjectFound = FALSE;
int ProjectIndex;
@ -5071,12 +5225,12 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
{
fprintf(stderr, "Missing Project Info for %s\n", Config.ProjectID);
FreeBuffer(&Index->Metadata.Buffer);
FreeBuffer(&Index->File.Buffer);
return RC_ERROR_PROJECT;
}
int ThemeStringLength = StringsDiffer(Config.Theme, "") ? (StringLength(Config.Theme) * 2) : (StringLength(Config.ProjectID) * 2);
char queryContainer[680 + ThemeStringLength];
char *Theme = StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID;
int ThemeStringAllowance = StringLength(Theme) * 2;
char queryContainer[680 + ThemeStringAllowance];
CopyString(queryContainer,
"<div class=\"cineraQueryContainer %s\">\n"
" <label for=\"query\">Query:</label>\n"
@ -5093,8 +5247,8 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
" <div id=\"cineraIndex\" class=\"%s\">\n"
" <div id=\"cineraIndexSort\">Sort: Old to New &#9206;</div>\n"
" <div id=\"cineraIndexEntries\">\n",
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID,
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID);
Theme,
Theme);
buffer URLPrefix;
ClaimBuffer(&URLPrefix, "URLPrefix", 1024);
@ -5118,7 +5272,7 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
" </script>\n"
" <script type=\"text/javascript\" src=\"%scinera_search.js\"></script>\n",
Config.ProjectID,
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID,
Theme,
Config.BaseURL,
Config.PlayerLocation,
StringsDiffer(Config.PlayerURLPrefix, "") ? Config.PlayerURLPrefix : Config.ProjectID,
@ -5132,18 +5286,26 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
if(!(CollationBuffers->Index.Location = malloc(CollationBuffers->Index.Size)))
{
FreeBuffer(&Index->Metadata.Buffer);
FreeBuffer(&Index->File.Buffer);
return(RC_ERROR_MEMORY);
}
CollationBuffers->Index.Ptr = CollationBuffers->Index.Location;
CopyStringToBuffer(&CollationBuffers->Index, queryContainer);
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
char *ProjectUnit = 0;
if(StringsDiffer(ProjectInfo[ProjectIndex].Unit, ""))
{
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
char Number[16];
CopyString(Number, This.BaseFilename + StringLength(Config.ProjectID));
ProjectUnit = ProjectInfo[ProjectIndex].Unit;
}
int ProjectIDLength = StringLength(Config.ProjectID);
char Number[16];
char Text[1024]; // NOTE(matt): Surely this will be big enough
index_metadata *This;
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata))
{
This = (index_metadata *)Index->Metadata.Buffer.Ptr;
CopyString(Number, This->BaseFilename + ProjectIDLength);
if(ProjectInfo[ProjectIndex].NumberingScheme == NS_LINEAR)
{
for(int i = 0; Number[i]; ++i)
@ -5155,24 +5317,18 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
}
}
SeekBufferForString(&Index->File.Buffer, "title: \"", C_SEEK_FORWARDS, C_SEEK_AFTER);
char Title[256];
CopyStringNoFormatT(Title, Index->File.Buffer.Ptr, '\n');
Title[StringLength(Title) - 1] = '\0';
ConstructPlayerURL(&PlayerURL, This->BaseFilename);
ConstructPlayerURL(&PlayerURL, This.BaseFilename);
if(StringsDiffer(ProjectInfo[ProjectIndex].Unit, ""))
if(ProjectUnit)
{
CopyStringToBuffer(&CollationBuffers->Index,
" <div>\n"
" <a href=\"%s\">", PlayerURL.Location);
char Text[1024]; // NOTE(matt): Surely this will be big enough
CopyString(Text, "%s %s: %s",
ProjectInfo[ProjectIndex].Unit, // TODO(matt): Do we need to special-case the various numbering schemes?
ProjectUnit, // TODO(matt): Do we need to special-case the various numbering schemes?
Number,
Title);
This->Title);
CopyStringToBufferHTMLSafe(&CollationBuffers->Index, Text);
@ -5184,15 +5340,13 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
{
CopyStringToBuffer(&CollationBuffers->Index,
" <div>\n"
" <a href=\"%s\">%s</a>\n"
" </div>\n",
PlayerURL.Location,
Title);
}
" <a href=\"%s\">", PlayerURL.Location);
Index->Metadata.Buffer.Ptr += sizeof(Index->Entry);
IndexEntryStart += This.Size;
Index->File.Buffer.Ptr = IndexEntryStart;
CopyStringToBufferHTMLSafe(&CollationBuffers->Index, This->Title);
CopyStringToBuffer(&CollationBuffers->Index,
"</a>\n"
" </div>\n");
}
}
DeclaimBuffer(&PlayerURL);
@ -5200,7 +5354,6 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
CopyStringToBuffer(&CollationBuffers->Index, Script);
FreeBuffer(&Index->Metadata.Buffer);
FreeBuffer(&Index->File.Buffer);
return RC_SUCCESS;
}
else
@ -5242,7 +5395,7 @@ StripSurroundingSlashes(char *String) // NOTE(matt): For relative paths
}
int
GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename)
GeneratePlayerPage(index *Index, int EntryIndex, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename)
{
buffer OutputDirectoryPath;
ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024);
@ -5281,7 +5434,12 @@ GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemp
ReadFileIntoBuffer(&Index->Metadata, 0);
Index->Metadata.Buffer.Ptr += sizeof(index_header);
int MetadataInsertionOffset = 0;
for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
#if BINARY_SEARCH
START_TIMING_BLOCK(" GeneratePlayerPage(%s) [passing in EntryIndex]: ", BaseFilename);
MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * EntryIndex;
#else
START_TIMING_BLOCK(" GeneratePlayerPage(%s) LinearSearch: ", BaseFilename);
for(EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex)
{
index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
if(!StringsDiffer(This.BaseFilename, Index->Entry.BaseFilename))
@ -5291,11 +5449,13 @@ GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemp
}
Index->Metadata.Buffer.Ptr += sizeof(index_metadata);
}
#endif
END_TIMING_BLOCK();
if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; }
fwrite(Index->Metadata.Buffer.Location, MetadataInsertionOffset, 1, Index->Metadata.Handle);
fwrite(&Index->Entry, sizeof(Index->Entry), 1, Index->Metadata.Handle);
fwrite(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry), Index->Metadata.FileSize - MetadataInsertionOffset - sizeof(Index->Entry), 1, Index->Metadata.Handle);
fwrite(Index->Metadata.Buffer.Location + MetadataInsertionOffset + sizeof(Index->Entry), Index->Metadata.FileSize - MetadataInsertionOffset - sizeof(Index->Entry), 1, Index->Metadata.Handle);
fclose(Index->Metadata.Handle);
FreeBuffer(&Index->Metadata.Buffer);
@ -5374,14 +5534,17 @@ DeletePlayerPageFromFilesystem(char *BaseFilename, char *PlayerLocation, bool Re
return RC_SUCCESS;
}
void
int
DeleteEntry(index *Index, char *BaseFilename)
{
if(DeleteFromIndex(Index, BaseFilename) == RC_SUCCESS)
int EntryIndex = 0;
if(DeleteFromIndex(Index, &EntryIndex, BaseFilename) == RC_SUCCESS)
{
LinkNeighbours(Index, BaseFilename, LINK_EXCLUDE);
LinkNeighbours(Index, EntryIndex, BaseFilename, LINK_EXCLUDE);
DeletePlayerPageFromFilesystem(BaseFilename, Config.PlayerLocation, FALSE);
return RC_SUCCESS;
}
return RC_NOOP;
}
int
@ -5419,24 +5582,28 @@ MonitorDirectory(index *Index, buffers *CollationBuffers, template *IndexTemplat
// TODO(matt): Maybe handle IN_ALL_EVENTS
if(Event->mask & IN_DELETE || Event->mask & IN_MOVED_FROM)
{
DeleteEntry(Index, BaseFilename);
if(DeleteEntry(Index, BaseFilename) == RC_SUCCESS)
{
GenerateIndexPage(Index, CollationBuffers, IndexTemplate);
}
}
else
{
switch(InsertIntoIndex(Index, CollationBuffers, &BespokeTemplate, BaseFilename))
int EntryIndex = 0;
switch(InsertIntoIndex(Index, &EntryIndex, CollationBuffers, &BespokeTemplate, BaseFilename))
{
case RC_SUCCESS:
case RC_UNFOUND:
LinkNeighbours(Index, BaseFilename, LINK_INCLUDE);
LinkNeighbours(Index, EntryIndex, BaseFilename, LINK_INCLUDE);
{
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
{
GeneratePlayerPage(Index, CollationBuffers, BespokeTemplate, BaseFilename);
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, BespokeTemplate, BaseFilename);
DeclaimTemplate(BespokeTemplate);
}
else
{
GeneratePlayerPage(Index, CollationBuffers, PlayerTemplate, BaseFilename);
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, BaseFilename);
}
GenerateIndexPage(Index, CollationBuffers, IndexTemplate);
} break;
@ -5804,20 +5971,21 @@ SyncIndexWithInput(index *Index, buffers *CollationBuffers, template *IndexTempl
if(!(StringsDiffer(Ptr, ".hmml")))
{
*Ptr = '\0';
switch(InsertIntoIndex(Index, CollationBuffers, &BespokeTemplate, ProjectFiles->d_name))
int EntryIndex = 0;
switch(InsertIntoIndex(Index, &EntryIndex, CollationBuffers, &BespokeTemplate, ProjectFiles->d_name))
{
case RC_UNFOUND:
LinkNeighbours(Index, ProjectFiles->d_name, LINK_INCLUDE);
LinkNeighbours(Index, EntryIndex, ProjectFiles->d_name, LINK_INCLUDE);
case RC_SUCCESS:
{
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
{
GeneratePlayerPage(Index, CollationBuffers, BespokeTemplate, ProjectFiles->d_name);
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, BespokeTemplate, ProjectFiles->d_name);
DeclaimTemplate(BespokeTemplate);
}
else
{
GeneratePlayerPage(Index, CollationBuffers, PlayerTemplate, ProjectFiles->d_name);
GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, ProjectFiles->d_name);
}
Inserted = TRUE;
}