version_sorter 1.1.1 → 2.0.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: 0b1041937013eee0cb93a83330c30e3f6c8b84c5
4
- data.tar.gz: d3f4d2a6834e1e0d79f6ef71cd58da5941c372ae
3
+ metadata.gz: 07d8f2b3a777fa9268641f6e66ec0feaf53173df
4
+ data.tar.gz: 7e87c239f9aa35b697646588c67f6e789177804a
5
5
  SHA512:
6
- metadata.gz: e2d9d09fc0cdc43726e65d197ab5f768e46d2581706fd738c460ab0d90ae9ad893e0241ebc8eeb452691c92f2072412133050026144c92738a19b5df3f62acd6
7
- data.tar.gz: d60d1df2d3bec7574541036ee0cd486aa448a82e8cd74d176a85d288d78b0f53debcf3d82f0dfa5b00e3368042331df58f164cc8a03c14b34cebc977a1035fef
6
+ metadata.gz: 05df11ccb84327dc57dccabe71fbdfa847047d28d45a1af451319510746c50b5fe315e7022c50fa6c72cb7c0ac23a65531a71630cbb087d6aeb0e16b22a2fd23
7
+ data.tar.gz: 8d068f96142e02c18c97459b49efae4338d2cca2b640689a86b918a172c39edf47e7320d3819dfa1021880cf283f6e96ba0414600d2b9195d1536087160e9692
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) K. Adam Christensen
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.
@@ -1,5 +1,3 @@
1
1
  require 'mkmf'
2
2
 
3
- $defs.push("-DBUILD_FOR_RUBY")
4
- have_library('pcre', 'pcre_compile')
5
3
  create_makefile("version_sorter")
@@ -11,203 +11,225 @@
11
11
  #include <stdio.h>
12
12
  #include <string.h>
13
13
  #include <ctype.h>
14
- #include "version_sorter.h"
15
-
16
-
17
- static VersionSortingItem * version_sorting_item_init(const char *, int);
18
- static void version_sorting_item_free(VersionSortingItem *);
19
- static void version_sorting_item_add_piece(VersionSortingItem *, char *);
20
- static void parse_version_word(VersionSortingItem *);
21
- static void create_normalized_version(VersionSortingItem *, const int);
22
- static int compare_by_version(const void *, const void *);
23
- static enum scan_state scan_state_get(const char);
24
-
14
+ #include <assert.h>
15
+ #include <ruby.h>
16
+
17
+ #define min(a, b) ((a) < (b) ? (a) : (b))
18
+ typedef int compare_callback_t(const void *, const void *);
19
+
20
+ struct version_number {
21
+ const char *original;
22
+ VALUE rb_version;
23
+ uint64_t num_flags;
24
+ int32_t size;
25
+ union version_comp {
26
+ uint32_t number;
27
+ struct strchunk {
28
+ uint16_t offset;
29
+ uint16_t len;
30
+ } string;
31
+ } comp[1];
32
+ };
33
+
34
+ static int
35
+ strchunk_cmp(const char *original_a, const struct strchunk *a,
36
+ const char *original_b, const struct strchunk *b)
37
+ {
38
+ size_t len = min(a->len, b->len);
39
+ int cmp = memcmp(original_a + a->offset, original_b + b->offset, len);
40
+ return cmp ? cmp : (int)(a->len - b->len);
41
+ }
25
42
 
26
- VersionSortingItem *
27
- version_sorting_item_init(const char *original, int idx)
43
+ static int
44
+ compare_version_number(const struct version_number *a,
45
+ const struct version_number *b)
28
46
  {
29
- VersionSortingItem *vsi = malloc(sizeof(VersionSortingItem));
30
- if (vsi == NULL) {
31
- DIE("ERROR: Not enough memory to allocate VersionSortingItem")
32
- }
33
- vsi->head = NULL;
34
- vsi->tail = NULL;
35
- vsi->node_len = 0;
36
- vsi->widest_len = 0;
37
- vsi->original = original;
38
- vsi->original_len = strlen(original);
39
- vsi->original_idx = idx;
40
- vsi->normalized = NULL;
41
- parse_version_word(vsi);
42
-
43
- return vsi;
47
+ int n, max_n = min(a->size, b->size);
48
+
49
+ for (n = 0; n < max_n; ++n) {
50
+ int num_a = (a->num_flags & (1ull << n)) != 0;
51
+ int num_b = (b->num_flags & (1ull << n)) != 0;
52
+
53
+ if (num_a == num_b) {
54
+ const union version_comp *ca = &a->comp[n];
55
+ const union version_comp *cb = &b->comp[n];
56
+ int cmp = 0;
57
+
58
+ if (num_a) {
59
+ cmp = (int)ca->number - (int)cb->number;
60
+ } else {
61
+ cmp = strchunk_cmp(
62
+ a->original, &ca->string,
63
+ b->original, &cb->string);
64
+ }
65
+
66
+ if (cmp) return cmp;
67
+ } else {
68
+ return num_a ? 1 : -1;
69
+ }
70
+ }
71
+
72
+ if (a->size < b->size)
73
+ return (b->num_flags & (1ull << n)) ? -1 : 1;
74
+
75
+ if (a->size > b->size)
76
+ return (a->num_flags & (1ull << n)) ? 1 : -1;
77
+
78
+ return 0;
44
79
  }
