1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
22
  
23
  
24
  
25
  
26
  
27
  
28
  
29
  
30
  
31
  
32
  
33
  
34
  
35
  
36
  
37
  
38
  
39
  
40
  
41
  
42
  
43
  
44
  
45
  
46
  
47
  
48
  
49
  
50
  
51
  
52
  
53
  
54
  
55
  
56
  
57
  
58
  
59
  
60
  
61
  
62
  
63
  
64
  
65
  
66
  
67
  
68
  
69
  
70
  
71
  
72
  
73
  
74
  
75
  
76
  
77
  
78
  
79
  
80
  
81
  
82
  
83
  
84
  
85
  
86
  
87
  
88
  
89
  
90
  
91
  
92
  
93
  
94
  
95
  
96
  
97
  
98
  
99
  
100
  
101
  
102
  
103
  
104
  
105
  
106
  
107
  
108
  
109
  
110
  
111
  
112
  
113
  
114
  
115
  
116
  
117
  
118
  
119
  
120
  
121
  
122
  
123
  
124
  
125
  
126
  
127
  
128
  
129
  
130
  
131
  
132
  
133
  
//! A RAM-based storage manager. 
//! 
//! This storage manager provides the means to save data to memory. 
//! In this manager I'll add reference documentation as comments to 
//! interfaces. It will be organized later in a more comprehensive format 
//! 
//! Settings will be added later. 
//! 
//! @thanks 
//!   Thanks to Francesco Chemolli <kinkie@@roxen.com> for the contribution. 
 
#pike __REAL_VERSION__ 
 
//! 
class Data { 
 
  inherit Cache.Data; 
 
  int _size=0; 
  mixed _data=0; 
  multiset(string) _deps; 
 
  void create(void|mixed value, void|int abs_expire_time, 
              void|float preciousness, 
              void|multiset(string) dependants) { 
    _data=value; 
    atime=ctime=time(1); 
    if (abs_expire_time) etime=abs_expire_time; 
    if (preciousness) cost=preciousness; 
    if (dependants) _deps=dependants; 
  } 
 
  int size() { 
    if (_size) return _size; 
    return (_size=recursive_low_size(_data)); 
  } 
 
  mixed data() { 
    return _data; 
  } 
 
} 
 
inherit Cache.Storage.Base; 
 
private mapping(string:mixed) data=([]); 
 
 
/* 
 * First an iterator over the contents. 
 * Since accesses to the data are not serialized to increase efficiency 
 * there are a few guidelines that must be followed in order to maintain 
 * consistency. 
 * 
 * First off, only one entity must be using the iterator at one time. 
 * That's not as bad as it seems, as the only entity needing to enumerate 
 * the entries in cache is the expiration policy manager, and there can 
 * be only one for each storage manager. 
 * 
 * The enumerator over the cache is initialized by a call to first(). 
 * Subsequent calls to next() return following entries in the cache. 
 * While it is guarranteed that each and all entries in the cache 
 * will be iterated upon by the enumerator, their order is NOT guarranteed 
 * to remain consistent across calls. 
 */ 
 
// these are used by the enumerator. While entries might be deleted while 
// enumerating, it won't bite us. 
private array(string) iter=0; 
private int current=0; 
 
int(0..0)|string first() { 
  iter=indices(data); 
  current=0; 
  return next(); 
} 
 
int(0..0)|string next() { 
  if (iter && current < sizeof(iter) && data[iter[current]]) 
    return iter[current++]; 
  iter=0; 
  return 0; 
} 
 
/* 
 * Guess what these do? 
 * I leave the data-object creation here, so that the storage manager 
 * can choose whatever data-class it pleases 
 */ 
void set(string key, mixed value, 
         void|int absolute_expire, 
         void|float preciousness, 
         void|multiset(string) dependants) { 
  data[key]=Data(value,absolute_expire,preciousness,dependants); 
} 
 
//! Fetches some data from the cache. If notouch is set, don't touch the 
//! data from the cache (meant to be used by the storage manager only) 
int(0..0)|Cache.Data get(string key, void|int notouch) { 
  mixed tmp; 
  tmp=data[key]; 
  if (!notouch && tmp) tmp->touch(); 
  return tmp; 
} 
 
void aget(string key, 
          function(string,int(0..0)|Cache.Data:void) callback) { 
  mixed rv=get(key); 
  callback(key,rv); 
} 
 
void delete(string key, void|int(0..1) hard) { 
  object(Cache.Data) rv=data[key]; 
  if (!rv) return; 
  multiset deps=rv->_deps; 
 
  //need to destruct this first, or we might infinite-loop recursing 
  //through the dependants (think of a bug listing a value depending 
  //on itself, or even worse, circularly. 
  if (hard) { 
    destruct(rv->value()); 
  } 
  m_delete(data,key); 
 
  if (deps) { 
    foreach((array)(deps), string dep) { 
      delete(dep,hard); 
    } 
  } 
  return 0; 
}