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; |
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 |