jupiter_algorithm.hpp

Go to the documentation of this file.
00001 /* libobby - Network text editing library
00002  * Copyright (C) 2005, 2006 0x539 dev group
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public
00015  * License along with this program; if not, write to the Free
00016  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 #ifndef _OBBY_JUPITER_ALGORITHM_HPP_
00020 #define _OBBY_JUPITER_ALGORITHM_HPP_
00021 
00022 #include <net6/non_copyable.hpp>
00023 #include "jupiter_error.hpp"
00024 #include "operation.hpp"
00025 #include "record.hpp"
00026 
00027 namespace obby
00028 {
00029 
00032 template<typename Document>
00033 class jupiter_algorithm: private net6::non_copyable
00034 {
00035 public:
00036         typedef Document document_type;
00037         typedef operation<document_type> operation_type;
00038         typedef record<document_type> record_type;
00039 
00040         jupiter_algorithm();
00041         ~jupiter_algorithm();
00042 
00045         std::auto_ptr<record_type> local_op(const operation_type& op);
00046 
00050         std::auto_ptr<operation_type> remote_op(const record_type& rec);
00051 protected:
00055         class operation_storage: private net6::non_copyable
00056         {
00057         public:
00060                 operation_storage(unsigned int count,
00061                                   const operation_type& op);
00062 
00065                 operation_storage(unsigned int count,
00066                                   std::auto_ptr<operation_type> op);
00067 
00070                 unsigned int get_count() const;
00071 
00074                 const operation_type& get_operation() const;
00075 
00078                 void reset_operation(const operation_type& new_op);
00079 
00082                 void reset_operation(std::auto_ptr<operation_type> new_op);
00083         protected:
00084                 unsigned int m_count;
00085                 std::auto_ptr<operation_type> m_operation;
00086         };
00087 
00090         void discard_operations(const record_type& rec);
00091 
00095         std::auto_ptr<operation_type> transform(const operation_type& op) const;
00096 
00099         void check_preconditions(const record_type& rec) const;
00100 
00101 protected:
00102         typedef std::list<operation_storage*> ack_list_type;
00103 
00104         vector_time m_time;
00105         ack_list_type m_ack_list;
00106 };
00107 
00108 template<typename Document>
00109 jupiter_algorithm<Document>::operation_storage::
00110 	operation_storage(unsigned int count,
00111                           const operation_type& op):
00112         m_count(count), m_operation(op.clone() )
00113 {
00114 }
00115 
00116 template<typename Document>
00117 jupiter_algorithm<Document>::operation_storage::
00118 	operation_storage(unsigned int count,
00119                           std::auto_ptr<operation_type> op):
00120         m_count(count), m_operation(op)
00121 {
00122 }
00123 
00124 template<typename Document>
00125 unsigned int jupiter_algorithm<Document>::operation_storage::get_count() const
00126 {
00127         return m_count;
00128 }
00129 
00130 template<typename Document>
00131 const typename jupiter_algorithm<Document>::operation_type&
00132 jupiter_algorithm<Document>::operation_storage::get_operation() const
00133 {
00134         return *m_operation;
00135 }
00136 
00137 template<typename Document>
00138 void jupiter_algorithm<Document>::operation_storage::
00139 	reset_operation(const operation_type& new_op)
00140 {
00141         m_operation.reset(new_op.clone() );
00142 }
00143 
00144 template<typename Document>
00145 void jupiter_algorithm<Document>::operation_storage::
00146 	reset_operation(std::auto_ptr<operation_type> new_op)
00147 {
00148         m_operation = new_op;
00149 }
00150 
00151 template<typename Document>
00152 jupiter_algorithm<Document>::jupiter_algorithm():
00153         m_time(0, 0)
00154 {
00155 }
00156 
00157 template<typename Document>
00158 jupiter_algorithm<Document>::~jupiter_algorithm()
00159 {
00160         for(typename ack_list_type::iterator iter = m_ack_list.begin();
00161             iter != m_ack_list.end();
00162             ++ iter)
00163         {
00164                 delete *iter;
00165         }
00166 }
00167 
00168 template<typename Document>
00169 std::auto_ptr<typename jupiter_algorithm<Document>::record_type>
00170 jupiter_algorithm<Document>::local_op(const operation_type& op)
00171 {
00172         std::auto_ptr<record_type> rec(new record_type(m_time, op) );
00173         m_ack_list.push_back(new operation_storage(m_time.get_local(), op) );
00174         m_time.inc_local();
00175         return rec;
00176 }
00177 
00178 template<typename Document>
00179 std::auto_ptr<typename jupiter_algorithm<Document>::operation_type>
00180 jupiter_algorithm<Document>::remote_op(const record_type& rec)
00181 {
00182         check_preconditions(rec);
00183         discard_operations(rec);
00184         std::auto_ptr<operation_type> op(transform(rec.get_operation()) );
00185         m_time.inc_remote();
00186         return op;
00187 }
00188 
00189 template<typename Document>
00190 void jupiter_algorithm<Document>::discard_operations(const record_type& rec)
00191 {
00192         typename ack_list_type::iterator iter;
00193         for(iter = m_ack_list.begin(); iter != m_ack_list.end(); ++ iter)
00194         {
00195                 if( (*iter)->get_count() < rec.get_time().get_remote() )
00196                         delete *iter;
00197                 else
00198                         break;
00199         }
00200 
00201         m_ack_list.erase(m_ack_list.begin(), iter);
00202 
00203         // Verify sequence order (TCP should ensure this, if noone sends
00204         // corrupt packets).
00205         if(rec.get_time().get_local() != m_time.get_remote() )
00206         {
00207                 throw jupiter_error(
00208                         "Sequence order mismatch: Incoming record's local "
00209                         "time does not match own remote time"
00210                 );
00211         }
00212 }
00213 
00214 template<typename Document>
00215 std::auto_ptr<typename jupiter_algorithm<Document>::operation_type>
00216 jupiter_algorithm<Document>::transform(const operation_type& op) const
00217 {
00218         std::auto_ptr<operation_type> new_op(op.clone() );
00219 
00220         for(typename ack_list_type::const_iterator iter = m_ack_list.begin();
00221             iter != m_ack_list.end();
00222             ++ iter)
00223         {
00224                 const operation_type* existing_op =
00225                         &(*iter)->get_operation();
00226                 operation_type* new_trans_op =
00227                         existing_op->transform(*new_op);
00228                 operation_type* existing_trans_op =
00229                         new_op->transform(*existing_op);
00230 
00231                 (*iter)->reset_operation(
00232                         std::auto_ptr<operation_type>(existing_trans_op)
00233                 );
00234 
00235                 new_op.reset(new_trans_op);
00236         }
00237 
00238         return new_op;
00239 }
00240 
00241 template<typename Document>
00242 void obby::jupiter_algorithm<Document>::
00243 	check_preconditions(const record_type& rec) const
00244 {
00245         if(!m_ack_list.empty() &&
00246            rec.get_time().get_remote() < m_ack_list.front()->get_count() )
00247         {
00248                 throw jupiter_error(
00249                         "Transformation precondition failed: Incoming remote "
00250                         "time is lower than oldest time in ack list"
00251                 );
00252         }
00253 
00254         if(rec.get_time().get_remote() > m_time.get_local() )
00255         {
00256                 throw jupiter_error(
00257                         "Transformation precondition failed: Incoming remote "
00258                         "time is greater than own local time"
00259                 );
00260         }
00261 
00262         if(rec.get_time().get_local() != m_time.get_remote() )
00263         {
00264                 throw jupiter_error(
00265                         "Transformation precondition failed: Incoming local "
00266                         "time does not match own remote time"
00267                 );
00268         }
00269 }
00270 
00271 } // namespace obby
00272 
00273 #endif // _OBBY_JUPITER_ALGORITHM_HPP_

Generated on Fri Jan 11 10:01:32 2008 for obby by  doxygen 1.5.1