diff options
author | jcid <devnull@localhost> | 2007-10-07 00:36:34 +0200 |
---|---|---|
committer | jcid <devnull@localhost> | 2007-10-07 00:36:34 +0200 |
commit | 93715c46a99c96d6c866968312691ec9ab0f6a03 (patch) | |
tree | 573f19ec6aa740844f53a7c0eb7114f04096bf64 /src/dicache.c |
Initial revision
Diffstat (limited to 'src/dicache.c')
-rw-r--r-- | src/dicache.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/src/dicache.c b/src/dicache.c new file mode 100644 index 00000000..eac01ec2 --- /dev/null +++ b/src/dicache.c @@ -0,0 +1,451 @@ +/* + * File: dicache.c + * + * Copyright 2000-2005 Jorge Arellano Cid <jcid@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + */ + +#include <sys/time.h> /* for libc5 compatibility */ +#include <string.h> /* for memset */ +#include <stdio.h> +#include <stdlib.h> + +#include "image.hh" +#include "web.hh" +#include "dicache.h" +#include "cache.h" +#include "prefs.h" + +typedef struct _DICacheNode DICacheNode; + +struct _DICacheNode { + int valid; /* flag */ + DilloUrl *url; /* primary "Key" for this dicache entry */ + DICacheEntry *first; /* pointer to the first dicache entry in this list */ +}; + +/* + * List of DICacheNode. One node per URL. Each node may have several + * versions of the same image in a linked list. + */ +static Dlist *CachedIMGs = NULL; + +static int dicache_size_total; /* invariant: dicache_size_total is + * the sum of the image sizes (3*w*h) + * of all the images in the dicache. */ + +/* + * Compare two dicache nodes + */ +static int Dicache_node_cmp(const void *v1, const void *v2) +{ + const DICacheNode *n1 = v1, *n2 = v2; + + return a_Url_cmp(n1->url, n2->url); +} + +/* + * Compare function for searching a node by Url + */ +static int Dicache_node_by_url_cmp(const void *v1, const void *v2) +{ + const DICacheNode *node = v1; + const DilloUrl *url = v2; + + return a_Url_cmp(node->url, url); +} + +/* + * Initialize dicache data + */ +void a_Dicache_init(void) +{ + CachedIMGs = dList_new(256); + dicache_size_total = 0; +} + +/* + * Create, and initialize a new, empty, dicache entry + */ +static DICacheEntry *Dicache_entry_new(void) +{ + DICacheEntry *entry = dNew(DICacheEntry, 1); + + entry->width = 0; + entry->height = 0; + entry->type = DILLO_IMG_TYPE_NOTSET; + entry->cmap = NULL; + entry->linebuf = NULL; + entry->v_imgbuf = NULL; + entry->RefCount = 1; + entry->TotalSize = 0; + entry->Y = 0; + entry->BitVec = NULL; + entry->State = DIC_Empty; + entry->version = 0; + entry->next = NULL; + + return entry; +} + +/* + * Add a new entry in the dicache + * (a single node (URL) may have several entries) + */ +DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url) +{ + DICacheEntry *entry; + DICacheNode *node; + + entry = Dicache_entry_new(); + + if ((node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp))) { + /* this URL is already in CachedIMGs, add entry at the END of the list */ + DICacheEntry *ptr = node->first; + + node->valid = 1; + for ( ; ptr->next; ptr = ptr->next); + ptr->next = entry; + entry->version = ptr->version+1; + entry->url = node->url; + + } else { /* no node yet, so create one */ + DICacheNode *node = dNew(DICacheNode, 1); + + node->url = a_Url_dup(Url); + entry->url = node->url; + node->first = entry; + node->valid = 1; + dList_insert_sorted(CachedIMGs, node, Dicache_node_cmp); + } + + return entry; +} + +/* + * Search an entry in the dicache (given the Url). + * Return value: a pointer to the entry of the _newest_ (i.e. highest) + * version if found; NULL otherwise. + */ +DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url) +{ + DICacheNode *node; + DICacheEntry *entry; + + node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp); + + if (!node || !node->valid) + return NULL; + + for (entry = node->first; (entry && entry->next); entry = entry->next); + + return entry; +} + +/* + * Search a particular version of a URL in the Dicache. + * Return value: a pointer to the entry if found; NULL otherwise. + */ +static DICacheEntry *Dicache_get_entry_version(const DilloUrl *Url, + int version) +{ + DICacheNode *node; + DICacheEntry *entry; + + node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp); + entry = (node) ? node->first : NULL; + + while (entry && entry->version != version) + entry = entry->next; + + return entry; +} + +/* + * Actually free a dicache entry, given the URL and the version number. + */ +static void Dicache_remove(const DilloUrl *Url, int version) +{ + DICacheNode *node; + DICacheEntry *entry, *prev; + + node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp); + prev = entry = (node) ? node->first : NULL; + + while (entry && (entry->version != version) ) { + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Eliminate this dicache entry */ + dFree(entry->cmap); + dFree(entry->linebuf); + a_Bitvec_free(entry->BitVec); + a_Image_imgbuf_unref(entry->v_imgbuf); + dicache_size_total -= entry->TotalSize; + + if (node->first == entry) { + if (!entry->next) { + /* last entry with this URL. Remove the node as well */ + dList_remove(CachedIMGs, node); + a_Url_free(node->url); + dFree(node); + } else + node->first = entry->next; + } else { + prev->next = entry->next; + } + dFree(entry); + } +} + +/* + * Unrefs the counter of a dicache entry, and _if_ no DwImage is acessing + * this buffer, then we call Dicache_free to do the dirty job. + */ +void a_Dicache_unref(const DilloUrl *Url, int version) +{ + DICacheEntry *entry; + + if ((entry = Dicache_get_entry_version(Url, version))) { + /*if (--entry->RefCount == 0 && (entry->next || !prefs.use_dicache)) {*/ + if (--entry->RefCount == 0) { + Dicache_remove(Url, version); + } + } +} + +/* + * Refs the counter of a dicache entry. + */ + +DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version) +{ + DICacheEntry *entry; + + if ((entry = Dicache_get_entry_version(Url, version))) { + ++entry->RefCount; + } + return entry; +} + +/* + * Invalidate this entry. This is used for the reloading mechanism. + * Can't erase current versions, but a_Dicache_get_entry must return NULL. + */ +void a_Dicache_invalidate_entry(const DilloUrl *Url) +{ + DICacheNode *node; + + node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp); + if (node) + node->valid = 0; +} + + +/* ------------------------------------------------------------------------- */ + +/* + * This function is a cache client; (but feeds its clients from dicache) + */ +void a_Dicache_callback(int Op, CacheClient_t *Client) +{ + /* todo: Handle Op = CA_Abort (to show what was got) --Jcid */ + uint_t i; + DilloWeb *Web = Client->Web; + DilloImage *Image = Web->Image; + DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url); + + dReturn_if_fail ( DicEntry != NULL ); + + /* when the data stream is not an image 'v_imgbuf' keeps NULL */ + if (Op == CA_Send && DicEntry->v_imgbuf) { + if (Image->height == 0 && DicEntry->State >= DIC_SetParms) { + /* Set parms */ + a_Image_set_parms( + Image, DicEntry->v_imgbuf, DicEntry->url, + DicEntry->version, DicEntry->width, DicEntry->height, + DicEntry->type); + } + if (DicEntry->State == DIC_Write) { + for (i = 0; i < DicEntry->height; ++i) + if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) && + !a_Bitvec_get_bit(Image->BitVec, (int)i) ) + a_Image_write(Image, DicEntry->v_imgbuf, + DicEntry->linebuf, i, FALSE); + } + } else if (Op == CA_Close || Op == CA_Abort) { + a_Image_close(Web->Image); + a_Bw_close_client(Web->bw, Client->Key); + } +} + +/* ------------------------------------------------------------------------- */ + +/* + * Set image's width, height & type + * (By now, we'll use the image information despite the html tags --Jcid) + */ +void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image, + uint_t width, uint_t height, DilloImgType type) +{ + DICacheEntry *DicEntry; + size_t Size = width * height * 3; + + dReturn_if_fail ( Image != NULL && width && height ); + /* Find the DicEntry for this Image */ + DicEntry = Dicache_get_entry_version(url, version); + dReturn_if_fail ( DicEntry != NULL ); + + /* Initialize the DicEntry */ + DicEntry->linebuf = dNew(uchar_t, width * 3); + dReturn_if_fail ( DicEntry->linebuf != NULL ); + + /* BUG: there's just one image-type now */ + #define I_RGB 0 + DicEntry->v_imgbuf = a_Image_imgbuf_new(Image->dw, I_RGB, width, height); + + /* This extra reference activates the dicache ALWAYS. + * Extra code is necessary in Imgbuf to be able to free it */ + //a_Image_imgbuf_ref(DicEntry->v_imgbuf); + + DicEntry->TotalSize = Size; + DicEntry->width = width; + DicEntry->height = height; + DicEntry->type = type; + DicEntry->BitVec = a_Bitvec_new((int)height); + DicEntry->State = DIC_SetParms; + + dicache_size_total += Size; + + /* Allocate and initialize this image */ + a_Image_set_parms(Image, DicEntry->v_imgbuf, url, version, + width, height, type); +} + +/* + * Implement the set_cmap method for the Image + */ +void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image, + const uchar_t *cmap, uint_t num_colors, + int num_colors_max, int bg_index) +{ + DICacheEntry *DicEntry = Dicache_get_entry_version(url, version); + + dReturn_if_fail ( DicEntry != NULL ); + + dFree(DicEntry->cmap); + DicEntry->cmap = dNew0(uchar_t, 3 * num_colors_max); + memcpy(DicEntry->cmap, cmap, 3 * num_colors); + if (bg_index >= 0 && (uint_t)bg_index < num_colors) { + DicEntry->cmap[bg_index * 3] = (Image->bg_color >> 16) & 0xff; + DicEntry->cmap[bg_index * 3 + 1] = (Image->bg_color >> 8) & 0xff; + DicEntry->cmap[bg_index * 3 + 2] = (Image->bg_color) & 0xff; + } + + a_Image_set_cmap(Image, DicEntry->cmap); + DicEntry->State = DIC_SetCmap; +} + +/* + * Implement the write method + * (Write a scan line into the Dicache entry) + * buf: row buffer + * Y : row number + * x : horizontal offset? (always zero) + */ +void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version, + const uchar_t *buf, int x, uint_t Y) +{ + DICacheEntry *DicEntry; + + dReturn_if_fail ( Image != NULL ); + DicEntry = Dicache_get_entry_version(url, version); + dReturn_if_fail ( DicEntry != NULL ); + dReturn_if_fail ( DicEntry->width > 0 && DicEntry->height > 0 ); + + a_Image_write(Image, DicEntry->v_imgbuf, buf, Y, TRUE); + DicEntry->Y = Y; + a_Bitvec_set_bit(DicEntry->BitVec, (int)Y); + DicEntry->State = DIC_Write; +} + +/* + * Implement the close method of the decoding process + */ +void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client) +{ + DilloWeb *Web = Client->Web; + DICacheEntry *DicEntry = Dicache_get_entry_version(url, version); + + dReturn_if_fail ( DicEntry != NULL ); + + DicEntry->State = DIC_Close; + dFree(DicEntry->cmap); + DicEntry->cmap = NULL; + dFree(DicEntry->linebuf); + DicEntry->linebuf = NULL; + a_Image_close(Web->Image); + a_Bw_close_client(Web->bw, Client->Key); +} + +/* + * Free the imgbuf (RGB data) of unused entries. + */ +void a_Dicache_cleanup(void) +{ + int i; + DICacheNode *node; + DICacheEntry *entry; + + for (i = 0; i < dList_length(CachedIMGs); ++i) { + node = dList_nth_data(CachedIMGs, i); + /* iterate each entry of this node */ + for (entry = node->first; entry; entry = entry->next) { + if (entry->v_imgbuf && + a_Image_imgbuf_last_reference(entry->v_imgbuf)) { + /* free this unused entry */ + if (entry->next) { + Dicache_remove(node->url, entry->version); + } else { + Dicache_remove(node->url, entry->version); + --i; + break; + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +/* + * Deallocate memory used by dicache module + * (Call this one at exit time) + */ +void a_Dicache_freeall(void) +{ + DICacheNode *node; + DICacheEntry *entry; + + /* Remove every dicache node and its entries */ + while ((node = dList_nth_data(CachedIMGs, 0))) { + while ((entry = node->first)) { + node->first = entry->next; + dFree(entry->cmap); + dFree(entry->linebuf); + a_Bitvec_free(entry->BitVec); + a_Image_imgbuf_unref(entry->v_imgbuf); + dicache_size_total -= entry->TotalSize; + } + dList_remove(CachedIMGs, node); + a_Url_free(node->url); + dFree(node); + } + dList_free(CachedIMGs); +} |