From ba26575890dcb3a5244a337a4e4758c01b9aca9b Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Sat, 26 Apr 2014 19:47:10 +0100
Subject: [PATCH] [Add] Started to work on cacheing the packfile.

---
 src/pack.c | 240 +++++++++++++++++++++++++++++++++++++++++------------
 src/pack.h |  10 +++
 2 files changed, 198 insertions(+), 52 deletions(-)

diff --git a/src/pack.c b/src/pack.c
index e1b2a48..3d2581c 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -33,8 +33,6 @@
  */
 
 /**
- * @struct Packfile
- *
  * @brief Abstracts around packfiles.
  */
 struct Packfile_s {
@@ -46,8 +44,36 @@ struct Packfile_s {
   uint32_t pos;   /**< Cursor position. */
   uint32_t start; /**< File start. */
   uint32_t end;   /**< File end. */
+
+  uint32_t flags; /**< Special control flags. */
 };
 
+/**
+ * @brief Allows much faster creation of packfiles.
+ */
+struct Packcache_s {
+#ifdef _POSIX_SOURCE
+  int fd; /**< File descriptor. */
+#else
+  FILE* fp; /* For non-posix. */
+#endif
+
+  char** index;     /**< Cached index for faster lookups. */
+  uint32_t* start;  /**< Cached index starts. */
+  uint32_t* nindex; /**< Number of index entries. */
+};
+
+/* Helper defines. */
+#ifdef _POSIX_SOURCE
+#define READ(f, b, n) if(read((f)->fd, (b), (n)) != (n)) { \
+  ERR("Fewer bytes read then expected"); \
+  return NULL; }
+#else
+#define READ(f, b, n) if(fread((b), 1, (n),(f)->fp) != (n)) { \
+  ERR("Fewer bytes read then expected"); \
+  return NULL; }
+#endif
+
 #undef DEBUG /* This will be spammy. */
 #define DEBUG(str, args...) do{;} while(0)
 
