Annotation of parser3/src/types/pa_vhashfile.C, revision 1.61

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.61    ! misha       8: static const char * const IDENT="$Date: 2009-07-06 08:48:24 $";
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.61    ! misha      43:        file_name=afile_name.taint_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.59      misha     151:        return new String(input_length? pa_strdup(input_cstr, input_length): 0, String::L_TAINTED);
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) {
1.59      misha     315:        String sfile_name(base_name);
1.34      paf       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: