oneliner 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -5,14 +5,15 @@ See http://en.wikipedia.org/wiki/Online_codes
5
5
  == Dependencies:
6
6
 
7
7
  Oneliner::SuperString:: openssl: http://www.openssl.org/
8
+ Oneliner::SuperString:: glib: http://www.gtk.org/download/
8
9
 
9
10
  == Sub packages:
10
11
 
11
- Oneliner::SuperString:: A String subclass that has the added power of Online Coding.
12
+ Oneliner::SuperString:: A class that has the super power of Online Coding.
12
13
 
13
14
  == Usage:
14
15
 
15
- You create Oneliner::SuperStrings just regular Strings, then you use Oneliner::SuperString#encode(size_of_chunk) to get Online Coded data from them.
16
+ You create Oneliner::SuperStrings with a regular String as argument, then you use Oneliner::SuperString#encode(size_of_chunk) to get Online Coded data from them.
16
17
 
17
18
  Then you use Oneliner::SuperString#decode!(chunk) on a new Oneliner::SuperString to recreate the data from the original String. Only a few extra percent (around 10 - my randomness seems to be suboptimal) is needed to recreate the original data this way.
18
19
 
@@ -30,8 +31,5 @@ Then you use Oneliner::SuperString#decode!(chunk) on a new Oneliner::SuperString
30
31
  redundant_packages << s2.encode(256)
31
32
  redundant_packages << s2.encode(256)
32
33
  redundant_packages << s2.encode(256)
33
- index = 0
34
- while index < redundant_packages.size && !s.decode!(redundant_packages[index])
35
- index += 1
36
- end
34
+ nil while s.decode!(redundant_packages.shift)
37
35
  assert_equal(s2, s)
@@ -28,6 +28,28 @@ unless have_library('ssl')
28
28
  crash "libssl needed"
29
29
  end
30
30
 
31
- dir_config("oneliner_ext")
31
+ unless find_executable("pkg-config")
32
+ crash("pkg-config needed")
33
+ end
34
+
35
+ unless have_library('glib-2.0')
36
+ crash "libglib-2.0 needed"
37
+ end
38
+
39
+ if ARGV.include?("-d")
40
+ $CFLAGS += " -D ONELINER_DEBUG"
41
+ end
42
+
43
+ if ARGV.include?("-O0")
44
+ $CFLAGS.gsub!(/-O./, "-O0")
45
+ else
46
+ $CFLAGS.gsub!(/-O./, "-O3")
47
+ end
48
+
49
+ if ARGV.include?("-gdb")
50
+ $CFLAGS += " -g -gdwarf-2 -g3"
51
+ end
52
+
53
+ $CFLAGS += " " + `pkg-config --cflags --libs glib-2.0`.strip
32
54
 
33
- create_makefile("oneliner_ext")
55
+ create_makefile("oneliner")
@@ -0,0 +1,1780 @@
1
+ // Archipelago - a distributed computing toolkit for ruby
2
+ // Copyright (C) 2007 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ //
4
+ // This program is free software; you can redistribute it and/or
5
+ // modify it under the terms of the GNU General Public License
6
+ // as published by the Free Software Foundation; either version 2
7
+ // of the License, or (at your option) any later version.
8
+ //
9
+ // This program is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with this program; if not, write to the Free Software
16
+ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ //
19
+ // Interested in how the hell this load of bollocks works?
20
+ // Begin by reading up on online codes at http://en.wikipedia.org/wiki/Online_codes, then
21
+ // read doc/architecture.txt in this repository.
22
+ //
23
+
24
+ #include "ruby.h"
25
+ #include <limits.h>
26
+ #include <openssl/aes.h>
27
+ #include <glib.h>
28
+ #include <stdlib.h>
29
+ #include <math.h>
30
+ #include <sys/time.h>
31
+ #include "oneliner.h"
32
+
33
+ //
34
+ // The classes
35
+ //
36
+
37
+ // The Oneliner::SuperString class, the one that does all the actual job.
38
+ static VALUE rb_superstring;
39
+ // The Oneliner::Chunk class, only for debugging purposes.
40
+ static VALUE rb_chunk;
41
+
42
+ //
43
+ // Utility macros.
44
+ //
45
+
46
+ //
47
+ // If we have debug turned on when compiling, lets do stuff with all these debug macros.
48
+ //
49
+ #ifdef ONELINER_DEBUG
50
+ // The indentation of DBEUG messages.
51
+ static guint32 DEBUG_indentation = 0;
52
+ // Only execute s if debugging.
53
+ #define IFDEBUG(s) s
54
+ // Make the indentation of normal DEBUG messages one greater.
55
+ #define INDENT indentation++;
56
+ // Make the indentation of normal DEBUG messages one less.
57
+ #define UNINDENT indentation--;
58
+ // Warn if x is NULL.
59
+ #define NULL_WARN(x) if ((x) == NULL) DEBUG("x is NULL!!!");
60
+ // Indent DEBUG_indentation spaces and print the debug message.
61
+ #define DEBUG(s, ...) { \
62
+ guint32 _indentation_tmp; \
63
+ fprintf(stderr, "DEBUG: "); \
64
+ for (indentation_tmp = 0; indentation_tmp < DEBUG_indentation; indentation_tmp++) fprintf(stderr, " "); \
65
+ fprintf(stderr, s, ## __VA_ARGS__); \
66
+ fprintf(stderr, "\n"); \
67
+ fflush(NULL); }
68
+ // Print out the byte in str of length len as zeroes and ones.
69
+ #define BITSTRING_DEBUG(msg,str,len) { \
70
+ fprintf(stderr, "DEBUG: %s: ", (msg)); \
71
+ rb_funcall(rb_const_get(rb_cObject, rb_intern("Kernel")), \
72
+ rb_intern("print"), \
73
+ 1, \
74
+ rb_funcall(rb_funcall(rb_funcall(rb_str_new((gchar *) (str), (len)), \
75
+ rb_intern("unpack"), \
76
+ 1, \
77
+ rb_str_new2("b*")), \
78
+ rb_intern("first"), \
79
+ 0), \
80
+ rb_intern("gsub"), \
81
+ 2, \
82
+ rb_funcall(rb_const_get(rb_cObject, rb_intern("Regexp")), \
83
+ rb_intern("new"), \
84
+ 1, \
85
+ rb_str_new2("(.{4,4})")), \
86
+ rb_str_new2("\\1,"))); \
87
+ fprintf(stderr, "\n"); \
88
+ fflush(NULL); }
89
+ // Print out the text in str of length len as a ruby inspected string.
90
+ #define STRING_DEBUG(msg,str,len) { \
91
+ fprintf(stderr, "DEBUG: %s: ", (msg)); \
92
+ rb_funcall(rb_const_get(rb_cObject, rb_intern("Kernel")), rb_intern("print"), 1, rb_funcall(rb_str_new((gchar *) (str), (len)), rb_intern("inspect"), 0)); \
93
+ fprintf(stderr, "\n"); \
94
+ fflush(NULL); }
95
+ //
96
+ // If we dont have debug turned on while compiling, just replace them with empty semicolons so as not to confuse the compiler.
97
+ //
98
+ #else
99
+ #define INDENT ;
100
+ #define UNINDENT ;
101
+ #define DEBUG(s, ...) ;
102
+ #define BITSTRING_DEBUG(msg,str,len) ;
103
+ #define IFDEBUG(s) ;
104
+ #endif
105
+
106
+ //
107
+ // Just iterate over ary freeing all content.
108
+ //
109
+ #define FREE_ARRAY_WITH_STRINGS(ary) { \
110
+ guint32 _free_ary_with_strings_int; \
111
+ for (_free_ary_with_strings_int = 0; \
112
+ _free_ary_with_strings_int < (ary)->len; \
113
+ _free_ary_with_strings_int++) { \
114
+ if (g_array_index((ary), guint8 *, _free_ary_with_strings_int) != NULL) \
115
+ g_free(g_array_index((ary), guint8 *, _free_ary_with_strings_int)); \
116
+ } \
117
+ g_array_free(ary, TRUE); \
118
+ }
119
+ // Returns the number of characters needed to print out the number i as a decimal number.
120
+ #define CHARS_FOR(i) ((guint32) (log10((gdouble) (i))) + 1)
121
+ // Returns a random number between 0 and max.
122
+ #define RANDOM(context,max) (oneliner_context_random(context) % (max))
123
+ // Returns a random float between >=0 and < 1.
124
+ #define RANDFLOAT(context) ( (gdouble) ( (guint32) oneliner_context_random(context) ) ) / ( (gdouble) G_MAXUINT32 )
125
+ // Will define a oneliner_superstring * named str and fetch the pointer from rb_obj into it.
126
+ #define RB_ONELINER(rb_obj,str) \
127
+ oneliner_superstring *str; \
128
+ Data_Get_Struct(rb_obj, oneliner_superstring, str);
129
+ // Will allocate a zeroed string of len characters for dst and copy src into it.
130
+ #define ALLOC_COPY8(dst,src,len) { \
131
+ (dst) = g_new0(guint8, (len) + 1); \
132
+ memcpy((dst), (src), sizeof(guint8) * (len)); }
133
+
134
+ // The Q constant of the algorithm (see http://en.wikipedia.org/wiki/Online_codes)
135
+ #define Q 3
136
+ // The E constant of the algorithm (see http://en.wikipedia.org/wiki/Online_codes)
137
+ #define E 0.01
138
+
139
+ //
140
+ // The c representation of a superstring.
141
+ //
142
+ typedef struct {
143
+ // The data_blocks of this superstring.
144
+ GArray *data_blocks;
145
+ // The aux blocks of this superstring.
146
+ GArray *aux_blocks;
147
+ // Our graph of check blocks.
148
+ GArray *check_block_graph;
149
+ // Our graph of aux blocks for the source blocks.
150
+ GArray *aux_block_graph;
151
+ // Our graph of aux blocks for the aux blocks themselves.
152
+ GArray *known_aux_blocks;
153
+ // The list of found blocks to analyze.
154
+ GList *to_analyze;
155
+ // The list of xor_blocks to free.
156
+ GList *to_free;
157
+ // The size of each block in bits.
158
+ guint32 block_size;
159
+ // The number of guint8's needed to contain those bits.
160
+ guint32 n_chars_in_block;
161
+ // The length of the original data.
162
+ guint32 data_len;
163
+ // The number of data_blocks needed to contain that data.
164
+ guint32 n_data_blocks_needed;
165
+ // The number of bytes needed to store those data_blocks.
166
+ guint32 n_bytes_for_data_blocks;
167
+ // The number of aux blocks needed for those blocks.
168
+ guint32 n_aux_blocks_needed;
169
+ // The number of check blocks we have decoded so far.
170
+ guint32 n_seen_check_blocks;
171
+ // Whether we have decided already that we are done decoding.
172
+ gboolean decode_done;
173
+ } oneliner_superstring;
174
+
175
+ //
176
+ // An as yet mysterious block.
177
+ //
178
+ typedef struct {
179
+ gboolean initialized;
180
+ guint8 *sum;
181
+ GHashTable *source_blocks;
182
+ oneliner_superstring *str;
183
+ gint32 index;
184
+ } oneliner_xor_block;
185
+
186
+ //
187
+ // An insertion to do into the graph of a superstring.
188
+ //
189
+ typedef struct {
190
+ guint32 source_block_nr;
191
+ oneliner_xor_block *check_block;
192
+ } oneliner_graph_insertion;
193
+
194
+ //
195
+ // A random context.
196
+ //
197
+ typedef guint8 oneliner_context;
198
+
199
+ // Just to solve a few dependency problems between the functions.
200
+ static guint8 *
201
+ oneliner_superstring_get_combined_block(oneliner_superstring *str, guint32 block_nr);
202
+ static void
203
+ oneliner_superstring_analyze(oneliner_superstring *str, guint32 found_block_nr);
204
+
205
+ //
206
+ // Encrypt the context again
207
+ //
208
+ static void
209
+ oneliner_context_randomize(oneliner_context *context)
210
+ {
211
+ AES_KEY key;
212
+
213
+ AES_set_encrypt_key((guint8 *) "0123456789012345", 128, &key);
214
+
215
+ AES_encrypt((guint8 *) context + 4,
216
+ (guint8 *) context + 4,
217
+ &key);
218
+ * (guint32 *) context = 1;
219
+ }
220
+
221
+ //
222
+ // Fill the buffer of context with seed and randomize it.
223
+ //
224
+ static gint32
225
+ oneliner_context_seed(oneliner_context *context, gint32 seed)
226
+ {
227
+ gint32 tmp;
228
+
229
+ for (tmp = 1; tmp < 5; tmp++)
230
+ ( (gint32 *) context )[tmp] = seed;
231
+
232
+ oneliner_context_randomize(context);
233
+
234
+ return seed;
235
+ }
236
+
237
+ //
238
+ // Return an int that is the next random in the context
239
+ //
240
+ static gint32
241
+ oneliner_context_random(oneliner_context *context)
242
+ {
243
+ gint32 *int_context = (gint32 *) context;
244
+ if (int_context[0] < 5) {
245
+ gint32 return_value = int_context[int_context[0]];
246
+ int_context[0]++;
247
+ return return_value;
248
+ } else {
249
+ oneliner_context_randomize(context);
250
+ return oneliner_context_random(context);
251
+ }
252
+ }
253
+
254
+ //
255
+ // Initialize a context.
256
+ //
257
+ static oneliner_context *
258
+ oneliner_context_new()
259
+ {
260
+ return (oneliner_context *) g_new0(guint8, 4 * 5);
261
+ }
262
+
263
+ //
264
+ // Get a random degree
265
+ //
266
+ static guint32
267
+ oneliner_context_get_degree(oneliner_context *context)
268
+ {
269
+ gdouble f;
270
+ guint32 tmp = 0;
271
+
272
+ f = RANDFLOAT(context);
273
+ while (f > 0) {
274
+ tmp++;
275
+ f = f - P[tmp];
276
+ }
277
+ return tmp;
278
+ }
279
+
280
+ //
281
+ // Make object into object ^ subject
282
+ //
283
+ static void
284
+ oneliner_string_xor(guint8 *object, guint8 *subject, guint32 len)
285
+ {
286
+ guint32 tmp;
287
+
288
+ #ifdef G_HAVE_GINT64
289
+ for (tmp = 0; tmp < len / 8; tmp++)
290
+ ( (guint64 *) object )[tmp] = ( (guint64 *) object )[tmp] ^ ( (guint64 *) subject )[tmp];
291
+ for (tmp = tmp * 2; tmp < len / 4; tmp++)
292
+ ( (guint32 *) object )[tmp] = ( (guint32 *) object )[tmp] ^ ( (guint32 *) subject )[tmp];
293
+ for (tmp = tmp * 4; tmp < len; tmp++)
294
+ object[tmp] = object[tmp] ^ subject[tmp];
295
+ #else
296
+ for (tmp = 0; tmp < len / 4; tmp++)
297
+ ( (guint32 *) object )[tmp] = ( (guint32 *) object )[tmp] ^ ( (guint32 *) subject )[tmp];
298
+ for (tmp = tmp * 4; tmp < len; tmp++)
299
+ object[tmp] = object[tmp] ^ subject[tmp];
300
+ #endif
301
+ }
302
+
303
+ //
304
+ // Ruby wrapper for oneliner_string_xor. Will make the self String into self ^ the object String.
305
+ //
306
+ static VALUE
307
+ rb_oneliner_string_xor(VALUE self, VALUE object)
308
+ {
309
+ Check_Type(object, T_STRING);
310
+ rb_str_modify(self);
311
+ oneliner_string_xor((guint8 *) RSTRING(self)->ptr, (guint8 *) RSTRING(object)->ptr, MIN(RSTRING(self)->len, RSTRING(object)->len));
312
+ return self;
313
+ }
314
+
315
+ //
316
+ // Iterator used in the hash inspector.
317
+ //
318
+ static void
319
+ oneliner_int_hash_debug_iterator(gpointer key, gpointer value, gpointer user_data)
320
+ {
321
+ gchar **buf = (gchar **) user_data;
322
+ guint32 key_int = GPOINTER_TO_UINT(key);
323
+ guint32 value_int = GPOINTER_TO_UINT(value);
324
+ guint32 extra_size_needed = CHARS_FOR(key_int) + CHARS_FOR(value_int) + 6 + 1;
325
+ guint32 new_size_needed = strlen(*buf) + extra_size_needed;
326
+ *buf = g_realloc(*buf, new_size_needed);
327
+ snprintf(*buf + strlen(*buf), extra_size_needed, "%i => %i, ", key_int, value_int);
328
+ }
329
+
330
+ //
331
+ // A hash inspector for GHashTables that considers all hashes as storing ints.
332
+ //
333
+ static gchar *
334
+ oneliner_int_hash_to_s(GHashTable *hash)
335
+ {
336
+ gchar *buf;
337
+ gchar *base = "<GHashTable: size='' data='";
338
+ guint32 size_needed = strlen(base) + CHARS_FOR(g_hash_table_size(hash)) + CHARS_FOR(G_MAXUINT32) + 1;
339
+ buf = g_new(gchar, size_needed);
340
+ snprintf(buf, size_needed, "<GHashTable:%i size='%i' data='", GPOINTER_TO_UINT(hash), g_hash_table_size(hash));
341
+ g_hash_table_foreach(hash, oneliner_int_hash_debug_iterator, &buf);
342
+ size_needed = strlen(buf) + 3;
343
+ buf = g_realloc(buf, size_needed);
344
+ snprintf(buf + strlen(buf), 3, "'>");
345
+ return buf;
346
+ }
347
+
348
+ //
349
+ // Prints out an inspection of hash.
350
+ //
351
+ static void
352
+ oneliner_int_hash_debug(GHashTable *hash)
353
+ {
354
+ gchar *s = oneliner_int_hash_to_s(hash);
355
+ DEBUG(s);
356
+ g_free(s);
357
+ }
358
+
359
+ // Returns if this xor_block is finished.
360
+ #define XOR_BLOCK_FINISHED(xor_block) ((xor_block)->initialized && \
361
+ (g_hash_table_size((xor_block)->source_blocks) == 1) && \
362
+ ((oneliner_xor_block_last_uses((xor_block)) % 2) == 1))
363
+ // Returns if this xor_block is empty.
364
+ #define XOR_BLOCK_EMPTY(xor_block) (g_hash_table_size((xor_block)->source_blocks) == 0)
365
+ // Returns if this xor_block is an aux block.
366
+ #define XOR_BLOCK_IS_AUX(xor_block) ((xor_block)->index > -1)
367
+ // Returns if this xor_block is a check block.
368
+ #define XOR_BLOCK_IS_CHECK(xor_block) ((xor_block)->index == -1)
369
+ // Returns a constant string for the type of this xor_block.
370
+ #define XOR_TYPE(xor_block) (XOR_BLOCK_IS_AUX((xor_block)) ? "aux_block" : "check_block")
371
+
372
+ //
373
+ // An inspector for xor_blocks.
374
+ //
375
+ static gchar *
376
+ oneliner_xor_block_to_s(oneliner_xor_block *xor_block)
377
+ {
378
+ gchar *rval;
379
+ gchar *source_blocks_buf;
380
+ gchar *base = "<xor_block: type='' index='' initialized='' sum='' source_blocks='";
381
+ guint32 size_needed = strlen(base) + 1 + 1 + CHARS_FOR(xor_block->index) + 1 + xor_block->str->n_chars_in_block + CHARS_FOR(G_MAXUINT32) + 1 + strlen(XOR_TYPE(xor_block));
382
+ rval = g_new(gchar, size_needed);
383
+ snprintf(rval, size_needed, "<xor_block:%i type='%s' index='%i' initialized='%i' sum='%i' source_blocks='", GPOINTER_TO_UINT(xor_block), XOR_TYPE(xor_block), xor_block->index, xor_block->initialized, *(xor_block->sum));
384
+ source_blocks_buf = oneliner_int_hash_to_s(xor_block->source_blocks);
385
+ size_needed = strlen(rval) + strlen(source_blocks_buf) + 4;
386
+ rval = g_realloc(rval, size_needed);
387
+ sprintf(rval + strlen(rval), "%s'>", source_blocks_buf);
388
+ g_free(source_blocks_buf);
389
+ return rval;
390
+ }
391
+
392
+ //
393
+ // Prints out an inspection for this xor_block.
394
+ //
395
+ static void
396
+ oneliner_xor_block_debug(oneliner_xor_block *xor_block)
397
+ {
398
+ gchar *s = oneliner_xor_block_to_s(xor_block);
399
+ DEBUG(s);
400
+ g_free(s);
401
+ }
402
+
403
+ //
404
+ // Allocates a new xor_block containing data of len, which is initialized and bound to str at index.
405
+ //
406
+ static oneliner_xor_block *
407
+ oneliner_xor_block_new(guint8 *data, guint32 len, gboolean initialized, oneliner_superstring *str, gint32 index)
408
+ {
409
+ oneliner_xor_block *rval = g_new0(oneliner_xor_block, 1);
410
+ ALLOC_COPY8(rval->sum, data, len);
411
+ rval->initialized = initialized;
412
+ rval->source_blocks = g_hash_table_new(NULL, NULL);
413
+ rval->str = str;
414
+ rval->index = index;
415
+ return rval;
416
+ }
417
+
418
+ //
419
+ // Remove the dependency on the source_block_nr from ary for this xor_block.
420
+ //
421
+ static void
422
+ oneliner_xor_block_free_and_remove_from_array(oneliner_xor_block *xor_block, GArray *ary, guint32 source_block_nr)
423
+ {
424
+ GHashTable *hash;
425
+
426
+ if (ary != NULL) {
427
+ if ((hash = g_array_index(ary, GHashTable *, source_block_nr)) != NULL) {
428
+ if (g_hash_table_lookup_extended(hash, xor_block, NULL, NULL)) {
429
+ g_hash_table_remove(hash, xor_block);
430
+ } else {
431
+ DEBUG("it doesnt exist as a dependency :/ dependencies are:");
432
+ rb_raise(rb_eRuntimeError, "We have a serious problem with the internal state, an xor_block that depended on a data/aux block wasnt referenced back!");
433
+ }
434
+ } else {
435
+ DEBUG("no hash for that source block exists :/");
436
+ rb_raise(rb_eRuntimeError, "We have a serious problem with the internal state, the source block for an xor_block that we want to remove as a dependency doesnt have a dependency hash!");
437
+ }
438
+ } else {
439
+ DEBUG("no array exists :/");
440
+ rb_raise(rb_eRuntimeError, "We have a serious problem with the internal state, the oneliner_superstring an xor_block is attached to doesnt have the array we want to remove the dependencies of the xor_block from!");
441
+ }
442
+ }
443
+
444
+ //
445
+ // Iterator for the freeing of xor_blocks.
446
+ //
447
+ // Will remove the dependency from the proper array in the oneliner_superstring of
448
+ // this xor_block depending on it being aux or check block.
449
+ //
450
+ static void
451
+ oneliner_xor_block_free_and_remove(gpointer key, gpointer data, gpointer user_data)
452
+ {
453
+ guint32 source_block_nr = GPOINTER_TO_UINT(key);
454
+ oneliner_xor_block *xor_block = (oneliner_xor_block *) user_data;
455
+
456
+ INDENT;
457
+
458
+ if (XOR_BLOCK_IS_CHECK(xor_block)) {
459
+ DEBUG("removing _%i_ as a dependency from it in check_block_graph", source_block_nr);
460
+ oneliner_xor_block_free_and_remove_from_array(xor_block, xor_block->str->check_block_graph, source_block_nr);
461
+ } else {
462
+ DEBUG("removing _%i_ as a dependency from it in aux_block_graph", source_block_nr);
463
+ oneliner_xor_block_free_and_remove_from_array(xor_block, xor_block->str->aux_block_graph, source_block_nr);
464
+ }
465
+
466
+ UNINDENT;
467
+ }
468
+
469
+ //
470
+ // Free the xor_block, and if free_dependencies also remove all its dependencies from the arrays
471
+ // of its oneliner_superstring.
472
+ //
473
+ static void
474
+ oneliner_xor_block_free(oneliner_xor_block *xor_block, gboolean free_dependencies)
475
+ {
476
+ INDENT;
477
+ DEBUG("freeing %s %i", XOR_TYPE(xor_block), GPOINTER_TO_UINT(xor_block));
478
+ g_free(xor_block->sum);
479
+ if (free_dependencies) {
480
+ g_hash_table_foreach(xor_block->source_blocks, oneliner_xor_block_free_and_remove, xor_block);
481
+ }
482
+ g_hash_table_destroy(xor_block->source_blocks);
483
+ if (free_dependencies && XOR_BLOCK_IS_AUX(xor_block) && xor_block->str->known_aux_blocks != NULL)
484
+ g_array_index(xor_block->str->known_aux_blocks, oneliner_xor_block *, xor_block->index) = NULL;
485
+ g_free(xor_block);
486
+ UNINDENT;
487
+ }
488
+
489
+ //
490
+ // Add a dependency for source_block_nr for this xor_block.
491
+ //
492
+ static void
493
+ oneliner_xor_block_add(oneliner_xor_block *xor_block, guint32 source_block_nr)
494
+ {
495
+ g_hash_table_insert(xor_block->source_blocks,
496
+ GUINT_TO_POINTER(source_block_nr),
497
+ GUINT_TO_POINTER(GPOINTER_TO_UINT(g_hash_table_lookup(xor_block->source_blocks,
498
+ GUINT_TO_POINTER(source_block_nr))) + 1));
499
+ }
500
+
501
+ //
502
+ // Set the sum of this xor_block to sum of length len. Will also set the xor_block to initialized.
503
+ //
504
+ static void
505
+ oneliner_xor_block_set_sum(oneliner_xor_block *xor_block, guint8 *sum, guint32 len)
506
+ {
507
+ oneliner_string_xor(xor_block->sum, sum, len);
508
+ xor_block->initialized = TRUE;
509
+ }
510
+
511
+ //
512
+ // The iterator used by xor_block_last_source_block.
513
+ //
514
+ static void
515
+ oneliner_xor_block_last_source_block_nr_iterator(gpointer key, gpointer data, gpointer user_data)
516
+ {
517
+ * (guint32 *) user_data = GPOINTER_TO_UINT(key);
518
+ }
519
+
520
+ //
521
+ // Returns the last source_block_nr that this xor_block depends on.
522
+ //
523
+ static guint32
524
+ oneliner_xor_block_last_source_block_nr(oneliner_xor_block *xor_block)
525
+ {
526
+ guint32 source_block_nr;
527
+ g_hash_table_foreach(xor_block->source_blocks, oneliner_xor_block_last_source_block_nr_iterator, &source_block_nr);
528
+ return source_block_nr;
529
+ }
530
+
531
+ //
532
+ // The iterator used by xor_block_last_uses.
533
+ //
534
+ static void
535
+ oneliner_xor_block_last_uses_iterator(gpointer key, gpointer data, gpointer user_data)
536
+ {
537
+ * (guint32 *) user_data = GPOINTER_TO_UINT(data);
538
+ }
539
+
540
+ //
541
+ // Returns the number of times this xor_block depends on its last source block.
542
+ //
543
+ static guint32
544
+ oneliner_xor_block_last_uses(oneliner_xor_block *xor_block)
545
+ {
546
+ guint32 uses;
547
+ g_hash_table_foreach(xor_block->source_blocks, oneliner_xor_block_last_uses_iterator, &uses);
548
+ return uses;
549
+ }
550
+
551
+ //
552
+ // XOR the sum of xor_block with found_block_nr from the oneliner_superstring of xor_block the
553
+ // requisite nr of times and then remove the dependency.
554
+ //
555
+ static void
556
+ oneliner_xor_block_apply(oneliner_xor_block *xor_block, guint32 found_block_nr)
557
+ {
558
+ guint32 uses;
559
+ guint32 tmp;
560
+
561
+ INDENT;
562
+ uses = GPOINTER_TO_UINT(g_hash_table_lookup(xor_block->source_blocks, GUINT_TO_POINTER(found_block_nr)));
563
+
564
+ if (uses < 1) {
565
+ DEBUG("its not used by this xor_block!!! :O");
566
+ IFDEBUG(oneliner_xor_block_debug(xor_block));
567
+ rb_raise(rb_eRuntimeError, "We have a serious problem with the internal state, the source block being applied to an xor_block doesnt seem to be included in it!");
568
+ }
569
+
570
+ if (uses % 2 == 1)
571
+ oneliner_string_xor(xor_block->sum, oneliner_superstring_get_combined_block(xor_block->str, found_block_nr), xor_block->str->n_chars_in_block);
572
+
573
+ g_hash_table_remove(xor_block->source_blocks, GUINT_TO_POINTER(found_block_nr));
574
+ UNINDENT;
575
+ }
576
+
577
+ //
578
+ // Create a new graph_insertion of check_block for source_block_nr.
579
+ //
580
+ static oneliner_graph_insertion *
581
+ oneliner_graph_insertion_new(oneliner_xor_block *check_block, guint32 source_block_nr)
582
+ {
583
+ oneliner_graph_insertion *rval = g_new0(oneliner_graph_insertion, 1);
584
+ rval->check_block = check_block;
585
+ rval->source_block_nr = source_block_nr;
586
+ return rval;
587
+ }
588
+
589
+ //
590
+ // Remove this graph insertion for block_nr and insertion, and remove its xor_block without
591
+ // going for its dependencies (which shouldnt exist, anyway).
592
+ //
593
+ static gboolean
594
+ oneliner_graph_insertion_free_with_check_block(gpointer block_nr, gpointer insertion, gpointer user_data)
595
+ {
596
+ oneliner_xor_block_free(( (oneliner_graph_insertion *) insertion )->check_block, FALSE);
597
+ g_free(insertion);
598
+ return TRUE;
599
+ }
600
+
601
+ //
602
+ // Insert the given insertion into the given oneliner_superstring.
603
+ //
604
+ static gboolean
605
+ oneliner_insertion_execute(gpointer block_nr, gpointer insertion, gpointer string)
606
+ {
607
+ oneliner_graph_insertion *ins = (oneliner_graph_insertion *) insertion;
608
+ oneliner_superstring *str = (oneliner_superstring *) string;
609
+ GHashTable *hash;
610
+
611
+ INDENT;
612
+ DEBUG("inserting check_block %i as a dependency for block _%i_", GPOINTER_TO_UINT(ins->check_block), ins->source_block_nr);
613
+
614
+ if ((hash = g_array_index(str->check_block_graph, GHashTable *, ins->source_block_nr)) == NULL) {
615
+ hash = g_array_index(str->check_block_graph, GHashTable *, ins->source_block_nr) = g_hash_table_new(NULL, NULL);
616
+ }
617
+
618
+ g_hash_table_insert(hash, ins->check_block, GUINT_TO_POINTER(ins->source_block_nr));
619
+
620
+ g_free(insertion);
621
+ UNINDENT;
622
+ return TRUE;
623
+ }
624
+
625
+
626
+ //
627
+ // Return the block size in bits for a string of the given length.
628
+ //
629
+ static guint32
630
+ oneliner_superstring_get_block_size(guint32 len) {
631
+ if (len < 1024) {
632
+ return (guint32) ceil((len / 1024.0) * 8.0);
633
+ } else {
634
+ return ((len / 128) / 8) * 8;
635
+ }
636
+ }
637
+
638
+ //
639
+ // Fill *block with a block_size (bitwise) piece of *data from pos (bitwise).
640
+ //
641
+ static void
642
+ oneliner_superstring_get_block_from_data(guint8 *data, guint32 pos, guint32 block_size, guint8 *block)
643
+ {
644
+ //
645
+ // Block sizes 1-7 are tricky,
646
+ // but all other sizes are divisible by 8 and therefore ok to do with a simple memcpy.
647
+ //
648
+ if (block_size < 8) {
649
+ // If the whole block fits within one char then we cant assume that we have permission
650
+ // to read the char after it, but if it DOESNT then we MUST assume that we have permission
651
+ // to read the char after it. Therefore the below fork where we read a char or a short from the data.
652
+ if ((pos + block_size - 1) / 8 == pos / 8) {
653
+ *block = data[pos / 8];
654
+ *block = *block << (8 - ((pos + block_size) % 8)) % 8;
655
+ *block = *block >> 8 - block_size;
656
+ } else {
657
+ guint16 short_containing_block = * (guint16 *) (data + (pos / 8));
658
+ short_containing_block = short_containing_block << (8 - ((pos + block_size) % 8)) % 8;
659
+ short_containing_block = short_containing_block >> 16 - block_size;
660
+ *block = (guint8) short_containing_block;
661
+ }
662
+ } else {
663
+ memcpy(block, data + (pos / 8), sizeof(guint8) * (block_size / 8));
664
+ }
665
+ }
666
+
667
+ //
668
+ // Take *data and return a GArray * with all the block_size blocks it contains.
669
+ // Needs hints as to how many bytes we need to contain all blocks, how long *data is,
670
+ // how many chars each block needs and how many blocks we need in total.
671
+ //
672
+ static GArray *
673
+ oneliner_superstring_build_blocks_from_data(guint32 n_bytes_for_blocks,
674
+ guint32 data_len,
675
+ guint32 block_size,
676
+ guint32 n_chars_in_block,
677
+ guint32 n_blocks_needed,
678
+ guint8 *data)
679
+ {
680
+ guint32 block_nr;
681
+ guint8 *tmp_block;
682
+ guint8 tmp_data[n_bytes_for_blocks];
683
+ GArray *rval = g_array_new(FALSE, FALSE, sizeof(guint8 *));
684
+
685
+ g_array_set_size(rval, n_blocks_needed);
686
+ memcpy((gchar *) tmp_data, (gchar *) data, sizeof(guint8) * data_len);
687
+ for (block_nr = 0; block_nr < n_blocks_needed; block_nr++) {
688
+ guint32 pos = block_nr * block_size;
689
+ tmp_block = g_new0(guint8, n_chars_in_block + 1);
690
+ oneliner_superstring_get_block_from_data(tmp_data, pos, block_size, tmp_block);
691
+ g_array_index(rval, guint8 *, block_nr) = tmp_block;
692
+ }
693
+
694
+ return rval;
695
+ }
696
+
697
+
698
+ //
699
+ // Ensure that str->data_blocks contains a GArray with all the data_blocks of this data.
700
+ //
701
+ static void
702
+ oneliner_superstring_build_data_blocks(oneliner_superstring *str, guint8 *data)
703
+ {
704
+ str->data_blocks = oneliner_superstring_build_blocks_from_data(str->n_bytes_for_data_blocks,
705
+ str->data_len,
706
+ str->block_size,
707
+ str->n_chars_in_block,
708
+ str->n_data_blocks_needed,
709
+ data);
710
+ }
711
+
712
+ //
713
+ // Take the block_size (bitwise) content of *block and insert it in *data at block_nr.
714
+ //
715
+ static void
716
+ oneliner_superstring_set_data_from_block(guint8 *data, guint32 block_nr, guint32 block_size, guint8 *block)
717
+ {
718
+ //
719
+ // If the block size is less than 8 then we have some tricky business, otherwise
720
+ // we know that it is a multiple of 8, which makes it into a simple memcpy.
721
+ //
722
+ if (block_size < 8) {
723
+ // If the entire block fits within one byte then we can not assume that we can write
724
+ // the byte after, but if it doesnt then we MUST assume that we CAN write the byte
725
+ // after, therefore the below fork where we write a char or a long depending.
726
+ if ((block_nr * block_size) / 8 == (((block_nr + 1) * block_size) - 1) / 8) {
727
+ // Set the byte we want to write to the block content.
728
+ guint8 byte_to_write = *block;
729
+ // Left shift it enough so that it matches the segment of data we want to write.
730
+ byte_to_write = byte_to_write << (block_nr * block_size) % 8;
731
+ // Then OR the corresponding byte in the data with the byte to write. Since
732
+ // the data is supposed to be initialized with zeroes this ought to do the trick.
733
+ data[(block_nr * block_size) / 8] = data[(block_nr * block_size) / 8] | byte_to_write;
734
+ } else {
735
+ // Set the short we want to write to the block content.
736
+ guint16 short_to_write = (guint16) *block;
737
+ // Left shift it enough so that it matches the segment of data we want to write.
738
+ short_to_write = short_to_write << (block_nr * block_size) % 8;
739
+ // Then OR the corresponding short in the data with the short to write. Since
740
+ // the data is supposed to be initialized with zeroes this ought to do the trick.
741
+ * (guint16 *) (data + (block_nr * block_size) / 8) = * (guint16 *) (data + (block_nr * block_size) / 8) | short_to_write;
742
+ }
743
+ } else {
744
+ memcpy((gchar *) data + (block_nr * (block_size / 8)), (gchar *) block, sizeof(guint8) * (block_size / 8));
745
+ }
746
+ }
747
+
748
+ //
749
+ // Take the given blocks of size block_size needing n_bytes_for_blocks to be stored completely and
750
+ // concatenate them into a string, then return the first data_len bytes of the result.
751
+ //
752
+ // If prepend_size is larger than 0 then the prepend string will be prepended to the result.
753
+ //
754
+ static guint8 *
755
+ oneliner_superstring_build_data_from_blocks(guint32 n_bytes_for_blocks,
756
+ guint32 data_len,
757
+ guint32 block_size,
758
+ GArray *blocks,
759
+ guint32 prepend_size,
760
+ guint8 *prepend)
761
+ {
762
+ guint32 index;
763
+ guint8 *tmp_data = g_new0(guint8, n_bytes_for_blocks + prepend_size + 1);
764
+ for (index = 0; index < blocks->len; index++) {
765
+ oneliner_superstring_set_data_from_block(tmp_data + prepend_size, index, block_size, g_array_index(blocks, guint8 *, index));
766
+ }
767
+ memcpy((gchar *) tmp_data, (gchar *) prepend, sizeof(guint8) * prepend_size);
768
+ return g_realloc(tmp_data, data_len);
769
+ }
770
+
771
+ //
772
+ // Return the guint8 * that the given *str describes with its data_blocks.
773
+ //
774
+ static guint8 *
775
+ oneliner_superstring_build_data(oneliner_superstring *str)
776
+ {
777
+ return oneliner_superstring_build_data_from_blocks(str->n_bytes_for_data_blocks,
778
+ str->data_len,
779
+ str->block_size,
780
+ str->data_blocks,
781
+ 0,
782
+ NULL);
783
+ }
784
+
785
+ //
786
+ // Create the aux_blocks GArray for *str using its blocks and length.
787
+ //
788
+ static void
789
+ oneliner_superstring_build_aux_blocks(oneliner_superstring *str)
790
+ {
791
+ guint32 tmp;
792
+ oneliner_context *context = oneliner_context_new();
793
+
794
+ oneliner_context_seed(context, (gint32) str->data_len);
795
+ str->aux_blocks = g_array_new(FALSE, TRUE, sizeof(guint8 *));
796
+ g_array_set_size(str->aux_blocks, str->n_aux_blocks_needed);
797
+
798
+ for (tmp = 0; tmp < str->n_data_blocks_needed; tmp++) {
799
+ guint32 tmp2;
800
+ DEBUG("selecting aux_blocks for data block _%i_ (%i)", tmp, *g_array_index(str->data_blocks, gchar *, tmp));
801
+ for (tmp2 = 0; tmp2 < Q; tmp2++) {
802
+ guint32 aux_block_nr = (guint32) RANDOM(context, str->n_aux_blocks_needed);
803
+ if (g_array_index(str->aux_blocks, guint *, aux_block_nr) == NULL) {
804
+ ALLOC_COPY8(g_array_index(str->aux_blocks, guint8 *, aux_block_nr),
805
+ g_array_index(str->data_blocks, gchar *, tmp),
806
+ str->n_chars_in_block);
807
+ } else {
808
+ oneliner_string_xor(g_array_index(str->aux_blocks, guint8 *, aux_block_nr),
809
+ g_array_index(str->data_blocks, guint8 *, tmp),
810
+ str->n_chars_in_block);
811
+ }
812
+ DEBUG(" %i => sum=%i", aux_block_nr, *g_array_index(str->aux_blocks, guint8 *, aux_block_nr));
813
+ }
814
+ }
815
+
816
+ g_free(context);
817
+ }
818
+
819
+ //
820
+ // Initialize all the precalculated state of *str using data_len.
821
+ //
822
+ static void
823
+ oneliner_superstring_initialize(oneliner_superstring *str, guint32 data_len)
824
+ {
825
+ DEBUG("initializing string %i", GPOINTER_TO_UINT(str));
826
+ // Make sure the length of our data (our String superclass content) is set.
827
+ str->data_len = (guint32) data_len;
828
+ // Make sure the block size we deem reasonable for this data length is set.
829
+ str->block_size = oneliner_superstring_get_block_size(str->data_len);
830
+ // Make sure the number of chars needed to contain such a block is set.
831
+ str->n_chars_in_block = (guint32) ceil(str->block_size / 8.0);
832
+ // Make sure the number of data_blocks needed to contain this data is set.
833
+ str->n_data_blocks_needed = (guint32) ceil((str->data_len * 8.0) / str->block_size);
834
+ // Make sure the number of bytes needed to contain those data_blocks is set.
835
+ str->n_bytes_for_data_blocks = (guint32) ceil((str->n_data_blocks_needed * str->block_size) / 8.0);
836
+ // Make sure the number of aux blocks needed for those data_blocks is set.
837
+ str->n_aux_blocks_needed = (guint32) ceil(0.55 * Q * E * str->n_data_blocks_needed);
838
+ // Make sure the number of known check blocks so far is set.
839
+ str->n_seen_check_blocks = 0;
840
+ // Make sure we dont think we have decoded already.
841
+ str->decode_done = FALSE;
842
+ // Make sure we have an initialized to_analyze.
843
+ str->to_analyze = NULL;
844
+ // Make sure we have an initialized to_free.
845
+ str->to_free = NULL;
846
+ // Make sure our graphs are initialized empty.
847
+ str->check_block_graph = NULL;
848
+ str->known_aux_blocks = NULL;
849
+ str->aux_block_graph = NULL;
850
+ }
851
+
852
+ //
853
+ // Initialize a superstring using (possibly) a String to encode.
854
+ //
855
+ static VALUE
856
+ rb_oneliner_superstring_initialize(int argc, VALUE *argv, VALUE self)
857
+ {
858
+ RB_ONELINER(self, str);
859
+
860
+ if (argc > 1) {
861
+ rb_raise(rb_eRuntimeError, "Oneliner::SuperString.new(String s = nil) takes at most 1 argument.");
862
+ } else if (argc == 1) {
863
+ Check_Type(argv[0], T_STRING);
864
+
865
+ if (RSTRING(argv[0])->len < 1)
866
+ rb_raise(rb_eRuntimeError, "First argument to Oneliner::SuperString.new(String s = nil) must be a String of at least size 1 if given.");
867
+
868
+ oneliner_superstring_initialize(str, RSTRING(argv[0])->len);
869
+
870
+ // Make sure that we have our data_blocks calculated properly.
871
+ oneliner_superstring_build_data_blocks(str, (guint8 *) RSTRING(argv[0])->ptr);
872
+ oneliner_superstring_build_aux_blocks(str);
873
+ str->decode_done = TRUE;
874
+ }
875
+ return self;
876
+ }
877
+
878
+ //
879
+ // Returns whether *str is done decoding.
880
+ //
881
+ static VALUE
882
+ oneliner_superstring_decode_done(oneliner_superstring *str)
883
+ {
884
+ guint32 tmp;
885
+ if (str->decode_done)
886
+ return Qtrue;
887
+ if (str->data_blocks == NULL)
888
+ return Qfalse;
889
+ if (str->n_seen_check_blocks < str->n_data_blocks_needed)
890
+ return Qfalse;
891
+ for (tmp = 0; tmp < str->n_data_blocks_needed; tmp++)
892
+ if (g_array_index(str->data_blocks, guint8 *, tmp) == NULL)
893
+ return Qfalse;
894
+ str->decode_done = TRUE;
895
+ return Qtrue;
896
+ }
897
+
898
+ //
899
+ // Returns a GArray * containing all the blocks described by *chunk of length chunk_len
900
+ // in the context of *str (its block_size, n_chars_in_block etc).
901
+ //
902
+ static GArray *
903
+ oneliner_superstring_get_blocks_from_chunk(oneliner_superstring *str, guint8 *chunk, guint32 chunk_len)
904
+ {
905
+ return oneliner_superstring_build_blocks_from_data(chunk_len,
906
+ chunk_len,
907
+ str->block_size,
908
+ str->n_chars_in_block,
909
+ (chunk_len * 8) / str->block_size,
910
+ chunk);
911
+ }
912
+
913
+ //
914
+ // Return the guint8 * at either source or aux block with index block_nr in *str.
915
+ //
916
+ static guint8 *
917
+ oneliner_superstring_get_combined_block(oneliner_superstring *str, guint32 block_nr)
918
+ {
919
+ if (block_nr < str->n_data_blocks_needed)
920
+ return g_array_index(str->data_blocks, guint8 *, block_nr);
921
+ else
922
+ return g_array_index(str->aux_blocks, guint8 *, block_nr - str->n_data_blocks_needed);
923
+ }
924
+
925
+ //
926
+ // Set either the source of aux block at block_nr of *str to *block.
927
+ //
928
+ static gboolean
929
+ oneliner_superstring_set_combined_block(oneliner_superstring *str, guint32 block_nr, guint8 *block)
930
+ {
931
+ if (block_nr < str->n_data_blocks_needed && g_array_index(str->data_blocks, guint8 *, block_nr) == NULL) {
932
+ ALLOC_COPY8(g_array_index(str->data_blocks, guint8 *, block_nr), block, str->n_chars_in_block);
933
+ return TRUE;
934
+ } else if (block_nr >= str->n_data_blocks_needed && g_array_index(str->aux_blocks, guint8 *, block_nr - str->n_data_blocks_needed) == NULL) {
935
+ ALLOC_COPY8(g_array_index(str->aux_blocks, guint8 *, block_nr - str->n_data_blocks_needed), block, str->n_chars_in_block);
936
+ return TRUE;
937
+ }
938
+ return FALSE;
939
+ }
940
+
941
+ //
942
+ // Analyze if the given xor_block will be finished or empty after applying a newly found block,
943
+ // and set and queue for analysis if we found something new.
944
+ //
945
+ static void
946
+ oneliner_superstring_analyze_source_block_with_hash(gpointer key, gpointer data, gpointer user_data)
947
+ {
948
+ guint32 source_block_nr;
949
+ guint32 found_block_nr = GPOINTER_TO_UINT(data);
950
+ oneliner_xor_block *xor_block = (oneliner_xor_block *) key;
951
+ oneliner_superstring *str = (oneliner_superstring *) user_data;
952
+
953
+ INDENT;
954
+ DEBUG("found %i for _%i_ (%i)",
955
+ GPOINTER_TO_UINT(xor_block),
956
+ found_block_nr,
957
+ *oneliner_superstring_get_combined_block(str, found_block_nr));
958
+ INDENT;
959
+ IFDEBUG(oneliner_xor_block_debug(xor_block));
960
+
961
+ // Apply this new knowledge to it.
962
+ oneliner_xor_block_apply(xor_block,
963
+ found_block_nr);
964
+
965
+ DEBUG("after applying it got size %i", g_hash_table_size(xor_block->source_blocks));
966
+
967
+ //
968
+ // If that meant that it is finished then dance the dance of setting the combined block,
969
+ // removing it from the GList of check blocks for that combined block,
970
+ // and analyzing the results.
971
+ //
972
+ // Otherwise if it is empty, just free it and remove it.
973
+ //
974
+ // In fact, right after we have entered this FINISHED block, we will (since we
975
+ // call analyze on the resulting block, which leads here) check the same xor_block
976
+ // again, reach the EMPTY block, and then free the block.
977
+ //
978
+ if (XOR_BLOCK_FINISHED(xor_block)) {
979
+
980
+ // Find the source block that it defines.
981
+ source_block_nr = oneliner_xor_block_last_source_block_nr(xor_block);
982
+
983
+ DEBUG("it is finished! queueing %s %i to be freed!", XOR_TYPE(xor_block), GPOINTER_TO_UINT(xor_block));
984
+
985
+ // And free the xor block.
986
+ str->to_free = g_list_append(str->to_free, xor_block);
987
+
988
+ // And set it.
989
+ // And now analyze what the newly found source block led to.
990
+ // (Right, it can be an aux block as well, but my logic is still correct, I believe.)
991
+ if (oneliner_superstring_set_combined_block(str, source_block_nr, xor_block->sum)) {
992
+ DEBUG("_%i_ (%i) was unknown, setting and queueing %i to be analyzed! ******************",
993
+ source_block_nr,
994
+ *oneliner_superstring_get_combined_block(str, source_block_nr),
995
+ source_block_nr);
996
+ str->to_analyze = g_list_append(str->to_analyze, GUINT_TO_POINTER(source_block_nr));
997
+ }
998
+
999
+ } else if (XOR_BLOCK_EMPTY(xor_block)) {
1000
+
1001
+ DEBUG("it is empty, queueing %i to be freed!", GPOINTER_TO_UINT(xor_block));
1002
+
1003
+ // Free the check block.
1004
+ str->to_free = g_list_append(str->to_free, xor_block);
1005
+
1006
+ }
1007
+ UNINDENT;
1008
+ UNINDENT;
1009
+ }
1010
+
1011
+ //
1012
+ // Go through the to_free queue of str and free all xor_blocks that need to be freed.
1013
+ //
1014
+ static void
1015
+ oneliner_superstring_free_as_necessary(oneliner_superstring *str)
1016
+ {
1017
+ GList *list_element = str->to_free;
1018
+
1019
+ INDENT;
1020
+ DEBUG("going through the to_free queue...");
1021
+
1022
+ while (list_element != NULL) {
1023
+ oneliner_xor_block_free((oneliner_xor_block *) list_element->data, TRUE);
1024
+ str->to_free = g_list_remove_link(str->to_free, list_element);
1025
+ g_list_free_1(list_element);
1026
+ list_element = str->to_free;
1027
+ }
1028
+ UNINDENT;
1029
+ }
1030
+
1031
+ //
1032
+ // See if there exists a hash of dependent check blocks for found_block_nr in ary
1033
+ // and if it does, analyze the results of applying it to them, then free all the check
1034
+ // blocks and remove the hash.
1035
+ //
1036
+ static void
1037
+ oneliner_superstring_analyze_source_block_with_array(oneliner_superstring *str,
1038
+ GArray *ary,
1039
+ guint32 found_block_nr)
1040
+ {
1041
+ GHashTable *hash;
1042
+
1043
+ INDENT;
1044
+ DEBUG("analyze_source_block_with_array called with _%i_ (%i) and %s",
1045
+ found_block_nr,
1046
+ *oneliner_superstring_get_combined_block(str, found_block_nr),
1047
+ ary == str->check_block_graph ? "check_block_graph" : "aux_block_graph");
1048
+
1049
+ if ((hash = g_array_index(ary, GHashTable *, found_block_nr)) != NULL) {
1050
+ DEBUG("found a hash:");
1051
+ IFDEBUG(oneliner_int_hash_debug(hash));
1052
+ g_hash_table_foreach(hash, oneliner_superstring_analyze_source_block_with_hash, str);
1053
+
1054
+ oneliner_superstring_free_as_necessary(str);
1055
+
1056
+ g_hash_table_destroy(hash);
1057
+ g_array_index(ary, GHashTable *, found_block_nr) = NULL;
1058
+ }
1059
+
1060
+ UNINDENT;
1061
+ }
1062
+
1063
+ //
1064
+ // Check to see if there is an un-initialized aux block at found_block_nr,
1065
+ // and if so initialize it. If that meant that it became finished or empty
1066
+ // then set and queue for analysis if it gave any new information.
1067
+ //
1068
+ static void
1069
+ oneliner_superstring_analyze_aux_block(oneliner_superstring *str,
1070
+ guint32 found_block_nr)
1071
+ {
1072
+ oneliner_xor_block *xor_block;
1073
+ guint32 source_block_nr;
1074
+ gboolean found_new_block;
1075
+
1076
+ INDENT;
1077
+ DEBUG("analyze_aux_block called with _%i_ (%i)",
1078
+ found_block_nr,
1079
+ *oneliner_superstring_get_combined_block(str, found_block_nr));
1080
+
1081
+ if ((xor_block = g_array_index(str->known_aux_blocks,
1082
+ oneliner_xor_block *,
1083
+ found_block_nr - str->n_data_blocks_needed)) != NULL) {
1084
+
1085
+ DEBUG("found an unset aux block %i", GPOINTER_TO_UINT(xor_block));
1086
+
1087
+ oneliner_xor_block_set_sum(xor_block, oneliner_superstring_get_combined_block(str, found_block_nr), str->n_chars_in_block);
1088
+ if (XOR_BLOCK_FINISHED(xor_block)) {
1089
+ source_block_nr = oneliner_xor_block_last_source_block_nr(xor_block);
1090
+
1091
+ DEBUG("it is finished, setting the xor block to the new sum, and queueing %i to be freed!", GPOINTER_TO_UINT(xor_block));
1092
+
1093
+ g_array_index(str->known_aux_blocks,
1094
+ oneliner_xor_block *,
1095
+ found_block_nr - str->n_data_blocks_needed) = NULL;
1096
+ str->to_free = g_list_append(str->to_free, xor_block);
1097
+
1098
+ if (oneliner_superstring_set_combined_block(str, source_block_nr, xor_block->sum)) {
1099
+ DEBUG("_%i_ (%i) was unknown, setting and queueing %i to be analyzed! ******************",
1100
+ source_block_nr,
1101
+ *oneliner_superstring_get_combined_block(str, source_block_nr),
1102
+ source_block_nr);
1103
+ str->to_analyze = g_list_append(str->to_analyze, GUINT_TO_POINTER(source_block_nr));
1104
+ }
1105
+
1106
+ } else if (XOR_BLOCK_EMPTY(xor_block)) {
1107
+ DEBUG("it is empty, queueing %i to be freed", GPOINTER_TO_UINT(xor_block));
1108
+ str->to_free = g_list_append(str->to_free, xor_block);
1109
+ }
1110
+
1111
+ oneliner_superstring_free_as_necessary(str);
1112
+ }
1113
+ UNINDENT;
1114
+ }
1115
+
1116
+ //
1117
+ // Analyze the new information provided by finding found_block_nr for str.
1118
+ //
1119
+ static void
1120
+ oneliner_superstring_analyze(oneliner_superstring *str, guint32 found_block_nr)
1121
+ {
1122
+
1123
+ INDENT;
1124
+ DEBUG("analyze called with _%i_", found_block_nr);
1125
+ //
1126
+ // Analyze results for any unresolved check blocks for this found block nr.
1127
+ //
1128
+ oneliner_superstring_analyze_source_block_with_array(str,
1129
+ str->check_block_graph,
1130
+ found_block_nr);
1131
+
1132
+ if (found_block_nr < str->n_data_blocks_needed) {
1133
+ //
1134
+ // Analyze results for any unresolved aux blocks for this found block nr.
1135
+ //
1136
+ oneliner_superstring_analyze_source_block_with_array(str,
1137
+ str->aux_block_graph,
1138
+ found_block_nr);
1139
+ } else {
1140
+ //
1141
+ // Analyze results for any unresolved SOURCE blocks for this found aux block.
1142
+ //
1143
+ oneliner_superstring_analyze_aux_block(str, found_block_nr);
1144
+ }
1145
+
1146
+ UNINDENT;
1147
+ }
1148
+
1149
+ //
1150
+ // Take the chunk of length chunk_len and seed and add it to the graphs of str, and queue for analysis any
1151
+ // results we find.
1152
+ //
1153
+ static void
1154
+ oneliner_superstring_add_to_graph(oneliner_superstring *str, guint32 seed, guint8 *chunk, guint32 chunk_len)
1155
+ {
1156
+ oneliner_context *context = oneliner_context_new();
1157
+ GArray *content_blocks;
1158
+ guint32 tmp;
1159
+ guint32 tmp2;
1160
+ guint32 unknown_source_blocks;
1161
+ guint32 unknown_source_block_nr;
1162
+ GHashTable *inserts = g_hash_table_new(NULL,NULL);
1163
+ oneliner_xor_block *xor_block;
1164
+ guint32 degree;
1165
+ guint32 block_nr;
1166
+ guint8 *block_content;
1167
+
1168
+ INDENT;
1169
+ DEBUG("add_to_graph called");
1170
+
1171
+ oneliner_context_seed(context, (gint32) seed);
1172
+
1173
+ content_blocks = oneliner_superstring_get_blocks_from_chunk(str, chunk, chunk_len);
1174
+
1175
+ DEBUG("the chunk '%s' of len %i has %i check blocks", chunk, chunk_len, content_blocks->len);
1176
+
1177
+ for (tmp = 0; tmp < content_blocks->len; tmp++) {
1178
+ DEBUG("looking at block with sum %i", *g_array_index(content_blocks, guint8 *, tmp));
1179
+ degree = oneliner_context_get_degree(context);
1180
+ if (degree == 1) {
1181
+ block_nr = RANDOM(context, str->n_data_blocks_needed + str->n_aux_blocks_needed);
1182
+ DEBUG("found block _%i_ in a single block check block", block_nr);
1183
+ if (oneliner_superstring_set_combined_block(str, block_nr, g_array_index(content_blocks, guint8 *, tmp))) {
1184
+ DEBUG("_%i_ (%i) was unknown, setting it and queueing it for analyze! *********************",
1185
+ block_nr,
1186
+ *oneliner_superstring_get_combined_block(str, block_nr));
1187
+ str->to_analyze = g_list_append(str->to_analyze, GUINT_TO_POINTER(block_nr));
1188
+ }
1189
+ } else {
1190
+ unknown_source_blocks = 0;
1191
+ unknown_source_block_nr = 0;
1192
+
1193
+ xor_block = oneliner_xor_block_new(g_array_index(content_blocks, guint8 *, tmp),
1194
+ str->n_chars_in_block,
1195
+ TRUE,
1196
+ str,
1197
+ -1);
1198
+
1199
+ DEBUG("found a multi block check block with blocks:");
1200
+ for (tmp2 = 0; tmp2 < degree; tmp2++) {
1201
+ block_nr = RANDOM(context, str->n_data_blocks_needed + str->n_aux_blocks_needed);
1202
+ block_content = oneliner_superstring_get_combined_block(str, block_nr);
1203
+ if (block_content != NULL) {
1204
+ oneliner_string_xor(xor_block->sum, block_content, str->n_chars_in_block);
1205
+ DEBUG(" _%i_ IS known!!!!!!!!! (making the block sum into %i)", block_nr, *(xor_block->sum));
1206
+ } else {
1207
+ DEBUG(" _%i_ is NOT known :.(", block_nr);
1208
+ oneliner_xor_block_add(xor_block, block_nr);
1209
+ if (!g_hash_table_lookup_extended(inserts, GUINT_TO_POINTER(block_nr), NULL, NULL))
1210
+ g_hash_table_insert(inserts, GUINT_TO_POINTER(block_nr), oneliner_graph_insertion_new(xor_block, block_nr));
1211
+ unknown_source_blocks++;
1212
+ unknown_source_block_nr = block_nr;
1213
+ }
1214
+ }
1215
+
1216
+ if (XOR_BLOCK_FINISHED(xor_block)) {
1217
+ DEBUG("found block _%i_ in a multi block check block (sum = %i) block with only one missing block", unknown_source_block_nr, GPOINTER_TO_UINT(xor_block->sum));
1218
+
1219
+ if (oneliner_superstring_set_combined_block(str, unknown_source_block_nr, xor_block->sum)) {
1220
+ DEBUG("_%i_ (%i) was unknown, setting it and queueing it for analysis *****************",
1221
+ unknown_source_block_nr,
1222
+ *oneliner_superstring_get_combined_block(str, unknown_source_block_nr));
1223
+ str->to_analyze = g_list_append(str->to_analyze, GUINT_TO_POINTER(unknown_source_block_nr));
1224
+ }
1225
+
1226
+ g_hash_table_foreach_remove(inserts, oneliner_graph_insertion_free_with_check_block, NULL);
1227
+
1228
+ } else if (XOR_BLOCK_EMPTY(xor_block)) {
1229
+ DEBUG("just deleting it, it is empty...");
1230
+ g_hash_table_foreach_remove(inserts, oneliner_graph_insertion_free_with_check_block, NULL);
1231
+ } else {
1232
+ DEBUG("inserting it into the check_block_graph");
1233
+ g_hash_table_foreach_remove(inserts, oneliner_insertion_execute, str);
1234
+ }
1235
+
1236
+ }
1237
+ (str->n_seen_check_blocks)++;
1238
+ }
1239
+
1240
+ FREE_ARRAY_WITH_STRINGS(content_blocks);
1241
+ g_hash_table_destroy(inserts);
1242
+ g_free(context);
1243
+ UNINDENT;
1244
+ }
1245
+
1246
+ //
1247
+ // Build a dependency graph for the aux blocks of str.
1248
+ //
1249
+ static void
1250
+ oneliner_superstring_build_aux_graph(oneliner_superstring *str)
1251
+ {
1252
+ oneliner_context *context = oneliner_context_new();
1253
+ guint32 tmp;
1254
+ guint8 *zeroes = g_new0(guint8, str->n_chars_in_block);
1255
+ guint32 tmp2;
1256
+ guint32 aux_block_nr;
1257
+ oneliner_xor_block *aux_block;
1258
+ GHashTable *aux_blocks;
1259
+
1260
+ INDENT;
1261
+ DEBUG("build_aux_graph called");
1262
+
1263
+ oneliner_context_seed(context, (gint32) str->data_len);
1264
+ str->known_aux_blocks = g_array_new(FALSE, TRUE, sizeof(oneliner_xor_block *));
1265
+ g_array_set_size(str->known_aux_blocks, str->n_aux_blocks_needed);
1266
+ str->aux_block_graph = g_array_new(FALSE, TRUE, sizeof(GHashTable *));
1267
+ g_array_set_size(str->aux_block_graph, str->n_data_blocks_needed);
1268
+
1269
+ for (tmp = 0; tmp < str->n_data_blocks_needed; tmp++) {
1270
+ aux_blocks = g_hash_table_new(NULL, NULL);
1271
+
1272
+ for (tmp2 = 0; tmp2 < Q; tmp2++) {
1273
+ aux_block_nr = RANDOM(context, str->n_aux_blocks_needed);
1274
+
1275
+ if ((aux_block = g_array_index(str->known_aux_blocks, oneliner_xor_block *, aux_block_nr)) == NULL) {
1276
+ aux_block = oneliner_xor_block_new(zeroes, str->n_chars_in_block, FALSE, str, aux_block_nr);
1277
+ DEBUG("creating aux_block nr %i", aux_block_nr, tmp);
1278
+ g_array_index(str->known_aux_blocks, oneliner_xor_block *, aux_block_nr) = aux_block;
1279
+ }
1280
+
1281
+ DEBUG("adding _%i_ as dependency to aux_block %i", tmp, aux_block_nr);
1282
+ oneliner_xor_block_add(aux_block, tmp);
1283
+ IFDEBUG(oneliner_xor_block_debug(aux_block));
1284
+
1285
+ if (!g_hash_table_lookup_extended(aux_blocks, aux_block, NULL, NULL)) {
1286
+ g_hash_table_insert(aux_blocks, aux_block, GUINT_TO_POINTER(tmp));
1287
+ }
1288
+ }
1289
+ DEBUG("setting aux_block_graph for _%i_", tmp);
1290
+ IFDEBUG(oneliner_int_hash_debug(aux_blocks));
1291
+ g_array_index(str->aux_block_graph, GHashTable *, tmp) = aux_blocks;
1292
+ }
1293
+
1294
+ UNINDENT;
1295
+ g_free(zeroes);
1296
+ g_free(context);
1297
+ }
1298
+
1299
+ //
1300
+ // Go through the queue of results to analyze of str until it is empty (note that it can be appended
1301
+ // while analyzing...)
1302
+ //
1303
+ static void
1304
+ oneliner_superstring_analyze_until_done(oneliner_superstring *str)
1305
+ {
1306
+ GList *list_element = str->to_analyze;
1307
+
1308
+ INDENT;
1309
+ DEBUG("going through the to_analyze queue...");
1310
+
1311
+ while (list_element != NULL) {
1312
+ oneliner_superstring_analyze(str, GPOINTER_TO_UINT(list_element->data));
1313
+ str->to_analyze = g_list_remove_link(str->to_analyze, list_element);
1314
+ g_list_free_1(list_element);
1315
+ list_element = str->to_analyze;
1316
+ }
1317
+
1318
+ UNINDENT;
1319
+ }
1320
+
1321
+ //
1322
+ // Decode chunk into self, and return whether self seems to be finished decoding.
1323
+ //
1324
+ static VALUE
1325
+ rb_oneliner_superstring_decode(VALUE self, VALUE chunk)
1326
+ {
1327
+ RB_ONELINER(self, str);
1328
+ guint32 data_len;
1329
+ guint32 seed;
1330
+ guint32 blocks_needed;
1331
+
1332
+ INDENT;
1333
+
1334
+ Check_Type(chunk, T_STRING);
1335
+
1336
+ DEBUG("decode called for string %i with chunk '%s'", GPOINTER_TO_UINT(str), RSTRING(chunk)->ptr);
1337
+
1338
+ if (RSTRING(chunk)->len < 8)
1339
+ rb_raise(rb_eRuntimeError, "Oneliner::SuperString#decode(String chunk) needs an argument that is at least 8 characters long.");
1340
+
1341
+ data_len = ((guint32 *) RSTRING(chunk)->ptr)[0];
1342
+ seed = ((guint32 *) RSTRING(chunk)->ptr)[1];
1343
+
1344
+ DEBUG("data_len=%i, seed=%i", data_len, seed);
1345
+
1346
+ if (str->data_len == 0) {
1347
+ str->data_len = data_len;
1348
+ } else if (str->data_len != data_len) {
1349
+ rb_raise(rb_eRuntimeError, "Oneliner::Superstring#decode(String chunk) needs all consecutive calls to be with arguments having the same first four characters. Didn't you use chunks produced using #encode calls from the same instance?");
1350
+ }
1351
+
1352
+ if (str->data_blocks == NULL) {
1353
+ DEBUG("first decode, setting up...");
1354
+ oneliner_superstring_initialize(str, data_len);
1355
+ str->data_blocks = g_array_new(FALSE, TRUE, sizeof(guint8 *));
1356
+ g_array_set_size(str->data_blocks, str->n_data_blocks_needed);
1357
+ str->aux_blocks = g_array_new(FALSE, TRUE, sizeof(guint8 *));
1358
+ g_array_set_size(str->aux_blocks, str->n_aux_blocks_needed);
1359
+ str->check_block_graph = g_array_new(FALSE, TRUE, sizeof(GHashTable *));
1360
+ g_array_set_size(str->check_block_graph, str->n_data_blocks_needed + str->n_aux_blocks_needed);
1361
+ oneliner_superstring_build_aux_graph(str);
1362
+ }
1363
+
1364
+ oneliner_superstring_add_to_graph(str, seed, (guint8 *) RSTRING(chunk)->ptr + 8, (guint32) RSTRING(chunk)->len - 8);
1365
+
1366
+ oneliner_superstring_analyze_until_done(str);
1367
+ UNINDENT;
1368
+ return oneliner_superstring_decode_done(str);
1369
+ }
1370
+
1371
+ //
1372
+ // Produce and return a new check block for str using the given context.
1373
+ //
1374
+ static guint8 *
1375
+ oneliner_superstring_get_check_block(oneliner_superstring *str, oneliner_context *context)
1376
+ {
1377
+ guint32 degree = oneliner_context_get_degree(context);
1378
+ guint32 block_nr = RANDOM(context, str->n_data_blocks_needed + str->n_aux_blocks_needed);
1379
+ guint8 *rval;
1380
+ guint32 tmp;
1381
+
1382
+ ALLOC_COPY8(rval, oneliner_superstring_get_combined_block(str, block_nr), str->n_chars_in_block);
1383
+ DEBUG("creating check block with degree %3i and data_blocks _%i_: %i", degree, block_nr, *oneliner_superstring_get_combined_block(str, block_nr));
1384
+ for (tmp = 2; tmp <= degree; tmp++) {
1385
+ block_nr = RANDOM(context, str->n_data_blocks_needed + str->n_aux_blocks_needed);
1386
+ DEBUG(" _%i_: %i", block_nr, *oneliner_superstring_get_combined_block(str, block_nr));
1387
+ oneliner_string_xor(rval,
1388
+ oneliner_superstring_get_combined_block(str, block_nr),
1389
+ str->n_chars_in_block);
1390
+ }
1391
+ DEBUG(" sum: %i ^^^^^", *rval);
1392
+ return rval;
1393
+ }
1394
+
1395
+ //
1396
+ // Encode a new chunk of check blocks for str and put it in an array pointer to by buffer of length len.
1397
+ //
1398
+ static guint32
1399
+ oneliner_superstring_encode(oneliner_superstring *str, guint8 **buffer, guint32 len)
1400
+ {
1401
+ oneliner_context *context = oneliner_context_new();
1402
+ guint8 prepend[8];
1403
+ gint32 tmp;
1404
+ guint32 needed_blocks = ((len - 8) * 8) / str->block_size;
1405
+ GArray *rval_ary = g_array_new(FALSE, FALSE, sizeof(guint8 *));
1406
+ guint32 resulting_size;
1407
+ struct timeval tv;
1408
+
1409
+ DEBUG("encode called for string %i with len %i (with block size %i that means %i blocks)",
1410
+ GPOINTER_TO_UINT(str),
1411
+ len,
1412
+ str->block_size,
1413
+ needed_blocks);
1414
+
1415
+ g_array_set_size(rval_ary, needed_blocks);
1416
+
1417
+ gettimeofday(&tv, NULL);
1418
+ srand(tv.tv_usec);
1419
+ tmp = (gint32) rand();
1420
+ oneliner_context_seed(context, tmp);
1421
+
1422
+ ( (guint32 *) prepend )[0] = str->data_len;
1423
+ ( (guint32 *) prepend )[1] = (guint32) tmp;
1424
+
1425
+ for (tmp = 0; tmp < needed_blocks; tmp++) {
1426
+ guint8 *check_block = oneliner_superstring_get_check_block(str, context);
1427
+ g_array_index(rval_ary, guint8 *, tmp) = check_block;
1428
+ }
1429
+
1430
+ // Here I let resulting_size describe the number of bytes needed to contain the rval_ary considering the block
1431
+ // size. And since we cant really truncate it at the end at this stage (like we can in the final build_data_from_blocks)
1432
+ // the data_len argument is the same as the n_bytes_for_blocks argument. Also, we want to prepend the original data-len
1433
+ // and seed, so we use the prepend argument.
1434
+ resulting_size = (guint32) ceil((needed_blocks * str->block_size) / 8) + 8;
1435
+ *buffer = oneliner_superstring_build_data_from_blocks(resulting_size,
1436
+ resulting_size,
1437
+ str->block_size,
1438
+ rval_ary,
1439
+ 8,
1440
+ prepend);
1441
+ g_free(context);
1442
+ FREE_ARRAY_WITH_STRINGS(rval_ary);
1443
+
1444
+ return resulting_size;
1445
+ }
1446
+
1447
+ //
1448
+ // Add the given xor_block to the given hash. Used as an iterator to clear up other hashes.
1449
+ //
1450
+ static void
1451
+ oneliner_superstring_add_xor_to_hash(gpointer key, gpointer data, gpointer user_data)
1452
+ {
1453
+ if (!g_hash_table_lookup_extended((GHashTable *) user_data, key, NULL, NULL)) {
1454
+ DEBUG("queueing xor_block %i to be freed", GPOINTER_TO_UINT(key));
1455
+ g_hash_table_insert((GHashTable *) user_data, key, NULL);
1456
+ }
1457
+ }
1458
+
1459
+ //
1460
+ // Go through the given ary and put all xor_blocks in its contained hahes into the
1461
+ // hashtable xors, then destroy the hashtables.
1462
+ //
1463
+ static void
1464
+ oneliner_superstring_free_ary_of_hashes_of_xor_blocks(GArray *ary, GHashTable *xors)
1465
+ {
1466
+ guint32 tmp;
1467
+ for (tmp = 0; tmp < ary->len; tmp++) {
1468
+ GHashTable *hash;
1469
+ if ((hash = (GHashTable *) g_array_index(ary, GHashTable *, tmp)) != NULL) {
1470
+ DEBUG("removing xor blocks belonging to _%i_", tmp);
1471
+ g_hash_table_foreach(hash, oneliner_superstring_add_xor_to_hash, xors);
1472
+ g_hash_table_destroy(hash);
1473
+ }
1474
+ }
1475
+ }
1476
+
1477
+ //
1478
+ // Free the given xor_block. Used to iterator over the hashtable containing all remaining
1479
+ // xor_blocks in an onliner_superstring when it is being freed.
1480
+ //
1481
+ static void
1482
+ oneliner_superstring_free_xors_killer(gpointer data, gpointer tmp1, gpointer tmp2)
1483
+ {
1484
+ oneliner_xor_block_free((oneliner_xor_block *) data, FALSE);
1485
+ }
1486
+
1487
+ //
1488
+ // Free the given oneliner_superstring by freeing its blocks and aux_blocks,
1489
+ // then go through the graphs and empty and free them, then free the xor_blocks that
1490
+ // existed in the graphs.
1491
+ //
1492
+ static void
1493
+ oneliner_superstring_free(oneliner_superstring *str)
1494
+ {
1495
+ GHashTable *xors = g_hash_table_new(NULL, NULL);
1496
+ guint32 tmp;
1497
+
1498
+ DEBUG("freeing string %i", GPOINTER_TO_UINT(str));
1499
+
1500
+ if (str->data_blocks != NULL) {
1501
+ FREE_ARRAY_WITH_STRINGS(str->data_blocks);
1502
+ }
1503
+ if (str->aux_blocks != NULL) {
1504
+ FREE_ARRAY_WITH_STRINGS(str->aux_blocks);
1505
+ }
1506
+
1507
+ if (str->check_block_graph != NULL) {
1508
+ INDENT;
1509
+ DEBUG("freeing check_block_graph");
1510
+ oneliner_superstring_free_ary_of_hashes_of_xor_blocks(str->check_block_graph, xors);
1511
+ g_array_free(str->check_block_graph, TRUE);
1512
+ str->check_block_graph = NULL;
1513
+ UNINDENT;
1514
+ }
1515
+ if (str->known_aux_blocks != NULL) {
1516
+ INDENT;
1517
+ DEBUG("freeing known_aux_blocks");
1518
+ for (tmp = 0; tmp < str->n_aux_blocks_needed; tmp++) {
1519
+ gpointer xor_block;
1520
+ if ((xor_block = g_array_index(str->known_aux_blocks, oneliner_xor_block *, tmp)) != NULL) {
1521
+ DEBUG("queueing aux block at _%i_ to be freed", tmp);
1522
+ g_hash_table_insert(xors, xor_block, NULL);
1523
+ }
1524
+ }
1525
+ g_array_free(str->known_aux_blocks, TRUE);
1526
+ str->known_aux_blocks = NULL;
1527
+ UNINDENT;
1528
+ }
1529
+ if (str->aux_block_graph != NULL) {
1530
+ INDENT;
1531
+ DEBUG("freeing aux_block_graph");
1532
+ oneliner_superstring_free_ary_of_hashes_of_xor_blocks(str->aux_block_graph, xors);
1533
+ g_array_free(str->aux_block_graph, TRUE);
1534
+ str->aux_block_graph = NULL;
1535
+ UNINDENT;
1536
+ }
1537
+
1538
+ DEBUG("freeing all xor blocks");
1539
+ g_hash_table_foreach(xors, oneliner_superstring_free_xors_killer, NULL);
1540
+ g_hash_table_destroy(xors);
1541
+ g_free(str);
1542
+ }
1543
+
1544
+ //
1545
+ // Create a new Oneliner::SuperString by allocating a new oneliner_superstring.
1546
+ //
1547
+ static VALUE
1548
+ rb_oneliner_superstring_alloc(VALUE klass)
1549
+ {
1550
+ oneliner_superstring *str = g_new0(oneliner_superstring, 1);
1551
+ return Data_Wrap_Struct(klass, NULL, oneliner_superstring_free, str);
1552
+ }
1553
+
1554
+ //
1555
+ // Create a ruby Array filled with all the char arrays of length len within container.
1556
+ //
1557
+ static VALUE
1558
+ oneliner_superstring_get_blocks(GArray *container, guint32 len)
1559
+ {
1560
+ guint32 tmp;
1561
+ VALUE rval = rb_ary_new();
1562
+ if (container == NULL)
1563
+ return rb_ary_new();
1564
+ for (tmp = 0; tmp < container->len; tmp++) {
1565
+ guint8 *tmp_block = g_array_index(container, guint8 *, tmp);
1566
+ if (tmp_block == NULL)
1567
+ rb_ary_push(rval, Qnil);
1568
+ else
1569
+ rb_ary_push(rval, rb_str_new((gchar *) tmp_block, len));
1570
+ }
1571
+ return rval;
1572
+ }
1573
+
1574
+ //
1575
+ // Return the data blocks of self.
1576
+ //
1577
+ static VALUE
1578
+ rb_oneliner_superstring_data_blocks(VALUE self)
1579
+ {
1580
+ RB_ONELINER(self, str);
1581
+ return oneliner_superstring_get_blocks(str->data_blocks, str->n_chars_in_block);
1582
+ }
1583
+
1584
+ //
1585
+ // Return the aux blocks of self.
1586
+ //
1587
+ static VALUE
1588
+ rb_oneliner_superstring_aux_blocks(VALUE self)
1589
+ {
1590
+ RB_ONELINER(self, str);
1591
+ return oneliner_superstring_get_blocks(str->aux_blocks, str->n_chars_in_block);
1592
+ }
1593
+
1594
+ //
1595
+ // Put together the data blocks of self and return the resulting String.
1596
+ //
1597
+ static VALUE
1598
+ rb_oneliner_superstring_to_s(VALUE self)
1599
+ {
1600
+ RB_ONELINER(self, str);
1601
+ VALUE rval;
1602
+ guint8 *tmp_data;
1603
+
1604
+ if (oneliner_superstring_decode_done(str) == Qfalse)
1605
+ return rb_str_new2("");
1606
+ tmp_data = oneliner_superstring_build_data(str);
1607
+ rval = rb_str_new((gchar *) tmp_data, str->data_len);
1608
+ g_free(tmp_data);
1609
+ return rval;
1610
+ }
1611
+
1612
+ //
1613
+ // Returns the block size of self.
1614
+ //
1615
+ static VALUE
1616
+ rb_oneliner_superstring_block_size(VALUE self)
1617
+ {
1618
+ RB_ONELINER(self, str);
1619
+ return INT2NUM(str->block_size);
1620
+ }
1621
+
1622
+ //
1623
+ // Returns whether self is done decoding.
1624
+ //
1625
+ static VALUE
1626
+ rb_oneliner_superstring_decode_done(VALUE self)
1627
+ {
1628
+ RB_ONELINER(self, str);
1629
+ return oneliner_superstring_decode_done(str);
1630
+ }
1631
+
1632
+ //
1633
+ // Return a newly created chunk of check blocks of size from self.
1634
+ //
1635
+ static VALUE
1636
+ rb_oneliner_superstring_encode(VALUE self, VALUE size)
1637
+ {
1638
+ RB_ONELINER(self, str);
1639
+ guint8 *chunk;
1640
+ VALUE rval;
1641
+ guint32 chunk_size;
1642
+
1643
+ Check_Type(size, T_FIXNUM);
1644
+ if (NUM2INT(size) < 8)
1645
+ rb_raise(rb_eRuntimeError, "Oneliner::SuperString#encode(Fixnum size) needs a size of at least 8.");
1646
+
1647
+ if (!str->decode_done)
1648
+ rb_raise(rb_eRuntimeError, "Oneliner::SuperString#encode(Fixnum size) can only be called on strings that are done decoding themselves.");
1649
+
1650
+ chunk_size = oneliner_superstring_encode(str, &chunk, NUM2INT(size));
1651
+
1652
+ rval = rb_str_new((gchar *) chunk, chunk_size);
1653
+
1654
+ g_free(chunk);
1655
+
1656
+ return rval;
1657
+ }
1658
+
1659
+ //
1660
+ // Returns the number of chars self would contain if you did to_s.
1661
+ //
1662
+ static VALUE
1663
+ rb_oneliner_superstring_size(VALUE self)
1664
+ {
1665
+ RB_ONELINER(self, str);
1666
+ return INT2NUM(str->data_len);
1667
+ }
1668
+
1669
+ //
1670
+ // Returns the number of check blocks processed by self.
1671
+ //
1672
+ static VALUE
1673
+ rb_oneliner_superstring_seen_check_blocks(VALUE self)
1674
+ {
1675
+ RB_ONELINER(self, str);
1676
+ return INT2NUM(str->n_seen_check_blocks);
1677
+ }
1678
+
1679
+ //
1680
+ // Iterator used to find out how many check blocks we have in our graphs.
1681
+ //
1682
+ static void
1683
+ rb_oneliner_superstring_known_check_blocks_iterator(gpointer key, gpointer data, gpointer user_data)
1684
+ {
1685
+ g_hash_table_insert((GHashTable *) user_data, key, NULL);
1686
+ }
1687
+
1688
+ //
1689
+ // Returns the number of check blocks in our graphs.
1690
+ //
1691
+ static VALUE
1692
+ rb_oneliner_superstring_known_check_blocks(VALUE self)
1693
+ {
1694
+ RB_ONELINER(self, str);
1695
+ guint32 tmp;
1696
+ guint32 tmp2;
1697
+ GHashTable *sum = g_hash_table_new(NULL, NULL);
1698
+ VALUE rval;
1699
+
1700
+ if (str->data_blocks == NULL)
1701
+ return INT2NUM(0);
1702
+ for (tmp = 0; tmp < str->n_data_blocks_needed + str->n_aux_blocks_needed; tmp++)
1703
+ if (g_array_index(str->check_block_graph, GHashTable *, tmp) != NULL)
1704
+ g_hash_table_foreach((GHashTable *) g_array_index(str->check_block_graph, GHashTable *, tmp),
1705
+ rb_oneliner_superstring_known_check_blocks_iterator,
1706
+ sum);
1707
+ rval = INT2NUM(g_hash_table_size(sum));
1708
+ g_hash_table_destroy(sum);
1709
+ return rval;
1710
+ }
1711
+
1712
+ //
1713
+ // Create a Onliner::Chunk to inspect a chunk of check blocks.
1714
+ //
1715
+ static VALUE
1716
+ rb_oneliner_chunk_initialize(VALUE self, VALUE chunk)
1717
+ {
1718
+ VALUE blocks_ary;
1719
+ GArray *blocks;
1720
+ guint32 data_len;
1721
+ guint32 seed;
1722
+ oneliner_superstring str;
1723
+
1724
+ data_len = ((guint32 *) RSTRING(chunk)->ptr)[0];
1725
+ seed = ((guint32 *) RSTRING(chunk)->ptr)[1];
1726
+
1727
+ Check_Type(chunk, T_STRING);
1728
+
1729
+ if (RSTRING(chunk)->len < 8)
1730
+ rb_raise(rb_eRuntimeError, "Oneliner::Chunk.new(String chunk) needs an argument that is at least 8 characters long.");
1731
+
1732
+ oneliner_superstring_initialize(&str, data_len);
1733
+
1734
+ blocks = oneliner_superstring_get_blocks_from_chunk(&str,
1735
+ (guint8 *) RSTRING(chunk)->ptr + 8,
1736
+ (guint32) RSTRING(chunk)->len - 8);
1737
+
1738
+ rb_ivar_set(self, rb_intern("@blocks"), oneliner_superstring_get_blocks(blocks,
1739
+ str.n_chars_in_block));
1740
+ rb_ivar_set(self, rb_intern("@seed"), INT2NUM(seed));
1741
+ rb_ivar_set(self, rb_intern("@data_len"), INT2NUM(data_len));
1742
+
1743
+ FREE_ARRAY_WITH_STRINGS(blocks);
1744
+
1745
+ return self;
1746
+ }
1747
+
1748
+ #ifdef __cplusplus
1749
+ extern "C" {
1750
+ #endif
1751
+ void Init_oneliner() {
1752
+ VALUE rb_string = rb_define_class("String", rb_cObject);
1753
+ rb_define_method(rb_string, "xor!", rb_oneliner_string_xor, 1);
1754
+
1755
+ VALUE rb_oneliner = rb_define_module("Oneliner");
1756
+ rb_superstring = rb_define_class_under(rb_oneliner,
1757
+ "SuperString",
1758
+ rb_cObject);
1759
+ rb_define_alloc_func(rb_superstring, rb_oneliner_superstring_alloc);
1760
+ rb_define_method(rb_superstring, "initialize", rb_oneliner_superstring_initialize, -1);
1761
+ rb_define_method(rb_superstring, "data_blocks", rb_oneliner_superstring_data_blocks, 0);
1762
+ rb_define_method(rb_superstring, "aux_blocks", rb_oneliner_superstring_aux_blocks, 0);
1763
+ rb_define_method(rb_superstring, "to_s", rb_oneliner_superstring_to_s, 0);
1764
+ rb_define_method(rb_superstring, "encode", rb_oneliner_superstring_encode, 1);
1765
+ rb_define_method(rb_superstring, "block_size", rb_oneliner_superstring_block_size, 0);
1766
+ rb_define_method(rb_superstring, "decode!", rb_oneliner_superstring_decode, 1);
1767
+ rb_define_method(rb_superstring, "done?", rb_oneliner_superstring_decode_done, 0);
1768
+ rb_define_method(rb_superstring, "size", rb_oneliner_superstring_size, 0);
1769
+ rb_define_method(rb_superstring, "seen_check_blocks", rb_oneliner_superstring_seen_check_blocks, 0);
1770
+ rb_define_method(rb_superstring, "known_check_blocks", rb_oneliner_superstring_known_check_blocks, 0);
1771
+
1772
+ rb_chunk = rb_define_class_under(rb_oneliner,
1773
+ "Chunk",
1774
+ rb_cObject);
1775
+ rb_define_method(rb_chunk, "initialize", rb_oneliner_chunk_initialize, 1);
1776
+ }
1777
+ #ifdef __cplusplus
1778
+ }
1779
+ #endif
1780
+