1. Andy Gross
  2. bashohash

Commits

Andy Gross  committed 186ba9f

initial commit - rebarized but not yet NIF-ized

  • Participants
  • Branches default

Comments (0)

Files changed (7)

File Makefile

View file
  • Ignore whitespace
+
+all:
+	./rebar compile eunit
+
+clean:
+	./rebar clean

File c_src/efnv_drv.c

View file
  • Ignore whitespace
+
+#include "fnv.h"
+#include <erl_driver.h>
+#include <ei.h>
+#include <stdio.h>
+
+#define write_int32(i, s) {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
+                        ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
+                        ((char*)(s))[2] = (char)((i) >> 8)  & 0xff; \
+                        ((char*)(s))[3] = (char)((i)        & 0xff);}
+
+static ErlDrvData init(ErlDrvPort port, char *cmd);
+static void stop(ErlDrvData handle);
+static void outputv(ErlDrvData handle, ErlIOVec *ev);
+static void send_hash(ErlDrvPort port, unsigned long hash);
+
+static ErlDrvData init(ErlDrvPort port, char *cmd) {
+  return (ErlDrvData) port;
+}
+
+static void stop(ErlDrvData handle) {
+  //noop
+}
+
+static void outputv(ErlDrvData handle, ErlIOVec *ev) {
+  ErlDrvPort port = (ErlDrvPort) handle;
+  ErlDrvTermData caller;
+  SysIOVec *bin;
+  int i, n, index = 0;
+  unsigned long hash;
+  unsigned long seed;
+  //first piece of the iovec is the seed
+  // printf("ev->size %d\n", ev->size);
+  // printf("ev-vsize %d\n", ev->vsize);
+  //apparently we start counting at 1 round here?
+  bin = &ev->iov[1];
+  // printf("bin->orig_size %d\n", bin->iov_len);
+  // printf("bin->iov_base %s\n", bin->iov_base);
+  ei_decode_version(bin->iov_base, &index, NULL);
+  ei_decode_ulong(bin->iov_base, &index, &seed);
+  hash = seed;
+  if (index < bin->iov_len) {
+    hash = fnv_hash(&bin->iov_base[index], bin->iov_len - index, hash);
+  }
+  for (i=2; i<ev->vsize; i++) {
+    bin = &ev->iov[i];
+    hash = fnv_hash(bin->iov_base, bin->iov_len, hash);
+  }
+  caller = driver_caller(port);
+  ErlDrvTermData spec[] = {
+    ERL_DRV_UINT, hash
+  };
+  driver_send_term(port, caller, spec, 2);
+}
+
+static int control(ErlDrvData data, unsigned int seed, char *buf, int len, char **rbuf, int rlen) {
+  unsigned int hash;
+  int index = 0;
+  // printf("length %d\n", len);
+  // printf("buf %s\n", buf);
+  hash = fnv_hash(buf, len, seed);
+  if (rlen < sizeof(hash)) {
+    (*rbuf) = (char *) driver_realloc(*rbuf, sizeof(hash));
+  }
+  write_int32(hash, *rbuf);
+  return sizeof(hash);
+}
+
+static void send_hash(ErlDrvPort port, unsigned long hash) {
+  ei_x_buff x;
+  ei_x_new_with_version(&x);
+  ei_x_encode_ulong(&x, hash);
+  driver_output(port, x.buff, x.index);
+  // printf("sent hash %d\n", hash);
+  ei_x_free(&x);
+}
+
+static ErlDrvEntry efnv_driver_entry = {
+    NULL,                             /* init */
+    init, 
+    stop, 
+    NULL,                             /* output */
+    NULL,                             /* ready_input */
+    NULL,                             /* ready_output */ 
+    "efnv_drv",                        /* the name of the driver */
+    NULL,                             /* finish */
+    NULL,                             /* handle */
+    control,                          /* control */
+    NULL,                             /* timeout */
+    outputv,                          /* outputv */
+    NULL,                             /* ready_async */
+    NULL,                             /* flush */
+    NULL,                             /* call */
+    NULL,                             /* event */
+    ERL_DRV_EXTENDED_MARKER,          /* ERL_DRV_EXTENDED_MARKER */
+    ERL_DRV_EXTENDED_MAJOR_VERSION,   /* ERL_DRV_EXTENDED_MAJOR_VERSION */
+    ERL_DRV_EXTENDED_MAJOR_VERSION,   /* ERL_DRV_EXTENDED_MINOR_VERSION */
+    ERL_DRV_FLAG_USE_PORT_LOCKING     /* ERL_DRV_FLAGs */
+    
+};
+
+DRIVER_INIT(efnv_driver) {
+  return &efnv_driver_entry;
+}

