feisty meow concerns codebase 2.140
cromp_transaction.cpp
Go to the documentation of this file.
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>
20#include <loggers/file_logger.h>
22#include <octopus/entity_defs.h>
23#include <octopus/infoton.h>
24#include <sockets/tcpip_stack.h>
26#include <textual/parser_bits.h>
27
28#include <stdio.h>
29
30using namespace basis;
31using namespace loggers;
32using namespace octopi;
33using namespace sockets;
34using namespace structures;
35using namespace textual;
36
37namespace cromp {
38
39//#define DEBUG_CROMP_TRANSACTION
40 // uncomment for noisy version.
41
42const 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() + "/transactions-cromp.log"), s)
52#else
53 #define LOG(s)
54#endif
55
56SAFE_STATIC(mutex, __cromp_transaction_lock, )
57
60
61const 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
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
95{
96 return cromp_name_array().length() // cromp identifier in header.
97 + id.packed_size(); // size of the request id.
98}
99
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
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))
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
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())
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
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;
210 if (packed_form[1] != 'r') WHACK_AND_GO;
212 if (packed_form[2] != 'o') WHACK_AND_GO;
214 if (packed_form[3] != 'm') WHACK_AND_GO;
216 if (packed_form[4] != 'p') WHACK_AND_GO;
218 if (packed_form[5] != '!') WHACK_AND_GO;
219 for (int k = 6; k < 14; k++) {
221 if (!parser_bits::is_hexadecimal(packed_form[k]))
223 }
224#ifdef DEBUG_CROMP_TRANSACTION
225 LOG("found header again...");
226#endif
227 return true; // looks like we resynched.
228 }
229}
230
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
#define LOG(s)
a_sprintf is a specialization of astring that provides printf style support.
Definition astring.h:440
void reset(int number=0, const contents *initial_contents=NULL_POINTER)
Resizes this array and sets the contents from an array of contents.
Definition array.h:349
array subarray(int start, int end) const
Returns the array segment between the indices "start" and "end".
Definition array.h:443
int length() const
Returns the current reported length of the allocated C array.
Definition array.h:115
outcome zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
Definition array.h:769
int last() const
Returns the last valid element in the array.
Definition array.h:118
Provides a dynamically resizable ASCII character string.
Definition astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition astring.h:113
auto_synchronizer simplifies concurrent code by automatically unlocking.
Definition mutex.h:113
A very common template for a dynamic array of bytes.
Definition byte_array.h:36
Outcomes describe the state of completion for an operation.
Definition outcome.h:31
int value() const
Definition outcome.h:51
static basis::outcome peek_header(const basis::byte_array &packed_form, int &length)
static bool resynchronize(basis::byte_array &packed_form)
static void flatten(basis::byte_array &packed_form, const octopi::infoton &request, const octopi::octopus_request_id &id)
static bool unflatten(basis::byte_array &packed_form, basis::byte_array &still_flat, octopi::octopus_request_id &id)
static const char * outcome_name(const basis::outcome &to_name)
static int minimum_flat_size(const octopi::octopus_request_id &id)
An infoton is an individual request parcel with accompanying information.
Definition infoton.h:32
static void fast_pack(basis::byte_array &packed_form, const infoton &to_pack)
flattens an infoton "to_pack" into the byte array "packed_form".
Definition infoton.cpp:162
static int fast_pack_overhead(const structures::string_array &classifier)
reports how much space is needed to pack the "classifier".
Definition infoton.cpp:155
Identifies requests made on an octopus by users.
int packed_size() const
reports how large the packed id will be.
virtual bool unpack(basis::byte_array &packed_form)
Restores the packable from the "packed_form".
static const char * outcome_name(const basis::outcome &to_name)
An array of strings with some additional helpful methods.
static bool is_hexadecimal(char look_at)
returns true if "look_at" is one of the hexadecimal characters.
#define continuable_error(c, f, i)
#define WHACK_AND_GO
#define CHECK_LENGTH
char * copy(char *str)
Definition makedep.cpp:554
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
const int MEGABYTE
Number of bytes in a megabyte.
unsigned char abyte
A fairly important unit which is seldom defined...
Definition definitions.h:51
void attach(byte_array &packed_form, const char *to_attach)
Packs a character string "to_attach" into "packed_form".
Definition astring.cpp:1018
unsigned int un_int
Abbreviated name for unsigned integers.
Definition definitions.h:62
const int MAXIMUM_TRANSACTION
byte_array & cromp_name_array()
A logger that sends to the console screen using the standard output device.
Provides access to the operating system's socket methods.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
#define SAFE_STATIC(type, func_name, parms)
Statically defines a singleton object whose scope is the program's lifetime.
#define static_class_name()