terom@0: #include "cache.h" terom@0: terom@1: #include terom@1: #include terom@1: #include terom@1: #include terom@1: #include terom@1: #include terom@2: #include terom@1: terom@1: terom@1: terom@1: static int pt_cache_new (struct pt_cache **cache_ptr, const char *path) terom@0: { terom@0: struct pt_cache *cache; terom@0: terom@1: // alloc terom@0: if ((cache = calloc(1, sizeof(*cache))) == NULL) terom@0: return -1; terom@0: terom@1: if ((cache->path = strdup(path)) == NULL) terom@1: goto error; terom@1: terom@1: // init terom@1: cache->fd = -1; terom@1: terom@1: // ok terom@1: *cache_ptr = cache; terom@1: terom@1: return 0; terom@1: terom@1: error: terom@1: // cleanup terom@1: if (cache) terom@1: pt_cache_destroy(cache); terom@1: terom@1: return -1; terom@1: } terom@1: terom@1: int pt_cache_open (struct pt_cache **cache_ptr, const char *path, int mode) terom@1: { terom@1: struct pt_cache *cache; terom@1: terom@1: // alloc terom@1: if (pt_cache_new(&cache, path)) terom@1: return -1; terom@1: terom@0: // ok terom@0: *cache_ptr = cache; terom@0: terom@0: return 0; terom@0: } terom@0: terom@2: int pt_cache_stale (struct pt_cache *cache, const char *img_path) terom@0: { terom@2: struct stat st_img, st_cache; terom@2: terom@2: // test original file terom@2: if (stat(img_path, &st_img) < 0) terom@2: return -1; terom@2: terom@2: // test cache file terom@2: if (stat(cache->path, &st_cache) < 0) { terom@2: // always stale if it doesn't exist yet terom@2: if (errno == ENOENT) terom@2: return 1; terom@2: else terom@2: return -1; terom@2: } terom@2: terom@2: // compare mtime terom@2: return (st_img.st_mtime > st_cache.st_mtime); terom@0: } terom@0: terom@0: /** terom@1: * Abort any incomplete open operation, cleaning up terom@1: */ terom@1: static void pt_cache_abort (struct pt_cache *cache) terom@1: { terom@1: if (cache->mmap != NULL) { terom@1: munmap(cache->mmap, cache->size); terom@1: terom@1: cache->mmap = NULL; terom@1: } terom@1: terom@1: if (cache->fd >= 0) { terom@1: close(cache->fd); terom@1: terom@1: cache->fd = -1; terom@1: } terom@1: } terom@1: terom@1: /** terom@1: * Open the cache file as an fd. terom@1: * terom@1: * XXX: needs locking terom@1: */ terom@1: static int pt_cache_open_fd (struct pt_cache *cache, int *fd_ptr) terom@1: { terom@1: int fd; terom@1: int flags = 0; terom@1: terom@1: // determine open flags terom@1: // XXX: O_RDONLY | O_WRONLY == O_RDWR? terom@1: if (cache->mode & PT_IMG_READ) terom@1: flags |= O_RDONLY; terom@1: terom@1: if (cache->mode & PT_IMG_WRITE) terom@1: flags |= (O_WRONLY | O_CREAT); terom@1: terom@1: // actual open() terom@1: if ((fd = open(cache->path, flags)) < 0) terom@1: return -1; terom@1: terom@1: // ok terom@1: *fd_ptr = fd; terom@1: terom@1: return 0; terom@1: } terom@1: terom@1: /** terom@1: * Mmap the opened cache file from offset PT_CACHE_PAGE, using the calculated size stored in cache->size terom@1: */ terom@1: static int pt_cache_open_mmap (struct pt_cache *cache, void **addr_ptr) terom@1: { terom@1: int prot = 0; terom@1: void *addr; terom@1: terom@1: // determine prot terom@1: if (cache->mode & PT_IMG_READ) terom@1: prot |= PROT_READ; terom@1: terom@1: if (cache->mode & PT_IMG_WRITE) terom@1: prot |= PROT_WRITE; terom@1: terom@1: // perform mmap() from second page on terom@1: if ((addr = mmap(NULL, cache->size, prot, MAP_SHARED, cache->fd, PT_CACHE_PAGE)) == MAP_FAILED) terom@1: return -1; terom@1: terom@1: // ok terom@1: *addr_ptr = addr; terom@1: terom@1: return 0; terom@1: } terom@1: terom@1: /** terom@1: * Write out the cache header into the opened file terom@1: */ terom@1: static int pt_cache_write_header (struct pt_cache *cache, const struct pt_cache_header *header) terom@1: { terom@1: size_t len = sizeof(*header); terom@1: const char *buf = (const char *) header; terom@1: terom@1: // seek to start terom@1: if (lseek(cache->fd, 0, SEEK_SET) != 0) terom@1: return -1; terom@1: terom@1: // write out full header terom@1: while (len) { terom@1: ssize_t ret; terom@1: terom@1: // try and write out the header terom@1: if ((ret = write(cache->fd, buf, len)) < 0) terom@1: return -1; terom@1: terom@1: // update offset terom@1: buf += ret; terom@1: len -= ret; terom@1: } terom@1: terom@1: // done terom@1: return 0; terom@1: } terom@1: terom@1: /** terom@0: * Create a new cache file, open it, and write out the header. terom@0: */ terom@1: static int pt_cache_open_create (struct pt_cache *cache, struct pt_cache_header *header) terom@0: { terom@2: // no access terom@2: if (!(cache->mode & PT_IMG_WRITE)) { terom@2: errno = EPERM; terom@2: return -1; terom@2: } terom@2: terom@1: // open terom@1: if (pt_cache_open_fd(cache, &cache->fd)) terom@1: return -1; terom@1: terom@1: // calculate data size terom@1: cache->size = sizeof(*header) + header->height * header->row_bytes; terom@1: terom@1: // grow file terom@1: if (ftruncate(cache->fd, PT_CACHE_PAGE + cache->size) < 0) terom@1: goto error; terom@1: terom@1: // open mmap terom@1: if (pt_cache_open_mmap(cache, (void **) &cache->mmap)) terom@1: goto error; terom@1: terom@1: // write header terom@1: if (pt_cache_write_header(cache, header)) terom@1: goto error; terom@1: terom@1: // done terom@1: return 0; terom@1: terom@1: error: terom@1: // cleanup terom@1: pt_cache_abort(cache); terom@1: terom@1: return -1; terom@0: } terom@0: terom@0: int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info) terom@0: { terom@0: struct pt_cache_header header; terom@1: terom@1: // XXX: check cache_mode terom@1: // XXX: check image doesn't use any options we don't handle terom@0: terom@0: memset(&header, 0, sizeof(header)); terom@0: terom@0: // fill in basic info terom@1: header.width = png_get_image_width(png, info); terom@1: header.height = png_get_image_height(png, info); terom@1: header.bit_depth = png_get_bit_depth(png, info); terom@1: header.color_type = png_get_color_type(png, info); terom@0: terom@0: // fill in other info terom@1: header.row_bytes = png_get_rowbytes(png, info); terom@1: terom@1: // create and write out header terom@1: if (pt_cache_open_create(cache, &header)) terom@1: return -1; terom@1: terom@1: // XXX: pallette etc. terom@1: terom@1: // write out raw image data a row at a time terom@1: for (size_t row = 0; row < header.height; row++) { terom@1: // read row data, non-interlaced terom@1: png_read_row(png, cache->mmap + row * header.row_bytes, NULL); terom@1: } terom@1: terom@1: // done! terom@1: return 0; terom@0: } terom@1: terom@1: void pt_cache_destroy (struct pt_cache *cache) terom@1: { terom@1: free(cache->path); terom@1: terom@1: pt_cache_abort(cache); terom@1: terom@1: free(cache); terom@1: }