File c_src/fnv.c

View file
  • Ignore whitespace
+
+#include "fnv.h"
+
+#define FNV_PRIME 16777619
+#define MAX 4294967296
+
+unsigned int fnv_hash(const void* key, int length, unsigned int seed) {
+  const unsigned char * data = (const unsigned char*) key;
+  int n;
+  unsigned int xord;
+  unsigned int hash = seed;
+  
+  for(n=0; n < length; n++) {
+    xord = hash ^ data[n];
+    hash = (xord * FNV_PRIME) % MAX;
+  }
+  return hash;
+}

File c_src/fnv.h

View file
  • Ignore whitespace
+
+
+unsigned int fnv_hash(const void* key, int length, unsigned int seed);

File ebin/efnv.app

View file
  • Ignore whitespace
+{application, efnv,
+ [
+  {description, ""},
+  {vsn, "0.1"},
+  {modules, [
+             efnv
+            ]},
+  {registered, []},
+  {applications, [
+                  kernel,
+                  stdlib
+                 ]},
+  {env, []}
+ ]}.

File rebar

  • Ignore whitespace
Binary file added.

File src/efnv.erl

View file
  • Ignore whitespace
+%%%-------------------------------------------------------------------
+%%% File:      fnv.erl
+%%% @author    Cliff Moon <> []
+%%% @copyright 2009 Cliff Moon
+%%% @doc
+%%%
+%%% @end
+%%%
+%%% @since 2009-01-29 by Cliff Moon
+%%%-------------------------------------------------------------------
+-module(efnv).
+-author('cliff@powerset.com').
+
+-define(SEED, 2166136261).
+%% API
+-export([start/0, stop/1, hash/1, hash/2]).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% @spec
+%% @doc
+%% @end
+%%--------------------------------------------------------------------
+start() ->
+  case load_driver() of
+    ok ->
+      Pid = spawn_link(fun() ->
+          P = open(),
+          register(efnv_drv, P),
+          loop(P)
+        end),
+      timer:sleep(1),
+      {ok, Pid};
+    {error, Err} ->
+      Msg = erl_ddll:format_error(Err),
+      {error, Msg}
+  end.
+
+stop(P) ->
+  unlink(P),
+  exit(P, die).
+
+hash(Thing) ->
+  hash(Thing, ?SEED).
+
+% hash(Thing, Seed) when is_list(Thing) -> %assume io_list
+%   P = get_or_open(),
+%   port_command(P, [term_to_binary(Seed)] ++ Thing),
+%   recv(P);
+
+hash(Thing, Seed) when is_binary(Thing) ->
+  P = get_or_open(),
+  convert(port_control(P, Seed, Thing));
+  % recv(P);
+
+hash(Thing, Seed) ->
+  P = get_or_open(),
+  convert(port_control(P, Seed, term_to_binary(Thing))).
+  % recv(P).
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+loop(P) ->
+  receive _ -> loop(P) end.
+
+convert(List) ->
+  <<Hash:32/unsigned-integer>> = list_to_binary(List),
+  Hash.
+
+get_or_open() ->
+  case get(efnv_drv) of
+    undefined ->
+      load_driver(),
+      P = open(),
+      put(efnv_drv, P),
+      P;
+    P -> P
+  end.
+
+open() ->
+  open_port({spawn, efnv_drv}, [binary]).
+
+load_driver() ->
+  Dir = filename:join([filename:dirname(code:which(?MODULE)), "..", "priv"]),
+  erl_ddll:load(Dir, "efnv_drv").
+
+recv(P) ->
+  receive
+    {P, {data, Bin}} -> binary_to_term(Bin);
+    V -> V
+  end.