Itzam/C++

Main Index

Created by Scott Robert Ladd at Coyote Gulch Productions.


complex_database.h

00001 //---------------------------------------------------------------------
00002 //  Algorithmic Conjurings @ http://www.coyotegulch.com
00003 //
00004 //  Itzam - An Embedded Database Engine
00005 //
00006 //  database_file.h
00007 //
00008 //---------------------------------------------------------------------
00009 //
00010 //  Copyright 2004, 2005, 2006 Scott Robert Ladd
00011 //
00012 //  This program is free software; you can redistribute it and/or modify
00013 //  it under the terms of the GNU General Public License as published by
00014 //  the Free Software Foundation; either version 2 of the License, or
00015 //  (at your option) any later version.
00016 //  
00017 //  This program is distributed in the hope that it will be useful,
00018 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //  GNU General Public License for more details.
00021 //  
00022 //  You should have received a copy of the GNU General Public License
00023 //  along with this program; if not, write to the
00024 //      Free Software Foundation, Inc.
00025 //      59 Temple Place - Suite 330
00026 //      Boston, MA 02111-1307, USA.
00027 //
00028 //-----------------------------------------------------------------------
00029 //
00030 //  For more information on this software package, please visit
00031 //  Scott's web site, Coyote Gulch Productions, at:
00032 //
00033 //      http://www.coyotegulch.com
00034 //  
00035 //-----------------------------------------------------------------------
00036 
00037 #if !defined(LIBITZAM_CPP_COMPLEX_DATABASE_H)
00038 #define LIBITZAM_CPP_COMPLEX_DATABASE_H
00039 
00040 // Standard C++
00041 #include <set>
00042 #include <vector>
00043 
00044 // include common classes
00045 #include "itzam_common.h"
00046 
00048 
00053 namespace itzam
00054 {
00055     using std::set;
00056     using std::vector;
00057     
00059 
00063     class ref_list
00064     {
00065         private:
00067             set<itzam_ref> m_refs;
00068         
00070             static const size_t BLOCK_SIZE = 256;
00071         
00072         public:
00074 
00077             ref_list()
00078               : m_refs()
00079             {
00080                 // nada
00081             }
00082 
00084 
00087             ref_list(const set<itzam_ref> & value)
00088               : m_refs(value)
00089             {
00090                 // nada
00091             }
00092 
00094 
00101             ref_list(const serial_data & source)
00102               : m_refs()
00103             {
00104                 size_t n = 0;
00105                 const itzam_ref * p = reinterpret_cast<const itzam_ref *>(source.get_data());
00106                 
00107                 while ((n < source.get_size()) && (*p != ITZAM_NULL_REF))
00108                 {
00109                     m_refs.insert(*p);
00110                     n += sizeof(itzam_ref);
00111                     ++p;
00112                 }
00113             }
00114 
00116 
00121             ref_list(const ref_list & source)
00122               : m_refs(source.m_refs)
00123             {
00124                 // nada
00125             }
00126 
00128 
00134             ref_list & operator = (const ref_list & source)
00135             {
00136                 m_refs = source.m_refs;
00137                 return *this;
00138             }
00139 
00141 
00147             ref_list & operator = (const set<itzam_ref> & source)
00148             {
00149                 m_refs = source;
00150                 return *this;
00151             }
00152 
00154 
00160             ref_list & operator = (const serial_data & source)
00161             {
00162                 m_refs = *((set<itzam_ref> *)source.get_data());
00163                 return *this;
00164             }
00165 
00167 
00172             ~ref_list()
00173             {
00174                 // nada, just for any derived classes
00175             }
00176 
00178 
00182             operator set<itzam_ref>() const
00183             {
00184                 return m_refs;
00185             }
00186 
00188 
00193             operator serial_data() const
00194             {
00195                 size_t size = (m_refs.size() / BLOCK_SIZE + 1) * BLOCK_SIZE;
00196                 
00197                 if (size == 0)
00198                     size = BLOCK_SIZE;
00199                 
00200                 itzam_ref * refs = (itzam_ref *)malloc(sizeof(itzam_ref) * size);
00201                 itzam_ref * p = refs;
00202                 
00203                 size_t n = 0;
00204                 for (set<itzam_ref>::const_iterator i = m_refs.begin(); i != m_refs.end(); ++i)
00205                 {
00206                     *p = *i;
00207                     ++p;
00208                     ++n;
00209                 }
00210                 
00211                 for (; n < size; ++n)
00212                 {
00213                     *p = ITZAM_NULL_REF;
00214                     ++p;
00215                 }
00216                 
00217                 return serial_data(refs,sizeof(itzam_ref) * size,true);
00218             }
00219             
00221 
00226             bool contains(const itzam_ref & ref)
00227             {
00228                 return (m_refs.count(ref) > 0);
00229             }
00230             
00232 
00236             void add(const itzam_ref & ref)
00237             {
00238                 if (!contains(ref))
00239                     m_refs.insert(ref);
00240             }
00241             
00243 
00247             void remove(const itzam_ref & ref)
00248             {
00249                 if (contains(ref))
00250                     m_refs.erase(ref);
00251             }
00252             
00254 
00257             void remove_all()
00258             {
00259                 m_refs.clear();
00260             }
00261             
00263 
00267             size_t count() const
00268             {
00269                 return m_refs.size();
00270             }
00271     };
00272 
00274 
00279     template <class key_t, class mutex_t = simple_mutex>
00280         class btree_index_base
00281           : public database_object,  public mutex_t
00282         {
00283             public:
00285                 typedef key_t   key_type;
00286             
00287             // internal data
00288             private:
00289                 itzam_btree m_btree;
00290 
00291             protected:
00293 
00297                 itzam_btree * get_btree()
00298                 {
00299                     return &m_btree;
00300                 }
00301                 
00303                 void lock()
00304                 {
00305                     mutex_t::lock();
00306                 }                
00307             
00309                 void unlock()
00310                 {
00311                     mutex_t::unlock();
00312                 }                
00313             
00315 
00322                 btree_index_base(const string & name, itzam_file_lock_mode lock_mode)
00323                     : database_object(name,lock_mode)
00324                 {
00325                     open();
00326                 }
00327             
00329 
00332                 virtual ~btree_index_base()
00333                 {
00334                     close();
00335                 }
00336                 
00337             public:
00339 
00345                 virtual void open()
00346                 {
00347                     database_error error = DB_ERROR_NONE;
00348 
00349                     mutex_t::lock();
00350 
00351                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00352                     {
00353                         case ITZAM_DB_OPEN:
00354                             // ignore opening an open database
00355                             break;
00356 
00357                         case ITZAM_DB_UNINIT:
00358                         case ITZAM_DB_CLOSED:
00359                         {
00360                             // check for file existence
00361                             struct stat dbstats;
00362                             itzam_state state;
00363 
00364                             if (0 == stat(m_name.c_str(),&dbstats))
00365                             {
00366                                 state = itzam_btree_open(btree_index_base<key_t,mutex_t>::get_btree(),
00367                                                          m_name.c_str(),
00368                                                          m_file_lock_mode, 
00369                                                          key_t::compare_keys,
00370                                                          false);
00371                             }
00372                             else
00373                             {
00374                                 state = itzam_btree_create(btree_index_base<key_t,mutex_t>::get_btree(),
00375                                                            m_name.c_str(),
00376                                                            m_file_lock_mode, 
00377                                                            ITZAM_BTREE_ORDER_DEFAULT,
00378                                                            key_t::compare_keys);
00379                             }
00380 
00381                             if (state == ITZAM_OKAY)
00382                                 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_OPEN;
00383                             else
00384                             {
00385                                 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN;
00386                                 error = DB_ERROR_OPEN_FAILED;
00387                             }
00388 
00389                             break;
00390                         }
00391 
00392                         default:
00393                             error = DB_ERROR_OPEN_BROKEN;
00394                     }
00395 
00396                     mutex_t::unlock();
00397 
00398                     if (error != DB_ERROR_NONE)
00399                         throw database_exception(error);
00400                 }
00401 
00403 
00408                 virtual void close()
00409                 {
00410                     database_error error = DB_ERROR_NONE;
00411 
00412                     mutex_t::lock();
00413 
00414                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00415                     {
00416                         case ITZAM_DB_OPEN:
00417                         {
00418                             // close the file
00419                             itzam_state state = itzam_btree_close(btree_index_base<key_t,mutex_t>::get_btree());
00420 
00421                             if (state == ITZAM_OKAY)
00422                                 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_CLOSED;
00423                             else
00424                             {
00425                                 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN;
00426                                 error = DB_ERROR_CLOSE_FAILED;
00427                             }
00428 
00429                             break;
00430                         }
00431 
00432                         case ITZAM_DB_CLOSED:
00433                             // just ignore closing a closed database
00434                             break;
00435 
00436                         default:
00437                             error = DB_ERROR_CLOSE_BROKEN;
00438                     }
00439 
00440                     mutex_t::unlock();
00441 
00442                     if (error != DB_ERROR_NONE)
00443                         throw database_exception(error);
00444                 }
00445 
00447 
00452                 virtual void insert(const key_t & key, itzam_ref ref) = 0;
00453 
00455 
00459                 //virtual void remove(const key_t & key) = 0;
00460 
00462 
00467                 virtual bool exists(const key_t & key) = 0;
00468         };
00469         
00471 
00477     template <class key_t, class mutex_t = simple_mutex>
00478         class btree_index
00479           : public btree_index_base<key_t,mutex_t>
00480         {
00481             public:
00483 
00490                 btree_index(const string & name, itzam_file_lock_mode lock_mode)
00491                     : btree_index_base<key_t,mutex_t>(name,lock_mode)
00492                 {
00493                     // everything handled by base class
00494                 }
00495             
00497 
00500                 ~btree_index()
00501                 {
00502                     // everything handled by base class
00503                 }
00504                 
00506 
00512                 virtual void insert(const key_t & key, itzam_ref ref)
00513                 {
00514                     database_error error = DB_ERROR_NONE;
00515 
00516                     mutex_t::lock();
00517 
00518                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00519                     {
00520                         case ITZAM_DB_OPEN:
00521                         {
00522                             serial_data raw_key = serial_data(key);
00523                             
00524                             // prevent duplicate keys
00525                             if (itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(),raw_key.get_data()) != ITZAM_NULL_REF)
00526                                 error = DB_ERROR_WRITE_DUPE;
00527                             else
00528                             {
00529                                 if (ref == ITZAM_NULL_REF)
00530                                     error = DB_ERROR_WRITE_FAILED;
00531                                 else
00532                                 {
00533                                     // add key to the database
00534                                     itzam_state state = itzam_btree_insert(btree_index_base<key_t,mutex_t>::get_btree(),
00535                                                                            raw_key.get_data(),
00536                                                                            raw_key.get_size(),
00537                                                                            ref);
00538 
00539                                     // handle an error          
00540                                     if (state != ITZAM_OKAY)
00541                                     {
00542                                         btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN;
00543                                         error = DB_ERROR_WRITE_FAILED;
00544                                     }
00545                                 }
00546                             }
00547 
00548                             break;
00549                         }
00550                         case ITZAM_DB_CLOSED:
00551                             error = DB_ERROR_WRITE_CLOSED;
00552                             break;
00553 
00554                         default:
00555                             error = DB_ERROR_WRITE_BROKEN;
00556                     }
00557 
00558                     mutex_t::unlock();
00559 
00560                     if (error != DB_ERROR_NONE)
00561                         throw database_exception(error);
00562                 }            
00563                 
00565 
00569                 virtual itzam_ref remove(const key_t & key)
00570                 {
00571                     itzam_ref ref = ITZAM_NULL_REF;
00572                     database_error error = DB_ERROR_NONE;
00573 
00574                     mutex_t::lock();
00575 
00576                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00577                     {
00578                         case ITZAM_DB_OPEN:
00579                         {
00580                             // remove key
00581                             itzam_state state = itzam_btree_remove(btree_index_base<key_t,mutex_t>::get_btree(),
00582                                                                    serial_data(key).get_data(),
00583                                                                    &ref);
00584 
00585                             // check for errors
00586                             if (state != ITZAM_OKAY)
00587                                 error = DB_ERROR_REMOVE_FAILED;
00588 
00589                             break;
00590                         }
00591 
00592                         case ITZAM_DB_CLOSED:
00593                             error = DB_ERROR_REMOVE_CLOSED;
00594                             break;
00595 
00596                         default:
00597                             error = DB_ERROR_REMOVE_BROKEN;
00598                     }
00599 
00600                     mutex_t::unlock();
00601 
00602                     if (error != DB_ERROR_NONE)
00603                         throw database_exception(error);
00604                     
00605                     return ref;
00606                 }
00607                 
00608 
00610 
00615                 virtual bool exists(const key_t & key)
00616                 {
00617                     database_error error = DB_ERROR_NONE;
00618 
00619                     mutex_t::lock();
00620 
00621                     bool result = false;
00622 
00623                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00624                     {
00625                         case ITZAM_DB_OPEN:
00626                             // find data from key
00627                             result = (ITZAM_NULL_REF != itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(),
00628                                                                          serial_data(key).get_data()));
00629                             break;
00630 
00631                         case ITZAM_DB_CLOSED:
00632                             error = DB_ERROR_EXISTS_CLOSED;
00633                             break;
00634 
00635                         default:
00636                             error = DB_ERROR_EXISTS_BROKEN;
00637                     }
00638 
00639                     mutex_t::unlock();
00640 
00641                     if (error != DB_ERROR_NONE)
00642                         throw database_exception(error);
00643 
00644                     return result;
00645                 }
00646                 
00648 
00653                 virtual itzam_ref find(const key_t & key)
00654                 {
00655                     database_error error = DB_ERROR_NONE;
00656 
00657                     mutex_t::lock();
00658 
00659                     itzam_ref ref = ITZAM_NULL_REF;
00660 
00661                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00662                     {
00663                         case ITZAM_DB_OPEN:
00664                             // find data from key
00665                             ref = itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(),
00666                                                    serial_data(key).get_data());
00667                             break;
00668 
00669                         case ITZAM_DB_CLOSED:
00670                             error = DB_ERROR_EXISTS_CLOSED;
00671                             break;
00672 
00673                         default:
00674                             error = DB_ERROR_EXISTS_BROKEN;
00675                     }
00676 
00677                     mutex_t::unlock();
00678 
00679                     if (error != DB_ERROR_NONE)
00680                         throw database_exception(error);
00681 
00682                     return ref;
00683                 }            
00684         };
00685     
00687 
00693     template <class key_t, class mutex_t = simple_mutex>
00694         class btree_index_with_duplicates
00695           : public btree_index_base<key_t,mutex_t>
00696         {
00697             public:
00699 
00706                 btree_index_with_duplicates(const string & name, itzam_file_lock_mode lock_mode)
00707                     : btree_index_base<key_t,mutex_t>(name,lock_mode)
00708                 {
00709                     // everything handled by base class
00710                 }
00711             
00713 
00716                 ~btree_index_with_duplicates()
00717                 {
00718                     // everything handled by base class
00719                 }
00720                 
00722 
00727                 virtual void insert(const key_t & key, itzam_ref ref)
00728                 {
00729                     itzam_state state;
00730                     database_error error = DB_ERROR_NONE;
00731 
00732                     mutex_t::lock();
00733 
00734                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00735                     {
00736                         case ITZAM_DB_OPEN:
00737                         {
00738                             // get a pointer to the data file
00739                             itzam_btree *    btree    = btree_index_base<key_t,mutex_t>::get_btree();
00740                             itzam_datafile * datafile = btree->m_datafile;
00741                             
00742                             // create a key object
00743                             serial_data raw_key = serial_data(key);
00744                             
00745                             // get a reference for the key
00746                             itzam_ref list_ref = itzam_btree_find(btree,raw_key.get_data());
00747                             
00748                             // did we find the key?
00749                             if (list_ref == ITZAM_NULL_REF)
00750                             {
00751                                 // key not found; we need to create a new reference list
00752                                 ref_list list;
00753                                 list.add(ref);
00754                                 
00755                                 // write new list to file
00756                                 serial_data raw_list = serial_data(list);
00757 
00758                                 itzam_ref list_ref = itzam_datafile_write(datafile,
00759                                                                           raw_list.get_data(),
00760                                                                           raw_list.get_size(),
00761                                                                           ITZAM_NULL_REF);
00762                                 
00763                                 if (list_ref != ITZAM_NULL_REF)
00764                                 {
00765                                     // add key to the database
00766                                     state = itzam_btree_insert(btree,
00767                                                                raw_key.get_data(),
00768                                                                raw_key.get_size(),
00769                                                                list_ref);
00770 
00771                                     // handle an error          
00772                                     if (state != ITZAM_OKAY)
00773                                         error = DB_ERROR_WRITE_FAILED;
00774                                 }
00775                                 else
00776                                     error = DB_ERROR_WRITE_FAILED;
00777                             }
00778                             else
00779                             {
00780                                 // key found, we need to read reference to list
00781                                 void * raw_list_data;
00782                                 itzam_int raw_list_size;
00783                                  
00784                                 // seek to record
00785                                 state = itzam_datafile_seek(datafile, list_ref);
00786                                  
00787                                 if (state == ITZAM_OKAY)
00788                                 {
00789                                     // read raw record
00790                                     state = itzam_datafile_read_alloc(datafile, &raw_list_data, &raw_list_size);
00791 
00792                                     if (state == ITZAM_OKAY)
00793                                     {
00794                                         // construct list object
00795                                         ref_list list(serial_data(raw_list_data,raw_list_size,true));
00796 
00797                                         // only add reference if it isn't in the list
00798                                         if (!list.contains(ref))
00799                                         {
00800                                             // add reference
00801                                             list.add(ref);
00802 
00803                                             // remove old key entry
00804                                             state = itzam_btree_remove(btree,
00805                                                                        raw_key.get_data(),
00806                                                                        &list_ref);
00807 
00808                                             // remove old reference from the datafile
00809                                             state = itzam_datafile_seek(datafile,
00810                                                                         list_ref);
00811 
00812                                             state = itzam_datafile_remove(datafile);
00813 
00814                                             // convert updated list to serial object
00815                                             serial_data raw_list = serial_data(list);
00816 
00817                                             // write updated table
00818                                             list_ref = itzam_datafile_write(datafile,
00819                                                                             raw_list.get_data(),
00820                                                                             raw_list.get_size(),
00821                                                                             ITZAM_NULL_REF);
00822 
00823                                             // add key back in with new reference list
00824                                             state = itzam_btree_insert(btree,
00825                                                                        raw_key.get_data(),
00826                                                                        raw_key.get_size(),
00827                                                                        list_ref);
00828                                         }
00829                                     }
00830                                     else
00831                                         error = DB_ERROR_READ_FAILED;
00832                                 }
00833                                 else
00834                                     error = DB_ERROR_READ_FAILED;
00835                             }
00836 
00837                             break;
00838                         }
00839                         case ITZAM_DB_CLOSED:
00840                             error = DB_ERROR_WRITE_CLOSED;
00841                             break;
00842 
00843                         default:
00844                             error = DB_ERROR_WRITE_BROKEN;
00845                     }
00846 
00847                     mutex_t::unlock();
00848 
00849                     if (error != DB_ERROR_NONE)
00850                     {
00851                         btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN;
00852                         throw database_exception(error);
00853                     }
00854                 }            
00855                 
00857 
00861                 virtual ref_list remove(const key_t & key)
00862                 {
00863                     ref_list result;
00864                     
00865                     database_error error = DB_ERROR_NONE;
00866 
00867                     mutex_t::lock();
00868 
00869                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00870                     {
00871                         case ITZAM_DB_OPEN:
00872                         {
00873                             // get a pointer to the data file
00874                             itzam_btree *    btree    = btree_index_base<key_t,mutex_t>::get_btree();
00875                             itzam_datafile * datafile = btree->m_datafile;
00876                             itzam_ref        ref;
00877                             
00878                             // remove key and (because of the NULL) data
00879                             itzam_state state = itzam_btree_remove(btree,
00880                                                                    serial_data(key).get_data(),
00881                                                                    &ref);
00882 
00883                             // check for errors
00884                             if (state == ITZAM_OKAY)
00885                             {
00886                                 // read and return 
00887                                 void * raw_list_data;
00888                                 itzam_int raw_list_size;
00889                                 
00890                                 // seek to record
00891                                 state = itzam_datafile_seek(datafile, ref);
00892                                  
00893                                 if (state == ITZAM_OKAY)
00894                                 {
00895                                     // read raw record
00896                                     state = itzam_datafile_read_alloc(datafile, &raw_list_data, &raw_list_size);
00897 
00898                                     if (state == ITZAM_OKAY)
00899                                         result = ref_list(serial_data(raw_list_data,raw_list_size,true));
00900                                     else
00901                                         error = DB_ERROR_REFLIST_MISSING; 
00902                                 }
00903                                 else
00904                                     error = DB_ERROR_REFLIST_MISSING; 
00905                             }
00906                             else
00907                                 error = DB_ERROR_REMOVE_FAILED;
00908 
00909                             break;
00910                         }
00911 
00912                         case ITZAM_DB_CLOSED:
00913                             error = DB_ERROR_REMOVE_CLOSED;
00914                             break;
00915 
00916                         default:
00917                             error = DB_ERROR_REMOVE_BROKEN;
00918                     }
00919 
00920                     mutex_t::unlock();
00921 
00922                     if (error != DB_ERROR_NONE)
00923                         throw database_exception(error);
00924                     
00925                     return result;
00926                 }
00927                 
00929 
00934                 virtual void remove_ref(const key_t & key, itzam_ref ref)
00935                 {
00936                     itzam_state state;
00937                     database_error error = DB_ERROR_NONE;
00938 
00939                     mutex_t::lock();
00940 
00941                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
00942                     {
00943                         case ITZAM_DB_OPEN:
00944                         {
00945                             // get a pointer to the data file
00946                             itzam_btree *    btree    = btree_index_base<key_t,mutex_t>::get_btree();
00947                             itzam_datafile * datafile = btree->m_datafile;
00948                             
00949                             // create a key object
00950                             serial_data raw_key = serial_data(key);
00951                             
00952                             // get a reference for the key
00953                             itzam_ref list_ref = itzam_btree_find(btree,raw_key.get_data());
00954                             
00955                             // did we find the list?
00956                             if (list_ref != ITZAM_NULL_REF)
00957                             {
00958                                 state = itzam_datafile_seek(datafile,list_ref);
00959                                 
00960                                 if (state == ITZAM_OKAY)
00961                                 {
00962                                     // need to read reference to list
00963                                     void * raw_list_data;
00964                                     itzam_int raw_list_size;
00965 
00966                                     // read raw record
00967                                     state = itzam_datafile_read_alloc(datafile, &raw_list_data, &raw_list_size);
00968 
00969                                     if (state == ITZAM_OKAY)
00970                                     {
00971                                         // construct list object
00972                                         ref_list list(serial_data(raw_list_data,raw_list_size,true));
00973 
00974                                         // only remove reference if it is in the list
00975                                         if (list.contains(ref))
00976                                         {
00977                                             // add reference
00978                                             list.remove(ref);
00979 
00980                                             // remove old key entry
00981                                             state = itzam_btree_remove(btree,
00982                                                                        raw_key.get_data(),
00983                                                                        &list_ref);
00984 
00985                                             if (state == ITZAM_OKAY)
00986                                             {
00987                                                 // remove old reference from the datafile
00988                                                 state = itzam_datafile_seek(datafile,
00989                                                                             list_ref);
00990 
00991                                                 if (state == ITZAM_OKAY)
00992                                                 {
00993                                                     state = itzam_datafile_remove(datafile);
00994 
00995                                                     if (state == ITZAM_OKAY)
00996                                                     {
00997                                                         // convert updated list to serial object
00998                                                         serial_data raw_list = serial_data(list);
00999 
01000                                                         // write updated table
01001                                                         list_ref = itzam_datafile_write(datafile,
01002                                                                                         raw_list.get_data(),
01003                                                                                         raw_list.get_size(),
01004                                                                                         ITZAM_NULL_REF);
01005 
01006                                                         if (state == ITZAM_OKAY)
01007                                                         {
01008                                                             // add key back in with new reference list
01009                                                             state = itzam_btree_insert(btree,
01010                                                                                        raw_key.get_data(),
01011                                                                                        raw_key.get_size(),
01012                                                                                        list_ref);
01013                                                         }
01014                                                     }
01015                                                 }
01016                                             }
01017                                         }
01018                                     }
01019                                     
01020                                     if (state != ITZAM_OKAY)
01021                                         error = DB_ERROR_READ_FAILED;
01022                                 }
01023                                 else
01024                                     error = DB_ERROR_READ_FAILED;
01025                             }
01026                             break;
01027                         }
01028 
01029                         case ITZAM_DB_CLOSED:
01030                             error = DB_ERROR_REMOVE_CLOSED;
01031                             break;
01032 
01033                         default:
01034                             error = DB_ERROR_REMOVE_BROKEN;
01035                     }
01036 
01037                     mutex_t::unlock();
01038 
01039                     if (error != DB_ERROR_NONE)
01040                         throw database_exception(error);
01041                 }
01042                 
01043 
01045 
01050                 virtual bool exists(const key_t & key)
01051                 {
01052                     database_error error = DB_ERROR_NONE;
01053 
01054                     mutex_t::lock();
01055 
01056                     bool result = false;
01057 
01058                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
01059                     {
01060                         case ITZAM_DB_OPEN:
01061                             // find data from key
01062                             result = (ITZAM_NULL_REF != itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(),
01063                                                                          serial_data(key).get_data()));
01064                             break;
01065 
01066                         case ITZAM_DB_CLOSED:
01067                             error = DB_ERROR_EXISTS_CLOSED;
01068                             break;
01069 
01070                         default:
01071                             error = DB_ERROR_EXISTS_BROKEN;
01072                     }
01073 
01074                     mutex_t::unlock();
01075 
01076                     if (error != DB_ERROR_NONE)
01077                         throw database_exception(error);
01078 
01079                     return result;
01080                 }
01081                 
01083 
01088                 virtual ref_list find(const key_t & key)
01089                 {
01090                     ref_list result;
01091                     itzam_state state;
01092                     database_error error = DB_ERROR_NONE;
01093 
01094                     mutex_t::lock();
01095 
01096                     itzam_ref ref = ITZAM_NULL_REF;
01097 
01098                     switch (btree_index_base<key_t,mutex_t>::database_object::m_state)
01099                     {
01100                         case ITZAM_DB_OPEN:
01101                         {
01102                             // get a pointer to the data file
01103                             itzam_btree *    btree    = btree_index_base<key_t,mutex_t>::get_btree();
01104                             itzam_datafile * datafile = btree->m_datafile;
01105                             
01106                             // find data from key
01107                             itzam_ref ref = itzam_btree_find(btree, serial_data(key).get_data());
01108                             
01109                             if (ref != ITZAM_NULL_REF)
01110                             {
01111                                 state = itzam_datafile_seek(datafile, ref);
01112                                 
01113                                 if (state == ITZAM_OKAY)
01114                                 {
01115                                     // read and return 
01116                                     void * raw_list_data;
01117                                     itzam_int raw_list_size;
01118 
01119                                     // read raw record
01120                                     state = itzam_datafile_read_alloc(btree_index_base<key_t,mutex_t>::get_btree()->m_datafile,
01121                                                                       &raw_list_data,
01122                                                                       &raw_list_size);
01123 
01124                                     if (state == ITZAM_OKAY)
01125                                         result = ref_list(serial_data(raw_list_data,raw_list_size,true));
01126                                     else
01127                                         error = DB_ERROR_REFLIST_MISSING; 
01128                                 }
01129                                 else
01130                                     error = DB_ERROR_REFLIST_MISSING; 
01131                             }
01132                                         
01133                             break;
01134                         }
01135                         case ITZAM_DB_CLOSED:
01136                             error = DB_ERROR_EXISTS_CLOSED;
01137                             break;
01138 
01139                         default:
01140                             error = DB_ERROR_EXISTS_BROKEN;
01141                     }
01142 
01143                     mutex_t::unlock();
01144 
01145                     if (error != DB_ERROR_NONE)
01146                         throw database_exception(error);
01147 
01148                     return result;
01149                 }            
01150         };
01151         
01153 
01158     template <class mutex_t = simple_mutex>
01159         class matrix_index
01160           : public database_object,  public mutex_t
01161         {
01162             // internal data
01163             private:
01164                 itzam_int    m_cols;
01165                 itzam_int    m_rows;
01166                 itzam_matrix m_matrix;
01167             
01168             public:
01170 
01179                 matrix_index(const string & name, itzam_file_lock_mode lock_mode, itzam_int cols, itzam_int rows)
01180                     : database_object(name, lock_mode),
01181                       m_cols(cols),
01182                       m_rows(rows)
01183                 {
01184                     open();
01185                 }
01186             
01188 
01191                 virtual ~matrix_index()
01192                 {
01193                     close();
01194                 }
01195                 
01197 
01203                 void open()
01204                 {
01205                     database_error error = DB_ERROR_NONE;
01206 
01207                     mutex_t::lock();
01208 
01209                     switch (matrix_index<mutex_t>::database_object::m_state)
01210                     {
01211                         case ITZAM_DB_OPEN:
01212                             // ignore opening an open file
01213                             break;
01214 
01215                         case ITZAM_DB_UNINIT:
01216                         case ITZAM_DB_CLOSED:
01217                         {
01218                             // check for file existence
01219                             struct stat dbstats;
01220                             itzam_state state;
01221 
01222                             if (0 == stat(m_name.c_str(),&dbstats))
01223                             {
01224                                 state = itzam_matrix_open(&m_matrix,
01225                                                           m_name.c_str(),
01226                                                           m_file_lock_mode,
01227                                                           false);
01228                             }
01229                             else
01230                             {
01231                                 state = itzam_matrix_create(&m_matrix,
01232                                                             m_cols,
01233                                                             m_rows,
01234                                                             m_name.c_str(),
01235                                                             m_file_lock_mode);
01236                             }
01237 
01238                             if (state == ITZAM_OKAY)
01239                                 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_OPEN;
01240                             else
01241                             {
01242                                 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_BROKEN;
01243                                 error = DB_ERROR_OPEN_FAILED;
01244                             }
01245 
01246                             break;
01247                         }
01248 
01249                         default:
01250                             error = DB_ERROR_OPEN_BROKEN;
01251                     }
01252 
01253                     mutex_t::unlock();
01254 
01255                     if (error != DB_ERROR_NONE)
01256                         throw database_exception(error);
01257                 }
01258 
01260 
01265                 void close()
01266                 {
01267                     database_error error = DB_ERROR_NONE;
01268 
01269                     mutex_t::lock();
01270 
01271                     switch (matrix_index<mutex_t>::database_object::m_state)
01272                     {
01273                         case ITZAM_DB_OPEN:
01274                         {
01275                             // close the file
01276                             itzam_state state = itzam_matrix_close(&m_matrix);
01277 
01278                             if (state == ITZAM_OKAY)
01279                                 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_CLOSED;
01280                             else
01281                             {
01282                                 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_BROKEN;
01283                                 error = DB_ERROR_CLOSE_FAILED;
01284                             }
01285 
01286                             break;
01287                        }
01288 
01289                         case ITZAM_DB_CLOSED:
01290                             // ignore closing a closed file
01291                             break;
01292 
01293                         default:
01294                             error = DB_ERROR_CLOSE_BROKEN;
01295                     }
01296 
01297                     mutex_t::unlock();
01298 
01299                     if (error != DB_ERROR_NONE)
01300                         throw database_exception(error);
01301                 }
01302 
01304 
01310                 void insert(itzam_int col, itzam_int row, itzam_ref ref)
01311                 {
01312                     database_error error = DB_ERROR_NONE;
01313 
01314                     mutex_t::lock();
01315 
01316                     switch (database_object::m_state)
01317                     {
01318                         case ITZAM_DB_OPEN:
01319                         {
01320                             // prevent duplicate keys
01321                             if (itzam_matrix_find(&m_matrix,col,row) != ITZAM_NULL_REF)
01322                                 error = DB_ERROR_WRITE_DUPE;
01323                             else
01324                             {
01325                                 if (ref == ITZAM_NULL_REF)
01326                                     error = DB_ERROR_WRITE_FAILED;
01327                                 else
01328                                 {
01329                                     // add key to the database
01330                                     itzam_state state = itzam_matrix_insert(&m_matrix,col,row,ref);
01331 
01332                                     // handle an error          
01333                                     if (state != ITZAM_OKAY)
01334                                     {
01335                                         database_object::m_state = ITZAM_DB_BROKEN;
01336                                         error = DB_ERROR_WRITE_FAILED;
01337                                     }
01338                                 }
01339                             }
01340 
01341                             break;
01342                         }
01343                         case ITZAM_DB_CLOSED:
01344                             error = DB_ERROR_WRITE_CLOSED;
01345                             break;
01346 
01347                         default:
01348                             error = DB_ERROR_WRITE_BROKEN;
01349                     }
01350 
01351                     mutex_t::unlock();
01352 
01353                     if (error != DB_ERROR_NONE)
01354                         throw database_exception(error);
01355                 }
01356 
01358 
01364                 virtual itzam_ref remove(itzam_int col, itzam_int row)
01365                 {
01366                     itzam_ref ref = ITZAM_NULL_REF;
01367                     database_error error = DB_ERROR_NONE;
01368 
01369                     mutex_t::lock();
01370 
01371                     switch (database_object::m_state)
01372                     {
01373                         case ITZAM_DB_OPEN:
01374                         {
01375                             // remove key
01376                             itzam_state state = itzam_matrix_remove(&m_matrix,col,row,&ref);
01377 
01378                             // check for errors
01379                             if (state != ITZAM_OKAY)
01380                                 error = DB_ERROR_REMOVE_FAILED;
01381 
01382                             break;
01383                         }
01384 
01385                         case ITZAM_DB_CLOSED:
01386                             error = DB_ERROR_REMOVE_CLOSED;
01387                             break;
01388 
01389                         default:
01390                             error = DB_ERROR_REMOVE_BROKEN;
01391                     }
01392 
01393                     mutex_t::unlock();
01394 
01395                     if (error != DB_ERROR_NONE)
01396                         throw database_exception(error);
01397                     
01398                     return ref;
01399                 }
01400 
01402 
01408                 bool exists(itzam_int col, itzam_int row)
01409                 {
01410                     database_error error = DB_ERROR_NONE;
01411 
01412                     mutex_t::lock();
01413 
01414                     bool result = false;
01415 
01416                     switch (database_object::m_state)
01417                     {
01418                         case ITZAM_DB_OPEN:
01419                             // find data from key
01420                             result = (ITZAM_NULL_REF != itzam_matrix_find(&m_matrix,col,row));
01421                             break;
01422 
01423                         case ITZAM_DB_CLOSED:
01424                             error = DB_ERROR_EXISTS_CLOSED;
01425                             break;
01426 
01427                         default:
01428                             error = DB_ERROR_EXISTS_BROKEN;
01429                     }
01430 
01431                     mutex_t::unlock();
01432 
01433                     if (error != DB_ERROR_NONE)
01434                         throw database_exception(error);
01435 
01436                     return result;
01437                 }
01438                 
01440 
01446                 itzam_ref find(itzam_int col, itzam_int row)
01447                 {
01448                     database_error error = DB_ERROR_NONE;
01449 
01450                     mutex_t::lock();
01451 
01452                     itzam_ref ref = ITZAM_NULL_REF;
01453 
01454                     switch (database_object::m_state)
01455                     {
01456                         case ITZAM_DB_OPEN:
01457                             // find data from key
01458                             ref = itzam_matrix_find(&m_matrix,col,row);
01459                             break;
01460 
01461                         case ITZAM_DB_CLOSED:
01462                             error = DB_ERROR_EXISTS_CLOSED;
01463                             break;
01464 
01465                         default:
01466                             error = DB_ERROR_EXISTS_BROKEN;
01467                     }
01468 
01469                     mutex_t::unlock();
01470 
01471                     if (error != DB_ERROR_NONE)
01472                         throw database_exception(error);
01473 
01474                     return ref;
01475                 }
01476         };
01477                 
01479 
01490     template < class record_t, class mutex_t = simple_mutex >
01491         class database_file
01492           : public database_object, public mutex_t
01493         {
01494             // type definitions
01495             public:
01497                 typedef record_t    record_type;
01498             
01499             // internal data
01500             protected:
01502                 itzam_datafile      m_datafile;
01503                 
01505                 void lock()
01506                 {
01507                     mutex_t::lock();
01508                 }                
01509     
01511                 void unlock()
01512                 {
01513                     mutex_t::unlock();
01514                 }                
01515             
01516             // interface
01517             public:
01519 
01527                 database_file(const string & name, itzam_file_lock_mode lock_mode)
01528                     : database_object(name, lock_mode)
01529                 {
01530                     open();
01531                 }
01532                 
01534 
01537                 virtual ~database_file()
01538                 {
01539                     close();
01540                 }
01541 
01543 
01549                 virtual void open()
01550                 {
01551                     database_error error = DB_ERROR_NONE;
01552 
01553                     mutex_t::lock();
01554 
01555                     switch (database_object::m_state)
01556                     {
01557                         case ITZAM_DB_OPEN:
01558                             // ignore opening an open database
01559                             break;
01560 
01561                         case ITZAM_DB_UNINIT:
01562                         case ITZAM_DB_CLOSED:
01563                         {
01564                             // check for file existence
01565                             struct stat dbstats;
01566                             itzam_state state;
01567 
01568                             if (0 == stat(m_name.c_str(),&dbstats))
01569                                 state = itzam_datafile_open(&m_datafile,m_name.c_str(),m_file_lock_mode,false);
01570                             else
01571                                 state = itzam_datafile_create(&m_datafile,m_name.c_str(),m_file_lock_mode);
01572 
01573                             if (state == ITZAM_OKAY)
01574                                 database_object::m_state = ITZAM_DB_OPEN;
01575                             else
01576                             {
01577                                 database_object::m_state = ITZAM_DB_BROKEN;
01578                                 error = DB_ERROR_OPEN_FAILED;
01579                             }
01580 
01581                             break;
01582                         }
01583 
01584                         default:
01585                             mutex_t::unlock();
01586                             throw database_exception(DB_ERROR_OPEN_BROKEN);
01587                     }
01588 
01589                     mutex_t::unlock();
01590 
01591                     if (error != DB_ERROR_NONE)
01592                         throw database_exception(error);
01593                 }
01594 
01596 
01601                 virtual void close()
01602                 {
01603                     database_error error = DB_ERROR_NONE;
01604 
01605                     mutex_t::lock();
01606 
01607                     switch (database_object::m_state)
01608                     {
01609                         case ITZAM_DB_OPEN:
01610                         {
01611                             // close the file
01612                             itzam_state state = itzam_datafile_close(&m_datafile);
01613 
01614                             if (state == ITZAM_OKAY)
01615                                 database_object::m_state = ITZAM_DB_CLOSED;
01616                             else
01617                             {
01618                                 database_object::m_state = ITZAM_DB_BROKEN;
01619                                 error = DB_ERROR_CLOSE_FAILED;
01620                             }
01621 
01622                             break;
01623                         }
01624 
01625                         case