Merge branch 'master' of feistymeow.org:feisty_meow
[feisty_meow.git] / octopi / library / cromp / cromp_transaction.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : cromp_transaction                                                 *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2000-$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 "cromp_transaction.h"
16
17 #include <basis/environment.h>
18 #include <basis/mutex.h>
19 #include <loggers/critical_events.h>
20 #include <loggers/file_logger.h>
21 #include <loggers/program_wide_logger.h>
22 #include <octopus/entity_defs.h>
23 #include <octopus/infoton.h>
24 #include <sockets/tcpip_stack.h>
25 #include <structures/static_memory_gremlin.h>
26 #include <textual/parser_bits.h>
27
28 #include <stdio.h>
29
30 using namespace basis;
31 using namespace loggers;
32 using namespace octopi;
33 using namespace sockets;
34 using namespace structures;
35 using namespace textual;
36
37 namespace cromp {
38
39 #define DEBUG_CROMP_TRANSACTION
40   // uncomment for noisy version.
41
42 const int MAXIMUM_TRANSACTION = 100 * MEGABYTE;
43   // the largest transaction we allow in cromp.  if more information needs
44   // to be passed, then do it in chunks.
45
46 #undef LOG
47 #ifdef DEBUG_CROMP_TRANSACTION
48   // since the transaction stuff is so low-level, we risk a feedback loop if
49   // we log stuff when the program wide logger is itself a communication
50   // object.
51   #define LOG(s) CLASS_EMERGENCY_LOG(file_logger(environment::TMP() + "/cromp_transaction.log"), s)
52 #else
53   #define LOG(s) 
54 #endif
55
56 SAFE_STATIC(mutex, __cromp_transaction_lock, )
57
58 cromp_transaction::~cromp_transaction()
59 {}
60
61 const char *cromp_transaction::outcome_name(const outcome &to_name)
62 {
63   switch (to_name.value()) {
64     case WAY_TOO_SMALL: return "WAY_TOO_SMALL";
65     case ILLEGAL_LENGTH: return "ILLEGAL_LENGTH";
66     default: return communication_commons::outcome_name(to_name);
67   }
68 }
69
70 byte_array &cromp_name_array()
71 {
72   static byte_array _hidden_cromp_array;
73   static bool _initted = false;
74   if (!_initted) {
75     auto_synchronizer l(__cromp_transaction_lock());
76     // check again in case someone scooped us.
77     if (!_initted) {
78       // add the special name field.
79       attach(_hidden_cromp_array, abyte('c'));
80       attach(_hidden_cromp_array, abyte('r'));
81       attach(_hidden_cromp_array, abyte('o'));
82       attach(_hidden_cromp_array, abyte('m'));
83       attach(_hidden_cromp_array, abyte('p'));
84       attach(_hidden_cromp_array, abyte('!'));
85       // add the space for the length.
86       for (int i = 0; i < 8; i++)
87         attach(_hidden_cromp_array, abyte('?'));
88       _initted = true;
89     }
90   }
91   return _hidden_cromp_array;
92 }
93
94 int cromp_transaction::minimum_flat_size(const octopus_request_id &id)
95 {
96   return cromp_name_array().length()  // cromp identifier in header.
97       + id.packed_size();  // size of the request id.
98 }
99
100 int cromp_transaction::minimum_flat_size(const string_array &classifier,
101     const octopus_request_id &id)
102 {
103   return minimum_flat_size(id)
104       + infoton::fast_pack_overhead(classifier);
105           // size required for infoton::fast_pack.
106 }
107
108 void cromp_transaction::flatten(byte_array &packed_form,
109     const infoton &request, const octopus_request_id &id)
110 {
111 #ifdef DEBUG_CROMP_TRANSACTION
112   FUNCDEF("pack");
113 #endif
114   int posn = packed_form.length();
115     // save where we started adding.
116
117   packed_form += cromp_name_array();
118     // add the cromp prefix and space for the length.
119
120   // add the identifier.
121   id.pack(packed_form);
122
123   // add the real data.
124   infoton::fast_pack(packed_form, request);
125 #ifdef DEBUG_CROMP_TRANSACTION
126   // make a copy of the packed infoton to compare.
127   byte_array temp_holding;
128   infoton::fast_pack(temp_holding, request);
129 #endif
130
131 //hmmm: check if too big!
132
133   // backpatch the length now.
134   a_sprintf len_string("%08x", packed_form.length() - posn);
135 #ifdef DEBUG_CROMP_TRANSACTION
136   LOG(a_sprintf("len string is %s", len_string.s()));
137 #endif
138   for (int j = 6; j < 14; j++)
139     packed_form[posn + j] = abyte(len_string[j - 6]);
140
141 #ifdef DEBUG_CROMP_TRANSACTION
142   byte_array copy = packed_form.subarray(posn, packed_form.last());
143   byte_array tempo;
144   octopus_request_id urfid;
145   if (!cromp_transaction::unflatten(copy, tempo, urfid))
146     continuable_error(static_class_name(), func,
147         "failed to unpack what we just packed.");
148   else if (urfid != id)
149     continuable_error(static_class_name(), func, "wrong id after unpack.");
150   else if (tempo != temp_holding)
151     continuable_error(static_class_name(), func, "wrong data after unpack.");
152 #endif
153
154 }
155
156 bool cromp_transaction::unflatten(byte_array &packed_form,
157     byte_array &still_flat, octopus_request_id &req_id)
158 {
159 #ifdef DEBUG_CROMP_TRANSACTION
160   FUNCDEF("unflatten");
161 #endif
162   still_flat.reset();
163   int len = 0;
164   // not ready yet.
165   if (peek_header(packed_form, len) != OKAY) {
166 #ifdef DEBUG_CROMP_TRANSACTION
167     LOG("failed to peek the header!");
168 #endif
169     return false;
170   }
171   packed_form.zap(0, 14 - 1);
172   if (!req_id.unpack(packed_form)) return false;
173   int array_len = len - 14 - req_id.packed_size();
174
175 #ifdef DEBUG_CROMP_TRANSACTION
176   if (array_len > packed_form.length())
177     continuable_error(static_class_name(), func,
178         "data needed is insufficient!  peek was wrong.");
179 #endif
180
181   still_flat = packed_form.subarray(0, array_len - 1);
182   packed_form.zap(0, array_len - 1);
183   return true;
184 }
185
186 #define WHACK_AND_GO { packed_form.zap(0, 0); continue; }
187
188 #define CHECK_LENGTH \
189   if (packed_form.length() < necessary_length) { \
190     /* to this point, we are happy with the contents. */ \
191     return true; \
192   } \
193   necessary_length++; /* require the next higher length. */
194
195 bool cromp_transaction::resynchronize(byte_array &packed_form)
196 {
197 #ifdef DEBUG_CROMP_TRANSACTION
198   FUNCDEF("resynchronize");
199 #endif
200   while (true) {
201     if (!packed_form.length()) {
202 //#ifdef DEBUG_CROMP_TRANSACTION
203       LOG("roasted entire contents...");
204 //#endif
205       return false;
206     }
207     if (packed_form[0] != 'c') WHACK_AND_GO;
208     int necessary_length = 2;
209     CHECK_LENGTH;
210     if (packed_form[1] != 'r') WHACK_AND_GO;
211     CHECK_LENGTH;
212     if (packed_form[2] != 'o') WHACK_AND_GO;
213     CHECK_LENGTH;
214     if (packed_form[3] != 'm') WHACK_AND_GO;
215     CHECK_LENGTH;
216     if (packed_form[4] != 'p') WHACK_AND_GO;
217     CHECK_LENGTH;
218     if (packed_form[5] != '!') WHACK_AND_GO;
219     for (int k = 6; k < 14; k++) {
220       CHECK_LENGTH;
221       if (!parser_bits::is_hexadecimal(packed_form[k]))
222         WHACK_AND_GO;
223     }
224 #ifdef DEBUG_CROMP_TRANSACTION
225     LOG("found header again...");
226 #endif
227     return true;  // looks like we resynched.
228   }
229 }
230
231 outcome cromp_transaction::peek_header(const byte_array &packed_form,
232     int &length)
233 {
234 #ifdef DEBUG_CROMP_TRANSACTION
235   FUNCDEF("peek_header");
236 #endif
237   length = 0;
238 #ifdef DEBUG_CROMP_TRANSACTION
239   LOG("checking for header");
240 #endif
241   if (packed_form.length() < 14) return WAY_TOO_SMALL;
242   if ( (packed_form[0] != 'c') || (packed_form[1] != 'r')
243       || (packed_form[2] != 'o') || (packed_form[3] != 'm')
244       || (packed_form[4] != 'p') || (packed_form[5] != '!') )
245     return GARBAGE;
246 #ifdef DEBUG_CROMP_TRANSACTION
247   LOG("obvious header bits look fine");
248 #endif
249
250   astring len_string;
251   for (int k = 6; k < 14; k++) {
252     if (!parser_bits::is_hexadecimal(packed_form[k])) {
253 #ifdef DEBUG_CROMP_TRANSACTION
254       LOG("found corruption in hex bytes");
255 #endif
256       return GARBAGE;
257     }
258     len_string += char(packed_form[k]);
259   }
260 #ifdef DEBUG_CROMP_TRANSACTION
261   LOG("length was unpacked okay");
262 #endif
263   basis::un_int temp_len = (basis::un_int)length;
264   int items = sscanf(len_string.s(), "%08x", &temp_len);
265   length = temp_len;
266   if (!items) {
267 #ifdef DEBUG_CROMP_TRANSACTION
268     LOG(astring("couldn't parse the len_string of: ") + len_string);
269 #endif
270     return GARBAGE;
271   }
272
273 #ifdef DEBUG_CROMP_TRANSACTION
274   LOG(a_sprintf("length string is %s, len calc is %d and bytes "
275       "given are %d", len_string.s(), length, packed_form.length()));
276 #endif
277   if (length > MAXIMUM_TRANSACTION) return ILLEGAL_LENGTH;
278   if (length > packed_form.length()) return PARTIAL;
279   return OKAY;
280 }
281
282 } //namespace.
283