45
80
 
46
- void
47
- version_sorting_item_free(VersionSortingItem *vsi)
81
+ static int
82
+ version_compare_cb(const void *a, const void *b)
48
83
  {
49
- VersionPiece *cur;
50
- while (cur = vsi->head) {
51
- vsi->head = cur->next;
52
- free(cur->str);
53
- free(cur);
54
- }
55
- if (vsi->normalized != NULL) {
56
- free(vsi->normalized);
57
- }
58
- free(vsi);
84
+ return compare_version_number(
85
+ (*(const struct version_number **)a),
86
+ (*(const struct version_number **)b));
59
87
  }
60
88
 
61
- void
62
- version_sorting_item_add_piece(VersionSortingItem *vsi, char *str)
89
+ static int
90
+ version_compare_cb_r(const void *a, const void *b)
63
91
  {
64
- VersionPiece *piece = malloc(sizeof(VersionPiece));
65
- if (piece == NULL) {
66
- DIE("ERROR: Not enough memory to allocate string linked list node")
67
- }
68
- piece->str = str;
69
- piece->len = strlen(str);
70
- piece->next = NULL;
71
-
72
- if (vsi->head == NULL) {
73
- vsi->head = piece;
74
- vsi->tail = piece;
75
- } else {
76
- vsi->tail->next = piece;
77
- vsi->tail = piece;
78
- }
79
- vsi->node_len++;
80
- if (piece->len > vsi->widest_len) {
81
- vsi->widest_len = piece->len;
82
- }
92
+ return -compare_version_number(
93
+ (*(const struct version_number **)a),
94
+ (*(const struct version_number **)b));
83
95
  }
84
96
 
85
- enum scan_state
86
- scan_state_get(const char c)
97
+ static struct version_number *
98
+ grow_version_number(struct version_number *version, int new_size)
87
99
  {
88
- if (isdigit(c)) {
89
- return digit;
90
- } else if (isalpha(c)) {
91
- return alpha;
92
- } else {
93
- return other;
94
- }
100
+ return xrealloc(version,
101
+ (sizeof(struct version_number) +
102
+ sizeof(union version_comp) * new_size));
103
+ }
95
104
 
105
+ static struct version_number *
106
+ parse_version_number(const char *string)
107
+ {
108
+ struct version_number *version = NULL;
109
+ uint64_t num_flags = 0x0;
110
+ uint16_t offset;
111
+ int comp_n = 0, comp_alloc = 4;
112
+
113
+ version = grow_version_number(version, comp_alloc);
114
+
115
+ for (offset = 0; string[offset] && comp_n < 64;) {
116
+ if (comp_n >= comp_alloc) {
117
+ comp_alloc += 4;
118
+ version = grow_version_number(version, comp_alloc);
119
+ }
120
+
121
+ if (isdigit(string[offset])) {
122
+ uint32_t number = 0;
123
+ uint16_t start = offset;
124
+ int overflown = 0;
125
+
126
+ while (isdigit(string[offset])) {
127
+ if (!overflown) {
128
+ uint32_t old_number = number;
129
+ number = (10 * number) + (string[offset] - '0');
130
+ if (number < old_number) overflown = 1;
131
+ }
132
+
133
+ offset++;
134
+ }
135
+
136
+ if (overflown) {
137
+ version->comp[comp_n].string.offset = start;
138
+ version->comp[comp_n].string.len = offset - start;
139
+ } else {
140
+ version->comp[comp_n].number = number;
141
+ num_flags |= (1 << comp_n);
142
+ }
143
+ comp_n++;
144
+ continue;
145
+ }
146
+
147
+ if (string[offset] == '-' || isalpha(string[offset])) {
148
+ uint16_t start = offset;
149
+
150
+ if (string[offset] == '-')
151
+ offset++;
152
+
153
+ while (isalpha(string[offset]))
154
+ offset++;
155
+
156
+ version->comp[comp_n].string.offset = start;
157
+ version->comp[comp_n].string.len = offset - start;
158
+ comp_n++;
159
+ continue;
160
+ }
161
+
162
+ offset++;
163
+ }
164
+
165
+ version->original = string;
166
+ version->num_flags = num_flags;
167
+ version->size = comp_n;
168
+
169
+ return version;
96
170
  }
