first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / octopi / library / octopus / infoton.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : infoton                                                           *
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 "infoton.h"
16
17 #include <basis/functions.h>
18 #include <loggers/critical_events.h>
19 #include <loggers/program_wide_logger.h>
20 #include <structures/string_array.h>
21 #include <textual/byte_formatter.h>
22
23 using namespace basis;
24 using namespace loggers;
25 using namespace structures;
26 using namespace textual;
27
28 namespace octopi {
29
30 #undef LOG
31 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
32
33 //#define DEBUG_INFOTON
34   // if uncommented, then extra checks are made.
35
36 const abyte FAST_PACK_VERSION = 0x14;
37   // this single byte version number should be increased when the network
38   // protocol changes.  it only ensures that the fast_pack method will reject
39   // a lower version.
40
41 infoton::infoton(const string_array &classifier)
42 : _classifier(new string_array(classifier))
43 {
44 //  FUNCDEF("constructor [string_array]");
45 }
46
47 infoton::infoton(const astring &class_1)
48 : _classifier(new string_array)
49 {
50 //  FUNCDEF("constructor [one string]");
51   *_classifier += class_1;
52 }
53
54 infoton::infoton(const astring &class_1, const astring &class_2)
55 : _classifier(new string_array)
56 {
57 //  FUNCDEF("constructor [two strings]");
58   *_classifier += class_1;
59   *_classifier += class_2;
60 }
61
62 infoton::infoton(const astring &class_1, const astring &class_2,
63     const astring &class_3)
64 : _classifier(new string_array)
65 {
66 //  FUNCDEF("constructor [three strings]");
67   *_classifier += class_1;
68   *_classifier += class_2;
69   *_classifier += class_3;
70 }
71
72 infoton::infoton(const infoton &to_copy)
73 : root_object(),
74   packable(),
75   clonable(),
76   _classifier(new string_array(*to_copy._classifier))
77 {}
78
79 infoton::~infoton()
80 { WHACK(_classifier); }
81
82 infoton &infoton::operator = (const infoton &to_copy)
83 { *_classifier = *to_copy._classifier; return *this; }
84
85 const string_array &infoton::classifier() const
86 { return *_classifier; }
87
88 bool infoton::check_classifier(const astring &classname, const astring &caller)
89 {
90   bool to_return = true;
91   if (!_classifier->length())
92     to_return = false;
93   for (int i = 0; i < _classifier->length(); i++) {
94     if (!(*_classifier)[i].length())
95       to_return = false;
96   }
97   if (!to_return) {
98     program_wide_logger::get().log(classname + "::" + caller
99         + ": invalid classifier provided.", ALWAYS_PRINT);
100   }
101   return to_return;
102 }
103
104 void infoton::set_classifier(const string_array &new_classifier)
105 {
106 #ifdef DEBUG_INFOTON
107   FUNCDEF("set_classifier [string_array]");
108 #endif
109   *_classifier = new_classifier;
110 #ifdef DEBUG_INFOTON
111   check_classifier(class_name(), func);
112 #endif
113 }
114
115 void infoton::set_classifier(const astring &class_1)
116 {
117 #ifdef DEBUG_INFOTON
118   FUNCDEF("set_classifier [1 string]");
119 #endif
120   _classifier->reset();
121   *_classifier += class_1;
122 #ifdef DEBUG_INFOTON
123   check_classifier(class_name(), func);
124 #endif
125 }
126
127 void infoton::set_classifier(const astring &class_1, const astring &class_2)
128 {
129 #ifdef DEBUG_INFOTON
130   FUNCDEF("set_classifier [2 strings]");
131 #endif
132   _classifier->reset();
133   *_classifier += class_1;
134   *_classifier += class_2;
135 #ifdef DEBUG_INFOTON
136   check_classifier(class_name(), func);
137 #endif
138 }
139
140 void infoton::set_classifier(const astring &class_1, const astring &class_2,
141     const astring &class_3)
142 {
143 #ifdef DEBUG_INFOTON
144   FUNCDEF("set_classifier [3 strings]");
145 #endif
146   _classifier->reset();
147   *_classifier += class_1;
148   *_classifier += class_2;
149   *_classifier += class_3;
150 #ifdef DEBUG_INFOTON
151   check_classifier(class_name(), func);
152 #endif
153 }
154
155 int infoton::fast_pack_overhead(const string_array &classifier)
156 {
157   return classifier.packed_size()  // for classifier.
158       + sizeof(int)  // for the package size.
159       + 1;  // for the version byte.
160 }
161
162 void infoton::fast_pack(byte_array &packed_form, const infoton &to_pack)
163 {
164 //  FUNCDEF("fast_pack");
165   structures::attach(packed_form, FAST_PACK_VERSION);
166     // add the tasty version byte as the very first item.
167   structures::pack_array(packed_form, to_pack.classifier());
168   // must first put the packed infoton into a byte array, then use the
169   // byte array's packing support.
170   int len_prior = packed_form.length();
171   structures::attach(packed_form, int(0));
172     // save space for length.
173 //hmmm: this could use obscure_pack for more reliability.
174   to_pack.pack(packed_form);
175   int added_len = packed_form.length() - sizeof(int) - len_prior;
176
177   // shift in the length in the place where we made space.
178   basis::un_int temp = basis::un_int(added_len);
179   for (basis::un_int i = 0; i < sizeof(int); i++) {
180     packed_form[len_prior + i] = abyte(temp % 0x100);
181     temp >>= 8;
182   }
183 }
184
185 bool infoton::test_fast_unpack(const byte_array &packed_form,
186     int &packed_length)
187 {
188 //  FUNCDEF("test_fast_unpack");
189   packed_length = 0;
190   if (!packed_form.length()) return false;
191
192   // make sure we have the right version number, first.
193   if (packed_form[0] != FAST_PACK_VERSION)
194     return false;
195
196   un_int strings_held = 0;
197   byte_array len_bytes = packed_form.subarray(1, 2 * sizeof(int));
198   if (!structures::obscure_detach(len_bytes, strings_held) || !strings_held) {
199     return false;
200   }
201
202   // check through all of the strings.
203   const void *zero_posn = packed_form.observe() + sizeof(int) * 2 + 1;
204   for (int i = 0; i < (int)strings_held; i++) {
205     // locate the zero termination if possible.
206     int index = int((abyte *)zero_posn - packed_form.observe());
207     zero_posn = memchr(packed_form.observe() + index, '\0',
208         packed_form.length() - index);
209     // make sure we could find the zero termination.
210     if (!zero_posn) {
211       // nope, never saw a zero.  good thing we checked.
212       return false;
213     }
214   }
215
216   // base our expected position for the data length on the position of the
217   // last string we found.
218   int datalen_start = int((abyte *)zero_posn - packed_form.observe()) + 1;
219   byte_array just_len = packed_form.subarray(datalen_start,
220       datalen_start + sizeof(int) - 1);
221   if (!structures::detach(just_len, packed_length)) return false;
222   packed_length += datalen_start + sizeof(int);
223     // include the classifier length and integer package length.
224   return true;
225 }
226
227 bool infoton::fast_unpack(byte_array &packed_form, string_array &classifier,
228     byte_array &info)
229 {
230   FUNCDEF("fast_unpack");
231   classifier.reset();
232   info.reset();
233   abyte version_checking = 0;
234   if (!structures::detach(packed_form, version_checking)) return false;
235   if (version_checking != FAST_PACK_VERSION) return false;
236   if (!structures::unpack_array(packed_form, classifier)) return false;
237   int len = 0;
238   if (!structures::detach(packed_form, len)) return false;
239   if (len > packed_form.length()) {
240     // not enough data.
241     continuable_error(static_class_name(), func, "failed to have enough data!");
242     return false;
243   }
244   info = packed_form.subarray(0, len - 1);
245   packed_form.zap(0, len - 1);
246   return true;
247 }
248
249 } //namespace.
250