basic image/cache code compiles
authorTero Marttila <terom@fixme.fi>
Sun, 27 Dec 2009 23:14:10 +0200
changeset 1 f3cde3db1fef
parent 0 cff7fac35cc2
child 2 05de54150a4c
basic image/cache code compiles
.hgignore
Makefile
src/lib/cache.c
src/lib/cache.h
src/lib/image.c
src/lib/image.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sun Dec 27 23:14:10 2009 +0200
@@ -0,0 +1,10 @@
+syntax: regexp
+
+\.[^/]+\.sw[op]$
+
+^build/
+^bin/
+^lib/
+^doc/html
+^dist
+^Learning Diary\.pdf$
--- a/Makefile	Sun Dec 27 22:01:17 2009 +0200
+++ b/Makefile	Sun Dec 27 23:14:10 2009 +0200
@@ -6,22 +6,17 @@
 # preprocessor flags
 CPPFLAGS = -Isrc/
 
+# libraries to use
+LOADLIBES = -lpng
+
 # output name
-DIST_NAME = 78949E-as1
+DIST_NAME = 78949E-as2
 DIST_RESOURCES = README "Learning Diary.pdf"
 
-all: depend bin/daemon lib/libnetdaemon.so bin/client
+all: depend lib/libpngtile.so
 
