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 +4 -6
- data/ext/extconf.rb +24 -2
- data/ext/oneliner.c +1780 -0
- data/ext/oneliner.h +4 -0
- data/tests/string_test.rb +19 -0
- data/tests/superstring_benchmark.rb +7 -5
- data/tests/superstring_test.rb +41 -32
- metadata +8 -7
- data/ext/oneliner_ext.c +0 -289
- data/lib/oneliner.rb +0 -21
- data/lib/oneliner/superstring.rb +0 -385
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
|
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
|
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
|
-
|
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)
|
data/ext/extconf.rb
CHANGED
@@ -28,6 +28,28 @@ unless have_library('ssl')
|
|
28
28
|
crash "libssl needed"
|
29
29
|
end
|
30
30
|
|
31
|
-
|
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("
|
55
|
+
create_makefile("oneliner")
|
data/ext/oneliner.c
ADDED
@@ -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
|
+
|