fast_xor 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2006-2009 Steve Sloan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.rdoc ADDED
@@ -0,0 +1,33 @@
1
+ = String XOR Ruby Extension
2
+
3
+ +fast_xor+ is a simple extension which provides fast in-place String XOR functions (suitable for cryptography).
4
+
5
+ == How do you use it?
6
+
7
+ require 'xor'
8
+
9
+ # two-argument version
10
+ a, b = 'a string', 'another string'
11
+ a.xor!(b)
12
+ a == "\000N\034\000\032\f\034G"
13
+
14
+ # three-argument version
15
+ a, b, c = 'a string', 'another string', 'yet another string'
16
+ a.xor!(b, c)
17
+ a == "y+h {bs3"
18
+
19
+ == How fast is "Fast"?
20
+
21
+ On my computer, about 1,000x faster than pure Ruby (your mileage my vary):
22
+
23
+ $ ruby test/benchmark.rb
24
+ user system total real
25
+ Ruby: 2.070000 0.930000 3.000000 ( 3.004088)
26
+ C : 0.010000 0.000000 0.010000 ( 0.003045)
27
+
28
+
29
+ Author:: Steve Sloan (mailto:steve@finagle.org)
30
+ Website:: http://github.com/CodeMonkeySteve/fast_xor
31
+ Copyright:: Copyright (c) 2009 Steve Sloan
32
+ License:: MIT
33
+
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/extensiontask'
4
+ require 'rake/testtask'
5
+ require 'jeweler'
6
+
7
+ namespace 'xor' do
8
+ desc "Build the XOR extension"
9
+ Rake::ExtensionTask.new :xor do |t|
10
+ t.dir = 'ext'
11
+ end
12
+ end
13
+
14
+ task :test => 'xor:xor'
15
+ Rake::TestTask.new do |t|
16
+ t.libs << 'ext'
17
+ t.test_files = FileList['test/test_*.rb']
18
+ t.verbose = true
19
+ end
20
+
21
+ task :all => ['xor:xor', :test]
22
+ task :default => :all
23
+
24
+ Jeweler::Tasks.new do |g|
25
+ g.name = 'fast_xor'
26
+ g.summary = 'Fast String XOR operator'
27
+ g.description = 'Provides a C-optimized method for in-place XORing of two (or three) strings'
28
+ g.email = 'steve@finagle.org'
29
+ g.homepage = 'http://github.com/CodeMonkeySteve/fast_xor'
30
+ g.authors = ['Steve Sloan']
31
+
32
+ g.files = %w(
33
+ README.rdoc MIT-LICENSE
34
+ rake/extensiontask.rb
35
+ ext/xor.c
36
+ test/test_xor.rb
37
+ test/benchmark.rb
38
+ )
39
+ g.extensions = ['Rakefile']
40
+ g.require_paths = %w(ext)
41
+
42
+ # g.has_rdoc = true
43
+ # g.rdoc_options = %w| --line-numbers --inline-source --main README.rdoc |
44
+ # g.extra_rdoc_files = %w| README.rdoc |
45
+ end
46
+ Jeweler::GemcutterTasks.new
data/ext/xor.c ADDED
@@ -0,0 +1,55 @@
1
+ #include <stdio.h>
2
+ #include <ruby.h>
3
+
4
+ VALUE string_xor( int argc, VALUE *argv, VALUE self ) {
5
+ const char *src = 0;
6
+ char *dest = 0 ;
7
+ size_t length = 0;
8
+
9
+ if ( (argc < 1) || (argc > 2) ) {
10
+ rb_raise( rb_eArgError, "wrong # of arguments(%d for 1 or 2)", argc );
11
+ return Qnil;
12
+ }
13
+
14
+ dest = STR2CSTR(self);
15
+ length = RSTRING(self)->len;
16
+
17
+ if ( TYPE(argv[0]) == T_STRING ) {
18
+ src = STR2CSTR(argv[0]);
19
+ size_t l = RSTRING(argv[0])->len;
20
+ if ( l < length )
21
+ length = l;
22
+ } else {
23
+ rb_raise( rb_eTypeError, "in method '" "xor" "', argument " "1"" of type '" "String""'" );
24
+ return Qnil;
25
+ }
26
+
27
+ if ( argc == 1 ) {
28
+ for ( ; length--; ++dest, ++src )
29
+ *dest ^= *src;
30
+
31
+ } else {
32
+ const char *src2 = 0;
33
+
34
+ if ( TYPE(argv[1]) == T_STRING ) {
35
+ src2 = STR2CSTR(argv[1]);
36
+ size_t l = RSTRING(argv[1])->len;
37
+ if ( l < length )
38
+ length = l;
39
+ } else {
40
+ rb_raise( rb_eTypeError, "in method '" "xor" "', argument " "2"" of type '" "String""'" );
41
+ return Qnil;
42
+ }
43
+
44
+ for ( ; length--; ++dest, ++src, ++src2 )
45
+ *dest ^= *src ^ *src2;
46
+ }
47
+
48
+ return self;
49
+ }
50
+
51
+
52
+ void Init_xor( void )
53
+ {
54
+ rb_define_method( rb_cString, "xor!", ((VALUE (*)(ANYARGS)) string_xor), -1 );
55
+ }
@@ -0,0 +1,191 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/tasklib'
4
+
5
+ # Rake tasks to build Ruby extensions
6
+
7
+ module Rake
8
+
9
+ # Create a build task that will generate a Ruby extension (e.g. .so) from one or more
10
+ # C (.c) or C++ (.cc, .cpp, .cxx) files, and is intended as a replcaement for mkmf.
11
+ # It determines platform-specific settings (e.g. file extensions, compiler flags, etc.)
12
+ # from rbconfig (note: examples assume *nix file extensions).
13
+ #
14
+ # *Note*: Strings vs Symbols
15
+ # In places where filenames are expected (e.g. lib_name and objs), Strings are used
16
+ # as verbatim filenames, while, Symbols have the platform-dependant extension
17
+ # appended (e.g. '.so' for libraries and '.o' for objects). Also, only Symbols
18
+ # have #dir prepended to them.
19
+ #
20
+ # Example:
21
+ # desc "build sample extension"
22
+ # # build sample.so (from foo.{c,cc,cxx,cpp}, through foo.o)
23
+ # Rake::ExtensionTask.new :sample => :foo do |t|
24
+ # # all extension files under this directory
25
+ # t.dir = 'ext'
26
+ # # link libraries (libbar.so)
27
+ # t.link_libs << 'bar'
28
+ # end
29
+ #
30
+ # Author:: Steve Sloan (mailto:steve@finagle.org)
31
+ # Copyright:: Copyright (c) 2006 Steve Sloan
32
+ # License:: GPL
33
+
34
+ class ExtensionTask < Rake::TaskLib
35
+ # The name of the extension
36
+ attr_accessor :name
37
+
38
+ # The filename of the extension library file (e.g. 'extension.so')
39
+ attr_accessor :lib_name
40
+
41
+ # Object files to build and link into the extension.
42
+ attr_accessor :objs
43
+
44
+ # The directory where the extension files (source, output, and
45
+ # intermediate) are stored.
46
+ attr_accessor :dir
47
+
48
+ # Environment configuration -- i.e. CONFIG from rbconfig, with a few other
49
+ # settings, and converted to lowercase-symbols.
50
+ attr_accessor :env
51
+
52
+ # Additional link libraries
53
+ attr_accessor :link_libs
54
+
55
+ # Same arguments as Rake::define_task
56
+ def initialize( args, &blk )
57
+ @env = @@DefaultEnv.dup
58
+ @name, @objs = resolve_args(args)
59
+ set_defaults
60
+ yield self if block_given?
61
+ define_tasks
62
+ end
63
+
64
+ # Generate default values. This is called from initialize _before_ the
65
+ # yield block.
66
+ #
67
+ # Defaults:
68
+ # - lib_name: name.so
69
+ # - objs: name.o (<- name.{c,cxx,cpp,cc})
70
+ # - dir: .
71
+ # - link_libs: <none>
72
+ def set_defaults
73
+ @lib_name ||= name.to_sym
74
+ @objs = [name.to_sym] unless @objs and @objs.any?
75
+ @dir ||= '.'
76
+ @link_libs ||= []
77
+ end
78
+
79
+ # Defines the library task.
80
+ def define_tasks
81
+ output_objs = @objs.collect { |obj| filepath obj, :objext }
82
+ output_lib = filepath lib_name, :dlext
83
+
84
+ task name => output_lib
85
+
86
+ file output_lib => output_objs do |t|
87
+ sh_cmd :ldshared, :dldflags, :ldflags,
88
+ {'-L' => :libdirs}, '-o', output_lib,
89
+ output_objs.join(' '),
90
+ link_libs.join(' '),
91
+ :libs, :dldlibs, :librubyarg_shared
92
+ end
93
+
94
+ CLEAN.include output_objs
95
+ CLOBBER.include output_lib
96
+ define_rules
97
+ end
98
+
99
+ # Defines C and C++ source-to-object rules, using the source extensions from env.
100
+ def define_rules
101
+ for ext in env[:c_exts]
102
+ Rake::Task.create_rule '.'+env[:objext] => '.'+ext do |r|
103
+ sh_cmd :cc, :cflags, :cppflags, {'-D' => :defines}, {'-I' => :includedirs}, {'-I' => :topdir},
104
+ '-c', '-o', r.name, r.sources
105
+ end
106
+ end
107
+
108
+ for ext in env[:cpp_exts]
109
+ Rake::Task.create_rule '.'+env[:objext] => '.'+ext do |r|
110
+ sh_cmd :cxx, :cxxflags, :cppflags, {'-D' => :defines}, {'-I' => :includedirs}, {'-I' => :topdir},
111
+ '-o', r.name, '-c', r.sources
112
+ end
113
+ end
114
+ end
115
+
116
+ class << self
117
+ # The default environment for all extensions.
118
+ @@DefaultEnv = {}
119
+ def env
120
+ @@DefaultEnv
121
+ end
122
+ def env=(e)
123
+ @@DefaultEnv = e
124
+ end
125
+
126
+ Config::CONFIG.merge(ENV).each { |k, v| @@DefaultEnv[k.downcase.to_sym] = v }
127
+ @@DefaultEnv = {
128
+ :cxx => 'c++',
129
+ :cxxflags => '',
130
+ :c_exts => ['c'],
131
+ :cpp_exts => ['cc', 'cxx', 'cpp'],
132
+ :includedirs => [],
133
+ :libdirs => [],
134
+ }.update(@@DefaultEnv)
135
+ end
136
+
137
+ protected
138
+
139
+ # Handles convenience filenames:
140
+ # * f (String) => f
141
+ # * f (Symbol) => dir/f.ext
142
+ def filepath( f, ext )
143
+ ext = env[ext] if Symbol === ext
144
+ Symbol === f ? File.join( dir, "#{f}.#{ext}" ) : f
145
+ end
146
+
147
+ # Convenience function for cnstructing command lines for build tools.
148
+ def optify( *opts )
149
+ return optify(*opts.first) if opts.size == 1 and opts.first.kind_of? Array
150
+ opts.collect do |opt|
151
+ case opt
152
+ when String then opt
153
+ when Symbol then optify env[opt]
154
+ when Hash
155
+ opt.collect do |k, v|
156
+ v = env[v] if v.kind_of? Symbol
157
+ if v.kind_of? Array
158
+ optify v.collect { |w| k.to_s + w.to_s }
159
+ elsif v
160
+ k.to_s + v.to_s
161
+ end
162
+ end
163
+ else
164
+ opt.to_s
165
+ end
166
+ end.join(' ').squeeze(' ')
167
+ end
168
+
169
+ def sh_cmd( cmd, *opts )
170
+ sh optify( cmd, *opts )
171
+ end
172
+
173
+ # For some reason, Rake::TaskManager.resolve_args can't be found, so snarf it.
174
+ def resolve_args(args)
175
+ case args
176
+ when Hash
177
+ fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1
178
+ fail "No Task Name Given" if args.size < 1
179
+ task_name = args.keys[0]
180
+ deps = args[task_name]
181
+ deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps)
182
+ else
183
+ task_name = args
184
+ deps = []
185
+ end
186
+ [task_name, deps]
187
+ end
188
+
189
+ end
190
+
191
+ end
data/test/benchmark.rb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.dirname($0) + '/../ext'
3
+ require 'benchmark'
4
+
5
+ class String
6
+ def slow_xor!(other)
7
+ i = 0
8
+ other.each_byte { |b| self[i] ^= b ; i += 1 }
9
+ end
10
+
11
+ require 'xor'
12
+ alias_method :fast_xor!, :xor!
13
+ end
14
+
15
+ a = ([255].pack('C')) * (2**17) # 128k
16
+ b = a.dup
17
+
18
+ n = 10
19
+ Benchmark.bm do |x|
20
+ x.report('Ruby:') do n.times { a.slow_xor! b } end
21
+ x.report('C :') do n.times { a.fast_xor! b } end
22
+ end
23
+
data/test/test_xor.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'test/unit'
2
+ require 'xor'
3
+
4
+ class XORTest < Test::Unit::TestCase
5
+ def setup
6
+ @len = 16
7
+ @zero = [0x00].pack('C') * @len
8
+ @one = [0xFF].pack('C') * @len
9
+ @x = (0...@len).collect { rand 256 }.pack('C*')
10
+ @invx = (0...@len).collect { |i| @x[i] ^ 0xFF }.pack('C*')
11
+ end
12
+
13
+ def assert_equal( a, b )
14
+ super a.unpack('H*'), b.unpack('H*')
15
+ end
16
+
17
+ def test_xor_same
18
+ assert_equal @zero, @x.dup.xor!(@x)
19
+ assert_equal @one, @x.dup.xor!(@invx)
20
+ assert_equal @x, @x.dup.xor!(@zero)
21
+ assert_equal @invx, @x.dup.xor!(@one)
22
+ end
23
+
24
+ def test_xor2_same
25
+ assert_equal @x, @x.dup.xor!(@x, @x)
26
+ assert_equal @invx, @x.dup.xor!(@x, @invx)
27
+ assert_equal @zero, @x.dup.xor!(@x, @zero)
28
+ assert_equal @one, @x.dup.xor!(@x, @one)
29
+
30
+ assert_equal @invx, @x.dup.xor!(@invx, @x)
31
+ assert_equal @x, @x.dup.xor!(@invx, @invx)
32
+ assert_equal @one, @x.dup.xor!(@invx, @zero)
33
+ assert_equal @zero, @x.dup.xor!(@invx, @one)
34
+
35
+ assert_equal @zero, @x.dup.xor!(@zero, @x)
36
+ assert_equal @one, @x.dup.xor!(@zero, @invx)
37
+ assert_equal @invx, @x.dup.xor!(@zero, @one)
38
+ assert_equal @x, @x.dup.xor!(@zero, @zero)
39
+
40
+ assert_equal @one, @x.dup.xor!(@one, @x)
41
+ assert_equal @zero, @x.dup.xor!(@one, @invx)
42
+ assert_equal @invx, @x.dup.xor!(@one, @zero)
43
+ assert_equal @x, @x.dup.xor!(@one, @one)
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fast_xor
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Steve Sloan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-26 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Provides a C-optimized method for in-place XORing of two (or three) strings
17
+ email: steve@finagle.org
18
+ executables: []
19
+
20
+ extensions:
21
+ - Rakefile
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - MIT-LICENSE
26
+ - README.rdoc
27
+ - ext/xor.c
28
+ - rake/extensiontask.rb
29
+ - test/benchmark.rb
30
+ - test/test_xor.rb
31
+ has_rdoc: true
32
+ homepage: http://github.com/CodeMonkeySteve/fast_xor
33
+ licenses: []
34
+
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --charset=UTF-8
38
+ require_paths:
39
+ - ext
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project:
55
+ rubygems_version: 1.3.5
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Fast String XOR operator
59
+ test_files:
60
+ - test/benchmark.rb
61
+ - test/test_xor.rb