| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- // Copyright 2010-2014 Wincent Colaiuta. All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are met:
- //
- // 1. Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- // 2. Redistributions in binary form must reproduce the above copyright notice,
- // this list of conditions and the following disclaimer in the documentation
- // and/or other materials provided with the distribution.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
- // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- // POSSIBILITY OF SUCH DAMAGE.
- #include <stdlib.h> /* for qsort() */
- #include <string.h> /* for strncmp() */
- #include "matcher.h"
- #include "match.h"
- #include "ext.h"
- #include "ruby_compat.h"
- // order matters; we want this to be evaluated only after ruby.h
- #ifdef HAVE_PTHREAD_H
- #include <pthread.h> /* for pthread_create, pthread_join etc */
- #endif
- // comparison function for use with qsort
- int cmp_alpha(const void *a, const void *b)
- {
- match_t a_match = *(match_t *)a;
- match_t b_match = *(match_t *)b;
- VALUE a_str = a_match.path;
- VALUE b_str = b_match.path;
- char *a_p = RSTRING_PTR(a_str);
- long a_len = RSTRING_LEN(a_str);
- char *b_p = RSTRING_PTR(b_str);
- long b_len = RSTRING_LEN(b_str);
- int order = 0;
- if (a_len > b_len) {
- order = strncmp(a_p, b_p, b_len);
- if (order == 0)
- order = 1; // shorter string (b) wins
- } else if (a_len < b_len) {
- order = strncmp(a_p, b_p, a_len);
- if (order == 0)
- order = -1; // shorter string (a) wins
- } else {
- order = strncmp(a_p, b_p, a_len);
- }
- return order;
- }
- // comparison function for use with qsort
- int cmp_score(const void *a, const void *b)
- {
- match_t a_match = *(match_t *)a;
- match_t b_match = *(match_t *)b;
- if (a_match.score > b_match.score)
- return -1; // a scores higher, a should appear sooner
- else if (a_match.score < b_match.score)
- return 1; // b scores higher, a should appear later
- else
- return cmp_alpha(a, b);
- }
- VALUE CommandTMatcher_initialize(int argc, VALUE *argv, VALUE self)
- {
- VALUE always_show_dot_files;
- VALUE never_show_dot_files;
- VALUE options;
- VALUE scanner;
- // process arguments: 1 mandatory, 1 optional
- if (rb_scan_args(argc, argv, "11", &scanner, &options) == 1)
- options = Qnil;
- if (NIL_P(scanner))
- rb_raise(rb_eArgError, "nil scanner");
- rb_iv_set(self, "@scanner", scanner);
- // check optional options hash for overrides
- always_show_dot_files = CommandT_option_from_hash("always_show_dot_files", options);
- never_show_dot_files = CommandT_option_from_hash("never_show_dot_files", options);
- rb_iv_set(self, "@always_show_dot_files", always_show_dot_files);
- rb_iv_set(self, "@never_show_dot_files", never_show_dot_files);
- return Qnil;
- }
- typedef struct {
- int thread_count;
- int thread_index;
- match_t *matches;
- long path_count;
- VALUE paths;
- VALUE abbrev;
- VALUE always_show_dot_files;
- VALUE never_show_dot_files;
- } thread_args_t;
- void *match_thread(void *thread_args)
- {
- long i;
- thread_args_t *args = (thread_args_t *)thread_args;
- for (i = args->thread_index; i < args->path_count; i += args->thread_count) {
- VALUE path = RARRAY_PTR(args->paths)[i];
- calculate_match(path,
- args->abbrev,
- args->always_show_dot_files,
- args->never_show_dot_files,
- &args->matches[i]);
- }
- return NULL;
- }
- VALUE CommandTMatcher_sorted_matches_for(int argc, VALUE *argv, VALUE self)
- {
- long i, limit, path_count, thread_count;
- #ifdef HAVE_PTHREAD_H
- long err;
- #endif
- match_t *matches;
- thread_args_t *thread_args;
- VALUE abbrev;
- VALUE always_show_dot_files;
- VALUE limit_option;
- VALUE never_show_dot_files;
- VALUE options;
- VALUE paths;
- VALUE results;
- VALUE scanner;
- VALUE threads_option;
- // process arguments: 1 mandatory, 1 optional
- if (rb_scan_args(argc, argv, "11", &abbrev, &options) == 1)
- options = Qnil;
- if (NIL_P(abbrev))
- rb_raise(rb_eArgError, "nil abbrev");
- abbrev = StringValue(abbrev);
- abbrev = rb_funcall(abbrev, rb_intern("downcase"), 0);
- // check optional options has for overrides
- limit_option = CommandT_option_from_hash("limit", options);
- threads_option = CommandT_option_from_hash("threads", options);
- // get unsorted matches
- scanner = rb_iv_get(self, "@scanner");
- paths = rb_funcall(scanner, rb_intern("paths"), 0);
- always_show_dot_files = rb_iv_get(self, "@always_show_dot_files");
- never_show_dot_files = rb_iv_get(self, "@never_show_dot_files");
- path_count = RARRAY_LEN(paths);
- matches = malloc(path_count * sizeof(match_t));
- if (!matches)
- rb_raise(rb_eNoMemError, "memory allocation failed");
- thread_count = NIL_P(threads_option) ? 1 : NUM2LONG(threads_option);
- #ifdef HAVE_PTHREAD_H
- #define THREAD_THRESHOLD 1000 /* avoid the overhead of threading when search space is small */
- if (path_count < THREAD_THRESHOLD)
- thread_count = 1;
- pthread_t *threads = malloc(sizeof(pthread_t) * thread_count);
- if (!threads)
- rb_raise(rb_eNoMemError, "memory allocation failed");
- #endif
- thread_args = malloc(sizeof(thread_args_t) * thread_count);
- if (!thread_args)
- rb_raise(rb_eNoMemError, "memory allocation failed");
- for (i = 0; i < thread_count; i++) {
- thread_args[i].thread_count = thread_count;
- thread_args[i].thread_index = i;
- thread_args[i].matches = matches;
- thread_args[i].path_count = path_count;
- thread_args[i].paths = paths;
- thread_args[i].abbrev = abbrev;
- thread_args[i].always_show_dot_files = always_show_dot_files;
- thread_args[i].never_show_dot_files = never_show_dot_files;
- #ifdef HAVE_PTHREAD_H
- if (i == thread_count - 1) {
- #endif
- // for the last "worker", we'll just use the main thread
- (void)match_thread(&thread_args[i]);
- #ifdef HAVE_PTHREAD_H
- } else {
- err = pthread_create(&threads[i], NULL, match_thread, (void *)&thread_args[i]);
- if (err != 0)
- rb_raise(rb_eSystemCallError, "pthread_create() failure (%d)", (int)err);
- }
- #endif
- }
- #ifdef HAVE_PTHREAD_H
- for (i = 0; i < thread_count - 1; i++) {
- err = pthread_join(threads[i], NULL);
- if (err != 0)
- rb_raise(rb_eSystemCallError, "pthread_join() failure (%d)", (int)err);
- }
- free(threads);
- #endif
- if (RSTRING_LEN(abbrev) == 0 ||
- (RSTRING_LEN(abbrev) == 1 && RSTRING_PTR(abbrev)[0] == '.'))
- // alphabetic order if search string is only "" or "."
- qsort(matches, path_count, sizeof(match_t), cmp_alpha);
- else
- // for all other non-empty search strings, sort by score
- qsort(matches, path_count, sizeof(match_t), cmp_score);
- results = rb_ary_new();
- limit = NIL_P(limit_option) ? 0 : NUM2LONG(limit_option);
- if (limit == 0)
- limit = path_count;
- for (i = 0; i < path_count && limit > 0; i++) {
- if (matches[i].score > 0.0) {
- rb_funcall(results, rb_intern("push"), 1, matches[i].path);
- limit--;
- }
- }
- free(matches);
- return results;
- }
|