97
171
 
98
- void
99
- parse_version_word(VersionSortingItem *vsi)
172
+ static VALUE
173
+ rb_version_sort_1(VALUE rb_self, VALUE rb_version_array, compare_callback_t cmp)
100
174
  {
101
- int start = 0, end = 0, size = 0;
102
- char current_char, next_char;
103
- char *part;
104
- enum scan_state current_state, next_state;
175
+ struct version_number **versions;
176
+ long length, i;
177
+ VALUE *rb_version_ptr;
105
178
 
106
- while ((current_char = vsi->original[start]) != '\0') {
107
- current_state = scan_state_get(current_char);
179
+ Check_Type(rb_version_array, T_ARRAY);
108
180
 
109
- if (current_state == other) {
110
- start++;
111
- end = start;
112
- continue;
113
- }
181
+ length = RARRAY_LEN(rb_version_array);
182
+ if (!length)
183
+ return rb_ary_new();
114
184
 
115
- do {
116
- end++;
117
- next_char = vsi->original[end];
118
- next_state = scan_state_get(next_char);
119
- } while (next_char != '\0' && current_state == next_state);
185
+ versions = xcalloc(length, sizeof(struct version_number *));
120
186
 
121
- size = end - start;
187
+ for (i = 0; i < length; ++i) {
188
+ VALUE rb_version = rb_ary_entry(rb_version_array, i);
189
+ versions[i] = parse_version_number(StringValuePtr(rb_version));
190
+ versions[i]->rb_version = rb_version;
191
+ }
122
192
 
123
- part = malloc((size+1) * sizeof(char));
124
- if (part == NULL) {
125
- DIE("ERROR: Not enough memory to allocate word")
126
- }
193
+ qsort(versions, length, sizeof(struct version_number *), cmp);
194
+ rb_version_ptr = RARRAY_PTR(rb_version_array);
127
195
 
128
- memcpy(part, vsi->original+start, size);
129
- part[size] = '\0';
196
+ for (i = 0; i < length; ++i) {
197
+ rb_version_ptr[i] = versions[i]->rb_version;
198
+ xfree(versions[i]);
199
+ }
200
+ xfree(versions);
201
+ return rb_version_array;
202
+ }
130
203
 
131
- version_sorting_item_add_piece(vsi, part);
204
+ static VALUE
205
+ rb_version_sort(VALUE rb_self, VALUE rb_versions)
206
+ {
207
+ return rb_version_sort_1(rb_self, rb_ary_dup(rb_versions), version_compare_cb);
208
+ }
132
209
 
133
- start = end;
134
- }
210
+ static VALUE
211
+ rb_version_sort_r(VALUE rb_self, VALUE rb_versions)
212
+ {
213
+ return rb_version_sort_1(rb_self, rb_ary_dup(rb_versions), version_compare_cb_r);
135
214
  }
136
215
 
137
- void
138
- create_normalized_version(VersionSortingItem *vsi, const int widest_len)
216
+ static VALUE
217
+ rb_version_sort_bang(VALUE rb_self, VALUE rb_versions)
139
218
  {
140
- VersionPiece *cur;
141
- int pos, i;
142
-
143
- char *result = malloc(((vsi->node_len * widest_len) + 1) * sizeof(char));
144
- if (result == NULL) {
145
- DIE("ERROR: Unable to allocate memory")
146
- }
147
- result[0] = '\0';
148
- pos = 0;
149
-
150
- for (cur = vsi->head; cur; cur = cur->next) {
151
-
152
- /* Left-Pad digits with a space */
153
- if (cur->len < widest_len && isdigit(cur->str[0])) {
154
- for (i = 0; i < widest_len - cur->len; i++) {
155
- result[pos] = ' ';
156
- pos++;
157
- }
158
- result[pos] = '\0';
159
- }
160
- strcat(result, cur->str);
161
- pos += cur->len;
162
-
163
- /* Right-Pad words with a space */
164
- if (cur->len < widest_len && isalpha(cur->str[0])) {
165
- for (i = 0; i < widest_len - cur->len; i++) {
166
- result[pos] = ' ';
167
- pos++;
168
- }
169
- result[pos] = '\0';
170
- }
171
- }
172
- vsi->normalized = result;
173
- vsi->widest_len = widest_len;
219
+ return rb_version_sort_1(rb_self, rb_versions, version_compare_cb);
174
220
  }
175
221
 
176
- int
177
- compare_by_version(const void *a, const void *b)
222
+ static VALUE
223
+ rb_version_sort_r_bang(VALUE rb_self, VALUE rb_versions)
178
224
  {
179
- return strcmp((*(const VersionSortingItem **)a)->normalized, (*(const VersionSortingItem **)b)->normalized);
225
+ return rb_version_sort_1(rb_self, rb_versions, version_compare_cb_r);
180
226
  }
181
227
 
182
- int*
183
- version_sorter_sort(char **list, size_t list_len)
228
+ void Init_version_sorter(void)
184
229
  {
185
- int i, widest_len = 0;
186
- VersionSortingItem *vsi;
187
- VersionSortingItem **sorting_list = calloc(list_len, sizeof(VersionSortingItem *));
188
- int *ordering = calloc(list_len, sizeof(int));
189
-
190
- for (i = 0; i < list_len; i++) {
191
- vsi = version_sorting_item_init(list[i], i);
192
- if (vsi->widest_len > widest_len) {
193
- widest_len = vsi->widest_len;
194
- }
195
- sorting_list[i] = vsi;
196
- }
197
-
198
- for (i = 0; i < list_len; i++) {
199
- create_normalized_version(sorting_list[i], widest_len);
200
- }
201
-
202
- qsort((void *) sorting_list, list_len, sizeof(VersionSortingItem *), &compare_by_version);
203
-
204
- for (i = 0; i < list_len; i++) {
205
- vsi = sorting_list[i];
206
- list[i] = (char *) vsi->original;
207
- ordering[i] = vsi->original_idx;
208
- version_sorting_item_free(vsi);
209
- }
210
- free(sorting_list);
211
-
212
- return ordering;
230
+ VALUE rb_mVersionSorter = rb_define_module("VersionSorter");
231
+ rb_define_module_function(rb_mVersionSorter, "sort", rb_version_sort, 1);
232
+ rb_define_module_function(rb_mVersionSorter, "rsort", rb_version_sort_r, 1);
233
+ rb_define_module_function(rb_mVersionSorter, "sort!", rb_version_sort_bang, 1);
234
+ rb_define_module_function(rb_mVersionSorter, "rsort!", rb_version_sort_r_bang, 1);
213
235
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: version_sorter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Wanstrath
@@ -9,42 +9,27 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-02-18 00:00:00.000000000 Z
12
+ date: 2015-02-19 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Fast sorting of version strings
14
+ description: VersionSorter is a C extension that does fast sorting of large sets of
15
+ version strings.
15
16
  email: chris@ozmm.org
16
17
  executables: []
17
18
  extensions:
18
19
  - ext/version_sorter/extconf.rb
19
- extra_rdoc_files:
20
- - README.markdown
20
+ extra_rdoc_files: []
21
21
  files:
22
- - ".gitattributes"
23
- - README.markdown
24
- - Rakefile
22
+ - LICENSE
25
23
  - ext/version_sorter/extconf.rb
26
- - ext/version_sorter/frameworks/cmockery.framework/Versions/A/Headers/cmockery.h
27
- - ext/version_sorter/frameworks/cmockery.framework/Versions/A/Resources/English.lproj/InfoPlist.strings
28
- - ext/version_sorter/frameworks/cmockery.framework/Versions/A/Resources/Info.plist
29
- - ext/version_sorter/frameworks/cmockery.framework/Versions/A/cmockery
30
- - ext/version_sorter/frameworks/cmockery.framework/cmockery
31
- - ext/version_sorter/rb_version_sorter.c
32
- - ext/version_sorter/test/main.c
33
24
  - ext/version_sorter/version_sorter.c
34
- - ext/version_sorter/version_sorter.h
35
- - ext/version_sorter/version_sorter.xcodeproj/project.pbxproj
36
- - lib/version_sorter/version.rb
37
- - test/tags.rb
38
- - test/tags.txt
39
- - test/version_sorter_test.rb
40
- homepage: http://github.com/defunkt/version_sorter
41
- licenses: []
25
+ homepage: https://github.com/defunkt/version_sorter
26
+ licenses:
27
+ - MIT
42
28
  metadata: {}
43
29
  post_install_message:
44
30
  rdoc_options: []
45
31
  require_paths:
46
32
  - lib
47
- - ext
48
33
  required_ruby_version: !ruby/object:Gem::Requirement
49
34
  requirements:
50
35
  - - ">="