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