-bin/daemon : lib/libnetdaemon.so \
-	build/obj/daemon/daemon.o build/obj/daemon/service.o build/obj/daemon/client.o build/obj/daemon/commands.o \
-    build/obj/daemon/process.o \
-	build/obj/shared/select.o build/obj/shared/log.o build/obj/shared/util.o build/obj/shared/signal.o
-
-lib/libnetdaemon.so : \
-    build/obj/lib/client.o build/obj/lib/commands.o \
-    build/obj/shared/proto.o
-
-bin/client : lib/libnetdaemon.so build/obj/shared/log.o
+lib/libpngtile.so : \
+	build/obj/lib/image.o build/obj/lib/cache.o
 
 SRC_PATHS = $(wildcard src/*/*.c)
 SRC_NAMES = $(patsubst src/%,%,$(SRC_PATHS))
--- a/src/lib/cache.c	Sun Dec 27 22:01:17 2009 +0200
+++ b/src/lib/cache.c	Sun Dec 27 23:14:10 2009 +0200
@@ -1,55 +1,234 @@
 #include "cache.h"
 
-static int pt_cache_new (struct pt_cache **cache_ptr)
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+
+
+static int pt_cache_new (struct pt_cache **cache_ptr, const char *path)
 {
     struct pt_cache *cache;
 
+    // alloc
     if ((cache = calloc(1, sizeof(*cache))) == NULL)
         return -1;
 
+    if ((cache->path = strdup(path)) == NULL)
+        goto error;
+
+    // init
+    cache->fd = -1;
+
+    // ok
+    *cache_ptr = cache;
+
+    return 0;
+
+error:
+    // cleanup
+    if (cache)
+        pt_cache_destroy(cache);
+
+    return -1;
+}
+
+int pt_cache_open (struct pt_cache **cache_ptr, const char *path, int mode)
+{
+    struct pt_cache *cache;
+    
+    // alloc
+    if (pt_cache_new(&cache, path))
+        return -1;
+    
     // ok
     *cache_ptr = cache;
 
     return 0;
 }
 
-int pt_cache_open (struct pt_cache **cache_ptr, struct pt_image *img, int mode)
-{
-    struct pt_cache *cache;
-    
-    // alloc
-    if (pt_cache_new(&cache))
-        return -1;
-
-    
-}
-
-bool pt_cache_fresh (struct pt_cache *cache)
+int pt_cache_stale (struct pt_cache *cache, const char *orig_path)
 {
     // TODO: stat + mtime
     return false;
 }
 
 /**
+ * Abort any incomplete open operation, cleaning up
+ */
+static void pt_cache_abort (struct pt_cache *cache)
+{
+    if (cache->mmap != NULL) {
+        munmap(cache->mmap, cache->size);
+
+        cache->mmap = NULL;
+    }
+
+    if (cache->fd >= 0) {
+        close(cache->fd);
+
+        cache->fd = -1;
+    }
+}
+
+/**
+ * Open the cache file as an fd.
+ *
+ * XXX: needs locking
+ */
+static int pt_cache_open_fd (struct pt_cache *cache, int *fd_ptr)
+{
+    int fd;
+    int flags = 0;
+
+    // determine open flags
+    // XXX: O_RDONLY | O_WRONLY == O_RDWR?
+    if (cache->mode & PT_IMG_READ)
+        flags |= O_RDONLY;
+
+    if (cache->mode & PT_IMG_WRITE)
+        flags |= (O_WRONLY | O_CREAT);
+
+    // actual open()
+    if ((fd = open(cache->path, flags)) < 0)
+        return -1;
+
+    // ok
+    *fd_ptr = fd;
+
+    return 0;
+}
+
+/**
+ * Mmap the opened cache file from offset PT_CACHE_PAGE, using the calculated size stored in cache->size
+ */
+static int pt_cache_open_mmap (struct pt_cache *cache, void **addr_ptr)
+{
+    int prot = 0;
+    void *addr;
+
+    // determine prot
+    if (cache->mode & PT_IMG_READ)
+        prot |= PROT_READ;
+
+    if (cache->mode & PT_IMG_WRITE)
+        prot |= PROT_WRITE;
+
+    // perform mmap() from second page on
+    if ((addr = mmap(NULL, cache->size, prot, MAP_SHARED, cache->fd, PT_CACHE_PAGE)) == MAP_FAILED)
+        return -1;
+
+    // ok
+    *addr_ptr = addr;
+
+    return 0;
+}
+
+/**
+ * Write out the cache header into the opened file
+ */
+static int pt_cache_write_header (struct pt_cache *cache, const struct pt_cache_header *header)
+{
+    size_t len = sizeof(*header);
+    const char *buf = (const char *) header;
+
+    // seek to start
+    if (lseek(cache->fd, 0, SEEK_SET) != 0)
+        return -1;
+
+    // write out full header
+    while (len) {
+        ssize_t ret;
+        
+        // try and write out the header
+        if ((ret = write(cache->fd, buf, len)) < 0)
+            return -1;
+
+        // update offset
+        buf += ret;
+        len -= ret;
+    }
+
+    // done
+    return 0;
+}
+
+/**
  * Create a new cache file, open it, and write out the header.
  */
-static int pt_cache_create (struct pt_cache *cache, struct pt_cache_header *header)
+static int pt_cache_open_create (struct pt_cache *cache, struct pt_cache_header *header)
 {
-    
+    // open
+    if (pt_cache_open_fd(cache, &cache->fd))
+        return -1;
+
+    // calculate data size
+    cache->size = sizeof(*header) + header->height * header->row_bytes;
+
+    // grow file
+    if (ftruncate(cache->fd, PT_CACHE_PAGE + cache->size) < 0)
+        goto error;
+
+    // open mmap
+    if (pt_cache_open_mmap(cache, (void **) &cache->mmap))
+        goto error;
+
+    // write header
+    if (pt_cache_write_header(cache, header))
+        goto error;
+
+    // done
+    return 0;
+
+error:
+    // cleanup
+    pt_cache_abort(cache);
+
+    return -1;
 }
 
 int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info)
 {
     struct pt_cache_header header;
+    
+    // XXX: check cache_mode
+    // XXX: check image doesn't use any options we don't handle
 
     memset(&header, 0, sizeof(header));
 
     // fill in basic info
-    header->width = png_get_image_width(png, info);
-    header->height = png_get_image_height(png, info);
-    header->bit_depth = png_get_bit_depth(png, info);
-    header->color_type = png_get_color_type(png, info);
+    header.width = png_get_image_width(png, info);
+    header.height = png_get_image_height(png, info);
+    header.bit_depth = png_get_bit_depth(png, info);
+    header.color_type = png_get_color_type(png, info);
 
     // fill in other info
-    header->row_bytes = png_get_rowbytes(png, info);
+    header.row_bytes = png_get_rowbytes(png, info);
+
+    // create and write out header
+    if (pt_cache_open_create(cache, &header))
+        return -1;
+
+    // XXX: pallette etc.
+
+    // write out raw image data a row at a time
+    for (size_t row = 0; row < header.height; row++) {
+        // read row data, non-interlaced
+        png_read_row(png, cache->mmap + row * header.row_bytes, NULL);
+    }
+
+    // done!
+    return 0;
 }
+
+void pt_cache_destroy (struct pt_cache *cache)
+{
+    free(cache->path);
+
+    pt_cache_abort(cache);
+
+    free(cache);
+}
--- a/src/lib/cache.h	Sun Dec 27 22:01:17 2009 +0200
+++ b/src/lib/cache.h	Sun Dec 27 23:14:10 2009 +0200
@@ -8,14 +8,37 @@
  */
 #include "image.h"
 
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <png.h>
+
 /**
  * State for cache access
  */
 struct pt_cache {
+    /** Filesystem path to cache file */
+    char *path;
 
+    /** The mode we are operating in, bitmask of PT_IMG_* */
+    int mode;
+    
+    /** Opened file */
+    int fd;
+
+    /** Memory-mapped file data, from the second page on */
+    uint8_t *mmap;
+
+    /** Size of the mmap'd segment in bytes */
+    size_t size;
 };
 
 /**
+ * Size of a cache file page in bytes
+ */
+#define PT_CACHE_PAGE 4096
+
+/**
  * On-disk header
  */
 struct pt_cache_header {
@@ -32,16 +55,21 @@
 /**
  * Construct the image cache info object associated with the given image.
  */
-int pt_cache_open (struct pt_cache **cache_ptr, struct pt_image *img, int mode);
+int pt_cache_open (struct pt_cache **cache_ptr, const char *path, int mode);
 
 /**
- * Verify if the cached data is still fresh compared to the original.
+ * Verify if the cached data has become stale compared to the given original file.
  */
-bool pt_cache_fresh (struct pt_cache *cache);
+int pt_cache_stale (struct pt_cache *cache, const char *orig_path);
 
 /**
  * Update the cache data from the given PNG image.
  */
 int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info);
 
