version_sorter 1.1.1 → 2.0.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.
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
  - - ">="