bitgirder-platform 0.1.7
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/LICENSE.txt +176 -0
- data/bin/ensure-test-db +117 -0
- data/bin/install-mysql +375 -0
- data/bin/tomcat7 +569 -0
- data/lib/bitgirder/concurrent.rb +400 -0
- data/lib/bitgirder/core.rb +1235 -0
- data/lib/bitgirder/etl.rb +58 -0
- data/lib/bitgirder/event/file.rb +485 -0
- data/lib/bitgirder/event/logger/testing.rb +140 -0
- data/lib/bitgirder/event/logger.rb +137 -0
- data/lib/bitgirder/event/testing.rb +88 -0
- data/lib/bitgirder/http.rb +255 -0
- data/lib/bitgirder/io/testing.rb +33 -0
- data/lib/bitgirder/io.rb +959 -0
- data/lib/bitgirder/irb.rb +35 -0
- data/lib/bitgirder/mysql.rb +60 -0
- data/lib/bitgirder/ops/java.rb +117 -0
- data/lib/bitgirder/ops/ruby.rb +235 -0
- data/lib/bitgirder/testing.rb +152 -0
- data/lib/doc-gen0.rb +0 -0
- data/lib/doc-gen1.rb +0 -0
- data/lib/doc-gen10.rb +0 -0
- data/lib/doc-gen11.rb +0 -0
- data/lib/doc-gen12.rb +0 -0
- data/lib/doc-gen13.rb +0 -0
- data/lib/doc-gen14.rb +0 -0
- data/lib/doc-gen15.rb +0 -0
- data/lib/doc-gen16.rb +0 -0
- data/lib/doc-gen17.rb +14 -0
- data/lib/doc-gen18.rb +0 -0
- data/lib/doc-gen19.rb +0 -0
- data/lib/doc-gen2.rb +0 -0
- data/lib/doc-gen20.rb +182 -0
- data/lib/doc-gen21.rb +0 -0
- data/lib/doc-gen3.rb +0 -0
- data/lib/doc-gen4.rb +0 -0
- data/lib/doc-gen5.rb +0 -0
- data/lib/doc-gen6.rb +0 -0
- data/lib/doc-gen7.rb +0 -0
- data/lib/doc-gen8.rb +0 -0
- data/lib/doc-gen9.rb +0 -0
- data/lib/mingle/bincodec.rb +512 -0
- data/lib/mingle/codec.rb +54 -0
- data/lib/mingle/http.rb +156 -0
- data/lib/mingle/io/stream.rb +142 -0
- data/lib/mingle/io.rb +160 -0
- data/lib/mingle/json.rb +257 -0
- data/lib/mingle/service.rb +110 -0
- data/lib/mingle-em.rb +92 -0
- data/lib/mingle.rb +2917 -0
- metadata +100 -0
data/lib/bitgirder/io.rb
ADDED
|
@@ -0,0 +1,959 @@
|
|
|
1
|
+
require 'bitgirder/core'
|
|
2
|
+
|
|
3
|
+
module BitGirder
|
|
4
|
+
module Io
|
|
5
|
+
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
require 'tempfile'
|
|
8
|
+
require 'json'
|
|
9
|
+
require 'yaml'
|
|
10
|
+
require 'base64'
|
|
11
|
+
require 'tmpdir'
|
|
12
|
+
|
|
13
|
+
include BitGirder::Core
|
|
14
|
+
include BitGirderMethods
|
|
15
|
+
extend BitGirderMethods
|
|
16
|
+
|
|
17
|
+
ORDER_LITTLE_ENDIAN = :little_endian
|
|
18
|
+
ORDER_BIG_ENDIAN = :big_endian
|
|
19
|
+
|
|
20
|
+
# Lazily load and assert presence of utf8 encoding
|
|
21
|
+
def enc_utf8
|
|
22
|
+
|
|
23
|
+
@@enc_utf8 ||=
|
|
24
|
+
( Encoding.find( "utf-8" ) or raise "No utf-8 encoding found (?!)" )
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module_function :enc_utf8
|
|
28
|
+
|
|
29
|
+
def fu()
|
|
30
|
+
BitGirderLogger.get_logger.is_debug? ? FileUtils::Verbose : FileUtils
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module_function :fu
|
|
34
|
+
|
|
35
|
+
def as_encoded( str, enc )
|
|
36
|
+
|
|
37
|
+
not_nil( str, :str )
|
|
38
|
+
not_nil( enc, :enc )
|
|
39
|
+
|
|
40
|
+
str.encoding == enc ? str : str.encode( enc )
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
module_function :as_encoded
|
|
44
|
+
|
|
45
|
+
def strict_encode64( str )
|
|
46
|
+
|
|
47
|
+
if RUBY_VERSION >= "1.9"
|
|
48
|
+
Base64.strict_encode64( str )
|
|
49
|
+
else
|
|
50
|
+
Base64.encode64( str ).split( /\n/ ).join( "" )
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
module_function :strict_encode64
|
|
55
|
+
|
|
56
|
+
# Despite the name, this method only enforces strictness when running in a ruby
|
|
57
|
+
# >= 1.9 for now, though we may backport and handcode integrity checks at some
|
|
58
|
+
# point for other rubies
|
|
59
|
+
def strict_decode64( str )
|
|
60
|
+
|
|
61
|
+
if RUBY_VERSION >= "1.9"
|
|
62
|
+
Base64.strict_decode64( str )
|
|
63
|
+
else
|
|
64
|
+
Base64.decode64( str )
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
module_function :strict_decode64
|
|
69
|
+
|
|
70
|
+
# Returns i as a little-endian 2's complement byte array; Algorithm is from
|
|
71
|
+
# http://stackoverflow.com/questions/5284369/ruby-return-byte-array-containing-twos-complement-representation-of-bignum-fi
|
|
72
|
+
# (with some cosmetic differences, including the return val's endianness).
|
|
73
|
+
#
|
|
74
|
+
# Though not stated there, it's worth noting that the reason for the end
|
|
75
|
+
# condition test of the 7th (sign) bit is to avoid stopping prematurely on
|
|
76
|
+
# inputs such as 0xff00, dropping the sign information from the result
|
|
77
|
+
#
|
|
78
|
+
def int_to_byte_array( i )
|
|
79
|
+
|
|
80
|
+
not_nil( i, :i )
|
|
81
|
+
|
|
82
|
+
res = []
|
|
83
|
+
|
|
84
|
+
begin
|
|
85
|
+
res << ( i & 0xff )
|
|
86
|
+
i >>= 8
|
|
87
|
+
end until ( i == 0 || i == -1 ) && ( res[ -1 ][ 7 ] == i[ 7 ] )
|
|
88
|
+
|
|
89
|
+
res
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
module_function :int_to_byte_array
|
|
93
|
+
|
|
94
|
+
def file_exists( d )
|
|
95
|
+
|
|
96
|
+
raise "File or directory #{d} does not exist" unless File.exist?( d )
|
|
97
|
+
d
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
module_function :file_exists
|
|
101
|
+
|
|
102
|
+
def open_tempfile( basename = nil, *rest, &blk )
|
|
103
|
+
|
|
104
|
+
basename ||= [ "bg-tmp-", ".tmp" ]
|
|
105
|
+
Tempfile::open( basename, *rest, &blk )
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def mktmpdir( *argv, &blk )
|
|
109
|
+
Dir.mktmpdir( *argv, &blk )
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
module_function :mktmpdir
|
|
113
|
+
|
|
114
|
+
def fsize( obj )
|
|
115
|
+
|
|
116
|
+
stat =
|
|
117
|
+
case obj
|
|
118
|
+
when File, Tempfile then File.stat( obj.path )
|
|
119
|
+
when String then File.stat( obj )
|
|
120
|
+
else raise TypeError, "Unhandled type for fsize: #{obj.class}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
stat.size
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
module_function :fsize
|
|
127
|
+
|
|
128
|
+
module_function :open_tempfile
|
|
129
|
+
|
|
130
|
+
def write_file( text, file )
|
|
131
|
+
|
|
132
|
+
fu().mkdir_p( File.dirname( file ) )
|
|
133
|
+
File.open( file, "w" ) { |io| io.print text }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
module_function :write_file
|
|
137
|
+
|
|
138
|
+
def first_line( file )
|
|
139
|
+
File.open( file ) { |io| io.readline.chomp }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
module_function :first_line
|
|
143
|
+
|
|
144
|
+
def slurp_io( io, blk_sz = 4096 )
|
|
145
|
+
|
|
146
|
+
not_nil( io, :io )
|
|
147
|
+
|
|
148
|
+
while s = io.read( blk_sz ); res = res ? res << s : s; end
|
|
149
|
+
|
|
150
|
+
res
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
module_function :slurp_io
|
|
154
|
+
|
|
155
|
+
def slurp( io, blk_sz = 4096 )
|
|
156
|
+
|
|
157
|
+
case io
|
|
158
|
+
when IO, Tempfile then slurp_io( io, blk_sz )
|
|
159
|
+
when String then File.open( io ) { |io2| slurp_io( io2, blk_sz ) }
|
|
160
|
+
else raise "Unknown slurp target: #{io} (#{io.class})"
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
module_function :slurp
|
|
165
|
+
|
|
166
|
+
def as_write_dest( obj )
|
|
167
|
+
|
|
168
|
+
not_nil( obj, :obj )
|
|
169
|
+
|
|
170
|
+
case obj
|
|
171
|
+
when IO, Tempfile then yield( io )
|
|
172
|
+
when String then File.open( obj, "w" ) { |io| yield( io ) }
|
|
173
|
+
else raise TypeError, "Unknown write dest: #{obj.class}"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
module_function :as_write_dest
|
|
178
|
+
|
|
179
|
+
def as_read_src( obj )
|
|
180
|
+
|
|
181
|
+
not_nil( obj, :obj )
|
|
182
|
+
|
|
183
|
+
case obj
|
|
184
|
+
when IO, Tempfile then yield( obj )
|
|
185
|
+
when String then File.open( file_exists( obj ) ) { |io| yield( io ) }
|
|
186
|
+
else raise TypeError, "Unkown read src: #{obj.class}"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
module_function :as_read_src
|
|
191
|
+
|
|
192
|
+
def as_json( obj ); JSON.generate( not_nil( obj, :obj ) ); end
|
|
193
|
+
|
|
194
|
+
module_function :as_json
|
|
195
|
+
|
|
196
|
+
def parse_json( str ); JSON.parse( not_nil( str, :str ) ); end
|
|
197
|
+
|
|
198
|
+
module_function :parse_json
|
|
199
|
+
|
|
200
|
+
def dump_json( obj, file )
|
|
201
|
+
|
|
202
|
+
not_nil( obj, :obj )
|
|
203
|
+
not_nil( file, :file )
|
|
204
|
+
|
|
205
|
+
json = as_json( obj )
|
|
206
|
+
|
|
207
|
+
as_write_dest( file ) { |io| io.print( json ) }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
module_function :dump_json
|
|
211
|
+
|
|
212
|
+
def load_json( file )
|
|
213
|
+
|
|
214
|
+
not_nil( file, :file )
|
|
215
|
+
as_read_src( file ) { |io| parse_json( slurp( io ) ) }
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
module_function :load_json
|
|
219
|
+
|
|
220
|
+
def dump_yaml( obj, dest )
|
|
221
|
+
|
|
222
|
+
not_nil( obj, :obj )
|
|
223
|
+
not_nil( dest, :dest )
|
|
224
|
+
|
|
225
|
+
as_write_dest( dest ) { |io| YAML.dump( obj, io ) }
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
module_function :dump_yaml
|
|
229
|
+
|
|
230
|
+
def load_yaml( src )
|
|
231
|
+
|
|
232
|
+
not_nil( src, :src )
|
|
233
|
+
|
|
234
|
+
as_read_src( src ) { |io| YAML.load( io ) }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
module_function :load_yaml
|
|
238
|
+
|
|
239
|
+
def is_executable( file )
|
|
240
|
+
|
|
241
|
+
file_exists( file )
|
|
242
|
+
raise "Not an executable: #{file}" unless File.executable?( file )
|
|
243
|
+
|
|
244
|
+
file
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
module_function :is_executable
|
|
248
|
+
|
|
249
|
+
# Effectively enables mdkir_p as an inline function to enable statements
|
|
250
|
+
# like:
|
|
251
|
+
#
|
|
252
|
+
# some_dir = ensure_dir( some_dir )
|
|
253
|
+
#
|
|
254
|
+
def ensure_dir( d )
|
|
255
|
+
|
|
256
|
+
fu().mkdir_p( d ) unless File.exist?( d )
|
|
257
|
+
d
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
module_function :ensure_dir
|
|
261
|
+
|
|
262
|
+
def ensure_wiped( d )
|
|
263
|
+
|
|
264
|
+
fu().rm_rf( d )
|
|
265
|
+
ensure_dir( d )
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
module_function :ensure_wiped
|
|
269
|
+
|
|
270
|
+
def ensure_dirs( *dirs )
|
|
271
|
+
dirs.each { |dir| ensure_dir( dir ) }
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
module_function :ensure_dirs
|
|
275
|
+
|
|
276
|
+
# Ensures that the directory referred to as dirname( file ) exists, and
|
|
277
|
+
# returns file itself (not the parent). Fails if dirname does not return
|
|
278
|
+
# anything meaningful for file.
|
|
279
|
+
def ensure_parent( file )
|
|
280
|
+
|
|
281
|
+
parent = File.dirname( file )
|
|
282
|
+
raise "No parent exists for #{file}" unless parent
|
|
283
|
+
|
|
284
|
+
ensure_dir( parent )
|
|
285
|
+
|
|
286
|
+
file
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
module_function :ensure_parent
|
|
290
|
+
|
|
291
|
+
def which( cmd, fail_on_miss = false )
|
|
292
|
+
|
|
293
|
+
not_nil( cmd, "cmd" )
|
|
294
|
+
|
|
295
|
+
if ( f = `which #{cmd}`.strip ) and f.empty?
|
|
296
|
+
raise "Cannot find command #{cmd.inspect} in path" if fail_on_miss
|
|
297
|
+
else
|
|
298
|
+
f
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
module_function :which
|
|
303
|
+
|
|
304
|
+
def debug_wait2( opts )
|
|
305
|
+
|
|
306
|
+
not_nil( opts, "opts" )
|
|
307
|
+
|
|
308
|
+
pid = has_key( opts, :pid )
|
|
309
|
+
|
|
310
|
+
name = opts[ :name ]
|
|
311
|
+
name_str = name ? "#{name} (pid #{pid})" : pid.to_s
|
|
312
|
+
code( "Waiting on #{name_str}" )
|
|
313
|
+
|
|
314
|
+
pid, status = Process::wait2( pid )
|
|
315
|
+
msg = "Process #{pid} exited with status #{status.exitstatus}"
|
|
316
|
+
|
|
317
|
+
if status.success?
|
|
318
|
+
code( msg )
|
|
319
|
+
else
|
|
320
|
+
opts[ :check_status ] ? ( raise msg ) : warn( msg )
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
[ pid, status ]
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
module_function :debug_wait2
|
|
327
|
+
|
|
328
|
+
def debug_kill( sig, pid, opts = {} )
|
|
329
|
+
|
|
330
|
+
not_nil( opts, "opts" )
|
|
331
|
+
|
|
332
|
+
msg = "Sending #{sig} to #{pid}"
|
|
333
|
+
msg += " (#{opts[ :name ]})" if opts.key?( :name )
|
|
334
|
+
|
|
335
|
+
BitGirderLogger.get_logger.code( msg )
|
|
336
|
+
Process.kill( sig, pid )
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
module_function :debug_kill
|
|
340
|
+
|
|
341
|
+
def read_full( io, len, buf = nil )
|
|
342
|
+
|
|
343
|
+
args = [ len ]
|
|
344
|
+
args << buf if buf
|
|
345
|
+
buf = io.read( *args )
|
|
346
|
+
|
|
347
|
+
if ( sz = buf == nil ? 0 : buf.bytesize ) < len
|
|
348
|
+
raise EOFError.new( "EOF after #{sz} bytes (wanted #{len})" )
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
buf
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
module_function :read_full
|
|
355
|
+
|
|
356
|
+
class DataUnitError < StandardError; end
|
|
357
|
+
|
|
358
|
+
class DataUnit < BitGirderClass
|
|
359
|
+
|
|
360
|
+
bg_attr :name
|
|
361
|
+
bg_attr :byte_scale
|
|
362
|
+
|
|
363
|
+
private_class_method :new
|
|
364
|
+
|
|
365
|
+
BYTE = self.send( :new, :name => :byte, :byte_scale => 1 )
|
|
366
|
+
KILOBYTE = self.send( :new, :name => :kilobyte, :byte_scale => 2 ** 10 )
|
|
367
|
+
MEGABYTE = self.send( :new, :name => :megabyte, :byte_scale => 2 ** 20 )
|
|
368
|
+
GIGABYTE = self.send( :new, :name => :gigabyte, :byte_scale => 2 ** 30 )
|
|
369
|
+
TERABYTE = self.send( :new, :name => :terabyte, :byte_scale => 2 ** 40 )
|
|
370
|
+
PETABYTE = self.send( :new, :name => :petabyte, :byte_scale => 2 ** 50 )
|
|
371
|
+
|
|
372
|
+
UNITS = [ BYTE, KILOBYTE, MEGABYTE, GIGABYTE, TERABYTE, PETABYTE ]
|
|
373
|
+
|
|
374
|
+
map_instance_of( String, Symbol ) do |val|
|
|
375
|
+
|
|
376
|
+
# Avoid string ops on val if we can
|
|
377
|
+
if res = UNITS.find { |u| u.name == res }
|
|
378
|
+
return res
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
case val.to_s.downcase.to_sym
|
|
382
|
+
when :b, :byte, :bytes then BYTE
|
|
383
|
+
when :k, :kb, :kilobyte, :kilobytes then KILOBYTE
|
|
384
|
+
when :m, :mb, :megabyte, :megabytes then MEGABYTE
|
|
385
|
+
when :g, :gb, :gigabyte, :gigabytes then GIGABYTE
|
|
386
|
+
when :t, :tb, :terabyte, :terabytes then TERABYTE
|
|
387
|
+
when :p, :pb, :petabyte, :petabytes then PETABYTE
|
|
388
|
+
else raise DataUnitError, "Unknown data unit: #{val}"
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
class DataSizeError < StandardError; end
|
|
394
|
+
|
|
395
|
+
class DataSize < BitGirderClass
|
|
396
|
+
|
|
397
|
+
include Comparable
|
|
398
|
+
|
|
399
|
+
bg_attr :unit, :processor => DataUnit
|
|
400
|
+
|
|
401
|
+
bg_attr :size, :validation => :nonnegative
|
|
402
|
+
|
|
403
|
+
map_instance_of( Integer ) do |val|
|
|
404
|
+
DataSize.new( :size => val, :unit => DataUnit::BYTE )
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
map_instance_of( String ) do |val|
|
|
408
|
+
|
|
409
|
+
if md = /^\s*(\d+)\s*([a-zA-Z]*)\s*$/.match( val )
|
|
410
|
+
if ( unit = md[ 2 ] ) == "" then unit = DataUnit::BYTE end
|
|
411
|
+
DataSize.new( :size => md[ 1 ].to_i, :unit => unit )
|
|
412
|
+
else
|
|
413
|
+
raise DataSizeError, "Invalid data size: #{val.inspect}"
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
public
|
|
418
|
+
def bytes
|
|
419
|
+
@size * @unit.byte_scale
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
public
|
|
423
|
+
def ==( o )
|
|
424
|
+
return true if o.equal?( self )
|
|
425
|
+
return false unless o.is_a?( DataSize )
|
|
426
|
+
return self.bytes == o.bytes
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
public
|
|
430
|
+
def <=>( o )
|
|
431
|
+
return nil unless o.is_a?( DataSize )
|
|
432
|
+
return self.bytes <=> o.bytes
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
public
|
|
436
|
+
def to_s
|
|
437
|
+
bytes.to_s
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
class BinaryConverter < BitGirderClass
|
|
442
|
+
|
|
443
|
+
require 'bigdecimal'
|
|
444
|
+
|
|
445
|
+
bg_attr :order,
|
|
446
|
+
:processor => :symbol,
|
|
447
|
+
:validation => lambda { |o|
|
|
448
|
+
unless o == ORDER_LITTLE_ENDIAN || o == ORDER_BIG_ENDIAN
|
|
449
|
+
raise "Unrecognized order: #{o}"
|
|
450
|
+
end
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
# Set @plat_order as an instance variable instead of as a class constant. We
|
|
454
|
+
# could theoretically set plat order as a class constant, but don't for 2
|
|
455
|
+
# reasons. One is that, in the event there is a bug in our detection, we
|
|
456
|
+
# don't want to fail any class requiring this file, since some code may
|
|
457
|
+
# never touch the BinaryConverter class anyway. Two is that we can use
|
|
458
|
+
# reflection during testing to simulate a different platform byte ordering
|
|
459
|
+
# on a test-by-test basis, rather than having to change a global constant
|
|
460
|
+
def initialize( *argv )
|
|
461
|
+
|
|
462
|
+
super( *argv )
|
|
463
|
+
|
|
464
|
+
@plat_order = BinaryConverter.detect_platform_order
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
private
|
|
468
|
+
def is_plat_order?
|
|
469
|
+
@order == @plat_order
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
private
|
|
473
|
+
def impl_write_int( num, nm, fmt )
|
|
474
|
+
|
|
475
|
+
not_nil( num, nm )
|
|
476
|
+
res = [ num ].pack( fmt )
|
|
477
|
+
|
|
478
|
+
res.reverse! unless is_plat_order?
|
|
479
|
+
|
|
480
|
+
res
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
private
|
|
484
|
+
def impl_read_int( str, nm, fmt )
|
|
485
|
+
|
|
486
|
+
not_nil( str, nm )
|
|
487
|
+
|
|
488
|
+
str = str.reverse unless is_plat_order?
|
|
489
|
+
str.unpack( fmt )[ 0 ]
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
{
|
|
493
|
+
:int8 => "c",
|
|
494
|
+
:int32 => "l",
|
|
495
|
+
:int64 => "q"
|
|
496
|
+
}.
|
|
497
|
+
each_pair do |type, fmt|
|
|
498
|
+
|
|
499
|
+
[ [ type, fmt ], [ :"u#{type}", fmt.upcase ] ].each do |pair|
|
|
500
|
+
|
|
501
|
+
type2, fmt2 = *pair
|
|
502
|
+
wm, rm = %w{ write read }.map { |s| :"#{s}_#{type2}" }
|
|
503
|
+
|
|
504
|
+
define_method( wm ) { |i| impl_write_int( i, :i, fmt2 ) }
|
|
505
|
+
define_method( rm ) { |s| impl_read_int( s, :s, fmt2 ) }
|
|
506
|
+
|
|
507
|
+
public wm, rm
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
private
|
|
512
|
+
def get_float_format( is64 )
|
|
513
|
+
|
|
514
|
+
fmt =
|
|
515
|
+
case @order
|
|
516
|
+
when ORDER_LITTLE_ENDIAN then 'e'
|
|
517
|
+
when ORDER_BIG_ENDIAN then 'g'
|
|
518
|
+
else raise "Unhandled order: #@order"
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
fmt = fmt.upcase if is64
|
|
522
|
+
|
|
523
|
+
fmt
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
private
|
|
527
|
+
def impl_write_float( num, nm, is64 )
|
|
528
|
+
|
|
529
|
+
not_nil( num, nm )
|
|
530
|
+
|
|
531
|
+
fmt = get_float_format( is64 )
|
|
532
|
+
[ num ].pack( fmt )
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
public
|
|
536
|
+
def write_float32( f )
|
|
537
|
+
impl_write_float( f, :f, false )
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
public
|
|
541
|
+
def write_float64( d )
|
|
542
|
+
impl_write_float( d, :d, true )
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
private
|
|
546
|
+
def impl_read_float( str, nm, is64 )
|
|
547
|
+
|
|
548
|
+
not_nil( str, nm )
|
|
549
|
+
|
|
550
|
+
fmt = get_float_format( is64 )
|
|
551
|
+
str.unpack( fmt )[ 0 ]
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
public
|
|
555
|
+
def read_float32( s )
|
|
556
|
+
impl_read_float( s, :s, false )
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
public
|
|
560
|
+
def read_float64( s )
|
|
561
|
+
impl_read_float( s, :s, true )
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
public
|
|
565
|
+
def write_bignum( i )
|
|
566
|
+
|
|
567
|
+
not_nil( i, :i )
|
|
568
|
+
|
|
569
|
+
arr = Io.int_to_byte_array( i )
|
|
570
|
+
arr.reverse! if @order == ORDER_BIG_ENDIAN
|
|
571
|
+
|
|
572
|
+
res = write_int32( arr.size )
|
|
573
|
+
arr.each { |b| res << b.chr }
|
|
574
|
+
|
|
575
|
+
res
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
private
|
|
579
|
+
def inflate_bignum( s )
|
|
580
|
+
|
|
581
|
+
# Ensure we process bytes in big-endian order
|
|
582
|
+
s = s.reverse if @order == ORDER_LITTLE_ENDIAN
|
|
583
|
+
|
|
584
|
+
res = s[ 0, 1 ].unpack( 'c' )[ 0 ] # res is now set and correctly signed
|
|
585
|
+
|
|
586
|
+
s[ 1 .. -1 ].each_byte do |b|
|
|
587
|
+
res <<= 8
|
|
588
|
+
res += b
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
res
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
public
|
|
595
|
+
def read_bignum( s )
|
|
596
|
+
read_bignum_with_info( s )[ 0 ]
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
public
|
|
600
|
+
def read_bignum_with_info( s )
|
|
601
|
+
|
|
602
|
+
not_nil( s, :s )
|
|
603
|
+
|
|
604
|
+
if ( len = s.size ) < 4
|
|
605
|
+
raise "Input does not have a size (input len: #{len})"
|
|
606
|
+
elsif len == 4
|
|
607
|
+
raise "Input has no integer data (input len: #{len})"
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
sz = read_int32( s[ 0, 4 ] )
|
|
611
|
+
rem = [ len - 4, len - sz ].min
|
|
612
|
+
|
|
613
|
+
if ( rem = s.size - 4 ) < sz
|
|
614
|
+
raise "Input specifies size #{sz} but actually is only of " \
|
|
615
|
+
"length #{rem}"
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
info = { :total_len => 4 + sz, :hdr_len => 4, :num_len => sz }
|
|
619
|
+
[ inflate_bignum( s[ 4, sz ] ), info ]
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
# write_bigdec (read_bigdec does the inverse) interprets ruby BigDecimal
|
|
623
|
+
# first as an unscaled value and a scale as defined in Java's BigDecimal
|
|
624
|
+
# class, and then serializes them as the concatenation of write_bignum(
|
|
625
|
+
# unscaled ) and write_int32( scale ), with each component's byte-order
|
|
626
|
+
# determined by @order
|
|
627
|
+
public
|
|
628
|
+
def write_bigdec( n )
|
|
629
|
+
|
|
630
|
+
not_nil( n, :n )
|
|
631
|
+
raise "Not a BigDecimal" unless n.is_a?( BigDecimal )
|
|
632
|
+
|
|
633
|
+
sign, unscaled, base, exp = n.split
|
|
634
|
+
raise "Unexpected base: #{base}" unless base == 10
|
|
635
|
+
raise "NaN not supported" if sign == 0
|
|
636
|
+
|
|
637
|
+
if ( unscaled_i = unscaled.to_i ) == 0
|
|
638
|
+
write_bignum( 0 ) + write_int32( 0 )
|
|
639
|
+
else
|
|
640
|
+
# sci_exp is the exponent we'd get using scientific notation
|
|
641
|
+
sci_exp = -unscaled.size + exp
|
|
642
|
+
write_bignum( unscaled_i * sign ) + write_int32( sci_exp )
|
|
643
|
+
end
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
public
|
|
647
|
+
def read_bigdec_with_info( s )
|
|
648
|
+
|
|
649
|
+
not_nil( s, :s )
|
|
650
|
+
|
|
651
|
+
unscaled, info1 = read_bignum_with_info( s )
|
|
652
|
+
unscaled_len = has_key( info1, :total_len )
|
|
653
|
+
|
|
654
|
+
scale = read_int32( s[ unscaled_len, 4 ] )
|
|
655
|
+
|
|
656
|
+
info2 = {
|
|
657
|
+
:total_len => unscaled_len + 4,
|
|
658
|
+
:unscaled_len => unscaled_len,
|
|
659
|
+
:scale_len => 4
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
num_str = unscaled.to_s( 10 ) + "e" + ( scale ).to_s
|
|
663
|
+
|
|
664
|
+
[ BigDecimal.new( num_str ), info2 ]
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
public
|
|
668
|
+
def read_bigdec( s )
|
|
669
|
+
read_bigdec_with_info( s )[ 0 ]
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
def self.detect_platform_order
|
|
673
|
+
|
|
674
|
+
case test = [ 1 ].pack( 's' )
|
|
675
|
+
when "\x01\x00" then ORDER_LITTLE_ENDIAN
|
|
676
|
+
when "\x00\x01" then ORDER_BIG_ENDIAN
|
|
677
|
+
else raise "Undetected byte order: #{test.inspect}"
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
class BinaryIo < BitGirderClass
|
|
683
|
+
|
|
684
|
+
bg_attr :io
|
|
685
|
+
bg_attr :order
|
|
686
|
+
|
|
687
|
+
attr_reader :conv
|
|
688
|
+
|
|
689
|
+
# pos returns the zero-indexed position of the next byte that would be read,
|
|
690
|
+
# in the case of a reader, or the number of bytes that have been written in
|
|
691
|
+
# the case of a writer. This value is only valid as long as no exceptions
|
|
692
|
+
# have been encountered in any of the read|write methods and all access to
|
|
693
|
+
# the underlying io object has been through this instance the read* methods
|
|
694
|
+
attr_reader :pos
|
|
695
|
+
|
|
696
|
+
public
|
|
697
|
+
def close
|
|
698
|
+
@io.close if @io.respond_to?( :close )
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
private
|
|
702
|
+
def impl_initialize
|
|
703
|
+
|
|
704
|
+
super
|
|
705
|
+
@conv = BinaryConverter.new( :order => @order )
|
|
706
|
+
@pos = 0
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def self.new_with_order( ord, opts )
|
|
710
|
+
self.new( { :order => ord }.merge( opts ) )
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def self.new_le( opts )
|
|
714
|
+
self.new_with_order( ORDER_LITTLE_ENDIAN, opts )
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def self.new_be( opts )
|
|
718
|
+
self.new_with_order( ORDER_BIG_ENDIAN, opts )
|
|
719
|
+
end
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
class BinaryWriter < BinaryIo
|
|
723
|
+
|
|
724
|
+
{
|
|
725
|
+
:int8 => 1,
|
|
726
|
+
:int32 => 4,
|
|
727
|
+
:int64 => 8,
|
|
728
|
+
:uint8 => 1,
|
|
729
|
+
:uint32 => 4,
|
|
730
|
+
:uint64 => 8,
|
|
731
|
+
:float32 => 4,
|
|
732
|
+
:float64 => 8
|
|
733
|
+
|
|
734
|
+
}.each_pair do |meth, sz|
|
|
735
|
+
|
|
736
|
+
meth = :"write_#{meth}"
|
|
737
|
+
|
|
738
|
+
define_method( meth ) do |val|
|
|
739
|
+
@io.write( @conv.send( meth, val ) )
|
|
740
|
+
@pos += sz
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
public
|
|
745
|
+
def write_bool( b )
|
|
746
|
+
write_int8( b ? 1 : 0 )
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
public
|
|
750
|
+
def write_full( buf )
|
|
751
|
+
@io.write( buf )
|
|
752
|
+
@pos += buf.bytesize
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
alias write write_full
|
|
756
|
+
|
|
757
|
+
private
|
|
758
|
+
def impl_write_buffer32( buf, sz )
|
|
759
|
+
|
|
760
|
+
write_int32( sz )
|
|
761
|
+
write_full( buf )
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
public
|
|
765
|
+
def write_buffer32( buf )
|
|
766
|
+
|
|
767
|
+
not_nil( buf, :buf )
|
|
768
|
+
impl_write_buffer32( buf, buf.bytesize )
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
public
|
|
772
|
+
def write_utf8( str )
|
|
773
|
+
|
|
774
|
+
not_nil( str, :str )
|
|
775
|
+
|
|
776
|
+
RubyVersions.when_19x { str = Io.as_encoded( str, Encoding::UTF_8 ) }
|
|
777
|
+
|
|
778
|
+
impl_write_buffer32( str, str.bytesize )
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
class BinaryReader < BinaryIo
|
|
783
|
+
|
|
784
|
+
public
|
|
785
|
+
def eof?
|
|
786
|
+
@io.eof?
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
public
|
|
790
|
+
def peekc
|
|
791
|
+
|
|
792
|
+
res = @io.getc.tap { |c| @io.ungetc( c ) }
|
|
793
|
+
|
|
794
|
+
case res
|
|
795
|
+
when String then res
|
|
796
|
+
when Fixnum then res.chr
|
|
797
|
+
else raise "Unexpected getc val: #{res.class}"
|
|
798
|
+
end
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
public
|
|
802
|
+
def peek_int8
|
|
803
|
+
peekc.unpack( 'c' )[ 0 ]
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
public
|
|
807
|
+
def read_full( len, buf = nil )
|
|
808
|
+
Io.read_full( @io, len, buf ).tap { @pos += len }
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
public
|
|
812
|
+
def read( *argv )
|
|
813
|
+
@io.read( *argv ).tap { |res| @pos += res.bytesize }
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
{
|
|
817
|
+
:int8 => 1,
|
|
818
|
+
:int32 => 4,
|
|
819
|
+
:int64 => 8,
|
|
820
|
+
:uint8 => 1,
|
|
821
|
+
:uint32 => 4,
|
|
822
|
+
:uint64 => 8,
|
|
823
|
+
:float32 => 4,
|
|
824
|
+
:float64 => 8
|
|
825
|
+
|
|
826
|
+
}.each_pair do |k, v|
|
|
827
|
+
meth = :"read_#{k}"
|
|
828
|
+
define_method( meth ) { @conv.send( meth, read_full( v ) ) }
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
public
|
|
832
|
+
def read_bool
|
|
833
|
+
read_int8 != 0
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
alias read_boolean read_bool
|
|
837
|
+
|
|
838
|
+
public
|
|
839
|
+
def read_buffer32
|
|
840
|
+
read_full( read_int32 )
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
public
|
|
844
|
+
def read_utf8
|
|
845
|
+
|
|
846
|
+
len = read_int32
|
|
847
|
+
|
|
848
|
+
str = "" * len
|
|
849
|
+
read_full( len, str )
|
|
850
|
+
RubyVersions.when_19x { str.force_encoding( "utf-8" ) }
|
|
851
|
+
|
|
852
|
+
str
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
class UnixProcessBuilder < BitGirderClass
|
|
857
|
+
|
|
858
|
+
bg_attr :cmd
|
|
859
|
+
|
|
860
|
+
bg_attr :argv, :default => []
|
|
861
|
+
|
|
862
|
+
bg_attr :env, :default => {}
|
|
863
|
+
|
|
864
|
+
bg_attr :opts, :default => {}
|
|
865
|
+
|
|
866
|
+
bg_attr :show_env_in_debug, :mutable => true, :required => false
|
|
867
|
+
|
|
868
|
+
def initialize( opts )
|
|
869
|
+
super( opts )
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
private
|
|
873
|
+
def debug_call( opts )
|
|
874
|
+
|
|
875
|
+
dbg = { :cmd => @cmd, :argv => @argv, :opts => @opts }
|
|
876
|
+
dbg[ :env ] = @env if @show_env_in_debug
|
|
877
|
+
|
|
878
|
+
code( "Doing #{opts[ :call_type ]} with #{dbg.inspect}" )
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
private
|
|
882
|
+
def str_map( val )
|
|
883
|
+
|
|
884
|
+
case val
|
|
885
|
+
|
|
886
|
+
when Array then val.map { |o| str_map( o ) }
|
|
887
|
+
|
|
888
|
+
when Hash
|
|
889
|
+
val.inject( {} ) do |h, pair|
|
|
890
|
+
h[ str_map( pair[ 0 ] ) ] = str_map( pair[ 1 ] )
|
|
891
|
+
h
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
else val.to_s
|
|
895
|
+
end
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
private
|
|
899
|
+
def get_call_argv
|
|
900
|
+
|
|
901
|
+
[ str_map( @env ), str_map( @cmd ), str_map( @argv ), @opts ].flatten
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
def get_call_argv18
|
|
905
|
+
|
|
906
|
+
res = get_call_argv
|
|
907
|
+
[ res[ 1 ], *( res[ 2 ] ) ] # [ cmd, *argv ]
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
public
|
|
911
|
+
def spawn
|
|
912
|
+
|
|
913
|
+
debug_call( :call_type => "spawn" )
|
|
914
|
+
Process.spawn( *get_call_argv )
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
public
|
|
918
|
+
def exec
|
|
919
|
+
|
|
920
|
+
debug_call( :call_type => "exec" )
|
|
921
|
+
Kernel.exec( *get_call_argv )
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
public
|
|
925
|
+
def system( opts = {} )
|
|
926
|
+
|
|
927
|
+
debug_call( :call_type => "system" )
|
|
928
|
+
|
|
929
|
+
proc_res_raise = lambda {
|
|
930
|
+
raise "Command exited with status #{$?.exitstatus}"
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
if RUBY_VERSION >= "1.9"
|
|
934
|
+
proc_res_raise.call unless Kernel.system( *get_call_argv )
|
|
935
|
+
else
|
|
936
|
+
if pid = Kernel.fork
|
|
937
|
+
Process.wait( pid )
|
|
938
|
+
proc_res_raise.call unless $?.success?
|
|
939
|
+
else
|
|
940
|
+
Kernel.exec( *get_call_argv18 )
|
|
941
|
+
end
|
|
942
|
+
end
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
public
|
|
946
|
+
def popen( mode, &blk )
|
|
947
|
+
|
|
948
|
+
debug_call( :call_type => "popen" )
|
|
949
|
+
|
|
950
|
+
if RUBY_VERSION >= "1.9"
|
|
951
|
+
IO.popen( get_call_argv, mode, &blk )
|
|
952
|
+
else
|
|
953
|
+
IO.popen( get_call_argv18.join( " " ), mode, &blk )
|
|
954
|
+
end
|
|
955
|
+
end
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
end
|
|
959
|
+
end
|