+/**
+ * Release all resources associated with the given cache object without any cleanup.
+ */
+void pt_cache_destroy (struct pt_cache *cache);
+
 #endif
--- a/src/lib/image.c	Sun Dec 27 22:01:17 2009 +0200
+++ b/src/lib/image.c	Sun Dec 27 23:14:10 2009 +0200
@@ -1,6 +1,12 @@
 #include "image.h"
+#include "cache.h"
 
-static int pt_image_new (struct pt_image **img_ptr, struct pt_ctx *ctx, const char *png_path)
+#include <stdlib.h>
+#include <limits.h> // for _POSIX_PATH_MAX
+
+#include <png.h>
+
+static int pt_image_new (struct pt_image **img_ptr, struct pt_ctx *ctx, const char *path)
 {
     struct pt_image *img;
 
@@ -8,7 +14,7 @@
     if ((img = calloc(1, sizeof(*img))) == NULL)
         return -1;
 
-    if ((img->png_path = strdup(png_path)) == NULL)
+    if ((img->path = strdup(path)) == NULL)
         goto error;
 
     // init
@@ -33,7 +39,7 @@
     FILE *fp;
     
     // open
-    if (fopen(img->png_path, "rb") < 0)
+    if (fopen(img->path, "rb") < 0)
         return -1;
 
     // ok
@@ -71,6 +77,7 @@
     png_init_io(png, fp);
 
     // ok
+    // XXX: what to do with fp?
     *png_ptr = png;
     *info_ptr = info;
 
@@ -113,40 +120,68 @@
     png_read_end(png, NULL);
 
     // clean up
-    png_destroy_read_struct(&png, &info);
+    png_destroy_read_struct(&png, &info, NULL);
 
     return 0;
 
 error:
     // clean up
-    png_destroy_read_struct(&png, &info);
+    png_destroy_read_struct(&png, &info, NULL);
 
     return -1;
 }
 
-int pt_image_open (struct pt_image **img_ptr, struct pt_ctx *ctx, const char *png_path, int cache_mode)
+/**
+ * Build a filesystem path representing the appropriate path for this image's cache entry, and store it in the given
+ * buffer.
+ */
+static int pt_image_cache_path (struct pt_image *image, char *buf, size_t len)
 {
-    struct pt_image *img;
+    // TODO: impl
+}
 
-    // XXX: verify that the png_path exists and looks like a PNG file
+int pt_image_open (struct pt_image **image_ptr, struct pt_ctx *ctx, const char *path, int cache_mode)
+{
+    struct pt_image *image;
+    char cache_path[_POSIX_PATH_MAX];
+
+    // XXX: verify that the path exists and looks like a PNG file
 
     // alloc
-    if (pt_image_new(&img, ctx, png_path))
+    if (pt_image_new(&image, ctx, path))
         return -1;
+    
+    // compute cache file path
+    if (pt_image_cache_path(image, cache_path, sizeof(cache_path)))
+        goto error;
 
     // open the cache object for this image
-    if (pt_cache_open(&img->cache, img, mode))
+    if (pt_cache_open(&image->cache, cache_path, cache_mode))
         goto error;
     
     // update if not fresh
-    if (!pt_cache_fresh(img->cache))
-        pt_image_update_cache(img);
+    // XXX: check cache_mode
+    // XXX: error handling
+    if (pt_cache_stale(image->cache, image->path))
+        pt_image_update_cache(image);
     
     // ok, ready for access
-    *img_ptr = img;
+    *image_ptr = image;
 
     return 0;
 
 error:
-    pt_image_destroy(img);
+    pt_image_destroy(image);
+
+    return -1;
 }
+
+void pt_image_destroy (struct pt_image *image)
+{
+    free(image->path);
+    
+    if (image->cache)
+        pt_cache_destroy(image->cache);
+
+    free(image);
+}
--- a/src/lib/image.h	Sun Dec 27 22:01:17 2009 +0200
+++ b/src/lib/image.h	Sun Dec 27 23:14:10 2009 +0200
@@ -13,12 +13,16 @@
     struct pt_ctx *ctx;
 
     /** Path to .png */
-    char *png_path;
+    char *path;
     
     /** Cache object */
     struct pt_cache *cache;
 };
 
+/**
+ * Release the given pt_image without any clean shutdown
+ */
+void pt_image_destroy (struct pt_image *image);
 
 
 #endif