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