maasha
maasha

Reputation: 1995

How to separate Ruby inlineC code to multiple files?

I would like to move the MATCH macro and bitmap to a separate file since I use these many places, and I would like to avoid repeating code. How may that be done?

require 'inline'

# Class to calculate the Levenshtein distance between two
# given strings.
# http://en.wikipedia.org/wiki/Levenshtein_distance
class Levenshtein
  BYTES_IN_INT = 4

  def self.distance(s, t)
    return 0        if s == t;
    return t.length if s.length == 0;
    return s.length if t.length == 0;

    v0 = "\0" * (t.length + 1) * BYTES_IN_INT
    v1 = "\0" * (t.length + 1) * BYTES_IN_INT

    l = self.new
    l.distance_C(s, t, s.length, t.length, v0, v1)
  end

  # >>>>>>>>>>>>>>> RubyInline C code <<<<<<<<<<<<<<<

  inline do |builder|
    # Macro for matching nucleotides including ambiguity codes.
    builder.prefix %{
      #define MATCH(A,B) ((bitmap[A] & bitmap[B]) != 0)
    }

    # Bitmap for matching nucleotides including ambiguity codes.
    # For each value bits are set from the left: bit pos 1 for A,
    # bit pos 2 for T, bit pos 3 for C, and bit pos 4 for G.
    builder.prefix %{
      char bitmap[256] = {
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 1,14, 4,11, 0, 0, 8, 7, 0, 0,10, 0, 5,15, 0,
          0, 0, 9,12, 2, 2,13, 3, 0, 6, 0, 0, 0, 0, 0, 0,
          0, 1,14, 4,11, 0, 0, 8, 7, 0, 0,10, 0, 5,15, 0,
          0, 0, 9,12, 2, 2,13, 3, 0, 6, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
      };
    }

    builder.prefix %{
      unsigned int min(unsigned int a, unsigned int b, unsigned int c)
      {
          unsigned int m = a;

          if (m > b) m = b;
          if (m > c) m = c;

          return m;
      }
    }

    builder.c %{
      VALUE distance_C(
        VALUE _s,       // string
        VALUE _t,       // string
        VALUE _s_len,   // string length
        VALUE _t_len,   // string length
        VALUE _v0,      // score vector
        VALUE _v1       // score vector
      )
      {
        char         *s     = (char *) StringValuePtr(_s);
        char         *t     = (char *) StringValuePtr(_t);
        unsigned int  s_len = FIX2UINT(_s_len);
        unsigned int  t_len = FIX2UINT(_t_len);
        unsigned int  *v0   = (unsigned int *) StringValuePtr(_v0);
        unsigned int  *v1   = (unsigned int *) StringValuePtr(_v1);

        unsigned int i    = 0;
        unsigned int j    = 0;
        unsigned int cost = 0;

        for (i = 0; i < t_len + 1; i++)
          v0[i] = i;

        for (i = 0; i < s_len; i++)
        {
          v1[0] = i + 1;

          for (j = 0; j < t_len; j++)
          {
            cost = (MATCH(s[i], t[j])) ? 0 : 1;
            v1[j + 1] = min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
          }

          for (j = 0; j < t_len + 1; j++)
            v0[j] = v1[j];
        }

        return UINT2NUM(v1[t_len]);
      }
    }
  end
end

Upvotes: 1

Views: 82

Answers (1)

Neil Slater
Neil Slater

Reputation: 27207

builder.prefix is just a method call, so you could create a Ruby method which called it with your macro and character array, and then add it to any class which wanted to use those C snippets inline. e.g.

module MixinCommonC
  def add_match_macro inline_builder
    inline_builder.prefix %{
      #define MATCH(A,B) ((bitmap[A] & bitmap[B]) != 0)
    }
  end
end

In your example code you make the following changes to use it:

At the start of the class

class Levenshtein
  extend MixinCommonC

(It's extend and not include, because the inline method is being called against the class, so can only access class methods inside the block)

Where you currently have the call to builder.prefix:

add_match_macro( builder )

Upvotes: 2

Related Questions