📄 cache.c
字号:
/*
* Copyright (C) Ericsson Mobile Communications AB, 2000.
* Licensed to AU-System AB.
* All rights reserved.
*
* This software is covered by the license agreement between
* the end user and AU-System AB, and may be used and copied
* only in accordance with the terms of the said agreement.
*
* Neither Ericsson Mobile Communications AB nor AU-System AB
* assumes any responsibility or liability for any errors or inaccuracies in
* this software, or any consequential, incidental or indirect damage arising
* out of the use of the Generic WAP Client software.
*/
/*
* cache.c
*
* Module for storing and retrieving WML pages in a cache.
*
* Created by Anders Edenbrandt.
*
* Revision history:
* 990926, AED: Complete rewrite to utilize the new Storage module.
* 991110, AED: All public routines now test if the cache has
* been initialized. The behaviour of
* Cache_PrepareSizeChange has changed to effect a
* shutdown of the cache, freeing all associated
* data structures.
* 991123, AED: added new function, Cache_DeleteMatchingPrefix.
* 991227, IPN: A minor changes in Cache_Write when the
* Must-Revalidate flag is set.
* 991229, IPN: Changes in Cache_GetParameters. Added two new
* parameters (Date and ETag), Check at CacheControl-MaxAge
* when calculating Expire values.
* 000114, AED: Added handling of Date and ETag. Both items can
* be stored in the cache block, and the function
* Cache_URLisPresent now returns the etag as well as
* the last modified date, in case the entry should
* be revalidated. Furthermore, Cache_Write will not
* replace an entry that has a more recent date-field.
* 000125, AED: Cache_PrepareSizeChange now calls MEMa_cachePrepared
* even if the cache is not currently initialized.
* 000127, IPN: A minor changes in Cache_GetParameters
* 000128, AED: The ETag string extracted from the request headers
* is handled as a dynamically allocated, temporary string.
* 000321, IPN: A minor correction in Expire-calculation.
* 000411, IPN: Handles MaxAge and MustRevalidate simultaneous in a response.
* 000511, AED: Should be possible to call Cache_Init after a restart.
* 000808, IPN: Added WmlBackNav parameter to Cache_URLisPresent. The normal
* caching rules is not used when request comes from the history.
* 001107, HEAD:Different handling of expire in cache_getParameters
* 001120, AED: New parameter in Cache_URLisPresent and Cache_Write,
* to distinguish between content belonging to WTA and
* normal content.
* Removed Cache_ConfirmURL. No longer used.
* Correction in Cache_Write, the alternative that does
* compaction.
* Correction in Cache_Init, potential memory leak.
* 001212, AED: Added use of File API. Configured with FILE_CACHE.
* 010403, IPN: Changes in Cache_GetParameters. Check also after CacheControl -
* EncodingVersion 1.3 and 1.4
* 010410, IPN: The function Cache_GetParameters is now available outside this file.
* 010430, MALU:Cast warnings removed.
* 010507, IPN: Removed some varnings.
*
*/
#include "confvars.h"
#include "cache.h"
#ifdef FILE_CACHE
#include "aapifile.h"
#else
#include "storage.h"
#endif
#include "aapimem.h"
#include "cmmnrsrc.h"
#include "waedef.h"
#include "url.h"
#include "hdrutil.h"
#include "wml_uafn.h"
/* Flag values for the 'flags' field in the cache records. */
#define PRESENT 0x01 /* The record has not been deleted. */
#define HASREAD 0x02 /* The record has been read at least once. */
#define MUSTREVALIDATE 0x04 /* This record must be revalidated. */
#define BELONGS_TO_WTA 0x08
/* Each record in the cache starts with this: */
typedef struct {
UINT32 url_hash; /* The hash value of the URL */
UINT16 url_len; /* The size of the URL, in bytes. */
UINT16 header_len; /* The size of the header. */
UINT32 body_len; /* The size of the body. */
UINT32 etag_len; /* Length of Entity Tag, in bytes. */
UINT32 expire; /* The expiration date, or 0 if it has none. */
UINT32 date; /* Date of response to request. */
UINT32 last_mod; /* The last modified date of the record. */
UINT16 flags; /* Flag values, see above. */
#ifdef FILE_CACHE
UINT32 creation_time; /* Used for evicting the oldest when cache is full. */
#endif
} CacheRecord;
/* Some information about each record in the cache is kept in memory,
* in a doubly linked list. The list is ordered by IDs, which means
* it has the oldest cached value first. */
typedef struct lnode {
struct lnode *next, *prev;
#ifdef FILE_CACHE
UINT32 creation_time;
#endif
UINT32 id; /* To identify the Storage block */
UINT32 hashval; /* Hashed value of the URL */
UINT8 isWTA; /* The WTA flag is set */
} ListNode;
#ifdef FILE_CACHE
static ListNode reclist = {&reclist, &reclist, 0, 0, 0, 0};
#else
static ListNode reclist = {&reclist, &reclist, 0, 0, 0};
#endif
/* Macros to add and remove nodes from the list. */
#define list_out(p) do { \
(p)->next->prev = (p)->prev; \
(p)->prev->next = (p)->next; \
} while (0)
#define list_addbefore(p, pb) do { \
(p)->prev = (pb)->prev; \
(p)->next = (pb); \
(pb)->prev->next = (p); \
(pb)->prev = (p); \
} while (0)
/*
* The Cache: implemented as a Storage Object.
*/
static struct {
UINT8 isInitialized;
#ifdef FILE_CACHE
UINT32 size;
#else
StorageObject store;
#endif
WAEMAINOBJECT *waeMainObj;
} theCache = {0};
/*
* Cache_Find
*
* Locate a record with a specific URL in the cache.
* "url" is the URL, as a null-terminated byte string.
* Returns a pointer to the node in the list, or NULL if it was not found.
* Also, the record for the found cache entry is stored in the location
* pointed to by "rec".
*/
static ListNode *
Cache_Find (BYTE *url, CacheRecord *rec, INT16 isWTA)
{
BYTE *tmp_url;
ListNode *p;
UINT32 hv;
if (!b_HashURL (url, &hv))
return NULL;
for (p = reclist.next; p != &reclist; p = p->next) {
if (((isWTA < 0) || (isWTA == p->isWTA)) && (p->hashval == hv)) {
#ifdef FILE_CACHE
if (FILEa_read ('C', p->id, rec, 0, sizeof (CacheRecord))
!= sizeof (CacheRecord)) {
#else
if (!Storage_Get (&(theCache.store), p->id, 0,
sizeof (CacheRecord), rec)) {
#endif
return NULL;
}
if ((tmp_url = NEWARRAY (BYTE, rec->url_len + 1)) == NULL) {
return NULL;
}
#ifdef FILE_CACHE
if (FILEa_read ('C', p->id, tmp_url, sizeof (CacheRecord),
rec->url_len) != rec->url_len) {
#else
if (!Storage_Get (&(theCache.store), p->id, sizeof (CacheRecord),
rec->url_len, tmp_url)) {
#endif
DEALLOC (&tmp_url);
return NULL;
}
tmp_url[rec->url_len] = '\0';
if (b_EqualURL (url, tmp_url, ALL_COMP)) {
DEALLOC (&tmp_url);
return p;
}
DEALLOC (&tmp_url);
}
}
return NULL;
}
/*
* Cache_Init
*
* This function should be called once the WAP Client's physical
* cachememory is ready to be accessed. If the new size is less
* than the previous size, then arbitrary data might be lost.
* This function also sets the new size in the WAEMAINOBJECT.
* If iFirstInit is equal to 1, the HASREAD flag in each cache
* record is cleared.
*/
VOID
Cache_Init (UINT32 liNewSize, /* The new size of the cache */
VOID *pvWAEMaStruct, /* Pointer to the WAEMAINOBJECT */
UINT16 iFirstInit /* Flag */
)
{
CacheRecord rec;
UINT32 *ida;
INT16 i;
ListNode *p, *q;
UINT16 len;
theCache.waeMainObj = (WAEMAINOBJECT*) pvWAEMaStruct;
if (theCache.waeMainObj != NULL) {
theCache.waeMainObj->liCacheSize = liNewSize;
}
#ifndef FILE_CACHE
if (!Storage_Init (&(theCache.store), liNewSize,
(ReadFunction *)MEMa_readCache,
(WriteFunction *)MEMa_writeCache)) {
return;
}
#endif
/* Set list of cached records to be empty. */
reclist.next = &reclist;
reclist.prev = &reclist;
#ifdef FILE_CACHE
theCache.size = 0;
if ((i = FILEa_getFileIds ('C', NULL, 0)) < 0) {
return;
}
len = (UINT16)i;
ida = OSConnectorAlloc (sizeof (UINT32) * len);
if (ida == NULL) {
return;
}
if ((i = FILEa_getFileIds ('C', ida, len)) < 0) {
OSConnectorFree (ida);
return;
}
len = (UINT16)i;
#else
if (!Storage_GetAllBlockIds (&(theCache.store), &ida, &len)) {
return;
}
#endif
for (i = 0; i < len; i++) {
#ifdef FILE_CACHE
if (ida[i] == 0)
continue;
#endif
p = NEWARRAY (ListNode, 1);
if (p == NULL)
return;
p->id = ida[i];
#ifdef FILE_CACHE
theCache.size += FILEa_getSize ('C', ida[i]);
if (FILEa_read ('C', ida[i], &rec, 0, sizeof (CacheRecord))
!= sizeof (CacheRecord)) {
#else
if (!Storage_Get (&(theCache.store), ida[i], 0,
sizeof (CacheRecord), &rec)) {
#endif
DEALLOC (&p);
return;
}
#ifdef FILE_CACHE
p->creation_time = rec.creation_time;
#endif
p->hashval = rec.url_hash;
p->isWTA = ((rec.flags & BELONGS_TO_WTA) ? 1 : 0);
/* Sort into reclist, sort ascending after creation time. */
#ifdef FILE_CACHE
for (q = reclist.next; (q != &reclist) &&
(q->creation_time < p->creation_time);
q = q->next);
#else
for (q = reclist.next; (q != &reclist) && (q->id < p->id);
q = q->next);
#endif
list_addbefore (p, q);
if (iFirstInit && (rec.flags & HASREAD)) {
rec.flags &= ~HASREAD;
#ifdef FILE_CACHE
if (FILEa_write ('C', ida[i], &rec, 0, sizeof (CacheRecord))
!= sizeof (CacheRecord)) {
#else
if (!Storage_Put (&(theCache.store), ida[i], 0,
sizeof (CacheRecord), &rec)) {
#endif
OSConnectorFree (ida);
return;
}
}
}
#ifdef FILE_CACHE
while (theCache.size > liNewSize) {
p = reclist.next;
if (p == &reclist)
break;
list_out (p);
theCache.size -= FILEa_getSize ('C', p->id);
FILEa_delete ('C', p->id);
DEALLOC (&p);
}
#endif
OSConnectorFree (ida);
theCache.isInitialized = 1;
}
/*
* Cache_PrepareSizeChange
*
* This function prepares the cache to be shut down,
* possibly changing its size. After a call to this function the cache
* cannot be accessed without first calling Cache_Init.
* At the time of call, the old size is still in effect.
* If the new size is less than the previous size, then
* cache records that do not fit are removed on a first-in first-out basis.
*/
VOID
Cache_PrepareSizeChange (UINT32 liNewSize)
{
ListNode *p;
if (!theCache.isInitialized) {
MEMa_cachePrepared ();
return;
}
#ifdef FILE_CACHE
while (theCache.size > liNewSize) {
p = reclist.next;
if (p == &reclist)
break;
list_out (p);
theCache.size -= FILEa_getSize ('C', p->id);
FILEa_delete ('C', p->id);
DEALLOC (&p);
}
#else
/* Remove oldest until bytesUsed is less than liNewSize */
while (Storage_BytesUsed (&(theCache.store)) > liNewSize) {
p = reclist.next;
if (p == &reclist)
break;
list_out (p);
Storage_DeleteBlock (&(theCache.store), p->id);
DEALLOC (&p);
}
/* Do a complete compaction */
while (Storage_Compact (&(theCache.store))) {
}
Storage_ChangeSize (&(theCache.store), liNewSize);
#endif
theCache.waeMainObj->liCacheSize = liNewSize;
/* Free the list of cached records */
while (reclist.next != &reclist) {
p = reclist.next;
list_out (p);
DEALLOC (&p);
}
#ifndef FILE_CACHE
Storage_Finalize (&(theCache.store));
#endif
theCache.isInitialized = 0;
MEMa_cachePrepared ();
}
/*
* Cache_ URLisPresent
*
* Check if a URL is in the cache.
* "pvUrl" is the URL as a null-terminated byte string.
* "iWmlBackNav" the normal caching rules is not used when
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -