node-marshal 0.2.1 → 0.2.2
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.
- checksums.yaml +4 -4
- data/COPYING +23 -23
- data/README.rdoc +56 -49
- data/bin/noderbc +63 -63
- data/bin/noderbc.bat +0 -0
- data/ext/node-marshal/base85r.c +194 -194
- data/ext/node-marshal/extconf.rb +2 -2
- data/ext/node-marshal/node193.h +321 -321
- data/ext/node-marshal/node220.h +347 -347
- data/ext/node-marshal/node230.h +361 -361
- data/ext/node-marshal/nodedump.c +2356 -2296
- data/ext/node-marshal/nodedump.h +60 -60
- data/ext/node-marshal/nodeinfo.c +619 -615
- data/lib/node-marshal.rb +227 -227
- data/test/lifegame.rb +145 -145
- data/test/test_base.rb +226 -226
- data/test/test_complex.rb +136 -136
- data/test/test_lifegame.rb +68 -68
- data/test/test_namedarg.rb +54 -0
- data/test/test_obfuscator.rb +36 -36
- data/test/test_qcall.rb +52 -52
- data/test/tinytet.rb +79 -79
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f8bf24b07c3adc1df9e6f12ab148f7428fee811
|
4
|
+
data.tar.gz: fa00130d6340112f071c2fc677f3684ecef18447
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64357a5c91bc8fc0f523683c1a8d83a83d7b112bff8d46bbecf0446249b94c03e9243361e11d5926ed772ca7599a6ae465a88cbd8d59ffd641a858ef3fea786d
|
7
|
+
data.tar.gz: 1707659e29f48e9ad3996ded44fcac8e622edaa7a6bed3d48f05a5e5231e6c6647a6fe6bea2705973289032d8a5a783c562db0fb77f7178f2746fa1a09e26b37
|
data/COPYING
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
Copyright (C) 2015-
|
2
|
-
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
|
3
|
-
|
4
|
-
Redistribution and use in source and binary forms, with or without
|
5
|
-
modification, are permitted provided that the following conditions
|
6
|
-
are met:
|
7
|
-
1. Redistributions of source code must retain the above copyright
|
8
|
-
notice, this list of conditions and the following disclaimer.
|
9
|
-
2. Redistributions in binary form must reproduce the above copyright
|
10
|
-
notice, this list of conditions and the following disclaimer in the
|
11
|
-
documentation and/or other materials provided with the distribution.
|
12
|
-
|
13
|
-
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
14
|
-
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
15
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
16
|
-
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
17
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
18
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
19
|
-
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
20
|
-
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
21
|
-
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
22
|
-
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
23
|
-
SUCH DAMAGE.
|
1
|
+
Copyright (C) 2015-2017 Alexey Voskov. All rights reserved.
|
2
|
+
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions
|
6
|
+
are met:
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
15
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
16
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
17
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
18
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
19
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
20
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
21
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
22
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
23
|
+
SUCH DAMAGE.
|
data/README.rdoc
CHANGED
@@ -1,49 +1,56 @@
|
|
1
|
-
== node-marshal
|
2
|
-
|
3
|
-
This gem is designed for transformation of Ruby source code (eiher in the form of files or strings) to the
|
4
|
-
Ruby nodes (syntax trees) used by Ruby MRI internals. Obtained nodes can be serialized to the platform-dependent
|
5
|
-
binary or ASCII strings and restored and launched from serialized format. Such kind of transformation is
|
6
|
-
irreversible and can be used for source code protection; the similar principle is used by RubyEncoder commercial
|
7
|
-
software. It also contains some subroutines that can be useful for code obfuscation by renaming symbols
|
8
|
-
inside the program.
|
9
|
-
|
10
|
-
The key features of node-marshal gem:
|
11
|
-
- Irreversible conversion of a source code to the Ruby node (abstract syntax tree)
|
12
|
-
- Ruby 1.9.3, 2.2.x and 2.3.x support (only MRI)
|
13
|
-
- Active usage of Ruby internals (mainly AST) and Ruby standard library (Marshal and Zlib)
|
14
|
-
- Set of tests for easy addition of new Ruby versions
|
15
|
-
- Result of compilation depends on Ruby version and used platform (x86, x64 etc.)
|
16
|
-
- Subroutines for obfuscation
|
17
|
-
- 2-clause BSD license suitable for creation of custom source code protection system
|
18
|
-
|
19
|
-
Changelog:
|
20
|
-
-
|
21
|
-
- Bugfix:
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
-
|
32
|
-
|
33
|
-
|
34
|
-
- Bugfix:
|
35
|
-
|
36
|
-
-
|
37
|
-
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
-
|
42
|
-
|
43
|
-
-
|
44
|
-
-
|
45
|
-
|
46
|
-
-
|
47
|
-
|
48
|
-
-
|
49
|
-
-
|
1
|
+
== node-marshal
|
2
|
+
|
3
|
+
This gem is designed for transformation of Ruby source code (eiher in the form of files or strings) to the
|
4
|
+
Ruby nodes (syntax trees) used by Ruby MRI internals. Obtained nodes can be serialized to the platform-dependent
|
5
|
+
binary or ASCII strings and restored and launched from serialized format. Such kind of transformation is
|
6
|
+
irreversible and can be used for source code protection; the similar principle is used by RubyEncoder commercial
|
7
|
+
software. It also contains some subroutines that can be useful for code obfuscation by renaming symbols
|
8
|
+
inside the program.
|
9
|
+
|
10
|
+
The key features of node-marshal gem:
|
11
|
+
- Irreversible conversion of a source code to the Ruby node (abstract syntax tree)
|
12
|
+
- Ruby 1.9.3, 2.2.x and 2.3.x support (only MRI)
|
13
|
+
- Active usage of Ruby internals (mainly AST) and Ruby standard library (Marshal and Zlib)
|
14
|
+
- Set of tests for easy addition of new Ruby versions
|
15
|
+
- Result of compilation depends on Ruby version and used platform (x86, x64 etc.)
|
16
|
+
- Subroutines for obfuscation
|
17
|
+
- 2-clause BSD license suitable for creation of custom source code protection system
|
18
|
+
|
19
|
+
Changelog:
|
20
|
+
- 01.MAY.2017 - 0.2.2
|
21
|
+
- Bugfix: NODE_KW_ARG processing implementation. Allows to use keyword (named) arguments
|
22
|
+
in Ruby 2.x. (thanks to Jarosław Salik for bugreport).
|
23
|
+
- Bugfix: NODE_LASGN and NODE_DASGN_CURR 2nd child special cases (-1 value). Mainly for
|
24
|
+
their usage inside NODE_KW_ARG child trees.
|
25
|
+
- test_namedarg.rb test was added (for so called keyword argument)
|
26
|
+
- Improved output of dump_tree_short (rb_args_info dump)
|
27
|
+
- 16.MAR.2016 - 0.2.1
|
28
|
+
- Bugfix: garbage collection of symbols kept in the literals table of the node dump
|
29
|
+
is now prohibited. Such GC caused hardly reproducible bugs after code loading.
|
30
|
+
(thanks to Gregory Siehień for suitable examples)
|
31
|
+
- Bugfix: improved parsing of NODE_ARRAY (correct processing of two cases of
|
32
|
+
not documented pointers (instead of longs) in 2nd child. It affects arrays,
|
33
|
+
NODE_HASH (hashes) and NODE_DSTR (strings in double quotes with #{} inside) ).
|
34
|
+
- Bugfix: now NodeMarshal class methods don't change the state of Ruby
|
35
|
+
garbage collector
|
36
|
+
- Improved NodeMarshal#dump_tree_short output (addresses of nodes are shown)
|
37
|
+
- Added NodeMarshal#to_h method (alias for NodeMarshal#to_hash)
|
38
|
+
- NodeMarshal#to_a and NodeMarshal#to_ary methods added (they show extended information
|
39
|
+
about Ruby AST including rb_args_info and ID *tbl internals)
|
40
|
+
- 11.JAN.2016 - 0.2.0
|
41
|
+
- Bugfix: || and && in NODE_OP_ASGN1 (e.g. in x['a'] ||= 'b' or x['b'] &&= false)
|
42
|
+
(this bug caused segfaults in some cases)
|
43
|
+
- Bugfix: NodeMarshal#dump_node_short
|
44
|
+
- Format version changed to NODEMARSHAL11 (support of symbols not representable in
|
45
|
+
the form of String is added)
|
46
|
+
- show_offsets property for controlling verbosity of NodeMarshal#dump_node_short output
|
47
|
+
- Improved information about licenses
|
48
|
+
- Improved rdoc documentation
|
49
|
+
- 24.DEC.2015 - 0.1.2
|
50
|
+
- Ruby 2.3.x preliminary support (including &. safe navigation operator)
|
51
|
+
- Bugfix: NODE_MATCH3 (a =~ /abc/) issue (reported by Gregory Siehień)
|
52
|
+
- Bugfix: NODE_BLOCK_PASS (short map syntax) issue (reported by Gregory Siehień)
|
53
|
+
- Bugfix: failure in the case of syntax errors in the input data (now ArgumentError
|
54
|
+
exception will be generated instead of it)
|
55
|
+
- Ability of symbols renaming (for hiding variables and constants name during code obfuscation)
|
56
|
+
- 04.MAY.2015 - 0.1.1 - first public version
|
data/bin/noderbc
CHANGED
@@ -1,63 +1,63 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
require_relative '../lib/node-marshal.rb'
|
3
|
-
|
4
|
-
help = <<-EOS
|
5
|
-
Ruby source files compiler from node-marshal gem. It is based
|
6
|
-
on NodeMarshal class. Source code is irreversibly transformed to the
|
7
|
-
Ruby node (syntax tree) serialized into ASCII string. It can be used
|
8
|
-
for code obfuscation and Ruby internals exploration.
|
9
|
-
|
10
|
-
(C) 2015-2016 Alexey Voskov. License: BSD-2-Clause.
|
11
|
-
|
12
|
-
Usage:
|
13
|
-
noderbc inpfile outfile [options]
|
14
|
-
|
15
|
-
Required arguments:
|
16
|
-
inpfile -- Name of input Ruby script (with extension)
|
17
|
-
outfile -- Name of output Ruby (with extension)
|
18
|
-
|
19
|
-
Options:
|
20
|
-
--compress=none -- No ZLib compression of the source
|
21
|
-
--compress=zlib -- Use ZLib compression of the source (default)
|
22
|
-
--so_path="str" -- String for inclusion of the node-marshal loader
|
23
|
-
Its default value is:
|
24
|
-
require_relative '../ext/node-marshal/nodemarshal.so'
|
25
|
-
|
26
|
-
EOS
|
27
|
-
|
28
|
-
if ARGV.length < 2
|
29
|
-
# No required number of input arguments: show short help
|
30
|
-
puts help
|
31
|
-
else
|
32
|
-
# Optional arguments processing
|
33
|
-
opts = {}
|
34
|
-
if ARGV.length > 2
|
35
|
-
ARGV[2..-1].each do |arg|
|
36
|
-
case arg
|
37
|
-
when '--compress=none'
|
38
|
-
opts[:compress] = false
|
39
|
-
when '--compress=zlib'
|
40
|
-
opts[:compress] = true
|
41
|
-
when /^--so_path=.+$/
|
42
|
-
str = arg[10..-1]
|
43
|
-
opts[:so_path] = str
|
44
|
-
else
|
45
|
-
puts "Unknown argument #{arg}"
|
46
|
-
exit
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
# Show given options
|
51
|
-
puts "Used options:"
|
52
|
-
if opts.size == 0
|
53
|
-
puts " default"
|
54
|
-
else
|
55
|
-
puts " compress: #{opts[:compress]}" if opts.has_key?(:compress)
|
56
|
-
puts " so_path: #{opts[:so_path]}" if opts.has_key?(:so_path)
|
57
|
-
end
|
58
|
-
# Required arguments processing
|
59
|
-
inpfile = ARGV[0]
|
60
|
-
outfile = ARGV[1]
|
61
|
-
raise 'inpfile and outfile cannot be equal' if inpfile == outfile
|
62
|
-
NodeMarshal.compile_rb_file(outfile, inpfile, opts)
|
63
|
-
end
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require_relative '../lib/node-marshal.rb'
|
3
|
+
|
4
|
+
help = <<-EOS
|
5
|
+
Ruby source files compiler from node-marshal gem. It is based
|
6
|
+
on NodeMarshal class. Source code is irreversibly transformed to the
|
7
|
+
Ruby node (syntax tree) serialized into ASCII string. It can be used
|
8
|
+
for code obfuscation and Ruby internals exploration.
|
9
|
+
|
10
|
+
(C) 2015-2016 Alexey Voskov. License: BSD-2-Clause.
|
11
|
+
|
12
|
+
Usage:
|
13
|
+
noderbc inpfile outfile [options]
|
14
|
+
|
15
|
+
Required arguments:
|
16
|
+
inpfile -- Name of input Ruby script (with extension)
|
17
|
+
outfile -- Name of output Ruby (with extension)
|
18
|
+
|
19
|
+
Options:
|
20
|
+
--compress=none -- No ZLib compression of the source
|
21
|
+
--compress=zlib -- Use ZLib compression of the source (default)
|
22
|
+
--so_path="str" -- String for inclusion of the node-marshal loader
|
23
|
+
Its default value is:
|
24
|
+
require_relative '../ext/node-marshal/nodemarshal.so'
|
25
|
+
|
26
|
+
EOS
|
27
|
+
|
28
|
+
if ARGV.length < 2
|
29
|
+
# No required number of input arguments: show short help
|
30
|
+
puts help
|
31
|
+
else
|
32
|
+
# Optional arguments processing
|
33
|
+
opts = {}
|
34
|
+
if ARGV.length > 2
|
35
|
+
ARGV[2..-1].each do |arg|
|
36
|
+
case arg
|
37
|
+
when '--compress=none'
|
38
|
+
opts[:compress] = false
|
39
|
+
when '--compress=zlib'
|
40
|
+
opts[:compress] = true
|
41
|
+
when /^--so_path=.+$/
|
42
|
+
str = arg[10..-1]
|
43
|
+
opts[:so_path] = str
|
44
|
+
else
|
45
|
+
puts "Unknown argument #{arg}"
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# Show given options
|
51
|
+
puts "Used options:"
|
52
|
+
if opts.size == 0
|
53
|
+
puts " default"
|
54
|
+
else
|
55
|
+
puts " compress: #{opts[:compress]}" if opts.has_key?(:compress)
|
56
|
+
puts " so_path: #{opts[:so_path]}" if opts.has_key?(:so_path)
|
57
|
+
end
|
58
|
+
# Required arguments processing
|
59
|
+
inpfile = ARGV[0]
|
60
|
+
outfile = ARGV[1]
|
61
|
+
raise 'inpfile and outfile cannot be equal' if inpfile == outfile
|
62
|
+
NodeMarshal.compile_rb_file(outfile, inpfile, opts)
|
63
|
+
end
|
data/bin/noderbc.bat
CHANGED
File without changes
|
data/ext/node-marshal/base85r.c
CHANGED
@@ -1,194 +1,194 @@
|
|
1
|
-
/*
|
2
|
-
* Implementation of own version of BASE85 binary data encoding
|
3
|
-
* adapted for the usage inside Ruby source code (i.e. without
|
4
|
-
* such symbols as \ " # { } '
|
5
|
-
*
|
6
|
-
* Format of the output stream:
|
7
|
-
* 1) First byte: number of bytes in the last chunk: from 0 to 3
|
8
|
-
* (0 means that the last chunk contains 4 bytes, i.e. everything
|
9
|
-
* is aligned). See val_to_char array for the used alphabet
|
10
|
-
* 2) big-endian 5-byte numbers (base 85)
|
11
|
-
* 3) empty string: arbitrary two bytes
|
12
|
-
*
|
13
|
-
* (C) 2015-2016 Alexey Voskov
|
14
|
-
* License: BSD-2-Clause
|
15
|
-
*/
|
16
|
-
#include <stdio.h>
|
17
|
-
#include <stdlib.h>
|
18
|
-
#include <inttypes.h>
|
19
|
-
#include <ruby.h>
|
20
|
-
#include <ruby/version.h>
|
21
|
-
|
22
|
-
#define BASE85R_STR_WIDTH 14 // Number of 5-byte groups in the string (12 for 60-byte string)
|
23
|
-
|
24
|
-
#define D0_VAL 1 // 85**0
|
25
|
-
#define D1_VAL 85 // 85**1
|
26
|
-
#define D2_VAL 7225 // 85**2
|
27
|
-
#define D3_VAL 614125 // 85**3
|
28
|
-
#define D4_VAL 52200625 // 85**4
|
29
|
-
|
30
|
-
static int di_val[5] = {D4_VAL, D3_VAL, D2_VAL, D1_VAL, D0_VAL};
|
31
|
-
|
32
|
-
|
33
|
-
/* Modified BASE85 digits */
|
34
|
-
static const char val_to_char[86] = // ASCIIZ string
|
35
|
-
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
36
|
-
"abcdefghijklmnopqrstuvwxyz"
|
37
|
-
"0123456789"
|
38
|
-
"!$%&()*-./"
|
39
|
-
":;<=>?@[]^"
|
40
|
-
",_|";
|
41
|
-
|
42
|
-
static int char_to_val[128];
|
43
|
-
|
44
|
-
|
45
|
-
/* Initializes internal tables that are required
|
46
|
-
for recoding */
|
47
|
-
void base85r_init_tables()
|
48
|
-
{
|
49
|
-
int i;
|
50
|
-
for (i = 0; i < 128; i++)
|
51
|
-
char_to_val[i] = -1;
|
52
|
-
|
53
|
-
|
54
|
-
for (i = 0; i < 85; i++)
|
55
|
-
{
|
56
|
-
if (char_to_val[(int) val_to_char[i]] != -1)
|
57
|
-
rb_raise(rb_eArgError, "Internal error");
|
58
|
-
char_to_val[(int) val_to_char[i]] = i;
|
59
|
-
}
|
60
|
-
|
61
|
-
for (i = 0; i < 85; i++)
|
62
|
-
if (char_to_val[(int) val_to_char[i]] != i)
|
63
|
-
rb_raise(rb_eArgError, "Internal error");
|
64
|
-
}
|
65
|
-
|
66
|
-
|
67
|
-
/* Calculate length of buffer for base85 encoding */
|
68
|
-
static int base85_encode_buf_len(int len)
|
69
|
-
{
|
70
|
-
// Calculate aligned (32-bit alignment) size of the buffer
|
71
|
-
int buf_len = ((len >> 2) << 2);
|
72
|
-
if (len % 4) buf_len += 4;
|
73
|
-
// Calculate size of the output buffer
|
74
|
-
buf_len = (buf_len * 5) / 4;
|
75
|
-
buf_len = (buf_len * 105) / 100;
|
76
|
-
buf_len += 32;
|
77
|
-
// Return buffer size
|
78
|
-
return buf_len;
|
79
|
-
}
|
80
|
-
|
81
|
-
/*
|
82
|
-
* Encode string to modified BASE85 ASCII.
|
83
|
-
* Note: call base85_init_tables before using of this function
|
84
|
-
*/
|
85
|
-
VALUE base85r_encode(VALUE input)
|
86
|
-
{
|
87
|
-
VALUE output;
|
88
|
-
int inp_len, out_len, out_buf_len;
|
89
|
-
int pos, outpos;
|
90
|
-
unsigned int val;
|
91
|
-
unsigned char *outptr, *inptr;
|
92
|
-
int i;
|
93
|
-
// Check input data type and allocate string
|
94
|
-
if (TYPE(input) != T_STRING)
|
95
|
-
rb_raise(rb_eArgError, "base85r_encode: input must be a string");
|
96
|
-
inp_len = RSTRING_LEN(input);
|
97
|
-
out_buf_len = base85_encode_buf_len(inp_len);
|
98
|
-
output = rb_str_new(NULL, out_buf_len);
|
99
|
-
// Begin conversion
|
100
|
-
outpos = 0;
|
101
|
-
outptr = (unsigned char *) RSTRING_PTR(output);
|
102
|
-
inptr = (unsigned char *) RSTRING_PTR(input);
|
103
|
-
outptr[outpos++] = 32;
|
104
|
-
outptr[outpos++] = val_to_char[inp_len % 4];
|
105
|
-
out_len = 2;
|
106
|
-
for (pos = 0; pos < inp_len; )
|
107
|
-
{
|
108
|
-
// Get four bytes
|
109
|
-
val = 0;
|
110
|
-
for (i = 24; i >= 0; i -= 8)
|
111
|
-
if (pos < inp_len) val |= inptr[pos++] << i;
|
112
|
-
// And transform them to five bytes
|
113
|
-
for (i = 0; i < 5; i++)
|
114
|
-
{
|
115
|
-
int digit = (val / di_val[i]) % 85;
|
116
|
-
char sym = val_to_char[digit];
|
117
|
-
outptr[outpos++] = sym;
|
118
|
-
}
|
119
|
-
out_len += 5;
|
120
|
-
// Newline addition
|
121
|
-
if (pos % (4 * BASE85R_STR_WIDTH) == 0)
|
122
|
-
{
|
123
|
-
out_len += 2;
|
124
|
-
outptr[outpos++] = 10;
|
125
|
-
outptr[outpos++]= 32;
|
126
|
-
}
|
127
|
-
}
|
128
|
-
// Check the state of memory
|
129
|
-
if (outpos >= out_buf_len)
|
130
|
-
rb_raise(rb_eArgError, "base85r_encode: internal memory error");
|
131
|
-
// Truncate the empty "tail" of the buffer and return the string
|
132
|
-
return rb_str_resize(output, out_len);
|
133
|
-
}
|
134
|
-
|
135
|
-
|
136
|
-
/*
|
137
|
-
* Decode string in modified BASE85 ASCII format.
|
138
|
-
* Note: call base85_init_tables before using of this function
|
139
|
-
*/
|
140
|
-
VALUE base85r_decode(VALUE input)
|
141
|
-
{
|
142
|
-
int inp_len, out_len, pos, shift;
|
143
|
-
unsigned int val = 0;
|
144
|
-
VALUE output;
|
145
|
-
unsigned char *inptr, *outptr;
|
146
|
-
int tail_len, i;
|
147
|
-
// Check input data type and allocate string
|
148
|
-
if (TYPE(input) != T_STRING)
|
149
|
-
rb_raise(rb_eArgError, "base85r_decode: input must be a string");
|
150
|
-
inp_len = RSTRING_LEN(input);
|
151
|
-
if (inp_len < 6 && inp_len != 2)
|
152
|
-
{ // String with 1 or more symbols
|
153
|
-
rb_raise(rb_eArgError, "base85r_decode: input string is too short");
|
154
|
-
}
|
155
|
-
output = rb_str_new(NULL, inp_len);
|
156
|
-
// Begin conversion
|
157
|
-
inptr = (unsigned char *) RSTRING_PTR(input);
|
158
|
-
outptr = (unsigned char *) RSTRING_PTR(output);
|
159
|
-
out_len = 0;
|
160
|
-
tail_len = -1;
|
161
|
-
shift = 0;
|
162
|
-
val = 0;
|
163
|
-
for (pos = 0; pos < inp_len; pos++)
|
164
|
-
{
|
165
|
-
int digit = char_to_val[(int) inptr[pos]];
|
166
|
-
if (digit != -1)
|
167
|
-
{
|
168
|
-
if (tail_len == -1)
|
169
|
-
{
|
170
|
-
tail_len = digit;
|
171
|
-
if (tail_len > 4)
|
172
|
-
rb_raise(rb_eArgError, "base85r_decode: input string is corrupted");
|
173
|
-
continue;
|
174
|
-
}
|
175
|
-
val += digit * di_val[shift++];
|
176
|
-
if (shift == 5)
|
177
|
-
{
|
178
|
-
for (i = 24; i >= 0; i -= 8)
|
179
|
-
*outptr++ = (val >> i) & 0xFF;
|
180
|
-
shift = 0; val = 0;
|
181
|
-
out_len += 4;
|
182
|
-
}
|
183
|
-
}
|
184
|
-
}
|
185
|
-
// Check if the byte sequence was valid
|
186
|
-
if (shift != 0)
|
187
|
-
rb_raise(rb_eArgError, "base85r_decode: input string is corrupted");
|
188
|
-
// Take into account unaligned "tail"
|
189
|
-
if (tail_len != 0)
|
190
|
-
{
|
191
|
-
out_len -= (4 - tail_len);
|
192
|
-
}
|
193
|
-
return rb_str_resize(output, out_len);
|
194
|
-
}
|
1
|
+
/*
|
2
|
+
* Implementation of own version of BASE85 binary data encoding
|
3
|
+
* adapted for the usage inside Ruby source code (i.e. without
|
4
|
+
* such symbols as \ " # { } '
|
5
|
+
*
|
6
|
+
* Format of the output stream:
|
7
|
+
* 1) First byte: number of bytes in the last chunk: from 0 to 3
|
8
|
+
* (0 means that the last chunk contains 4 bytes, i.e. everything
|
9
|
+
* is aligned). See val_to_char array for the used alphabet
|
10
|
+
* 2) big-endian 5-byte numbers (base 85)
|
11
|
+
* 3) empty string: arbitrary two bytes
|
12
|
+
*
|
13
|
+
* (C) 2015-2016 Alexey Voskov
|
14
|
+
* License: BSD-2-Clause
|
15
|
+
*/
|
16
|
+
#include <stdio.h>
|
17
|
+
#include <stdlib.h>
|
18
|
+
#include <inttypes.h>
|
19
|
+
#include <ruby.h>
|
20
|
+
#include <ruby/version.h>
|
21
|
+
|
22
|
+
#define BASE85R_STR_WIDTH 14 // Number of 5-byte groups in the string (12 for 60-byte string)
|
23
|
+
|
24
|
+
#define D0_VAL 1 // 85**0
|
25
|
+
#define D1_VAL 85 // 85**1
|
26
|
+
#define D2_VAL 7225 // 85**2
|
27
|
+
#define D3_VAL 614125 // 85**3
|
28
|
+
#define D4_VAL 52200625 // 85**4
|
29
|
+
|
30
|
+
static int di_val[5] = {D4_VAL, D3_VAL, D2_VAL, D1_VAL, D0_VAL};
|
31
|
+
|
32
|
+
|
33
|
+
/* Modified BASE85 digits */
|
34
|
+
static const char val_to_char[86] = // ASCIIZ string
|
35
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
36
|
+
"abcdefghijklmnopqrstuvwxyz"
|
37
|
+
"0123456789"
|
38
|
+
"!$%&()*-./"
|
39
|
+
":;<=>?@[]^"
|
40
|
+
",_|";
|
41
|
+
|
42
|
+
static int char_to_val[128];
|
43
|
+
|
44
|
+
|
45
|
+
/* Initializes internal tables that are required
|
46
|
+
for recoding */
|
47
|
+
void base85r_init_tables()
|
48
|
+
{
|
49
|
+
int i;
|
50
|
+
for (i = 0; i < 128; i++)
|
51
|
+
char_to_val[i] = -1;
|
52
|
+
|
53
|
+
|
54
|
+
for (i = 0; i < 85; i++)
|
55
|
+
{
|
56
|
+
if (char_to_val[(int) val_to_char[i]] != -1)
|
57
|
+
rb_raise(rb_eArgError, "Internal error");
|
58
|
+
char_to_val[(int) val_to_char[i]] = i;
|
59
|
+
}
|
60
|
+
|
61
|
+
for (i = 0; i < 85; i++)
|
62
|
+
if (char_to_val[(int) val_to_char[i]] != i)
|
63
|
+
rb_raise(rb_eArgError, "Internal error");
|
64
|
+
}
|
65
|
+
|
66
|
+
|
67
|
+
/* Calculate length of buffer for base85 encoding */
|
68
|
+
static int base85_encode_buf_len(int len)
|
69
|
+
{
|
70
|
+
// Calculate aligned (32-bit alignment) size of the buffer
|
71
|
+
int buf_len = ((len >> 2) << 2);
|
72
|
+
if (len % 4) buf_len += 4;
|
73
|
+
// Calculate size of the output buffer
|
74
|
+
buf_len = (buf_len * 5) / 4;
|
75
|
+
buf_len = (buf_len * 105) / 100;
|
76
|
+
buf_len += 32;
|
77
|
+
// Return buffer size
|
78
|
+
return buf_len;
|
79
|
+
}
|
80
|
+
|
81
|
+
/*
|
82
|
+
* Encode string to modified BASE85 ASCII.
|
83
|
+
* Note: call base85_init_tables before using of this function
|
84
|
+
*/
|
85
|
+
VALUE base85r_encode(VALUE input)
|
86
|
+
{
|
87
|
+
VALUE output;
|
88
|
+
int inp_len, out_len, out_buf_len;
|
89
|
+
int pos, outpos;
|
90
|
+
unsigned int val;
|
91
|
+
unsigned char *outptr, *inptr;
|
92
|
+
int i;
|
93
|
+
// Check input data type and allocate string
|
94
|
+
if (TYPE(input) != T_STRING)
|
95
|
+
rb_raise(rb_eArgError, "base85r_encode: input must be a string");
|
96
|
+
inp_len = RSTRING_LEN(input);
|
97
|
+
out_buf_len = base85_encode_buf_len(inp_len);
|
98
|
+
output = rb_str_new(NULL, out_buf_len);
|
99
|
+
// Begin conversion
|
100
|
+
outpos = 0;
|
101
|
+
outptr = (unsigned char *) RSTRING_PTR(output);
|
102
|
+
inptr = (unsigned char *) RSTRING_PTR(input);
|
103
|
+
outptr[outpos++] = 32;
|
104
|
+
outptr[outpos++] = val_to_char[inp_len % 4];
|
105
|
+
out_len = 2;
|
106
|
+
for (pos = 0; pos < inp_len; )
|
107
|
+
{
|
108
|
+
// Get four bytes
|
109
|
+
val = 0;
|
110
|
+
for (i = 24; i >= 0; i -= 8)
|
111
|
+
if (pos < inp_len) val |= inptr[pos++] << i;
|
112
|
+
// And transform them to five bytes
|
113
|
+
for (i = 0; i < 5; i++)
|
114
|
+
{
|
115
|
+
int digit = (val / di_val[i]) % 85;
|
116
|
+
char sym = val_to_char[digit];
|
117
|
+
outptr[outpos++] = sym;
|
118
|
+
}
|
119
|
+
out_len += 5;
|
120
|
+
// Newline addition
|
121
|
+
if (pos % (4 * BASE85R_STR_WIDTH) == 0)
|
122
|
+
{
|
123
|
+
out_len += 2;
|
124
|
+
outptr[outpos++] = 10;
|
125
|
+
outptr[outpos++]= 32;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
// Check the state of memory
|
129
|
+
if (outpos >= out_buf_len)
|
130
|
+
rb_raise(rb_eArgError, "base85r_encode: internal memory error");
|
131
|
+
// Truncate the empty "tail" of the buffer and return the string
|
132
|
+
return rb_str_resize(output, out_len);
|
133
|
+
}
|
134
|
+
|
135
|
+
|
136
|
+
/*
|
137
|
+
* Decode string in modified BASE85 ASCII format.
|
138
|
+
* Note: call base85_init_tables before using of this function
|
139
|
+
*/
|
140
|
+
VALUE base85r_decode(VALUE input)
|
141
|
+
{
|
142
|
+
int inp_len, out_len, pos, shift;
|
143
|
+
unsigned int val = 0;
|
144
|
+
VALUE output;
|
145
|
+
unsigned char *inptr, *outptr;
|
146
|
+
int tail_len, i;
|
147
|
+
// Check input data type and allocate string
|
148
|
+
if (TYPE(input) != T_STRING)
|
149
|
+
rb_raise(rb_eArgError, "base85r_decode: input must be a string");
|
150
|
+
inp_len = RSTRING_LEN(input);
|
151
|
+
if (inp_len < 6 && inp_len != 2)
|
152
|
+
{ // String with 1 or more symbols
|
153
|
+
rb_raise(rb_eArgError, "base85r_decode: input string is too short");
|
154
|
+
}
|
155
|
+
output = rb_str_new(NULL, inp_len);
|
156
|
+
// Begin conversion
|
157
|
+
inptr = (unsigned char *) RSTRING_PTR(input);
|
158
|
+
outptr = (unsigned char *) RSTRING_PTR(output);
|
159
|
+
out_len = 0;
|
160
|
+
tail_len = -1;
|
161
|
+
shift = 0;
|
162
|
+
val = 0;
|
163
|
+
for (pos = 0; pos < inp_len; pos++)
|
164
|
+
{
|
165
|
+
int digit = char_to_val[(int) inptr[pos]];
|
166
|
+
if (digit != -1)
|
167
|
+
{
|
168
|
+
if (tail_len == -1)
|
169
|
+
{
|
170
|
+
tail_len = digit;
|
171
|
+
if (tail_len > 4)
|
172
|
+
rb_raise(rb_eArgError, "base85r_decode: input string is corrupted");
|
173
|
+
continue;
|
174
|
+
}
|
175
|
+
val += digit * di_val[shift++];
|
176
|
+
if (shift == 5)
|
177
|
+
{
|
178
|
+
for (i = 24; i >= 0; i -= 8)
|
179
|
+
*outptr++ = (val >> i) & 0xFF;
|
180
|
+
shift = 0; val = 0;
|
181
|
+
out_len += 4;
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
// Check if the byte sequence was valid
|
186
|
+
if (shift != 0)
|
187
|
+
rb_raise(rb_eArgError, "base85r_decode: input string is corrupted");
|
188
|
+
// Take into account unaligned "tail"
|
189
|
+
if (tail_len != 0)
|
190
|
+
{
|
191
|
+
out_len -= (4 - tail_len);
|
192
|
+
}
|
193
|
+
return rb_str_resize(output, out_len);
|
194
|
+
}
|