gap50 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/README.md +2 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gap.rb +17 -0
- data/gap50.gemspec +35 -0
- data/lib/gap50.rb +1 -0
- data/lib/gap50/cache.rb +2 -0
- data/lib/gap50/cache/cache.rb +98 -0
- data/lib/gap50/cache/hashchain.rb +98 -0
- data/lib/gap50/ext.rb +3 -0
- data/lib/gap50/ext/7za.rb +4 -0
- data/lib/gap50/ext/aria2c.rb +4 -0
- data/lib/gap50/ext/ruby25.rb +24 -0
- data/lib/gap50/gap.rb +7 -0
- data/lib/gap50/gap/cfunc.rb +53 -0
- data/lib/gap50/gap/copy.rb +27 -0
- data/lib/gap50/gap/dll.rb +40 -0
- data/lib/gap50/gap/exec.rb +39 -0
- data/lib/gap50/gap/gap.rb +3 -0
- data/lib/gap50/gap/require.rb +96 -0
- data/lib/gap50/gap50.rb +9 -0
- data/lib/gap50/preload.rb +12 -0
- data/lib/gap50/samsara.rb +4 -0
- data/lib/gap50/samsara/maker.rb +113 -0
- data/lib/gap50/samsara/md5.rb +44 -0
- data/lib/gap50/samsara/random.rb +53 -0
- data/lib/gap50/samsara/samsara.rb +195 -0
- data/lib/gap50/version.rb +3 -0
- metadata +118 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
module Gap
|
2
|
+
class DLLFunction
|
3
|
+
def initialize(path, name)
|
4
|
+
@path = path
|
5
|
+
@name = name
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(*args)
|
9
|
+
param = args.map do |x|
|
10
|
+
case x
|
11
|
+
when Integer
|
12
|
+
"L"
|
13
|
+
else
|
14
|
+
"p"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
begin
|
18
|
+
Win32API.new(@path, @name, param, "L").call(*args)
|
19
|
+
rescue LoadError
|
20
|
+
Win32API.new(File.expand_path(@path), @name, param, "L").call(*args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class DLL
|
26
|
+
def initialize(filename, sam = Gap::Main, &block)
|
27
|
+
@sam = sam
|
28
|
+
@filename = filename
|
29
|
+
@realpath = @sam.genfile(@filename, &block).tr("/", "\\")
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](name)
|
33
|
+
DLLFunction.new @realpath, name
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(sym, *args)
|
37
|
+
self[sym.to_s].call(*args)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Gap
|
2
|
+
class Exec
|
3
|
+
def initialize(sam = Gap::Main)
|
4
|
+
@sam = sam
|
5
|
+
end
|
6
|
+
|
7
|
+
def exec(cmdline, input)
|
8
|
+
md5 = MD5.hexdigest(cmdline) + MD5.hexdigest(input)
|
9
|
+
inputfile = _file md5, ".input"
|
10
|
+
outputfile = _file md5, ".output"
|
11
|
+
outf = md5 + ".output"
|
12
|
+
key = [Samsara::Meta.new(:Exec), outf]
|
13
|
+
@sam.gen key do
|
14
|
+
@sam.writefile inputfile, input
|
15
|
+
system "#{cmdline} < #{inputfile} > #{outputfile}"
|
16
|
+
@sam.readfile outputfile
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def marshal(cmdline, input)
|
21
|
+
Marshal.load exec(cmdline, input)
|
22
|
+
end
|
23
|
+
|
24
|
+
DEFAULT_EXEC = Exec.new
|
25
|
+
def self.exec(*a)
|
26
|
+
DEFAULT_EXEC.exec(*a)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.marshal(*a)
|
30
|
+
DEFAULT_EXEC.marshal(*a)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def _file(name, ext = "")
|
35
|
+
"temp/exec_" << name << ext
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Gap
|
2
|
+
class Require
|
3
|
+
def initialize(sam = Gap::Main, strict = true)
|
4
|
+
@strict = strict
|
5
|
+
@sam = sam
|
6
|
+
end
|
7
|
+
|
8
|
+
def require(*args)
|
9
|
+
if @strict
|
10
|
+
args.each{|x|
|
11
|
+
_require_strict x
|
12
|
+
}
|
13
|
+
else
|
14
|
+
args.each{|x|
|
15
|
+
_require x
|
16
|
+
}
|
17
|
+
end
|
18
|
+
ensure
|
19
|
+
$@.replace caller if $@
|
20
|
+
end
|
21
|
+
|
22
|
+
ORIGIN = Kernel.method(:require)
|
23
|
+
INSTANCE_ORIGIN = Kernel.instance_method(:require)
|
24
|
+
DEFAULT_REQUIRE = Require.new
|
25
|
+
REQUIRE_STACK = [[INSTANCE_ORIGIN, Kernel]]
|
26
|
+
|
27
|
+
|
28
|
+
def replace_kernel!
|
29
|
+
that = self
|
30
|
+
REQUIRE_STACK.push [self.class.instance_method(:require), self]
|
31
|
+
_fix_kernel
|
32
|
+
ensure
|
33
|
+
$@.replace caller if $@
|
34
|
+
end
|
35
|
+
|
36
|
+
def back_require!
|
37
|
+
REQUIRE_STACK.pop
|
38
|
+
_fix_kernel
|
39
|
+
ensure
|
40
|
+
$@.replace caller if $@
|
41
|
+
end
|
42
|
+
|
43
|
+
def use_kernel!
|
44
|
+
Kernel.send :define_method, INSTANCE_ORIGIN
|
45
|
+
ensure
|
46
|
+
$@.replace caller if $@
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def _require(a)
|
51
|
+
ORIGIN.call a
|
52
|
+
rescue
|
53
|
+
if ex.to_s =~ /^LoadError: .*? -- (.*)$/
|
54
|
+
file = $1
|
55
|
+
path = "./" + @sam.genfile(file)
|
56
|
+
ORIGIN.call path
|
57
|
+
else
|
58
|
+
raise ArgumentError, "Can't load #{a}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def _require_strict(file)
|
63
|
+
[file, file + ".rb"].each{|x|
|
64
|
+
begin
|
65
|
+
if @sam.has_file_in?(x)
|
66
|
+
path = "./" + @sam.genfile(x)
|
67
|
+
return ORIGIN.call path
|
68
|
+
end
|
69
|
+
rescue LoadError
|
70
|
+
ensure
|
71
|
+
$@.replace caller if $@
|
72
|
+
end
|
73
|
+
}
|
74
|
+
ORIGIN.call file
|
75
|
+
ensure
|
76
|
+
$@.replace caller if $@
|
77
|
+
end
|
78
|
+
|
79
|
+
def _fix_kernel
|
80
|
+
if REQUIRE_STACK[-1]
|
81
|
+
m, o = REQUIRE_STACK[-1]
|
82
|
+
if o == Kernel
|
83
|
+
use_kernel!
|
84
|
+
else
|
85
|
+
Kernel.send :define_method, :require do |*args|
|
86
|
+
m.bind(o).call(*args)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
else
|
90
|
+
use_kernel!
|
91
|
+
end
|
92
|
+
ensure
|
93
|
+
$@.replace caller if $@
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/gap50/gap50.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
module Gap
|
2
|
+
class Maker
|
3
|
+
RND = Random.new
|
4
|
+
attr_accessor :temp, :sam
|
5
|
+
|
6
|
+
|
7
|
+
def initialize(sam = nil, temp = genname, &block)
|
8
|
+
@sam = sam
|
9
|
+
@temp = temp
|
10
|
+
if defined? instance_exec
|
11
|
+
transaction do |m|
|
12
|
+
instance_exec &block
|
13
|
+
end
|
14
|
+
else
|
15
|
+
transaction do |m|
|
16
|
+
instance_eval &block
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def path(name)
|
22
|
+
@temp + "/" + name
|
23
|
+
end
|
24
|
+
|
25
|
+
def readfile(name)
|
26
|
+
@sam.readfile path(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def writefile(name, value)
|
30
|
+
@sam.writefile path(name), value
|
31
|
+
end
|
32
|
+
|
33
|
+
def copyfile(src, dest)
|
34
|
+
writefile dest, @sam.readfile(src)
|
35
|
+
end
|
36
|
+
|
37
|
+
def exec(cmd)
|
38
|
+
system "cd #{@temp} && #{cmd}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def transaction
|
42
|
+
_create
|
43
|
+
ret = yield self
|
44
|
+
_import_all
|
45
|
+
_cleanup
|
46
|
+
ret
|
47
|
+
end
|
48
|
+
|
49
|
+
def asBase64
|
50
|
+
_import_all
|
51
|
+
@sam.toBase64
|
52
|
+
end
|
53
|
+
|
54
|
+
def asMarshal
|
55
|
+
_import_all
|
56
|
+
@sam.toMarshal
|
57
|
+
end
|
58
|
+
|
59
|
+
def genlib(name, text)
|
60
|
+
writefile "cpp/#{name}.cpp", %{#define GAPI(type) extern "C" type __stdcall
|
61
|
+
#{text}
|
62
|
+
}
|
63
|
+
exec "g++ cpp/#{name}.cpp -o cpp/#{name}.dll -static -s -shared -m32 -Wl,-add-stdcall-alias"
|
64
|
+
end
|
65
|
+
|
66
|
+
def from(name)
|
67
|
+
@sam = Samsara.new name
|
68
|
+
_export_all
|
69
|
+
end
|
70
|
+
|
71
|
+
alias COPY copyfile
|
72
|
+
alias WRITE writefile
|
73
|
+
alias READ readfile
|
74
|
+
alias RUN exec
|
75
|
+
alias FROM from
|
76
|
+
alias CXX genlib
|
77
|
+
private
|
78
|
+
def _export_all
|
79
|
+
@sam.each{|k, v|
|
80
|
+
if k[0] == Samsara::MetaFile
|
81
|
+
@sam.export k[1], @temp
|
82
|
+
end
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def _import_all
|
87
|
+
len = @temp.length + 1
|
88
|
+
Dir.glob(@temp + "/**/*") do |f|
|
89
|
+
next if FileTest.directory?(f)
|
90
|
+
name = f[len..-1]
|
91
|
+
@sam.import name, @temp
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def _cleanup
|
96
|
+
@sam.rmdir_p(@temp)
|
97
|
+
end
|
98
|
+
|
99
|
+
def _create
|
100
|
+
if !FileTest.directory?(temp)
|
101
|
+
Dir.mkdir temp
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def genname
|
106
|
+
while 0
|
107
|
+
name = "temp-#{RND.hex(16)}"
|
108
|
+
return name if !FileTest.exists?(name)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Gap
|
2
|
+
MD5_INIT = Win32API.new("ADVAPI32", "MD5Init", "p", "L")
|
3
|
+
MD5_UPDATE = Win32API.new("ADVAPI32", "MD5Update", "ppL", "L")
|
4
|
+
MD5_FINAL = Win32API.new("ADVAPI32", "MD5Final", "p", "L")
|
5
|
+
class MD5
|
6
|
+
def initialize
|
7
|
+
@ctx = "\0" * 128 # buf
|
8
|
+
MD5_INIT.call(@ctx)
|
9
|
+
end
|
10
|
+
|
11
|
+
def update(str)
|
12
|
+
MD5_UPDATE.call(@ctx, str, str.unpack("C*").size)
|
13
|
+
end
|
14
|
+
|
15
|
+
def digest
|
16
|
+
MD5_FINAL.call(@ctx)
|
17
|
+
@ctx[88, 16]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.digest(a)
|
21
|
+
x = MD5.new
|
22
|
+
x.update a
|
23
|
+
x.digest
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.hexdigest(a)
|
27
|
+
digest(a).unpack("H*").first
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.file(fn)
|
31
|
+
open(fn, 'rb') do |f|
|
32
|
+
x = MD5.new
|
33
|
+
while (r = f.read(10240))
|
34
|
+
x.update(r)
|
35
|
+
end
|
36
|
+
x.digest
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.filehex(fn)
|
41
|
+
file(fn).unpack("H*").first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Gap
|
2
|
+
CR_ACQUIRE = Win32API.new("ADVAPI32", "CryptAcquireContext", "pLLLL", "L")
|
3
|
+
CR_GENRANDOM = Win32API.new("ADVAPI32", "CryptGenRandom", "LLp", "L")
|
4
|
+
CR_RELEASE = Win32API.new("ADVAPI32", "CryptReleaseContext", "LL", "L")
|
5
|
+
class Random
|
6
|
+
REG = {}
|
7
|
+
def self.auto_release_proc
|
8
|
+
proc{|id|
|
9
|
+
if REG.include? id
|
10
|
+
_dispose REG[id]
|
11
|
+
REG.delete id
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_auto_release(id, ptr)
|
17
|
+
REG[object_id] = ptr
|
18
|
+
ObjectSpace.define_finalizer self, auto_release_proc
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
buf = "\0" * 4
|
23
|
+
CR_ACQUIRE.call buf, 0, 0, 1, -268435456
|
24
|
+
@ptr, = buf.unpack("L")
|
25
|
+
self.class.register_auto_release object_id, @ptr
|
26
|
+
end
|
27
|
+
|
28
|
+
def bytes(len = 16)
|
29
|
+
buf = "\0" * len
|
30
|
+
CR_GENRANDOM.call @ptr, len, buf
|
31
|
+
buf
|
32
|
+
end
|
33
|
+
|
34
|
+
def hex(len = 16)
|
35
|
+
bytes(len).unpack("H*").first
|
36
|
+
end
|
37
|
+
|
38
|
+
def next_int
|
39
|
+
bytes(4).unpack("L").first
|
40
|
+
end
|
41
|
+
|
42
|
+
def self._dispose(ptr)
|
43
|
+
if ptr
|
44
|
+
CR_RELEASE.call ptr, 0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def dispose
|
49
|
+
self.class._dispose @ptr
|
50
|
+
@ptr = nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module Gap
|
2
|
+
class Samsara
|
3
|
+
Meta = Struct.new :meta
|
4
|
+
MetaFile = Meta.new :file
|
5
|
+
MetaMD5 = Meta.new :filemd5
|
6
|
+
attr_accessor :filepath
|
7
|
+
def initialize(name, filepath = "samfile")
|
8
|
+
@cache = Gap::Cache.new name
|
9
|
+
@filepath = filepath
|
10
|
+
end
|
11
|
+
|
12
|
+
def fromBase64(b64)
|
13
|
+
@cache.fromBase64 b64
|
14
|
+
end
|
15
|
+
|
16
|
+
def toBase64
|
17
|
+
@cache.toBase64
|
18
|
+
end
|
19
|
+
|
20
|
+
def fromMarshal(text)
|
21
|
+
@cache.fromMarshal text
|
22
|
+
end
|
23
|
+
|
24
|
+
def toMarshal
|
25
|
+
@cache.toMarshal
|
26
|
+
end
|
27
|
+
|
28
|
+
def each(&block)
|
29
|
+
@cache.each(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def export(file, dir)
|
33
|
+
_export file, dir
|
34
|
+
end
|
35
|
+
|
36
|
+
def import(file, dir)
|
37
|
+
_import file, dir
|
38
|
+
end
|
39
|
+
#
|
40
|
+
# gen : [Any] -> ([Any] -> Any) -> Any
|
41
|
+
#
|
42
|
+
def gen(*path)
|
43
|
+
if @cache.has?(path)
|
44
|
+
@cache[path]
|
45
|
+
else
|
46
|
+
@cache.transaction do |db|
|
47
|
+
db[path] = yield path
|
48
|
+
db[path]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# genfile : filename -> [Any] -> (filename -> [Any] -> Any) -> Any
|
55
|
+
#
|
56
|
+
def genfile(filename, args = nil, &block)
|
57
|
+
realpath = file(filename)
|
58
|
+
md5key = [MetaMD5, filename]
|
59
|
+
filekey = [MetaFile, filename]
|
60
|
+
if @cache.has?(md5key) &&
|
61
|
+
@cache.has?(filekey) &&
|
62
|
+
FileTest.file?(realpath) &&
|
63
|
+
MD5.filehex(realpath) == @cache[md5key]
|
64
|
+
elsif @cache.has?(filekey)
|
65
|
+
_writefile realpath, @cache[filekey]
|
66
|
+
else
|
67
|
+
u = yield filekey
|
68
|
+
@cache.transaction do |db|
|
69
|
+
db[md5key] = MD5.hexdigest u
|
70
|
+
db[filekey] = u
|
71
|
+
_writefile realpath, @cache[filekey]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
realpath
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_file_in?(filename)
|
78
|
+
md5key = [MetaMD5, filename]
|
79
|
+
filekey = [MetaFile, filename]
|
80
|
+
@cache.has?(md5key) &&
|
81
|
+
@cache.has?(filekey) &&
|
82
|
+
@cache[md5key] != nil &&
|
83
|
+
MD5.hexdigest(@cache[filekey]) == @cache[md5key]
|
84
|
+
end
|
85
|
+
|
86
|
+
def need_update_file?(filename, realfile)
|
87
|
+
if has_file_in?(filename)
|
88
|
+
md5key = [MetaMD5, filename]
|
89
|
+
MD5.filehex(realfile) != @cache[md5key]
|
90
|
+
else
|
91
|
+
true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def file(name)
|
96
|
+
_file(name)
|
97
|
+
end
|
98
|
+
|
99
|
+
def writefile(a, b)
|
100
|
+
_writefile a, b
|
101
|
+
end
|
102
|
+
|
103
|
+
def readfile(a)
|
104
|
+
_readfile a
|
105
|
+
end
|
106
|
+
|
107
|
+
def mangle(name)
|
108
|
+
_mangle(name)
|
109
|
+
end
|
110
|
+
|
111
|
+
def rmdir_p(name)
|
112
|
+
_rmdir_p(name)
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
private
|
117
|
+
def _file(name)
|
118
|
+
File.join(@filepath, _mangle(name))
|
119
|
+
end
|
120
|
+
|
121
|
+
def _mangle(name)
|
122
|
+
name.gsub("://", "$SEP$/").gsub(/[:"']/){ "$#{$&.unpack("H")}$" }
|
123
|
+
end
|
124
|
+
|
125
|
+
def _demangle(name)
|
126
|
+
name.gsub("$SEP$/", "://").gsub(/\$([a-f0-9]+)\$/) { [$1].pack("H") }
|
127
|
+
end
|
128
|
+
|
129
|
+
def _mkdirp(name)
|
130
|
+
names = name.tr("\\", "/").split("/")[0..-2]
|
131
|
+
names.inject(""){|a, b|
|
132
|
+
if a == ""
|
133
|
+
path = b
|
134
|
+
else
|
135
|
+
path = a + "/" + b
|
136
|
+
end
|
137
|
+
if !FileTest.directory?(path)
|
138
|
+
Dir.mkdir(path)
|
139
|
+
end
|
140
|
+
path
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
def _readfile(name)
|
145
|
+
open(name, 'rb') do |f|
|
146
|
+
f.read
|
147
|
+
end
|
148
|
+
end
|
149
|
+
def _writefile(name, val)
|
150
|
+
_mkdirp(name)
|
151
|
+
open(name, "wb") do |f|
|
152
|
+
f.write val
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def _export(file, dir)
|
157
|
+
if has_file_in?(file)
|
158
|
+
name = _mangle(dir + "/" + file)
|
159
|
+
filekey = [MetaFile, file]
|
160
|
+
_writefile(name, @cache[filekey])
|
161
|
+
else
|
162
|
+
raise "File not found #{file}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def _import(file, dir)
|
167
|
+
name = _mangle(dir + "/" + file)
|
168
|
+
@cache.transaction do |db|
|
169
|
+
filekey = [MetaFile, file]
|
170
|
+
md5key = [MetaMD5, file]
|
171
|
+
db.remove filekey
|
172
|
+
db.remove md5key
|
173
|
+
content = _readfile name
|
174
|
+
db[md5key] = MD5.hexdigest(content)
|
175
|
+
db[filekey] = content
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def _rmdir_p(name)
|
180
|
+
raise if name == nil || name[0] == "/"
|
181
|
+
files = [name]
|
182
|
+
Dir.glob(name + "/**/*") do |f|
|
183
|
+
files << f
|
184
|
+
end
|
185
|
+
files.sort!{|a, b| b.length <=> a.length}
|
186
|
+
files.each{|x|
|
187
|
+
if FileTest.file?(x)
|
188
|
+
File.delete x
|
189
|
+
else
|
190
|
+
Dir.delete x
|
191
|
+
end
|
192
|
+
}
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|