Annotation of parser3/src/types/pa_vhashfile.C, revision 1.50
1.1 parser 1: /** @file
2: Parser: @b table class.
3:
1.20 paf 4: Copyright(c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)
1.19 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1 parser 6: */
7:
1.50 ! paf 8: static const char * const IDENT="$Date: 2006/01/17 14:41:17 $";
1.1 parser 9:
1.32 paf 10: #include "pa_globals.h"
1.48 paf 11: #include "pa_common.h"
1.32 paf 12: #include "pa_threads.h"
1.1 parser 13: #include "pa_vtable.h"
14: #include "pa_vstring.h"
15: #include "pa_vhashfile.h"
1.32 paf 16: #include "pa_vdate.h"
17:
18: // consts
19:
1.41 paf 20: const uint HASHFILE_VALUE_SERIALIZED_VERSION=0x0001;
1.1 parser 21:
22: // methods
23:
1.22 paf 24: void check(const char *step, apr_status_t status) {
25: if(status==APR_SUCCESS)
26: return;
27:
1.40 paf 28: const char* str=strerror(status);
1.33 paf 29: throw Exception("file.access",
1.22 paf 30: 0,
1.33 paf 31: "%s error: %s (%d)",
1.40 paf 32: step, str?str:"<unknown>", status);
1.22 paf 33: }
34:
35: void VHashfile::open(const String& afile_name) {
1.48 paf 36: file_name=afile_name.cstr(String::L_FILE_SPEC);
37:
38: if(!entry_exists(file_name))
39: create_dir_for_file(afile_name);
40:
41: check("apr_sdbm_open(shared)", apr_sdbm_open(&m_db, file_name,
1.27 paf 42: APR_CREATE|APR_READ|APR_SHARELOCK,
1.22 paf 43: 0664, 0));
44: }
45:
1.34 paf 46: void VHashfile::close() {
47: check_db();
48:
49: check("apr_sdbm_close", apr_sdbm_close(m_db)); m_db=0;
50: }
51:
52: void VHashfile::check_db() const {
53: if(!m_db)
54: throw Exception(0,
55: 0,
56: "%s is closed", type());
57: }
58:
59: apr_sdbm_t *VHashfile::get_db_for_reading() const {
60: check_db();
61:
62: return m_db;
63: }
64:
65: apr_sdbm_t *VHashfile::get_db_for_writing() {
66: check_db();
67:
68: if(apr_sdbm_rdonly(m_db)) {
69: // reopen in write mode & exclusive lock
70: close();
71: check("apr_sdbm_open(exclusive)", apr_sdbm_open(&m_db, file_name,
1.27 paf 72: APR_WRITE,
73: 0664, 0));
74: }
1.34 paf 75:
76: return m_db;
1.27 paf 77: }
78:
1.22 paf 79: VHashfile::~VHashfile() {
1.34 paf 80: if(m_db)
81: close();
1.22 paf 82: }
83:
1.32 paf 84: struct Hashfile_value_serialized_prolog {
1.41 paf 85: uint version;
1.32 paf 86: time_t time_to_die;
87: };
88:
1.35 paf 89: apr_sdbm_datum_t VHashfile::serialize_value(const String& string, time_t time_to_die) const {
1.32 paf 90: apr_sdbm_datum_t result;
91:
92: size_t length=string.length();
93: result.dsize=sizeof(Hashfile_value_serialized_prolog)+length;
94: result.dptr=new(PointerFreeGC) char[result.dsize];
95:
96: Hashfile_value_serialized_prolog& prolog=*reinterpret_cast<Hashfile_value_serialized_prolog*>(result.dptr);
97: char *output_cstr=result.dptr+sizeof(Hashfile_value_serialized_prolog);
98:
99: prolog.version=HASHFILE_VALUE_SERIALIZED_VERSION;
100: prolog.time_to_die=time_to_die;
1.45 paf 101: if(length) // reported errors on storing empty values to hashfiles, but without details. maybe here [win32, intel:solaris, freebsd were OK...]
102: memcpy(output_cstr, string.cstr(), length);
1.32 paf 103:
104: return result;
105: }
106:
1.35 paf 107: const String* VHashfile::deserialize_value(apr_sdbm_datum_t key, const apr_sdbm_datum_t value) {
108: // key not found || it's surely not in our format
1.38 paf 109: if(!value.dptr || (size_t)value.dsize<sizeof(Hashfile_value_serialized_prolog))
1.35 paf 110: return 0;
111:
1.41 paf 112: // [WARNING: not cast, addresses must be %4=0 on sparc]
1.42 paf 113: Hashfile_value_serialized_prolog prolog;
114: memcpy(&prolog, value.dptr, sizeof(prolog));
1.41 paf 115:
1.42 paf 116: if(prolog.version!=HASHFILE_VALUE_SERIALIZED_VERSION
117: || (prolog.time_to_die/*specified*/
118: && (prolog.time_to_die <= time(0)/*expired*/))) {
1.35 paf 119: // old format || exipred value
120: remove(key);
1.32 paf 121: return 0;
1.35 paf 122: }
1.32 paf 123:
1.35 paf 124: char *input_cstr=value.dptr+sizeof(Hashfile_value_serialized_prolog);
125: size_t input_length=value.dsize-sizeof(Hashfile_value_serialized_prolog);
1.32 paf 126:
1.47 paf 127: return new String(input_length? pa_strdup(input_cstr, input_length): 0, input_length, true);
1.32 paf 128: }
129:
1.27 paf 130: void VHashfile::put_field(const String& aname, Value *avalue) {
1.34 paf 131: apr_sdbm_t *db=get_db_for_writing();
1.22 paf 132:
1.8 parser 133: time_t time_to_die=0;
134: const String *value_string;
135:
1.23 paf 136: if(HashStringValue *hash=avalue->get_hash()) {
137: if(Value *value_value=hash->get(value_name)) {
1.9 parser 138: if(value_value->get_junction())
1.23 paf 139: throw Exception(0,
140: 0,
141: VALUE_NAME" must not be code");
1.8 parser 142:
143: value_string=&value_value->as_string();
144:
1.32 paf 145: if(Value *expires=hash->get(expires_name)) {
146: if(Value* vdate=expires->as(VDATE_TYPE, false))
147: time_to_die=static_cast<VDate*>(vdate)->get_time(); // $expires[DATE]
148: else if(double days_till_expire=expires->as_double())
149: time_to_die=time(NULL)+(time_t)(60*60*24*days_till_expire); // $expires(days)
150: }
1.8 parser 151: } else
1.23 paf 152: throw Exception(0,
1.8 parser 153: &aname,
1.23 paf 154: "put hash value must contain ."VALUE_NAME);
1.8 parser 155: } else
156: value_string=&avalue->as_string();
1.43 paf 157:
158: if(aname.is_empty())
159: throw Exception("parser.runtime",
160: 0,
161: "hashfile key must not be empty");
1.5 parser 162:
1.23 paf 163: apr_sdbm_datum_t key;
164: key.dptr=const_cast<char*>(aname.cstr());
1.24 paf 165: key.dsize=aname.length();
1.23 paf 166:
1.32 paf 167: apr_sdbm_datum_t value=serialize_value(*value_string, time_to_die);
1.23 paf 168:
169: check("apr_sdbm_store", apr_sdbm_store(db, key, value, APR_SDBM_REPLACE));
1.1 parser 170: }
171:
1.11 paf 172: Value *VHashfile::get_field(const String& aname) {
1.34 paf 173: apr_sdbm_t *db=get_db_for_reading();
174:
1.23 paf 175: apr_sdbm_datum_t key;
176: key.dptr=const_cast<char*>(aname.cstr());
1.24 paf 177: key.dsize=aname.length();
1.23 paf 178:
179: apr_sdbm_datum_t value;
180:
181: check("apr_sdbm_fetch", apr_sdbm_fetch(db, &value, key));
182:
1.35 paf 183: const String *sresult=deserialize_value(key, value);
1.32 paf 184: return sresult? new VString(*sresult): 0;
1.24 paf 185: }
186:
1.35 paf 187: void VHashfile::remove(const apr_sdbm_datum_t key) {
1.34 paf 188: apr_sdbm_t *db=get_db_for_writing();
1.27 paf 189:
1.35 paf 190: check("apr_sdbm_delete", apr_sdbm_delete(db, key));
191: }
192:
193: void VHashfile::remove(const String& aname) {
1.24 paf 194: apr_sdbm_datum_t key;
195: key.dptr=const_cast<char*>(aname.cstr());
196: key.dsize=aname.length();
197:
1.35 paf 198: remove(key);
1.1 parser 199: }
200:
1.50 ! paf 201: void VHashfile::for_each(bool callback(apr_sdbm_datum_t, void*), void* info) const {
1.34 paf 202: apr_sdbm_t *db=get_db_for_reading();
203:
1.30 paf 204: // collect keys
1.49 paf 205: Array<apr_sdbm_datum_t>* keys=0;
1.25 paf 206: check("apr_sdbm_lock", apr_sdbm_lock(db, APR_FLOCK_SHARED));
207: try {
1.30 paf 208: apr_sdbm_datum_t key;
209: if(apr_sdbm_firstkey(db, &key)==APR_SUCCESS)
1.49 paf 210: {
211: size_t count=0;
1.25 paf 212: do {
1.49 paf 213: // must cound beforehead, becase doing reallocs later would be VERY slow and cause HUGE fragmentation
214: count++;
1.30 paf 215: } while(apr_sdbm_nextkey(db, &key)==APR_SUCCESS);
1.49 paf 216:
217: keys=new Array<apr_sdbm_datum_t>(count);
218:
219: if(apr_sdbm_firstkey(db, &key)==APR_SUCCESS)
220: do {
221: // must clone because it points to page which may go away
222: // [if they modify hashfile inside foreach]
223: key.dptr = pa_strdup(key.dptr, key.dsize);
224: *keys+=key;
225: } while(apr_sdbm_nextkey(db, &key)==APR_SUCCESS);
226: }
1.25 paf 227: } catch(...) {
228: check("apr_sdbm_unlock", apr_sdbm_unlock(db));
229: rethrow;
1.4 parser 230: }
1.30 paf 231: check("apr_sdbm_unlock", apr_sdbm_unlock(db));
1.4 parser 232:
1.30 paf 233: // iterate them
1.49 paf 234: if(keys)
235: keys->for_each(callback, info);
1.26 paf 236: }
1.27 paf 237:
238: #ifndef DOXYGEN
239: struct For_each_string_callback_info {
1.35 paf 240: VHashfile* self;
1.27 paf 241: void* nested_info;
1.50 ! paf 242: bool (*nested_callback)(const String::Body, const String&, void*);
1.27 paf 243: };
244: #endif
1.50 ! paf 245: static bool for_each_string_callback(apr_sdbm_datum_t apkey, void* ainfo) {
1.27 paf 246: For_each_string_callback_info& info=*static_cast<For_each_string_callback_info *>(ainfo);
1.35 paf 247: apr_sdbm_t *db=info.self->get_db_for_reading();
1.27 paf 248:
249: apr_sdbm_datum_t apvalue;
1.35 paf 250: check("apr_sdbm_fetch", apr_sdbm_fetch(db, &apvalue, apkey));
251:
252: if(const String* svalue=info.self->deserialize_value(apkey, apvalue)) {
253: const char *clkey=pa_strdup(apkey.dptr, apkey.dsize);
1.27 paf 254:
1.50 ! paf 255: return info.nested_callback(clkey, *svalue, info.nested_info);
1.35 paf 256: }
1.50 ! paf 257: return false;
1.27 paf 258: }
1.50 ! paf 259: void VHashfile::for_each(bool callback(const String::Body, const String&, void*), void* ainfo) {
1.27 paf 260: For_each_string_callback_info info;
261:
1.35 paf 262: info.self=this;
1.27 paf 263: info.nested_info=ainfo;
264: info.nested_callback=callback;
265:
266: for_each(for_each_string_callback, &info);
267: }
268:
1.50 ! paf 269: static bool clear_delete_key(apr_sdbm_datum_t key, void* adb) {
1.30 paf 270: check("apr_sdbm_delete", apr_sdbm_delete(static_cast<apr_sdbm_t*>(adb), key));
1.50 ! paf 271: return false;
1.27 paf 272: }
273: void VHashfile::clear() {
1.34 paf 274: apr_sdbm_t *db=get_db_for_writing();
1.27 paf 275:
1.30 paf 276: for_each(clear_delete_key, db);
1.27 paf 277: }
278:
1.26 paf 279:
1.50 ! paf 280: static bool get_hash__put(const String::Body key, const String& value, void* aresult) {
1.26 paf 281: static_cast<HashStringValue*>(aresult)->put(key, new VString(value));
1.50 ! paf 282: return false;
1.26 paf 283: }
284: HashStringValue *VHashfile::get_hash() {
285: HashStringValue& result=*new HashStringValue();
286:
287: for_each(get_hash__put, &result);
288: return &result;
1.34 paf 289: }
290:
291: static void delete_file(const char* base_name, const char* ext) {
292: String sfile_name(base_name, false/*already removed tainting at ::open*/);
293: sfile_name<<ext;
294: file_delete(sfile_name);
295: }
296:
297: void VHashfile::delete_files() {
298: close();
299: delete_file(file_name, APR_SDBM_DIRFEXT);
300: delete_file(file_name, APR_SDBM_PAGFEXT);
1.1 parser 301: }
E-mail: