unnatural 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e73154b350d906339b8003056791db6c5f94e098
4
- data.tar.gz: ed95cd6dee1227b8f55cce433998685fd1b2c4bb
3
+ metadata.gz: 9634e4ac888d2b64eb49af87a89cd124569a56bf
4
+ data.tar.gz: 65f819eddbaf0d32983aac0b874b506de6211112
5
5
  SHA512:
6
- metadata.gz: d0c8774280f8fdba7f71b1b39bbe63b6558a9a1b4659807b33a7c8b4acbb0c441739184e9155e0b440479fa29981403aad18bf3fc983eaca01c5d2e4fc1c5426
7
- data.tar.gz: 96c913547575eb192399c6074237748c3defb54032fc0f5d4f3644293ba24a81c846e91589398ac09c1b6e19ad575284e2dc57c96327c1795fc02ca27e4beae8
6
+ metadata.gz: 6b10ba4248e41c4295b8fb6117adbb7a1038e8f3812fceba905097fc17c58a32dae3b23d1704498aea796c06a21ce7c99e1e95fe26c7f73f9a7b4bd5bf0acaf8
7
+ data.tar.gz: 80c87c122827309aeb58c611cead2674052b577d33799e2301f97a3311ce1fc8a1e4b42c6920dcd38af99c4fa61e4a6af5d540b72ef2929b69224999518dc479
data/.gitignore CHANGED
@@ -7,4 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
- /lib/unnatural/fast_compare.so
10
+ *.so
11
+ *.o
data/.travis.yml CHANGED
@@ -1,13 +1,13 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.0.0
5
4
  - 2.1.10
6
5
  - 2.2.5
7
6
  - 2.3.1
8
- matrix:
9
- include:
10
- - rvm: jruby
11
- env: JRUBY_OPTS='-Xcompat.version=2.0'
7
+ - jruby-1.7.20
8
+ - jruby-9.0.5.0
12
9
  before_install:
13
- - if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler; fi
10
+ - gem install bundler
11
+ cache:
12
+ - apt
13
+ - bundler
data/README.md CHANGED
@@ -12,7 +12,7 @@ Unnatural defines a natural sort as one where:
12
12
 
13
13
  Unnatural does not (currently) provide support for:
14
14
 
15
- 1. non-ASCII-compatible encoding
15
+ 1. non-ASCII-compatible encoding (although the pure ruby comparison functions seem to work)
16
16
  2. any number representation other than simple decimal integers
17
17
  3. whitespace insensitivity (i.e., one space and two spaces can be considered as different)
18
18
 
@@ -20,7 +20,7 @@ Unnatural provides four algorithms, all of which use Ruby's built-in quicksort a
20
20
 
21
21
  ### Unnatural::Fast
22
22
 
23
- Compares strings byte-by-byte. Comparison function implemented in C. Much faster than any of the pure Ruby options. The default, except on JRuby. Not available on JRuby.
23
+ Compares strings byte-by-byte. Comparison function implemented in C. Does not appear to sort unicode strings correctly. Much faster than any of the pure Ruby options. The default.
24
24
 
25
25
  ### Unnatural::Scan
26
26
 
@@ -32,7 +32,7 @@ Compares strings by spliting them into arrays of alternating string and integer
32
32
 
33
33
  ### Unnatural::Substitution
34
34
 
35
- Compares strings by zero-padding integer sequences such that all are the same length. Pure Ruby. Tends to be outperformed by `Unnatural::Scan` on longer strings. The default on JRuby.
35
+ Compares strings by zero-padding integer sequences such that all are the same length. Pure Ruby. Tends to be outperformed by `Unnatural::Scan` on longer strings. Recommended for sorting short unicode strings.
36
36
 
37
37
  ## Installation
38
38
 
@@ -96,7 +96,6 @@ The default can be changed throughout an application:
96
96
 
97
97
  ```ruby
98
98
  # use Scan instead of Fast
99
- # (or, if on JRruby, use Scan instead of Substitution)
100
99
  Unnatural.algorithm = Unnatural::Scan
101
100
  ```
102
101
 
data/Rakefile CHANGED
@@ -1,27 +1,27 @@
1
+ require 'bundler/setup'
1
2
  require 'bundler/gem_tasks'
3
+
2
4
  require 'rake/testtask'
3
5
 
6
+ require 'rake'
7
+ require 'rake/clean'
8
+
9
+ require 'ffi'
10
+ require 'ffi-compiler/compile_task'
11
+
12
+ CLEAN.include('ext/unnatural/*{.o,.log,.so,.bundle}')
13
+ CLEAN.include('lib/**/*{.o,.log,.so,.bundle}')
14
+
4
15
  Rake::TestTask.new(:test) do |t|
5
16
  t.libs << 'test'
6
17
  t.libs << 'lib'
7
18
  t.test_files = FileList['test/**/*_test.rb']
8
19
  end
9
20
 
10
- require 'rake/extensiontask'
11
- spec = Gem::Specification.load('unnatural.gemspec')
12
- Rake::ExtensionTask.new do |ext|
13
- ext.name = 'fast_compare'
14
- ext.ext_dir = 'ext/unnatural'
15
- ext.lib_dir = 'lib/unnatural'
16
- ext.gem_spec = spec
21
+ desc 'FFI compiler'
22
+ namespace 'ffi-compiler' do
23
+ FFI::Compiler::CompileTask.new('ext/unnatural/unnatural_ext')
17
24
  end
25
+ task compile_ffi: ['ffi-compiler:default']
18
26
 
19
- task :benchmark do
20
- require './test/benchmark.rb'
21
- end
22
-
23
- if RUBY_ENGINE == 'jruby'
24
- task default: :test
25
- else
26
- task default: [:compile, :test]
27
- end
27
+ task default: [:clean, :compile_ffi, :test]
@@ -0,0 +1,3 @@
1
+ require 'ffi-compiler/compile_task'
2
+
3
+ FFI::Compiler::CompileTask.new('unnatural_ext')
@@ -0,0 +1,48 @@
1
+ #define DIGIT_TO_I(C) (C - '0')
2
+ #define IS_DIGIT(C) ('0' <= C && C <= '9')
3
+ #define IS_LOWER(C) ('a' <= C && C <= 'z')
4
+ #define UPCASE(C) (IS_LOWER(C) ? C - ('a' - 'A') : C)
5
+
6
+ int compare(char* a_str, int a_length, char* b_str, int b_length) {
7
+ int a_pos = 0;
8
+ int b_pos = 0;
9
+ int a_number = 0;
10
+ int b_number = 0;
11
+ char a, b;
12
+ int diff = 0;
13
+
14
+ for (a_pos = 0, b_pos = 0; a_pos < a_length && b_pos < b_length; a_pos++, b_pos++) {
15
+ a = a_str[a_pos];
16
+ b = b_str[b_pos];
17
+
18
+ if ( IS_DIGIT(a) && !IS_DIGIT(b)) return -1;
19
+ if (!IS_DIGIT(a) && IS_DIGIT(b)) return +1;
20
+
21
+ if (IS_DIGIT(a) && IS_DIGIT(b)) {
22
+ while (IS_DIGIT(a)) {
23
+ a_number = a_number * 10 + DIGIT_TO_I(a);
24
+ a_pos++;
25
+ a = a_str[a_pos];
26
+ }
27
+
28
+ while (IS_DIGIT(b)) {
29
+ b_number = b_number * 10 + DIGIT_TO_I(b);
30
+ b_pos++;
31
+ b = b_str[b_pos];
32
+ }
33
+
34
+ diff = a_number - b_number;
35
+ if (diff != 0) return diff;
36
+
37
+ a_number = 0;
38
+ b_number = 0;
39
+ a_pos--;
40
+ b_pos--;
41
+ } else {
42
+ diff = UPCASE(a) - UPCASE(b);
43
+ if (diff != 0) return diff;
44
+ }
45
+ }
46
+
47
+ return a_length - b_length;
48
+ }
@@ -1,21 +1,30 @@
1
- unless RUBY_ENGINE == 'jruby'
2
- require 'unnatural/fast_compare'
1
+ require 'ffi'
2
+ require 'ffi-compiler/loader'
3
3
 
4
- module Unnatural
5
- module Fast
6
- def self.sort(enumerable)
7
- enumerable.sort { |a, b| compare(a, b) }
8
- end
4
+ module Unnatural
5
+ module Fast
6
+ module Ext
7
+ extend FFI::Library
8
+ ffi_lib FFI::Compiler::Loader.find('unnatural_ext')
9
+ attach_function :compare, [:string, :int, :string, :int], :int
10
+ end
9
11
 
10
- def self.sort_by(enumerable)
11
- raise ArgumentError, "Block expected but none given" unless block_given?
12
- enumerable
13
- .map { |e| [(yield e), e] }
14
- .sort { |a, b| compare(a, b) }
15
- .map { |ary| ary[1] }
16
- end
12
+ def self.compare(a, b)
13
+ Ext.compare(a, a.size, b, b.size)
14
+ end
15
+
16
+ def self.sort(enumerable)
17
+ enumerable.sort { |a, b| compare(a, b) }
17
18
  end
18
- end
19
19
 
20
- Unnatural.algorithm ||= Unnatural::Fast
20
+ def self.sort_by(enumerable)
21
+ raise ArgumentError, 'Block expected but none given' unless block_given?
22
+ enumerable
23
+ .map { |e| [(yield e), e] }
24
+ .sort { |a, b| compare(a.first, b.first) }
25
+ .map { |ary| ary[1] }
26
+ end
27
+ end
21
28
  end
29
+
30
+ Unnatural.algorithm ||= Unnatural::Fast
@@ -1,3 +1,3 @@
1
1
  module Unnatural
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
data/unnatural.gemspec CHANGED
@@ -14,18 +14,17 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = 'https://github.com/bjmllr/unnatural'
15
15
  spec.license = 'MIT'
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = 'exe'
19
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ .reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.files += Dir.glob('ext/unnatural/*')
20
20
  spec.require_paths = ['lib']
21
21
 
22
- unless RUBY_ENGINE == 'jruby'
23
- spec.extensions << 'ext/unnatural/extconf.rb'
24
- end
22
+ spec.extensions = ['ext/unnatural/Rakefile']
25
23
 
26
- spec.add_development_dependency 'bundler'
27
- spec.add_development_dependency 'rake', '~> 10.0'
28
- spec.add_development_dependency 'rake-compiler', '~>0.9'
24
+ spec.add_dependency 'ffi-compiler', '~> 1.0'
25
+ spec.add_dependency 'rake', '~> 10.0'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.12'
29
28
  spec.add_development_dependency 'minitest', '~> 5.0'
30
29
  spec.add_development_dependency 'benchmark-ips'
31
30
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unnatural
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Miller
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-12 00:00:00.000000000 Z
11
+ date: 2016-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: ffi-compiler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
19
+ version: '1.0'
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -31,7 +31,7 @@ dependencies:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
- type: :development
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
@@ -39,19 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake-compiler
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.9'
47
+ version: '1.12'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.9'
54
+ version: '1.12'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -85,7 +85,7 @@ email:
85
85
  - bjmllr@gmail.com
86
86
  executables: []
87
87
  extensions:
88
- - ext/unnatural/extconf.rb
88
+ - ext/unnatural/Rakefile
89
89
  extra_rdoc_files: []
90
90
  files:
91
91
  - ".gitignore"
@@ -97,8 +97,8 @@ files:
97
97
  - Rakefile
98
98
  - bin/console
99
99
  - bin/setup
100
- - ext/unnatural/extconf.rb
101
- - ext/unnatural/fast_compare.c
100
+ - ext/unnatural/Rakefile
101
+ - ext/unnatural/unnatural_ext.c
102
102
  - lib/unnatural.rb
103
103
  - lib/unnatural/fast.rb
104
104
  - lib/unnatural/scan.rb
@@ -1,4 +0,0 @@
1
- require 'mkmf'
2
- extension_name = 'unnatural/fast_compare'
3
- dir_config(extension_name)
4
- create_makefile(extension_name)
@@ -1,77 +0,0 @@
1
- #include <ruby.h>
2
-
3
- static VALUE rb_mUnnatural;
4
- static VALUE rb_mFast;
5
-
6
- #define DIGIT_TO_I(C) (C - '0')
7
- #define IS_DIGIT(C) ('0' <= C && C <= '9')
8
- #define IS_LOWER(C) ('a' <= C && C <= 'z')
9
- #define UPCASE(C) (IS_LOWER(C) ? C - ('a' - 'A') : C)
10
-
11
- VALUE compare(VALUE self, VALUE a_arg, VALUE b_arg) {
12
- VALUE a_value, b_value;
13
- int a_length, b_length, a_pos, b_pos, a_number, b_number, diff;
14
- char *a_str, *b_str, a, b;
15
-
16
- if (RB_TYPE_P(a_arg, T_ARRAY)) {
17
- a_value = rb_ary_entry(a_arg, 0);
18
- b_value = rb_ary_entry(b_arg, 0);
19
- } else {
20
- a_value = a_arg;
21
- b_value = b_arg;
22
- }
23
-
24
- a_length = RSTRING_LEN(a_value);
25
- b_length = RSTRING_LEN(b_value);
26
- a_str = RSTRING_PTR(a_value);
27
- b_str = RSTRING_PTR(b_value);
28
- a_pos = 0;
29
- b_pos = 0;
30
- a_number = 0;
31
- b_number = 0;
32
- diff = 0;
33
-
34
- for (a_pos = 0, b_pos = 0; a_pos < a_length && b_pos < b_length; a_pos++, b_pos++) {
35
- a = a_str[a_pos];
36
- b = b_str[b_pos];
37
-
38
- if ( IS_DIGIT(a) && !IS_DIGIT(b)) return INT2FIX(-1);
39
- if (!IS_DIGIT(a) && IS_DIGIT(b)) return INT2FIX(+1);
40
-
41
- if (IS_DIGIT(a) && IS_DIGIT(b)) {
42
- while (IS_DIGIT(a)) {
43
- a_number = a_number * 10 + DIGIT_TO_I(a);
44
- a_pos++;
45
- a = a_str[a_pos];
46
- }
47
-
48
- while (IS_DIGIT(b)) {
49
- b_number = b_number * 10 + DIGIT_TO_I(b);
50
- b_pos++;
51
- b = b_str[b_pos];
52
- }
53
-
54
- diff = a_number - b_number;
55
- if (diff != 0) return INT2FIX(diff);
56
-
57
- a_number = 0;
58
- b_number = 0;
59
- a_pos--;
60
- b_pos--;
61
- } else {
62
- diff = UPCASE(a) - UPCASE(b);
63
- if (diff != 0) return INT2FIX(diff);
64
- }
65
- }
66
-
67
- diff = a_length - b_length;
68
- if (diff != 0) return INT2FIX(diff);
69
-
70
- return INT2FIX(0);
71
- }
72
-
73
- void Init_fast_compare() {
74
- rb_mUnnatural = rb_define_module("Unnatural");
75
- rb_mFast = rb_define_module_under(rb_mUnnatural, "Fast");
76
- rb_define_singleton_method(rb_mFast, "compare", compare, 2);
77
- }