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