src/lib/cache.c
changeset 17 baf3fe7c6354
parent 15 01de253f3bbf
child 18 f92a24ab046e
equal deleted inserted replaced
16:6e781cf3d459 17:baf3fe7c6354
     1 #include "cache.h"
     1 #include "cache.h"
     2 #include "shared/util.h"
     2 #include "shared/util.h"
     3 #include "shared/log.h" // only LOG_DEBUG
     3 #include "shared/log.h" // only LOG_DEBUG
       
     4 #include "error.h"
     4 
     5 
     5 #include <stdlib.h>
     6 #include <stdlib.h>
     6 #include <unistd.h>
     7 #include <unistd.h>
     7 #include <sys/types.h>
     8 #include <sys/types.h>
     8 #include <sys/stat.h>
     9 #include <sys/stat.h>
    13 
    14 
    14 
    15 
    15 int pt_cache_new (struct pt_cache **cache_ptr, const char *path, int mode)
    16 int pt_cache_new (struct pt_cache **cache_ptr, const char *path, int mode)
    16 {
    17 {
    17     struct pt_cache *cache;
    18     struct pt_cache *cache;
       
    19     int err;
    18 
    20 
    19     // alloc
    21     // alloc
    20     if ((cache = calloc(1, sizeof(*cache))) == NULL)
    22     if ((cache = calloc(1, sizeof(*cache))) == NULL)
    21         return -1;
    23         RETURN_ERROR(PT_ERR_MEM);
    22 
    24 
    23     if ((cache->path = strdup(path)) == NULL)
    25     if ((cache->path = strdup(path)) == NULL)
    24         goto error;
    26         JUMP_SET_ERROR(err, PT_ERR_MEM);
    25 
    27 
    26     // init
    28     // init
    27     cache->fd = -1;
    29     cache->fd = -1;
    28     cache->mode = mode;
    30     cache->mode = mode;
    29 
    31 
    35 error:
    37 error:
    36     // cleanup
    38     // cleanup
    37     if (cache)
    39     if (cache)
    38         pt_cache_destroy(cache);
    40         pt_cache_destroy(cache);
    39 
    41 
    40     return -1;
    42     return err;
    41 }
    43 }
    42 
    44 
    43 int pt_cache_status (struct pt_cache *cache, const char *img_path)
    45 int pt_cache_status (struct pt_cache *cache, const char *img_path)
    44 {
    46 {
    45     struct stat st_img, st_cache;
    47     struct stat st_img, st_cache;
    46     
    48     
    47     // test original file
    49     // test original file
    48     if (stat(img_path, &st_img) < 0)
    50     if (stat(img_path, &st_img) < 0)
    49         return -1;
    51         RETURN_ERROR(PT_ERR_IMG_STAT);
    50     
    52     
    51     // test cache file
    53     // test cache file
    52     if (stat(cache->path, &st_cache) < 0) {
    54     if (stat(cache->path, &st_cache) < 0) {
    53         // always stale if it doesn't exist yet
    55         // always stale if it doesn't exist yet
    54         if (errno == ENOENT)
    56         if (errno == ENOENT)
    55             return PT_CACHE_NONE;
    57             return PT_CACHE_NONE;
    56         else
    58         else
    57             return -1;
    59             RETURN_ERROR(PT_ERR_CACHE_STAT);
    58     }
    60     }
    59 
    61 
    60     // compare mtime
    62     // compare mtime
    61     if (st_img.st_mtime > st_cache.st_mtime)
    63     if (st_img.st_mtime > st_cache.st_mtime)
    62         return PT_CACHE_STALE;
    64         return PT_CACHE_STALE;
    65         return PT_CACHE_FRESH;
    67         return PT_CACHE_FRESH;
    66 }
    68 }
    67 
    69 
    68 int pt_cache_info (struct pt_cache *cache, struct pt_image_info *info)
    70 int pt_cache_info (struct pt_cache *cache, struct pt_image_info *info)
    69 {
    71 {
       
    72     int err;
       
    73 
    70     // ensure open
    74     // ensure open
    71     if (pt_cache_open(cache))
    75     if ((err = pt_cache_open(cache)))
    72         return -1;
    76         return err;
    73 
    77 
    74     info->width = cache->header->width;
    78     info->width = cache->header->width;
    75     info->height = cache->header->height;
    79     info->height = cache->header->height;
    76 
    80 
    77     return 0;
    81     return 0;
   105 {
   109 {
   106     int fd;
   110     int fd;
   107     
   111     
   108     // actual open()
   112     // actual open()
   109     if ((fd = open(cache->path, O_RDONLY)) < 0)
   113     if ((fd = open(cache->path, O_RDONLY)) < 0)
   110         return -1;
   114         RETURN_ERROR_ERRNO(PT_ERR_OPEN_MODE, EACCES);
   111 
   115 
   112     // ok
   116     // ok
   113     *fd_ptr = fd;
   117     *fd_ptr = fd;
   114 
   118 
   115     return 0;
   119     return 0;
   123     int fd;
   127     int fd;
   124     char tmp_path[1024];
   128     char tmp_path[1024];
   125     
   129     
   126     // get .tmp path
   130     // get .tmp path
   127     if (path_with_fext(cache->path, tmp_path, sizeof(tmp_path), ".tmp"))
   131     if (path_with_fext(cache->path, tmp_path, sizeof(tmp_path), ".tmp"))
   128         return -1;
   132         RETURN_ERROR(PT_ERR_PATH);
   129 
   133 
   130     // open for write, create
   134     // open for write, create
   131     // XXX: locking?
   135     // XXX: locking?
   132     if ((fd = open(tmp_path, O_RDWR | O_CREAT, 0644)) < 0)
   136     if ((fd = open(tmp_path, O_RDWR | O_CREAT, 0644)) < 0)
   133         return -1;
   137         RETURN_ERROR(PT_ERR_CACHE_OPEN_TMP);
   134 
   138 
   135     // ok
   139     // ok
   136     *fd_ptr = fd;
   140     *fd_ptr = fd;
   137 
   141 
   138     return 0;
   142     return 0;
   156         prot |= PROT_WRITE;
   160         prot |= PROT_WRITE;
   157     }
   161     }
   158 
   162 
   159     // perform mmap() from second page on
   163     // perform mmap() from second page on
   160     if ((addr = mmap(NULL, PT_CACHE_HEADER_SIZE + cache->size, prot, MAP_SHARED, cache->fd, 0)) == MAP_FAILED)
   164     if ((addr = mmap(NULL, PT_CACHE_HEADER_SIZE + cache->size, prot, MAP_SHARED, cache->fd, 0)) == MAP_FAILED)
   161         return -1;
   165         RETURN_ERROR(PT_ERR_CACHE_MMAP);
   162 
   166 
   163     // ok
   167     // ok
   164     *addr_ptr = addr;
   168     *addr_ptr = addr;
   165 
   169 
   166     return 0;
   170     return 0;
   174     size_t len = sizeof(*header);
   178     size_t len = sizeof(*header);
   175     char *buf = (char *) header;
   179     char *buf = (char *) header;
   176 
   180 
   177     // seek to start
   181     // seek to start
   178     if (lseek(cache->fd, 0, SEEK_SET) != 0)
   182     if (lseek(cache->fd, 0, SEEK_SET) != 0)
   179         return -1;
   183         RETURN_ERROR(PT_ERR_CACHE_SEEK);
   180 
   184 
   181     // write out full header
   185     // write out full header
   182     while (len) {
   186     while (len) {
   183         ssize_t ret;
   187         ssize_t ret;
   184         
   188         
   185         // try and write out the header
   189         // try and write out the header
   186         if ((ret = read(cache->fd, buf, len)) < 0)
   190         if ((ret = read(cache->fd, buf, len)) <= 0)
   187             return -1;
   191             RETURN_ERROR(PT_ERR_CACHE_READ);
   188 
   192 
   189         // update offset
   193         // update offset
   190         buf += ret;
   194         buf += ret;
   191         len -= ret;
   195         len -= ret;
   192     }
   196     }
   203     size_t len = sizeof(*header);
   207     size_t len = sizeof(*header);
   204     const char *buf = (const char *) header;
   208     const char *buf = (const char *) header;
   205 
   209 
   206     // seek to start
   210     // seek to start
   207     if (lseek(cache->fd, 0, SEEK_SET) != 0)
   211     if (lseek(cache->fd, 0, SEEK_SET) != 0)
   208         return -1;
   212         RETURN_ERROR(PT_ERR_CACHE_SEEK);
   209 
   213 
   210     // write out full header
   214     // write out full header
   211     while (len) {
   215     while (len) {
   212         ssize_t ret;
   216         ssize_t ret;
   213         
   217         
   214         // try and write out the header
   218         // try and write out the header
   215         if ((ret = write(cache->fd, buf, len)) < 0)
   219         if ((ret = write(cache->fd, buf, len)) <= 0)
   216             return -1;
   220             RETURN_ERROR(PT_ERR_CACHE_WRITE);
   217 
   221 
   218         // update offset
   222         // update offset
   219         buf += ret;
   223         buf += ret;
   220         len -= ret;
   224         len -= ret;
   221     }
   225     }
   228  * Create a new .tmp cache file, open it, and write out the header.
   232  * Create a new .tmp cache file, open it, and write out the header.
   229  */
   233  */
   230 static int pt_cache_create (struct pt_cache *cache, struct pt_cache_header *header)
   234 static int pt_cache_create (struct pt_cache *cache, struct pt_cache_header *header)
   231 {
   235 {
   232     void *base;
   236     void *base;
       
   237     int err;
   233 
   238 
   234     // no access
   239     // no access
   235     if (!(cache->mode & PT_OPEN_UPDATE)) {
   240     if (!(cache->mode & PT_OPEN_UPDATE))
   236         errno = EPERM;
   241         RETURN_ERROR(PT_ERR_OPEN_MODE);
   237         return -1;
       
   238     }
       
   239 
   242 
   240     // open as .tmp
   243     // open as .tmp
   241     if (pt_cache_open_tmp_fd(cache, &cache->fd))
   244     if ((err = pt_cache_open_tmp_fd(cache, &cache->fd)))
   242         return -1;
   245         return err;
   243 
   246 
   244     // calculate data size
   247     // calculate data size
   245     cache->size = sizeof(*header) + header->height * header->row_bytes;
   248     cache->size = sizeof(*header) + header->height * header->row_bytes;
   246 
   249 
   247     // grow file
   250     // grow file
   248     if (ftruncate(cache->fd, PT_CACHE_HEADER_SIZE + cache->size) < 0)
   251     if (ftruncate(cache->fd, PT_CACHE_HEADER_SIZE + cache->size) < 0)
   249         goto error;
   252         JUMP_SET_ERROR(err, PT_ERR_CACHE_TRUNC);
   250 
   253 
   251     // mmap header and data
   254     // mmap header and data
   252     if (pt_cache_open_mmap(cache, &base, false))
   255     if ((err = pt_cache_open_mmap(cache, &base, false)))
   253         goto error;
   256         JUMP_ERROR(err);
   254 
   257 
   255     cache->header = base;
   258     cache->header = base;
   256     cache->data = base + PT_CACHE_HEADER_SIZE;
   259     cache->data = base + PT_CACHE_HEADER_SIZE;
   257 
   260 
   258     // write header
   261     // write header
   259     // XXX: should just mmap...
   262     // XXX: should just mmap...
   260     if (pt_cache_write_header(cache, header))
   263     if ((err = pt_cache_write_header(cache, header)))
   261         goto error;
   264         JUMP_ERROR(err);
   262 
   265 
   263     // done
   266     // done
   264     return 0;
   267     return 0;
   265 
   268 
   266 error:
   269 error:
   267     // cleanup
   270     // cleanup
   268     pt_cache_abort(cache);
   271     pt_cache_abort(cache);
   269 
   272 
   270     return -1;
   273     return err;
   271 }
   274 }
   272 
   275 
   273 /**
   276 /**
   274  * Rename the opened .tmp to .cache
   277  * Rename the opened .tmp to .cache
   275  */
   278  */
   277 {
   280 {
   278     char tmp_path[1024];
   281     char tmp_path[1024];
   279     
   282     
   280     // get .tmp path
   283     // get .tmp path
   281     if (path_with_fext(cache->path, tmp_path, sizeof(tmp_path), ".tmp"))
   284     if (path_with_fext(cache->path, tmp_path, sizeof(tmp_path), ".tmp"))
   282         return -1;
   285         RETURN_ERROR(PT_ERR_PATH);
   283 
   286 
   284     // rename
   287     // rename
   285     if (rename(tmp_path, cache->path) < 0)
   288     if (rename(tmp_path, cache->path) < 0)
   286         return -1;
   289         RETURN_ERROR(PT_ERR_CACHE_RENAME_TMP);
   287 
   290 
   288     // ok
   291     // ok
   289     return 0;
   292     return 0;
   290 }
   293 }
   291 
   294 
   292 int pt_cache_open (struct pt_cache *cache)
   295 int pt_cache_open (struct pt_cache *cache)
   293 {
   296 {
   294     struct pt_cache_header header;
   297     struct pt_cache_header header;
   295     void *base;
   298     void *base;
       
   299     int err;
   296 
   300 
   297     // ignore if already open
   301     // ignore if already open
   298     if (cache->header && cache->data)
   302     if (cache->header && cache->data)
   299         return 0;
   303         return 0;
   300 
   304 
   301     // open the .cache
   305     // open the .cache
   302     if (pt_cache_open_read_fd(cache, &cache->fd))
   306     if ((err = pt_cache_open_read_fd(cache, &cache->fd)))
   303         return -1;
   307         return err;
   304 
   308 
   305     // read in header
   309     // read in header
   306     if (pt_cache_read_header(cache, &header))
   310     if ((err = pt_cache_read_header(cache, &header)))
   307         return -1;
   311         JUMP_ERROR(err);
   308 
   312 
   309     // calculate data size
   313     // calculate data size
   310     cache->size = sizeof(header) + header.height * header.row_bytes;
   314     cache->size = sizeof(header) + header.height * header.row_bytes;
   311 
   315 
   312     // mmap header and data
   316     // mmap header and data
   313     if (pt_cache_open_mmap(cache, &base, true))
   317     if ((err = pt_cache_open_mmap(cache, &base, true)))
   314         goto error;
   318         JUMP_ERROR(err);
   315 
   319 
   316     cache->header = base;
   320     cache->header = base;
   317     cache->data = base + PT_CACHE_HEADER_SIZE;
   321     cache->data = base + PT_CACHE_HEADER_SIZE;
   318 
   322 
   319     // done
   323     // done
   321 
   325 
   322 error:
   326 error:
   323     // cleanup
   327     // cleanup
   324     pt_cache_abort(cache);
   328     pt_cache_abort(cache);
   325 
   329 
   326     return -1;
   330     return err;
   327 }
   331 }
   328 
   332 
   329 int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info)
   333 int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info)
   330 {
   334 {
   331     struct pt_cache_header header;
   335     struct pt_cache_header header;
       
   336     int err;
   332     
   337     
   333     // XXX: check cache_mode
   338     // XXX: check cache_mode
   334     // XXX: check image doesn't use any options we don't handle
   339     // XXX: check image doesn't use any options we don't handle
   335     // XXX: close any already-opened cache file
   340     // XXX: close any already-opened cache file
   336 
   341 
   364         int num_palette;
   369         int num_palette;
   365         png_colorp palette;
   370         png_colorp palette;
   366 
   371 
   367         if (png_get_PLTE(png, info, &palette, &num_palette) == 0)
   372         if (png_get_PLTE(png, info, &palette, &num_palette) == 0)
   368             // XXX: PLTE chunk not read?
   373             // XXX: PLTE chunk not read?
   369             return -1;
   374             RETURN_ERROR(PT_ERR_PNG);
   370         
   375         
   371         // should only be 256 of them at most
   376         // should only be 256 of them at most
   372         assert(num_palette <= PNG_MAX_PALETTE_LENGTH);
   377         assert(num_palette <= PNG_MAX_PALETTE_LENGTH);
   373     
   378     
   374         // copy
   379         // copy
   377         
   382         
   378         log_debug("num_palette=%u", num_palette);
   383         log_debug("num_palette=%u", num_palette);
   379     }
   384     }
   380 
   385 
   381     // create .tmp and write out header
   386     // create .tmp and write out header
   382     if (pt_cache_create(cache, &header))
   387     if ((err = pt_cache_create(cache, &header)))
   383         return -1;
   388         return err;
   384 
   389 
   385 
   390 
   386     // write out raw image data a row at a time
   391     // write out raw image data a row at a time
   387     for (size_t row = 0; row < header.height; row++) {
   392     for (size_t row = 0; row < header.height; row++) {
   388         // read row data, non-interlaced
   393         // read row data, non-interlaced
   389         png_read_row(png, cache->data + row * header.row_bytes, NULL);
   394         png_read_row(png, cache->data + row * header.row_bytes, NULL);
   390     }
   395     }
   391 
   396 
   392 
   397 
   393     // move from .tmp to .cache
   398     // move from .tmp to .cache
   394     if (pt_cache_create_done(cache))
   399     if ((err = pt_cache_create_done(cache)))
   395         return -1;
   400         return err;
   396 
   401 
   397     // done!
   402     // done!
   398     return 0;
   403     return 0;
   399 }
   404 }
   400 
   405 
   454         clip_y = ti->y + ti->height;
   459         clip_y = ti->y + ti->height;
   455 
   460 
   456 
   461 
   457     // allocate buffer for a single row of image data
   462     // allocate buffer for a single row of image data
   458     if ((rowbuf = malloc(ti->width * cache->header->col_bytes)) == NULL)
   463     if ((rowbuf = malloc(ti->width * cache->header->col_bytes)) == NULL)
   459         return -1;
   464         RETURN_ERROR(PT_ERR_MEM);
   460 
   465 
   461     // how much data we actually have for each row, in px and bytes
   466     // how much data we actually have for each row, in px and bytes
   462     // from [(tile x)---](clip x)
   467     // from [(tile x)---](clip x)
   463     size_t row_px = clip_x - ti->x;
   468     size_t row_px = clip_x - ti->x;
   464     size_t row_bytes = row_px * cache->header->col_bytes;
   469     size_t row_bytes = row_px * cache->header->col_bytes;
   490 int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti)
   495 int pt_cache_tile_png (struct pt_cache *cache, png_structp png, png_infop info, const struct pt_tile_info *ti)
   491 {
   496 {
   492     int err;
   497     int err;
   493 
   498 
   494     // check within bounds
   499     // check within bounds
   495     if (ti->x >= cache->header->width || ti->y >= cache->header->height) {
   500     if (ti->x >= cache->header->width || ti->y >= cache->header->height)
   496         // completely outside
   501         // completely outside
   497         errno = EINVAL;
   502         RETURN_ERROR(PT_ERR_TILE_CLIP);
   498         return -1;
       
   499     }
       
   500 
   503 
   501     // ensure open
   504     // ensure open
   502     if (pt_cache_open(cache))
   505     if ((err = pt_cache_open(cache)))
   503         return -1;
   506         return err;
   504 
   507 
   505     // set basic info
   508     // set basic info
   506     png_set_IHDR(png, info, ti->width, ti->height, cache->header->bit_depth, cache->header->color_type,
   509     png_set_IHDR(png, info, ti->width, ti->height, cache->header->bit_depth, cache->header->color_type,
   507             PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT
   510             PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT
   508     );
   511     );
   525     else
   528     else
   526         // fill in clipped regions
   529         // fill in clipped regions
   527         err = write_png_data_clipped(cache, png, info, ti);
   530         err = write_png_data_clipped(cache, png, info, ti);
   528 
   531 
   529     if (err)
   532     if (err)
   530         return -1;
   533         return err;
   531     
   534     
   532     // done, flush remaining output
   535     // done, flush remaining output
   533     png_write_flush(png);
   536     png_write_flush(png);
   534 
   537 
   535     // ok
   538     // ok