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