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

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

E-mail: