oneliner 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+