mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			343 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "stx.h"
 | |
| #include "common.h"
 | |
| 
 | |
| 
 | |
| /*****************************************
 | |
|  * Basic types definitions
 | |
|  */
 | |
| 
 | |
| struct _stx_centry {
 | |
|   void               *key;           /* key for doing lookups */
 | |
|   void               *data;          /* data in the cache */
 | |
|   size_t             weight;         /* "weight" of this entry */
 | |
|   struct _stx_centry *next;          /* next entry */
 | |
|   struct _stx_centry **pthis;
 | |
|   stx_clist_t        lru_link;       /* for putting this entry on LRU list */
 | |
|   int                ref_count;      /* use count for this entry */
 | |
|   int                delete_pending; /* pending delete flag */
 | |
| };
 | |
| 
 | |
| struct _stx_cache {
 | |
|   size_t	    max_size;    /* max size of cache */
 | |
|   size_t            cur_size;    /* current size of cache */
 | |
| 
 | |
|   size_t            max_weight;  /* cache capacity */
 | |
|   size_t            cur_weight;  /* current total "weight" of all entries */
 | |
| 
 | |
|   size_t	    hash_size;   /* size of hash table */
 | |
|   stx_cache_entry_t **table;     /* hash table for this cache */
 | |
| 
 | |
|   stx_clist_t       lru_list;    /* least-recently-used list */
 | |
| 
 | |
|   /* Cache stats */
 | |
|   unsigned long     hits;        /* num cache hits */
 | |
|   unsigned long     lookups;     /* num cache lookups */
 | |
|   unsigned long     inserts;     /* num inserts */
 | |
|   unsigned long     deletes;     /* num deletes */
 | |
| 
 | |
|   /* Functions */
 | |
|   unsigned long (*key_hash_fn)(const void *);
 | |
|   long          (*key_cmp_fn)(const void *, const void *);
 | |
|   void          (*cleanup_fn)(void *, void *);
 | |
| };
 | |
| 
 | |
| 
 | |
| #define STX_CACHE_ENTRY_PTR(_qp) \
 | |
|   ((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link)))
 | |
| 
 | |
| 
 | |
| /*****************************************
 | |
|  * Cache methods
 | |
|  */
 | |
| 
 | |
| stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight,
 | |
| 			      size_t hash_size,
 | |
| 			      unsigned long (*key_hash_fn)(const void *key),
 | |
| 			      long (*key_cmp_fn)(const void *key1,
 | |
| 						 const void *key2),
 | |
| 			      void (*cleanup_fn)(void *key, void *data))
 | |
| {
 | |
|     stx_cache_t *newcache;
 | |
| 
 | |
|     newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t));
 | |
|     if (newcache == NULL)
 | |
| 	return NULL;
 | |
|     newcache->table = (stx_cache_entry_t **)calloc(hash_size,
 | |
| 					         sizeof(stx_cache_entry_t *));
 | |
|     if (newcache->table == NULL) {
 | |
| 	free(newcache);
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     newcache->max_size = max_size;
 | |
|     newcache->max_weight = max_weight;
 | |
|     newcache->hash_size = hash_size;
 | |
|     STX_CLIST_INIT_CLIST(&(newcache->lru_list));
 | |
|     newcache->key_hash_fn = key_hash_fn;
 | |
|     newcache->key_cmp_fn = key_cmp_fn;
 | |
|     newcache->cleanup_fn = cleanup_fn;
 | |
| 
 | |
|     return newcache;
 | |
| }
 | |
| 
 | |
| 
 | |
| void stx_cache_empty(stx_cache_t *cache)
 | |
