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 = { version CINERA_APP_VERSION = {
.Major = 0, .Major = 0,
.Minor = 5, .Minor = 5,
.Patch = 37 .Patch = 38
}; };
// TODO(matt): Copy in the DB 3 stuff from cinera_working.c // TODO(matt): Copy in the DB 3 stuff from cinera_working.c
#define CINERA_DB_VERSION 3 #define CINERA_DB_VERSION 3
#define DEBUG 0
#define DEBUG_MEM 0
typedef unsigned int bool; typedef unsigned int bool;
#define TRUE 1 #define TRUE 1
#define FALSE 0 #define FALSE 0
#define DEBUG 0
#define DEBUG_MEM 0
#define BINARY_SEARCH 1
bool PROFILING = 0;
#include <stdarg.h> // NOTE(matt): varargs #include <stdarg.h> // NOTE(matt): varargs
#include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror #include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror
#include <stdlib.h> // NOTE(matt): calloc, malloc, free #include <stdlib.h> // NOTE(matt): calloc, malloc, free
@ -44,6 +45,10 @@ typedef unsigned int bool;
#include <sys/inotify.h> // NOTE(matt): inotify #include <sys/inotify.h> // NOTE(matt): inotify
#include <unistd.h> // NOTE(matt): sleep() #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 Kilobytes(Bytes) Bytes << 10
#define Megabytes(Bytes) Bytes << 20 #define Megabytes(Bytes) Bytes << 20
@ -880,11 +885,17 @@ DeclaimBuffer(buffer *Buffer)
MemoryArena.Ptr - MemoryArena.Location); MemoryArena.Ptr - MemoryArena.Location);
#endif #endif
LogUsage(Buffer); LogUsage(Buffer);
if(PercentageUsed >= 80.0f) if(PercentageUsed >= 95.0f)
{ {
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org // 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); 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; 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(&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(&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(&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, CopyStringToBuffer(&AnnotationHeader,
" <div data-timestamp=\"%d\"", " <div data-timestamp=\"%d\"",
@ -4373,7 +4384,61 @@ SeekBufferForString(buffer *Buffer, char *String,
} }
int 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); int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0);
switch(IndexMetadataFileReadCode) switch(IndexMetadataFileReadCode)
@ -4399,12 +4464,11 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
int IndexEntryInsertionStart = -1; int IndexEntryInsertionStart = -1;
int IndexEntryInsertionEnd = -1; int IndexEntryInsertionEnd = -1;
Index->Header.EntryCount = 0; Index->Header.EntryCount = 0;
char *IndexEntryStart;
bool Found = FALSE; bool Found = FALSE;
int EntryIndex;
neighbours Neighbours = { 0 }; neighbours Neighbours = { 0 };
index_metadata *This, *Next; index_metadata *This = { 0 };
index_metadata *Next = { 0 };
if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS) if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS)
{ {
// TODO(matt): Index validation? // TODO(matt): Index validation?
@ -4421,15 +4485,70 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
Index->Metadata.Buffer.Ptr += sizeof(Index->Header); Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
Index->File.Buffer.Ptr += StringLength("---\n"); 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; This = (index_metadata *)Index->Metadata.Buffer.Ptr;
if(!StringsDiffer(This->BaseFilename, BaseFilename)) if(!StringsDiffer(This->BaseFilename, BaseFilename))
{ {
// Reinsert // Reinsert
if(EntryIndex < (Index->Header.EntryCount - 1)) if(*EntryIndex < (Index->Header.EntryCount - 1))
{ {
Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry)); Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry));
Neighbours.Next.BaseFilename = Next->BaseFilename; Neighbours.Next.BaseFilename = Next->BaseFilename;
@ -4463,9 +4582,11 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
Neighbours.Prev.Title = This->Title; Neighbours.Prev.Title = This->Title;
} }
} }
#endif
} }
else else
{ {
START_TIMING_BLOCK(" InsertIntoIndex(%s) NoSearch: ", BaseFilename);
// NOTE(matt): Initialising new index_header // NOTE(matt): Initialising new index_header
Index->Header.CurrentDBVersion = CINERA_DB_VERSION; Index->Header.CurrentDBVersion = CINERA_DB_VERSION;
Index->Header.CurrentAppVersion = CINERA_APP_VERSION; Index->Header.CurrentAppVersion = CINERA_APP_VERSION;
@ -4507,6 +4628,7 @@ InsertIntoIndex(index *Index, buffers *CollationBuffers, template **BespokeTempl
} }
closedir(OutputDirectoryHandle); closedir(OutputDirectoryHandle);
} }
END_TIMING_BLOCK();
char InputFile[StringLength(BaseFilename) + StringLength(".hmml")]; char InputFile[StringLength(BaseFilename) + StringLength(".hmml")];
CopyString(InputFile, "%s.hmml", BaseFilename); 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)) switch(ReadFileIntoBuffer(&Index->Metadata, 0))
{ {
@ -4801,8 +4923,24 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
index_metadata *Prev = { 0 }; index_metadata *Prev = { 0 };
index_metadata *This = { 0 }; index_metadata *This = { 0 };
index_metadata *Next = { 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) switch(LinkType)
{ {
case LINK_INCLUDE: case LINK_INCLUDE:
@ -4848,6 +4986,8 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
} }
} break; } break;
} }
#endif
END_TIMING_BLOCK();
if(!Prev && !Next) if(!Prev && !Next)
{ {
@ -4953,7 +5093,7 @@ int LinkNeighbours(index *Index, char *BaseFilename, int LinkType)
} }
int int
DeleteFromIndex(index *Index, char *BaseFilename) DeleteFromIndex(index *Index, int *EntryIndex, char *BaseFilename)
{ {
// TODO(matt): LogError() // TODO(matt): LogError()
switch(ReadFileIntoBuffer(&Index->Metadata, 0)) switch(ReadFileIntoBuffer(&Index->Metadata, 0))
@ -4985,9 +5125,23 @@ DeleteFromIndex(index *Index, char *BaseFilename)
int DeleteMetadataFrom = -1; int DeleteMetadataFrom = -1;
int DeleteFileFrom = -1; int DeleteFileFrom = -1;
int DeleteFileTo = -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; index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
if(!StringsDiffer(This.BaseFilename, BaseFilename)) if(!StringsDiffer(This.BaseFilename, BaseFilename))
@ -5001,6 +5155,9 @@ DeleteFromIndex(index *Index, char *BaseFilename)
} }
SizeAcc += This.Size; SizeAcc += This.Size;
} }
#endif
END_TIMING_BLOCK();
if(Found) if(Found)
{ {
@ -5019,6 +5176,11 @@ DeleteFromIndex(index *Index, char *BaseFilename)
} }
else 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->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; } 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 int
IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy malloc's CollationBuffers->Index 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 if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_SUCCESS)
// the index file multiple times
int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0);
int IndexFileReadCode = ReadFileIntoBuffer(&Index->File, 0);
if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS)
{ {
Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr; Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr;
Index->Metadata.Buffer.Ptr += sizeof(Index->Header); Index->Metadata.Buffer.Ptr += sizeof(Index->Header);
Index->File.Buffer.Ptr += StringLength("---\n");
char *IndexEntryStart = Index->File.Buffer.Ptr;
bool ProjectFound = FALSE; bool ProjectFound = FALSE;
int ProjectIndex; 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); fprintf(stderr, "Missing Project Info for %s\n", Config.ProjectID);
FreeBuffer(&Index->Metadata.Buffer); FreeBuffer(&Index->Metadata.Buffer);
FreeBuffer(&Index->File.Buffer);
return RC_ERROR_PROJECT; return RC_ERROR_PROJECT;
} }
int ThemeStringLength = StringsDiffer(Config.Theme, "") ? (StringLength(Config.Theme) * 2) : (StringLength(Config.ProjectID) * 2); char *Theme = StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID;
char queryContainer[680 + ThemeStringLength]; int ThemeStringAllowance = StringLength(Theme) * 2;
char queryContainer[680 + ThemeStringAllowance];
CopyString(queryContainer, CopyString(queryContainer,
"<div class=\"cineraQueryContainer %s\">\n" "<div class=\"cineraQueryContainer %s\">\n"
" <label for=\"query\">Query:</label>\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=\"cineraIndex\" class=\"%s\">\n"
" <div id=\"cineraIndexSort\">Sort: Old to New &#9206;</div>\n" " <div id=\"cineraIndexSort\">Sort: Old to New &#9206;</div>\n"
" <div id=\"cineraIndexEntries\">\n", " <div id=\"cineraIndexEntries\">\n",
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID, Theme,
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID); Theme);
buffer URLPrefix; buffer URLPrefix;
ClaimBuffer(&URLPrefix, "URLPrefix", 1024); ClaimBuffer(&URLPrefix, "URLPrefix", 1024);
@ -5118,7 +5272,7 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
" </script>\n" " </script>\n"
" <script type=\"text/javascript\" src=\"%scinera_search.js\"></script>\n", " <script type=\"text/javascript\" src=\"%scinera_search.js\"></script>\n",
Config.ProjectID, Config.ProjectID,
StringsDiffer(Config.Theme, "") ? Config.Theme : Config.ProjectID, Theme,
Config.BaseURL, Config.BaseURL,
Config.PlayerLocation, Config.PlayerLocation,
StringsDiffer(Config.PlayerURLPrefix, "") ? Config.PlayerURLPrefix : Config.ProjectID, 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))) if(!(CollationBuffers->Index.Location = malloc(CollationBuffers->Index.Size)))
{ {
FreeBuffer(&Index->Metadata.Buffer); FreeBuffer(&Index->Metadata.Buffer);
FreeBuffer(&Index->File.Buffer);
return(RC_ERROR_MEMORY); return(RC_ERROR_MEMORY);
} }
CollationBuffers->Index.Ptr = CollationBuffers->Index.Location; CollationBuffers->Index.Ptr = CollationBuffers->Index.Location;
CopyStringToBuffer(&CollationBuffers->Index, queryContainer); 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; ProjectUnit = ProjectInfo[ProjectIndex].Unit;
}
int ProjectIDLength = StringLength(Config.ProjectID);
char Number[16]; char Number[16];
CopyString(Number, This.BaseFilename + StringLength(Config.ProjectID)); 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) if(ProjectInfo[ProjectIndex].NumberingScheme == NS_LINEAR)
{ {
for(int i = 0; Number[i]; ++i) 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); ConstructPlayerURL(&PlayerURL, This->BaseFilename);
char Title[256];
CopyStringNoFormatT(Title, Index->File.Buffer.Ptr, '\n');
Title[StringLength(Title) - 1] = '\0';
ConstructPlayerURL(&PlayerURL, This.BaseFilename); if(ProjectUnit)
if(StringsDiffer(ProjectInfo[ProjectIndex].Unit, ""))
{ {
CopyStringToBuffer(&CollationBuffers->Index, CopyStringToBuffer(&CollationBuffers->Index,
" <div>\n" " <div>\n"
" <a href=\"%s\">", PlayerURL.Location); " <a href=\"%s\">", PlayerURL.Location);
char Text[1024]; // NOTE(matt): Surely this will be big enough
CopyString(Text, "%s %s: %s", 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, Number,
Title); This->Title);
CopyStringToBufferHTMLSafe(&CollationBuffers->Index, Text); CopyStringToBufferHTMLSafe(&CollationBuffers->Index, Text);
@ -5184,15 +5340,13 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
{ {
CopyStringToBuffer(&CollationBuffers->Index, CopyStringToBuffer(&CollationBuffers->Index,
" <div>\n" " <div>\n"
" <a href=\"%s\">%s</a>\n" " <a href=\"%s\">", PlayerURL.Location);
" </div>\n",
PlayerURL.Location,
Title);
}
Index->Metadata.Buffer.Ptr += sizeof(Index->Entry); CopyStringToBufferHTMLSafe(&CollationBuffers->Index, This->Title);
IndexEntryStart += This.Size; CopyStringToBuffer(&CollationBuffers->Index,
Index->File.Buffer.Ptr = IndexEntryStart; "</a>\n"
" </div>\n");
}
} }
DeclaimBuffer(&PlayerURL); DeclaimBuffer(&PlayerURL);
@ -5200,7 +5354,6 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m
CopyStringToBuffer(&CollationBuffers->Index, Script); CopyStringToBuffer(&CollationBuffers->Index, Script);
FreeBuffer(&Index->Metadata.Buffer); FreeBuffer(&Index->Metadata.Buffer);
FreeBuffer(&Index->File.Buffer);
return RC_SUCCESS; return RC_SUCCESS;
} }
else else
@ -5242,7 +5395,7 @@ StripSurroundingSlashes(char *String) // NOTE(matt): For relative paths
} }
int int
GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename) GeneratePlayerPage(index *Index, int EntryIndex, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename)
{ {
buffer OutputDirectoryPath; buffer OutputDirectoryPath;
ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024); ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024);
@ -5281,7 +5434,12 @@ GeneratePlayerPage(index *Index, buffers *CollationBuffers, template *PlayerTemp
ReadFileIntoBuffer(&Index->Metadata, 0); ReadFileIntoBuffer(&Index->Metadata, 0);
Index->Metadata.Buffer.Ptr += sizeof(index_header); Index->Metadata.Buffer.Ptr += sizeof(index_header);
int MetadataInsertionOffset = 0; 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; index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr;
if(!StringsDiffer(This.BaseFilename, Index->Entry.BaseFilename)) 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); 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; } 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->Metadata.Buffer.Location, MetadataInsertionOffset, 1, Index->Metadata.Handle);
fwrite(&Index->Entry, sizeof(Index->Entry), 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); fclose(Index->Metadata.Handle);
FreeBuffer(&Index->Metadata.Buffer); FreeBuffer(&Index->Metadata.Buffer);
@ -5374,14 +5534,17 @@ DeletePlayerPageFromFilesystem(char *BaseFilename, char *PlayerLocation, bool Re
return RC_SUCCESS; return RC_SUCCESS;
} }
void int
DeleteEntry(index *Index, char *BaseFilename) 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); DeletePlayerPageFromFilesystem(BaseFilename, Config.PlayerLocation, FALSE);
return RC_SUCCESS;
} }
return RC_NOOP;
} }
int int
@ -5419,24 +5582,28 @@ MonitorDirectory(index *Index, buffers *CollationBuffers, template *IndexTemplat
// TODO(matt): Maybe handle IN_ALL_EVENTS // TODO(matt): Maybe handle IN_ALL_EVENTS
if(Event->mask & IN_DELETE || Event->mask & IN_MOVED_FROM) if(Event->mask & IN_DELETE || Event->mask & IN_MOVED_FROM)
{ {
DeleteEntry(Index, BaseFilename); if(DeleteEntry(Index, BaseFilename) == RC_SUCCESS)
{
GenerateIndexPage(Index, CollationBuffers, IndexTemplate);
}
} }
else else
{ {
switch(InsertIntoIndex(Index, CollationBuffers, &BespokeTemplate, BaseFilename)) int EntryIndex = 0;
switch(InsertIntoIndex(Index, &EntryIndex, CollationBuffers, &BespokeTemplate, BaseFilename))
{ {
case RC_SUCCESS: case RC_SUCCESS:
case RC_UNFOUND: case RC_UNFOUND:
LinkNeighbours(Index, BaseFilename, LINK_INCLUDE); LinkNeighbours(Index, EntryIndex, BaseFilename, LINK_INCLUDE);
{ {
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, "")) if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, ""))
{ {
GeneratePlayerPage(Index, CollationBuffers, BespokeTemplate, BaseFilename); GeneratePlayerPage(Index, EntryIndex, CollationBuffers, BespokeTemplate, BaseFilename);
DeclaimTemplate(BespokeTemplate); DeclaimTemplate(BespokeTemplate);
} }
else else
{ {
GeneratePlayerPage(Index, CollationBuffers, PlayerTemplate, BaseFilename); GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, BaseFilename);
} }
GenerateIndexPage(Index, CollationBuffers, IndexTemplate); GenerateIndexPage(Index, CollationBuffers, IndexTemplate);
} break; } break;
@ -5804,20 +5971,21 @@ SyncIndexWithInput(index *Index, buffers *CollationBuffers, template *IndexTempl
if(!(StringsDiffer(Ptr, ".hmml"))) if(!(StringsDiffer(Ptr, ".hmml")))
{ {
*Ptr = '\0'; *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: case RC_UNFOUND:
LinkNeighbours(Index, ProjectFiles->d_name, LINK_INCLUDE); LinkNeighbours(Index, EntryIndex, ProjectFiles->d_name, LINK_INCLUDE);
case RC_SUCCESS: case RC_SUCCESS:
{ {
if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, "")) 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); DeclaimTemplate(BespokeTemplate);
} }
else else
{ {
GeneratePlayerPage(Index, CollationBuffers, PlayerTemplate, ProjectFiles->d_name); GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, ProjectFiles->d_name);
} }
Inserted = TRUE; Inserted = TRUE;
} }