first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / octopi / library / synchronic / list_manager.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : list_manager                                                      *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2002-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "bundle_list.h"
16 #include "list_manager.h"
17
18 #include <basis/astring.h>
19 #include <basis/functions.h>
20 #include <basis/mutex.h>
21 #include <structures/string_array.h>
22
23 using namespace basis;
24 using namespace octopi;
25 using namespace structures;
26 using namespace timely;
27
28 namespace synchronic {
29
30 //#define DEBUG_LIST_MANAGER
31   // uncomment for noisier version.
32
33 #undef GRAB_LOCK
34 #define GRAB_LOCK \
35   auto_synchronizer l(*_locking)
36
37 #undef LOG
38 #define LOG(to_print) \
39   CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
40
41 list_manager::list_manager(const string_array &list_name, bool backgrounded)
42 : tentacle(list_name, backgrounded),
43   _entries(new bundle_list),
44   _locking(new mutex)
45 {
46 }
47
48 list_manager::~list_manager()
49 {
50   WHACK(_entries);
51   WHACK(_locking);
52 }
53
54 const string_array &list_manager::list_name() const { return group(); }
55
56 int list_manager::entries() const
57 {
58   GRAB_LOCK;
59   return _entries->elements();
60 }
61
62 void list_manager::reset()
63 {
64   GRAB_LOCK;
65   _entries->zap(0, _entries->elements() - 1);
66 }
67
68 bool list_manager::is_listed(const string_array &classifier)
69 {
70   GRAB_LOCK;
71   int indy = locked_find(classifier);
72   return !negative(indy);
73 }
74
75 bool list_manager::update(const string_array &classifier, int offset)
76 {
77   GRAB_LOCK;
78   int indy = locked_find(classifier);
79   if (negative(indy)) return false;  // not found.
80   _entries->borrow(indy)->_updated = time_stamp(offset);
81   return true;
82 }
83
84 void list_manager::clean(int older_than)
85 {
86   GRAB_LOCK;
87   for (int i = 0; i < _entries->elements(); i++) {
88     synchronizable *curr = _entries->borrow(i);
89     if (curr->_updated < time_stamp(-older_than)) {
90       // this one is too old to keep around.
91       _entries->zap(i, i);
92       i--;  // skip back before deleted item.
93     }
94   }
95 }
96
97 bool list_manager::zap(const string_array &classifier)
98 {
99   GRAB_LOCK;
100   int indy = locked_find(classifier);
101   if (negative(indy)) return false;  // not found.
102   _entries->zap(indy, indy);
103   return true;  // did find and whack it.
104 }
105
106 int list_manager::locked_find(const string_array &classifier)
107 {
108   for (int i = 0; i < _entries->elements(); i++) {
109     // check that the classifier lengths are equal; otherwise no match.
110     if (_entries->get(i)->classifier().length() != classifier.length())
111       continue;
112     // starting from the end of most significance, we compare the strings.
113     // we don't want to bother comparing the end that's most likely to be
114     // the same for items in the list (the front, that is).
115     bool problems = false;
116     for (int j = classifier.length() - 1; j >= 0; j--) {
117       if (_entries->get(i)->classifier()[j] != classifier[j]) {
118         problems = true;
119         break;  // get out now since we're hosed.
120       }
121     }
122     if (problems) continue;  // nope, there was a mismatch.
123     // success; this guy matches.
124     return i;
125   }
126   return common::NOT_FOUND;  // not found.
127 }
128
129 synchronizable *list_manager::clone_object(const string_array &classifier)
130 {
131   GRAB_LOCK;
132   int indy = locked_find(classifier);
133   if (negative(indy)) return NIL;
134   return dynamic_cast<synchronizable *>(_entries->get(indy)->clone());
135 }
136
137 void list_manager::retrieve(bundle_list &to_fill) const
138 {
139   to_fill.reset();
140   GRAB_LOCK;
141   for (int i = 0; i < _entries->elements(); i++)
142     to_fill += dynamic_cast<synchronizable *>(_entries->get(i)->clone());
143 }
144
145 outcome list_manager::consume(infoton &to_chow,
146     const octopus_request_id &formal(item_id), byte_array &transformed)
147 {
148 #ifdef DEBUG_LIST_MANAGER
149   FUNCDEF("consume");
150 #endif
151   transformed.reset();
152   synchronizable *bun = dynamic_cast<synchronizable *>(&to_chow);
153   if (!bun) return BAD_INPUT;
154
155   GRAB_LOCK;
156
157   // now perform an appropriate action depending on the type of update.
158   switch (bun->_mod) {
159     case synchronizable::ADDED:
160     case synchronizable::CHANGED: {
161       // see if the item already exists; if it does, overwrite it.
162       int indy = locked_find(bun->classifier());
163       if (negative(indy)) {
164         // the item is new, so just drop it in the list.
165         *_entries += dynamic_cast<synchronizable *>(bun->clone());
166       } else {
167         // not a new item, so merge with the existing contents.
168         _entries->borrow(indy)->merge(*bun);
169         _entries->borrow(indy)->_updated = time_stamp();
170       }
171       return OKAY;
172     }
173     case synchronizable::DELETED: {
174       int indy = locked_find(bun->classifier());
175       if (non_negative(indy)) {
176         // found it, so whack the entry as needed by calling merge.
177         outcome ret = _entries->borrow(indy)->merge(*bun);
178         _entries->borrow(indy)->_updated = time_stamp();
179         if (ret == synchronizable::EMPTY) {
180           // they have told us that this must go now.
181 #ifdef DEBUG_LIST_MANAGER
182           LOG(astring("removing entry now due to merge outcome: ")
183               + _entries->borrow(indy)->text_form());
184 #endif
185           _entries->zap(indy, indy);
186         }
187         return OKAY;
188       } else {
189         // that item was not listed.
190 #ifdef DEBUG_LIST_MANAGER
191         LOG(astring("could not find entry for ") + bun->text_form());
192 #endif
193         return NOT_FOUND;
194       }
195       break;
196     }
197     default: return NO_HANDLER;
198   }
199   return OKAY;
200 }
201
202 void list_manager::expunge(const octopus_entity &formal(to_remove))
203 {
204 //  FUNCDEF("expunge");
205 }
206
207 } //namespace.
208