tduehr-libmatty 0.9.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/History.txt +3 -0
- data/README.txt +28 -0
- data/Rakefile +35 -0
- data/bin/libmatty +8 -0
- data/lib/libmatty.rb +49 -0
- data/lib/libmatty/array.rb +42 -0
- data/lib/libmatty/class.rb +167 -0
- data/lib/libmatty/dir.rb +23 -0
- data/lib/libmatty/duplicable.rb +39 -0
- data/lib/libmatty/enumerable.rb +16 -0
- data/lib/libmatty/file.rb +32 -0
- data/lib/libmatty/fixnum.rb +25 -0
- data/lib/libmatty/hash.rb +47 -0
- data/lib/libmatty/integer.rb +120 -0
- data/lib/libmatty/ipaddr.rb +113 -0
- data/lib/libmatty/irb.rb +29 -0
- data/lib/libmatty/math.rb +6 -0
- data/lib/libmatty/module.rb +67 -0
- data/lib/libmatty/numeric.rb +12 -0
- data/lib/libmatty/object.rb +33 -0
- data/lib/libmatty/range.rb +16 -0
- data/lib/libmatty/socket.rb +20 -0
- data/lib/libmatty/string.rb +450 -0
- data/lib/libmatty/symbol.rb +15 -0
- data/spec/libmatty_spec.rb +8 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_libmatty.rb +0 -0
- metadata +108 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
class Range
|
2
|
+
module RangeExtensions
|
3
|
+
# again, surprised this isn't in the library
|
4
|
+
def each_backwards
|
5
|
+
max.to_i.downto(min) {|i| yield i}
|
6
|
+
end
|
7
|
+
|
8
|
+
# Take a random number out of a range
|
9
|
+
#
|
10
|
+
def choice
|
11
|
+
rand(self.last - self.first) + self.first
|
12
|
+
end
|
13
|
+
alias_method :choose, :choice
|
14
|
+
end
|
15
|
+
include RangeExtensions
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Socket
|
2
|
+
module SocketExtensions
|
3
|
+
module ClassMethods
|
4
|
+
def addr(host, port=nil)
|
5
|
+
if not port
|
6
|
+
raise "bad!" if not host =~ /(.*):(.*)/
|
7
|
+
host = $1
|
8
|
+
port = $2
|
9
|
+
end
|
10
|
+
port = port.to_i
|
11
|
+
Socket.pack_sockaddr_in(port, host)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(klass)
|
16
|
+
klass.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
include SocketExtensions
|
20
|
+
end
|
@@ -0,0 +1,450 @@
|
|
1
|
+
class String
|
2
|
+
module StringExtensions
|
3
|
+
|
4
|
+
if String.instance_methods.include? :toutf16
|
5
|
+
alias_method :to_utf16, :toutf16
|
6
|
+
else
|
7
|
+
# Convert a string to "Unicode", ie, the way Win32 expects it, including
|
8
|
+
# trailing NUL.
|
9
|
+
def to_utf16
|
10
|
+
require 'iconv'
|
11
|
+
Iconv.iconv("utf-16LE", "utf-8", self).first + "\x00\x00"
|
12
|
+
end
|
13
|
+
alias_method :toutf16, :to_utf16
|
14
|
+
end
|
15
|
+
|
16
|
+
if String.instance_methods.include? :toutf8
|
17
|
+
# purely for old code using this library that may otherwise still work in 1.9
|
18
|
+
alias_method :to_utf8, :toutf8
|
19
|
+
alias_method :to_ascii, :toutf8
|
20
|
+
alias_method :from_utf16, :toutf8
|
21
|
+
else
|
22
|
+
# Convert a "Unicode" (Win32-style) string back to native Ruby UTF-8;
|
23
|
+
# get rid of any trailing NUL.
|
24
|
+
def from_utf16
|
25
|
+
require 'iconv'
|
26
|
+
ret = Iconv.iconv("utf-8", "utf-16le", self).first
|
27
|
+
if ret[-1] == 0
|
28
|
+
ret = ret[0..-2]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
alias_method :to_utf8, :from_utf16
|
32
|
+
alias_method :to_ascii, :from_utf16
|
33
|
+
alias_method :toutf8, :from_utf16
|
34
|
+
end
|
35
|
+
|
36
|
+
# Convenience for parsing UNICODE strings from a buffer
|
37
|
+
# Assumes last char ends in 00, which is not always true but works in English
|
38
|
+
def from_utf16_buffer
|
39
|
+
self[0..index("\0\0\0")+2].from_utf16
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sometimes string buffers passed through Win32 interfaces come with
|
43
|
+
# garbage after the trailing NUL; this method gets rid of that, like
|
44
|
+
# String#trim
|
45
|
+
def asciiz
|
46
|
+
begin
|
47
|
+
self[0..self.index("\x00")-1]
|
48
|
+
rescue
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def asciiz!
|
54
|
+
replace asciiz
|
55
|
+
end
|
56
|
+
|
57
|
+
# shortcut for hex sanity with regex
|
58
|
+
# remember kids, always pratice safe hex
|
59
|
+
def is_hex? ; (self =~ /^[a-f0-9]+$/i)? true : false ; end
|
60
|
+
|
61
|
+
# My entry into the hexdump race. Outputs canonical hexdump, uses
|
62
|
+
# StringIO for speed, could be cleaned up with "ljust", and should
|
63
|
+
# probably use table lookup instead of to_s(16) method calls.
|
64
|
+
def hexdump(capture=false)
|
65
|
+
require 'stringio'
|
66
|
+
sio = StringIO.new
|
67
|
+
rem = size - 1
|
68
|
+
off = 0
|
69
|
+
|
70
|
+
while rem > 0
|
71
|
+
pbuf = ""
|
72
|
+
pad = (15 - rem) if rem < 16
|
73
|
+
pad ||= 0
|
74
|
+
|
75
|
+
sio.write(("0" * (8 - (x = off.to_s(16)).size)) + x + " ")
|
76
|
+
|
77
|
+
0.upto(15-pad) do |i|
|
78
|
+
c = self[off]
|
79
|
+
x = c.to_s(16)
|
80
|
+
sio.write(("0" * (2 - x.size)) + x + " ")
|
81
|
+
if c.printable?
|
82
|
+
pbuf << c
|
83
|
+
else
|
84
|
+
pbuf << "."
|
85
|
+
end
|
86
|
+
off += 1
|
87
|
+
rem -= 1
|
88
|
+
sio.write(" ") if i == 7
|
89
|
+
end
|
90
|
+
|
91
|
+
sio.write("-- " * pad) if pad > 0
|
92
|
+
sio.write(" |#{ pbuf }|\n")
|
93
|
+
end
|
94
|
+
|
95
|
+
sio.rewind()
|
96
|
+
if capture
|
97
|
+
sio.read()
|
98
|
+
else
|
99
|
+
puts sio.read()
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# convert a string to its idiomatic ruby class name
|
104
|
+
def class_name
|
105
|
+
r = ""
|
106
|
+
up = true
|
107
|
+
each_byte do |c|
|
108
|
+
if c == 95
|
109
|
+
if up
|
110
|
+
r << "::"
|
111
|
+
else
|
112
|
+
up = true
|
113
|
+
end
|
114
|
+
else
|
115
|
+
m = up ? :upcase : :to_s
|
116
|
+
r << (c.chr.send(m))
|
117
|
+
up = false
|
118
|
+
end
|
119
|
+
end
|
120
|
+
r
|
121
|
+
end
|
122
|
+
|
123
|
+
if not String.instance_methods.include?(:starts_with?)
|
124
|
+
# Insane that this isn't in the library by default.
|
125
|
+
# 1.9 does so we don't add it then
|
126
|
+
def starts_with? x
|
127
|
+
self[0..x.size-1] == x
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
if not String.instance_methods.include?(:starts_with?)
|
132
|
+
def ends_with? x
|
133
|
+
self[-(x.size)..-1] == x
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Cribbed from Ero Carrera's pefile; a relatively expensive entropy
|
138
|
+
# function, gives a float result of random-bits-per-byte.
|
139
|
+
def entropy
|
140
|
+
e = 0
|
141
|
+
0.upto(255) do |i|
|
142
|
+
x = count(i.chr)/size.to_f
|
143
|
+
if x > 0
|
144
|
+
e += - x * Math.log2(x)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
return e
|
149
|
+
end
|
150
|
+
|
151
|
+
# The driver function for String#strings below; really, this will
|
152
|
+
# run on any Enumerable that contains Fixnums.
|
153
|
+
def nextstring(opts={})
|
154
|
+
off = opts[:offset] || 0
|
155
|
+
sz = opts[:minimum] || 7
|
156
|
+
u = opts[:unicode] || false
|
157
|
+
l = size
|
158
|
+
i = off
|
159
|
+
while i < l
|
160
|
+
if self[i].printable?
|
161
|
+
start = i
|
162
|
+
cnt = 1
|
163
|
+
i += 1
|
164
|
+
lastu = false
|
165
|
+
while i < l
|
166
|
+
if self[i].printable?
|
167
|
+
lastu = false
|
168
|
+
cnt += 1
|
169
|
+
i += 1
|
170
|
+
elsif u and self[i] == 0 and not lastu
|
171
|
+
lastu = true
|
172
|
+
i += 1
|
173
|
+
else
|
174
|
+
break
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
return([start, i - start]) if cnt >= sz
|
179
|
+
else
|
180
|
+
i += 1
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
return false, false
|
185
|
+
end
|
186
|
+
|
187
|
+
# A la Unix strings(1). With a block, yields offset, string length,
|
188
|
+
# and contents. Otherwise returns a list. Accepts options:
|
189
|
+
# :unicode: superficial but effective Win32 Unicode support, skips NULs
|
190
|
+
# :minimum: minimum length of returned strings, ala strings -10
|
191
|
+
def strings(opts={})
|
192
|
+
ret = []
|
193
|
+
opts[:offset] ||= 0
|
194
|
+
while 1
|
195
|
+
off, size = nextstring(opts)
|
196
|
+
break if not off
|
197
|
+
opts[:offset] += (off + size)
|
198
|
+
if block_given?
|
199
|
+
yield off, size, self[off,size]
|
200
|
+
else
|
201
|
+
ret << [off, size, self[off,size]]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
ret
|
205
|
+
end
|
206
|
+
|
207
|
+
# A hacked up adler16 checksum, a la Andrew Tridgell. This is probably
|
208
|
+
# even slower than Ruby's native CRC support. A weak, trivial checksum,
|
209
|
+
# part of rsync.
|
210
|
+
def adler
|
211
|
+
a, b = 0, 0
|
212
|
+
0.upto(size-1) {|i| a += self[i]}
|
213
|
+
a %= 65536
|
214
|
+
0.upto(size-1) {|i| b += ((size-i)+1) * self[i]}
|
215
|
+
b %= 65536
|
216
|
+
return (a|(b<<16))
|
217
|
+
end
|
218
|
+
|
219
|
+
# Convert binary strings back to integers
|
220
|
+
def to_l32; unpack("L").first; end
|
221
|
+
def to_b32; unpack("N").first; end
|
222
|
+
def to_l16; unpack("v").first; end
|
223
|
+
def to_b16; unpack("n").first; end
|
224
|
+
def to_u8; self[0]; end
|
225
|
+
def shift_l32; shift(4).to_l32; end
|
226
|
+
def shift_b32; shift(4).to_b32; end
|
227
|
+
def shift_l16; shift(2).to_l16; end
|
228
|
+
def shift_b16; shift(2).to_b16; end
|
229
|
+
def shift_u8; shift(1).to_u8; end
|
230
|
+
|
231
|
+
# oh, it's exactly what it sounds like.
|
232
|
+
def method_name
|
233
|
+
r = ""
|
234
|
+
scoped = false
|
235
|
+
each_byte do |c|
|
236
|
+
if c == 58
|
237
|
+
if not scoped
|
238
|
+
r << "_"
|
239
|
+
scoped = true
|
240
|
+
else
|
241
|
+
scoped = false
|
242
|
+
end
|
243
|
+
else
|
244
|
+
if r.size == 0
|
245
|
+
r << c.chr.downcase
|
246
|
+
else
|
247
|
+
if c.upper?
|
248
|
+
r << "_"
|
249
|
+
r << c.chr.downcase
|
250
|
+
else
|
251
|
+
r << c.chr
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
return r
|
257
|
+
end
|
258
|
+
|
259
|
+
# I love you String#ljust
|
260
|
+
def pad(size, char="\x00")
|
261
|
+
ljust(size, char)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Convert a string into hex characters
|
265
|
+
def hexify
|
266
|
+
l = []
|
267
|
+
each_byte{|b| l << "%02x" % b}
|
268
|
+
l.join
|
269
|
+
end
|
270
|
+
|
271
|
+
# convert a string to hex characters in place
|
272
|
+
def hexify!
|
273
|
+
self.replace hexify
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
# Convert a string of raw hex characters (no %'s or anything) into binary
|
278
|
+
def dehexify
|
279
|
+
(ret||="") << (me||=clone).shift(2).to_i(16).chr while not (me||=clone).empty?
|
280
|
+
return ret
|
281
|
+
end
|
282
|
+
|
283
|
+
# Convert a string of raw hex characters (no %'s or anything) into binary in place
|
284
|
+
def dehexify!
|
285
|
+
(ret||="") << (me||=clone).shift(2).to_i(16).chr while not (me||=clone).empty?
|
286
|
+
self.replace ret
|
287
|
+
end
|
288
|
+
|
289
|
+
# OR two strings together. Slow. Handles mismatched lengths by zero-extending
|
290
|
+
def or(str)
|
291
|
+
max = size < str.size ? str.size : size
|
292
|
+
ret = ""
|
293
|
+
0.upto(max-1) do |i|
|
294
|
+
x = self[i] || 0
|
295
|
+
y = str[i] || 0
|
296
|
+
ret << (x | y).chr
|
297
|
+
end
|
298
|
+
return ret
|
299
|
+
end
|
300
|
+
|
301
|
+
# XOR two strings. wrapping around if str is shorter than self.
|
302
|
+
def xor(str)
|
303
|
+
r = []
|
304
|
+
size.times do |i|
|
305
|
+
r << (self[i] ^ str[i % str.size]).chr
|
306
|
+
end
|
307
|
+
return r.join
|
308
|
+
end
|
309
|
+
|
310
|
+
def xor!(str)
|
311
|
+
size.times do |i|
|
312
|
+
self[i] ^= str[i % str.size]
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# byte rotation cypher (yes it's been useful)
|
317
|
+
def rotate_bytes(k=0)
|
318
|
+
r = []
|
319
|
+
each_byte do |b|
|
320
|
+
r << ((b + k) % 256).chr
|
321
|
+
end
|
322
|
+
return r.join
|
323
|
+
end
|
324
|
+
|
325
|
+
# Insanely useful shorthand: pop bytes off the front of a string
|
326
|
+
def shift(count=1)
|
327
|
+
return self if count == 0
|
328
|
+
slice! 0..(count-1)
|
329
|
+
end
|
330
|
+
|
331
|
+
def underscore
|
332
|
+
first = false
|
333
|
+
gsub(/[a-z0-9][A-Z]/) do |m|
|
334
|
+
"#{ m[0].chr }_#{ m[1].chr.downcase }"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# "foo: bar".shift_tok /:\s*/ => "foo" # leaving "bar"
|
339
|
+
def shift_tok(rx)
|
340
|
+
src = rx.source if rx.kind_of? Regexp
|
341
|
+
rx = Regexp.new "(#{ src })"
|
342
|
+
idx = (self =~ rx)
|
343
|
+
if idx
|
344
|
+
ret = shift(idx)
|
345
|
+
shift($1.size)
|
346
|
+
return ret
|
347
|
+
else
|
348
|
+
shift(self.size)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Base64 encode
|
353
|
+
def b64(len=nil)
|
354
|
+
ret = [self].pack("m").gsub("\n", "")
|
355
|
+
if len and Numeric === len
|
356
|
+
ret.scan(/.{1,#{len}}/).join("\n") + "\n"
|
357
|
+
else
|
358
|
+
ret
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# Base64 decode
|
363
|
+
def d64; self.unpack("m")[0]; end
|
364
|
+
|
365
|
+
# Returns a single null-terminated ascii string from beginning of self.
|
366
|
+
# This will return the entire string if no null is encountered.
|
367
|
+
#
|
368
|
+
# Parameters:
|
369
|
+
#
|
370
|
+
# off = specify an optional beggining offset
|
371
|
+
#
|
372
|
+
def cstring(off=0)
|
373
|
+
self[ off, self.index("\x00") || self.size ]
|
374
|
+
end
|
375
|
+
|
376
|
+
# returns CRC32 checksum for the string object
|
377
|
+
def crc32
|
378
|
+
## pure ruby version. slower, but here for reference (found on some forum)
|
379
|
+
# r = 0xFFFFFFFF
|
380
|
+
# self.each_byte do |b|
|
381
|
+
# r ^= b
|
382
|
+
# 8.times do
|
383
|
+
# r = (r>>1) ^ (0xEDB88320 * (r & 1))
|
384
|
+
# end
|
385
|
+
# end
|
386
|
+
# r ^ 0xFFFFFFFF
|
387
|
+
# # or... we can just use:
|
388
|
+
require 'zlib'
|
389
|
+
Zlib.crc32 self
|
390
|
+
end
|
391
|
+
|
392
|
+
# TODO: this needs to work differently....
|
393
|
+
# # Returns a reference to actual constant for a given name in namespace
|
394
|
+
# # can be used to lookup classes from enums and such
|
395
|
+
# def const_lookup(ns=Object)
|
396
|
+
# if c=ns.constants.select {|n| n == self.class_name } and not c.empty?
|
397
|
+
# ns.const_get(c.first)
|
398
|
+
# end
|
399
|
+
# end
|
400
|
+
|
401
|
+
# Return a self encapsulated in a StringIO object.
|
402
|
+
def to_stringio
|
403
|
+
require 'stringio'
|
404
|
+
StringIO.new(self)
|
405
|
+
end
|
406
|
+
|
407
|
+
# a pbcopy from irb on any String object
|
408
|
+
# yes, we're a mac shop.
|
409
|
+
def pbcopy
|
410
|
+
IO.popen("pbcopy", "w") {|io| io.write self}
|
411
|
+
end if Object::RUBY_PLATFORM =~ /darwin/
|
412
|
+
|
413
|
+
def md5
|
414
|
+
require 'digest/md5'
|
415
|
+
Digest::MD5.digest(self).hexify
|
416
|
+
end
|
417
|
+
|
418
|
+
def sha1
|
419
|
+
require 'digest/sha1'
|
420
|
+
Digest::SHA1.digest(self).hexify
|
421
|
+
end
|
422
|
+
|
423
|
+
def sha256
|
424
|
+
require 'digest/sha256'
|
425
|
+
Digest::SHA256.digest(self).hexify
|
426
|
+
end
|
427
|
+
|
428
|
+
def sha512
|
429
|
+
require 'digest/sha512'
|
430
|
+
Digest::SHA512.digest(self).hexify
|
431
|
+
end
|
432
|
+
|
433
|
+
# fast 37 hash of a string, for non-security stuff.
|
434
|
+
def hashcode
|
435
|
+
return 5381 if not ((l = self.size) and l > 0)
|
436
|
+
code = 0
|
437
|
+
0.upto(l-1) do |i|
|
438
|
+
code = ((code << 5) - code) + self[i]
|
439
|
+
end
|
440
|
+
code
|
441
|
+
end
|
442
|
+
|
443
|
+
# Returns a Time object for a string representing seconds since epoch
|
444
|
+
# fmt: format to be passed to String#to_i (see String#to_i)
|
445
|
+
def time_at(fmt=0)
|
446
|
+
Time.at(self.to_i(fmt))
|
447
|
+
end
|
448
|
+
end
|
449
|
+
include StringExtensions
|
450
|
+
end
|