@@ -62,9 +88,142 @@ struct Packfile_s {
 
 const uint64_t magic = 0x25524573; /**< File magic number: sER% */
 
+/* Flags. */
+#define PACKFILE_FROMCACHE (1<<0) /**< Packfile comes from a packcache. */
+
+static off_t getfilesize(const char* filename);
+
+/**
+ * @brief Open a packfile as a cache.
+ *    @param packfile Name of the packfile to cache.
+ *    @return NULL if an error occured or the Packcache.
+ */
+Packcache_t* pack_openCache(const char* packfile) {
+  int j;
+  uint32_t i;
+  char buf[PATH_MAX];
+  Packcache_t* cache;
+
+  /* Allocate memory. */
+  cache = calloc(1, sizeof(Packcache_t));
+  if(cache == NULL) {
+    ERR("Out of memory.");
+    return NULL;
+  }
+
+  /* Open file. */
+#ifdef _POSIX_SOURCE
+  cache->fd = open(packfile, O_RDONLY);
+  if(cache->fd == -1) {
+#else
+  cache->fp = fopen(packfile, "rb");
+  if(cache->fp == NULL) {
+#endif
+    ERR("Error opening %s: %s", packfile, strerror(errno));
+    return NULL;
+  } 
+  /* Check for validity. */
+  READ(cache, buf, sizeof(magic));
+  if(memcmp(buf, &magic, sizeof(magic))) {
+    ERR("File %s is not a valid packfile", packfile);
+    return NULL;
+  }
+
+  /* Get the number of files and allocate memory. */
+  READ(cache, &cache->nindex, 4);
+  cache->index = calloc(cache->nindex, sizeof(char*));
+  cache->start = calloc(cache->nindex, sizeof(uint32_t));
+
+  /* Read index. */
+  for(i = 0; i < cache->nindex; i++) { /* Start to search files. */
+    j = 0;
+    READ(cache, &buf[j], 1); /* Get the name. */
+    while(buf[j++] != '\0')
+      READ(cache, &buf[j], 1);
+
+    cache->index[i] = strdup(buf);
+    READ(cache, &cache->start[i], 4);
+    DEBUG("'%s' found at %d", filename, cache->start[i]);
+    break;
+  }
+  /* Return the built cache. */
+  return cache;
+}
+
+/**
+ * @brief Close a Packcache.
+ *    @param cache Packcache to close.
+ */
+void pack_closeCache(Packcache_t* cache) {
+  uint32_t i;
+
+  /* Close file. */
+#ifdef _POSIX_SOURCE
+  close(cache->fd);
+#else
+  fclose(cache->fp);
+#endif
+
+  /* Free memory. */
+  if(cache->nindex > 0) {
+    for(i = 0; i < cache->nindex; i++)
+      free(cache->index[i]);
+    free(cache->index);
+    free(cache->start);
+  }
+}
+
+/**
+ * @brief Open a packfile from a Packcache.
+ *    @param cache Packcache to create Packfile from.
+ *    @param filename Name of the file to open in the Cache.
+ *    @return A packfile for filename from cache.
+ */
+Packfile_t* pack_openFromCache(Packcache_t* cache, const char* filename) {
+  uint32_t i;
+  Packfile_t* file;
+
+  file = calloc(1, sizeof(Packfile_t));
+
+  for(i = 0; i < cache->nindex; i++) {
+    if(strcmp(cache->index[i], filename)==0) {
+      /* Copy file. */
+#ifdef _POSIX_SOURCE
+      file->fd = dup(cache->fd);
+#else
+      file->fp = cache->fp;
+#endif
+
+      /* Copy information. */
+      file->flags |= PACKFILE_FROMCACHE;
+      file->start = cache->start[i];
+
+      /* Seek. */
+      if(file->start) { /* Go to the beginning of the file. */
+#ifdef _POSIX_SOURCE
+        if((uint32_t)lseek(file->fd, file->start, SEEK_SET) != file->start) {
+#else
+        fseek(file->fp, file->start, SEEK_SET);
+        if(errno) {
+#endif
+          ERR("Failure to seek to file start: %s", strerror(errno));
+          return NULL;
+        }
+
+        READ(file, &file->end, 4);
+        DEBUG("\t%d bytes", file->end);
+        file->pos = file->start;
+        file->end += file->start;
+      }
+
+      break;
+    }
+  }
+
+    return NULL;
+}
+
 /**
- * @fn static off_t getfilesize(const char* filename)
- *
  * @brief Get the file size.
  *    @param filename File to get the size of.
  *    @return The size of the file.
@@ -269,15 +428,6 @@ int pack_files(const char* outfile, const char** infiles, const uint32_t nfiles)
 }
 #undef WRITE
 
-#ifdef _POSIX_SOURCE
-#define READ(b,n) if(read(file->fd, (b), (n))!=(n)) { \
-  ERR("Fewer bytes read than expected."); \
-  free(buf); return NULL; }
-#else
-#define READ(b,n) if(fread((b), 1, (n), file->fp)!=(n)) { \
-  ERR("Fewer bytes read then expected"); \
-  free(buf); return NULL; }
-#endif
 /**
  * @brief Open a file in the packfile for reading.
  *    @param file Packfile to store data into.
@@ -288,13 +438,12 @@ int pack_files(const char* outfile, const char** infiles, const uint32_t nfiles)
 Packfile_t* pack_open(const char* packfile, const char* filename) {
   int j;
   uint32_t nfiles, i;
-  char* buf = malloc(PATH_MAX);
+  char buf[PATH_MAX];
   Packfile_t* file;
 
   /* Allocate memory. */
   file = malloc(sizeof(Packfile_t));
   memset(file, 0, sizeof(Packfile_t));
-  buf = malloc(PATH_MAX);
 
 #ifdef _POSIX_SOURCE
   file->fd = open(packfile, O_RDONLY);
@@ -307,23 +456,23 @@ Packfile_t* pack_open(const char* packfile, const char* filename) {
     return NULL;
   }
 
-  READ(buf, sizeof(magic)); /* Make sure it's a packfile. */
+  READ(file, buf, sizeof(magic)); /* Make sure it's a packfile. */
   if(memcmp(buf, &magic, sizeof(magic))) {
     ERR("File %s is not a valid packfile", filename);
     return NULL;
   }
 
-  READ(&nfiles, 4);
+  READ(file, &nfiles, 4);
   for(i = 0; i < nfiles; i++) {
     /* Start to search files. */
     j = 0;
-    READ(&buf[j], 1); /* Get the name. */
+    READ(file, &buf[j], 1); /* Get the name. */
     while(buf[j++] != '\0')
-      READ(&buf[j], 1);
+      READ(file, &buf[j], 1);
 
     if(strcmp(filename, buf) == 0) {
       /* We found the file. */
-      READ(&file->start, 4);
+      READ(file, &file->start, 4);
       DEBUG("'%s' found at %d", filename, file->start);
       break;
     }
@@ -333,7 +482,6 @@ Packfile_t* pack_open(const char* packfile, const char* filename) {
     fseek(file->fp, 4, SEEK_CUR);
 #endif
   }
-  free(buf);
 
   if(file->start) {
     /* Go to the beginning of the file. */
@@ -346,7 +494,7 @@ Packfile_t* pack_open(const char* packfile, const char* filename) {
       ERR("Failure to seek to file start: %s", strerror(errno));
       return NULL;
     }
-    READ(&file->end, 4);
+    READ(file, &file->end, 4);
     DEBUG("\t%d bytes", file->end);
     file->pos = file->start;
     file->end += file->start;
@@ -356,7 +504,6 @@ Packfile_t* pack_open(const char* packfile, const char* filename) {
   }
   return file;
 }
-#undef READ
 
 /**
  * @brief Reads data from a packfile.
@@ -368,12 +515,12 @@ Packfile_t* pack_open(const char* packfile, const char* filename) {
  *    @return Bytes to read or -1 on error.
  */
 ssize_t pack_read(Packfile_t* file, void* buf, size_t count) {
+  int bytes;
+
   if(file->pos + count > file->end)
     count = file->end - file->pos; /* Can't go past the end! */
   if(count == 0) return 0;
 
-  int bytes;
-
 #ifdef _POSIX_SOURCE
   if((bytes = read(file->fd, buf, count)) == -1) {
 #else
@@ -538,66 +685,52 @@ void* pack_readfile(const char* packfile, const char* filename, uint32_t* filesi
  *    @param nfiles Stores the amount of files in packfile.
  *    @return An array of filenames in packfile.
  */
-#ifdef _POSIX_SOURCE
-#define READ(b,n) if(read(fd, (b), (n))!=(n)) { \
-  ERR("Too few bytes read"); \
-  return NULL; }
-#else
-#define READ(b,n) if(fread((b), 1, (n), fp) != (n)) { \
-  ERR("Too few bytes read"); \
-  return NULL; }
-#endif
 char** pack_listfiles(const char* packfile, uint32_t* nfiles) {
-#ifdef _POSIX_SOURCE
-  int fd;
-#else
-  FILE* fp;
-#endif
   int j;
   uint32_t i;
+  Packfile_t file;
   char** filenames;
   char* buf = malloc(sizeof(magic));
 
   *nfiles = 0;
 
 #ifdef _POSIX_SOURCE
-  fd = open(packfile, O_RDONLY);
-  if(fd == -1) {
+  file.fd = open(packfile, O_RDONLY);
+  if(file.fd == -1) {
 #else
-  fp = fopen(packfile, "rb");
-  if(fp == NULL) {
+  file.fp = fopen(packfile, "rb");
+  if(file.fp == NULL) {
 #endif
     ERR("opening %s: %s", packfile, strerror(errno));
     return NULL;
   }
-  READ(buf, sizeof(magic)); /* Make sure it is a packfile. */
+  READ(&file, buf, sizeof(magic)); /* Make sure it's a packfile. */
   if(memcmp(buf, &magic, sizeof(magic))) {
     ERR("File %s is not a valid packfile", packfile);
     return NULL;
   }
 
-  READ(nfiles, 4);
+  READ(&file, nfiles, 4);
   filenames = malloc(((*nfiles)+1)*sizeof(char*));
   for(i = 0; i < *nfiles; i++) {
     /* Start searching files. */
     j = 0;
     filenames[i] = malloc(PATH_MAX * sizeof(char));
-    READ(&filenames[i][j], 1); /* Get the name. */
+    READ(&file, &filenames[i][j], 1); /* Get the name. */
     while(filenames[i][j++] != '\0')
-      READ(&filenames[i][j], 1);
-    READ(buf, 4); /* skip the location. */
+      READ(&file, &filenames[i][j], 1);
+    READ(&file, buf, 4); /* skip the location. */
   }
   free(buf);
 
 #ifdef _POSIX_SOURCE
-  close(fd);
+  close(file.fd);
 #else
-  fclose(fp);
+  fclose(file.fp);
 #endif
 
   return filenames;
 }
-#undef READ
 
 /**
  * @brief Closes a packfile.
@@ -609,7 +742,10 @@ int pack_close(Packfile_t* file) {
 #ifdef _POSIX_SOURCE
   i = close(file->fd);
 #else
-  i = fclose(file->fp);
+  if(file->flags & PACKFILE_FROMCACHE)
+    i = 0;
+  else
+    i = fclose(file->fp);
 #endif
   return (i) ? -1 : 0;
 }
diff --git a/src/pack.h b/src/pack.h
index 2be5f36..22e7372 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -9,6 +9,16 @@
 struct Packfile_s;
 typedef struct Packfile_s Packfile_t;
 
+struct Packcache_s;
+typedef struct Packcache_s Packcache_t;
+
+/*
+ * Packcache manipulation.
+ */
+Packcache_t* pack_openCache(const char* packfile);
+void pack_closeCache(Packcache_t* cache);
+Packfile_t* pack_openFromCache(Packcache_t* cache, const char* filename);
+
 /* Packfile manipulation. Automatically allocated and freed (with open and close). */
 
 /* Basic. */