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