| {
 | |
|     size_t i;
 | |
|     stx_cache_entry_t *entry, *next_entry;
 | |
| 
 | |
|     for (i = 0; i < cache->hash_size; i++) {
 | |
| 	entry = cache->table[i];
 | |
| 	while (entry) {
 | |
| 	    next_entry = entry->next;
 | |
| 	    stx_cache_entry_delete(cache, entry);
 | |
| 	    entry = next_entry;
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void stx_cache_traverse(stx_cache_t *cache,
 | |
| 			void (*callback)(void *key, void *data))
 | |
| {
 | |
|     size_t i;
 | |
|     stx_cache_entry_t *entry;
 | |
| 
 | |
|     for (i = 0; i < cache->hash_size; i++) {
 | |
| 	for (entry = cache->table[i]; entry; entry = entry->next) {
 | |
| 	    if (!entry->delete_pending)
 | |
| 		(*callback)(entry->key, entry->data);
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void stx_cache_traverse_lru(stx_cache_t *cache,
 | |
| 			    void (*callback)(void *key, void *data),
 | |
| 			    unsigned int n)
 | |
| {
 | |
|     stx_clist_t *q;
 | |
|     stx_cache_entry_t *entry;
 | |
| 
 | |
|     for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n;
 | |
| 	 q = q->next, n--) {
 | |
| 	entry = STX_CACHE_ENTRY_PTR(q);
 | |
| 	(*callback)(entry->key, entry->data);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void stx_cache_traverse_mru(stx_cache_t *cache,
 | |
| 			    void (*callback)(void *key, void *data),
 | |
| 			    unsigned int n)
 | |
| {
 | |
|     stx_clist_t *q;
 | |
|     stx_cache_entry_t *entry;
 | |
| 
 | |
|     for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n;
 | |
| 	 q = q->prev, n--) {
 | |
| 	entry = STX_CACHE_ENTRY_PTR(q);
 | |
| 	(*callback)(entry->key, entry->data);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| size_t stx_cache_getsize(stx_cache_t *cache)
 | |
| {
 | |
|     return cache->cur_size;
 | |
| }
 | |
| 
 | |
| 
 | |
| size_t stx_cache_getweight(stx_cache_t *cache)
 | |
| {
 | |
|     return cache->cur_weight;
 | |
| }
 | |
| 
 | |
| 
 | |
| void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info)
 | |
| {
 | |
|     info->max_size = cache->max_size;
 | |
|     info->max_weight = cache->max_weight;
 | |
|     info->hash_size = cache->hash_size;
 | |
|     info->cur_size = cache->cur_size;
 | |
|     info->cur_weight = cache->cur_weight;
 | |
|     info->hits = cache->hits;
 | |
|     info->lookups = cache->lookups;
 | |
|     info->inserts = cache->inserts;
 | |
|     info->deletes = cache->deletes;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************
 | |
|  * Cache entry methods
 | |
|  */
 | |
| 
 | |
| stx_cache_entry_t *stx_cache_entry_create(void *key, void *data,
 | |
| 					  size_t weight)
 | |
| {
 | |
|     stx_cache_entry_t *newentry;
 | |
| 
 | |
|     newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t));
 | |
|     if (newentry == NULL)
 | |
| 	return NULL;
 | |
| 
 | |
|     newentry->key = key;
 | |
|     newentry->data = data;
 | |
|     newentry->weight = weight;
 | |
| 
 | |
|     return newentry;
 | |
| }
 | |
| 
 | |
| 
 | |
| void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry)
 | |
| {
 | |
|     entry->delete_pending = 1;
 | |
| 
 | |
|     if (entry->ref_count > 0)
 | |
| 	return;
 | |
| 
 | |
|     if (entry->pthis) {
 | |
| 	*entry->pthis = entry->next;
 | |
| 	if (entry->next)
 | |
| 	    entry->next->pthis = entry->pthis;
 | |
| 
 | |
| 	cache->cur_size--;
 | |
| 	cache->cur_weight -= entry->weight;
 | |
| 	cache->deletes++;
 | |
| 	STX_CLIST_REMOVE_LINK(&(entry->lru_link));
 | |
|     }
 | |
| 
 | |
|     if (cache->cleanup_fn)
 | |
| 	cache->cleanup_fn(entry->key, entry->data);
 | |
| 
 | |
|     entry->pthis = NULL;
 | |
|     entry->key   = NULL;
 | |
|     entry->data  = NULL;
 | |
|     free(entry);
 | |
| }
 | |
| 
 | |
| 
 | |
| stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key)
 | |
| {
 | |
|     unsigned long bucket;
 | |
|     stx_cache_entry_t *entry;
 | |
| 
 | |
|     cache->lookups++;
 | |
|     bucket = cache->key_hash_fn(key) % cache->hash_size;
 | |
|     for (entry = cache->table[bucket]; entry; entry = entry->next) {
 | |
| 	if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0)
 | |
| 	    break;
 | |
|     }
 | |
|     if (entry) {
 | |
| 	cache->hits++;
 | |
| 	if (entry->ref_count == 0)
 | |
| 	    STX_CLIST_REMOVE_LINK(&(entry->lru_link));
 | |
| 	entry->ref_count++;
 | |
|     }
 | |
| 
 | |
|     return entry;
 | |
| }
 | |
| 
 | |
| 
 | |
| void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry)
 | |
| {
 | |
|     if (entry->ref_count == 0)
 | |
| 	return;
 | |
| 
 | |
|     entry->ref_count--;
 | |
| 
 | |
|     if (entry->ref_count == 0) {
 | |
| 	STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list));
 | |
| 	if (entry->delete_pending)
 | |
| 	    stx_cache_entry_delete(cache, entry);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry)
 | |
| {
 | |
|     stx_cache_entry_t *old_entry;
 | |
|     unsigned long bucket;
 | |
| 
 | |
|     /*
 | |
|      * If cache capacity is exceeded, try to remove LRU entries till there is
 | |
|      * enough room or LRU list is empty.
 | |
|      */
 | |
|     while (cache->cur_weight + entry->weight > cache->max_weight) {
 | |
| 	old_entry = stx_cache_entry_getlru(cache);
 | |
| 	if (!old_entry) {
 | |
| 	    /* cache capacity is exceeded and all entries are in use */
 | |
| 	    return -1;
 | |
| 	}
 | |
| 	stx_cache_entry_delete(cache, old_entry);
 | |
|     }
 | |
| 
 | |
|     /* If cache size is exceeded, remove LRU entry */
 | |
|     if (cache->cur_size >= cache->max_size) {
 | |
| 	old_entry = stx_cache_entry_getlru(cache);
 | |
| 	if (!old_entry) {
 | |
| 	    /* cache size is exceeded and all entries are in use */
 | |
| 	    return -1;
 | |
| 	}
 | |
| 	stx_cache_entry_delete(cache, old_entry);
 | |
|     }
 | |
| 
 | |
|     /* Don't add duplicate entries in the cache */
 | |
|     bucket = cache->key_hash_fn(entry->key) % cache->hash_size;
 | |
|     for (old_entry = cache->table[bucket]; old_entry;
 | |
| 	 old_entry = old_entry->next) {
 | |
| 	if (!old_entry->delete_pending &&
 | |
| 	    cache->key_cmp_fn(entry->key, old_entry->key) == 0)
 | |
| 	    break;
 | |
|     }
 | |
|     if (old_entry)
 | |
| 	stx_cache_entry_delete(cache, old_entry);
 | |
| 
 | |
|     /* Insert in the hash table */
 | |
|     entry->next = cache->table[bucket];
 | |
|     cache->table[bucket] = entry;
 | |
|     entry->pthis = &cache->table[bucket];
 | |
|     if (entry->next)
 | |
| 	entry->next->pthis = &entry->next;
 | |
|     entry->ref_count++;
 | |
| 
 | |
|     cache->inserts++;
 | |
|     cache->cur_size++;
 | |
|     cache->cur_weight += entry->weight;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache)
 | |
| {
 | |
|     if (STX_CLIST_IS_EMPTY(&(cache->lru_list)))
 | |
| 	return NULL;
 | |
| 
 | |
|     return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list)));
 | |
| }
 | |
| 
 | |
| 
 | |
| int stx_cache_entry_sizeof(void)
 | |
| {
 | |
|     return (int)sizeof(stx_cache_entry_t);
 | |
| }
 | |
| 
 | |
| 
 | |
| void *stx_cache_entry_getdata(stx_cache_entry_t *entry)
 | |
| {
 | |
|     return entry->data;
 | |
| }
 | |
| 
 | |
| 
 | |
| void *stx_cache_entry_getkey(stx_cache_entry_t *entry)
 | |
| {
 | |
|     return entry->key;
 | |
| }
 | |
| 
 | |
| 
 | |
| size_t stx_cache_entry_getweight(stx_cache_entry_t *entry)
 | |
| {
 | |
|     return entry->weight;
 | |
| }
 | |
| 
 |