Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit f028092d1f96fd603f22295b6ebf1c48d7085bbf @tuncer committed
Showing with 1,301 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +28 −0 LICENSE
  3. +12 −0 Makefile
  4. +6 −0 NOTICE
  5. +14 −0 README.md
  6. +274 −0 c_src/hashtable.c
  7. +199 −0 c_src/hashtable.h
  8. +85 −0 c_src/hashtable_private.h
  9. +310 −0 c_src/sendfile_drv.c
  10. +16 −0 ebin/sendfile.app
  11. +4 −0 rebar.config
  12. +121 −0 src/sendfile.erl
  13. +119 −0 src/sendfile_drv.erl
  14. +92 −0 support/getrebar
  15. +17 −0 test/sendfile_test.erl
4 .gitignore
@@ -0,0 +1,4 @@
+ebin/*.beam
+c_src/*.o
+priv/*.so
+support/rebar
28 LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2008, Steve Vinoski. All Rights Reserved.
+Copyright (c) 2010, Tuncer Ayaz. All Rights Reserved.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of "sendfile" nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+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 OWNER 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.
12 Makefile
@@ -0,0 +1,12 @@
+.PHONY: all clean install test
+
+REBAR=$(shell sh -c 'PATH=$(PATH):support which rebar||support/getrebar||echo false')
+
+all:
+ $(REBAR) compile
+
+clean:
+ $(REBAR) clean
+
+install:
+ $(REBAR) install
6 NOTICE
@@ -0,0 +1,6 @@
+This product includes software covered by the following licenses:
+
+ c_src/hashtable.[c,h] c_src/hashtable_private.h
+ Copyright (c) 2002, 2004, Christopher Clark
+ made available under a BSD-style license that
+ can be found in the respective hashtable source files.
14 README.md
@@ -0,0 +1,14 @@
+Erlang sendfile() linked-in driver
+==================================
+
+**sendfile** is a linked-in driver for the sendfile syscall.
+
+Based on original driver from [yaws](http://yaws.hyber.org).
+
+Building and Installing
+-----------------------
+
+sendfile is built with [rebar](http://bitbucket.org/basho/rebar/) and
+we do expect `rebar` to be in the search `PATH`.
+If `rebar` can not be found in the search `PATH` it will be
+automatically downloaded to `support/rebar` for local usage.
274 c_src/hashtable.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#include "hashtable.h"
+#include "hashtable_private.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+/*
+Credit for primes table: Aaron Krowne
+ http://br.endernet.org/~akrowne/
+ http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
+*/
+static const unsigned int primes[] = {
+53, 97, 193, 389,
+769, 1543, 3079, 6151,
+12289, 24593, 49157, 98317,
+196613, 393241, 786433, 1572869,
+3145739, 6291469, 12582917, 25165843,
+50331653, 100663319, 201326611, 402653189,
+805306457, 1610612741
+};
+const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
+const float max_load_factor = 0.65;
+
+/*****************************************************************************/
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashf) (void*),
+ int (*eqf) (void*,void*))
+{
+ struct hashtable *h;
+ unsigned int pindex, size = primes[0];
+ /* Check requested hashtable isn't too large */
+ if (minsize > (1u << 30)) return NULL;
+ /* Enforce size as prime */
+ for (pindex=0; pindex < prime_table_length; pindex++) {
+ if (primes[pindex] > minsize) { size = primes[pindex]; break; }
+ }
+ h = (struct hashtable *)malloc(sizeof(struct hashtable));
+ if (NULL == h) return NULL; /*oom*/
+ h->table = (struct entry **)malloc(sizeof(struct entry*) * size);
+ if (NULL == h->table) { free(h); return NULL; } /*oom*/
+ memset(h->table, 0, size * sizeof(struct entry *));
+ h->tablelength = size;
+ h->primeindex = pindex;
+ h->entrycount = 0;
+ h->hashfn = hashf;
+ h->eqfn = eqf;
+ h->loadlimit = (unsigned int) ceil(size * max_load_factor);
+ return h;
+}
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k)
+{
+ /* Aim to protect against poor hash functions by adding logic here
+ * - logic taken from java 1.4 hashtable source */
+ unsigned int i = h->hashfn(k);
+ i += ~(i << 9);
+ i ^= ((i >> 14) | (i << 18)); /* >>> */
+ i += (i << 4);
+ i ^= ((i >> 10) | (i << 22)); /* >>> */
+ return i;
+}
+
+/*****************************************************************************/
+static int
+hashtable_expand(struct hashtable *h)
+{
+ /* Double the size of the table to accomodate more entries */
+ struct entry **newtable;
+ struct entry *e;
+ struct entry **pE;
+ unsigned int newsize, i, index;
+ /* Check we're not hitting max capacity */
+ if (h->primeindex == (prime_table_length - 1)) return 0;
+ newsize = primes[++(h->primeindex)];
+
+ newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize);
+ if (NULL != newtable)
+ {
+ memset(newtable, 0, newsize * sizeof(struct entry *));
+ /* This algorithm is not 'stable'. ie. it reverses the list
+ * when it transfers entries between the tables */
+ for (i = 0; i < h->tablelength; i++) {
+ while (NULL != (e = h->table[i])) {
+ h->table[i] = e->next;
+ index = indexFor(newsize,e->h);
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ free(h->table);
+ h->table = newtable;
+ }
+ /* Plan B: realloc instead */
+ else
+ {
+ newtable = (struct entry **)
+ realloc(h->table, newsize * sizeof(struct entry *));
+ if (NULL == newtable) { (h->primeindex)--; return 0; }
+ h->table = newtable;
+ memset(newtable[h->tablelength], 0, newsize - h->tablelength);
+ for (i = 0; i < h->tablelength; i++) {
+ for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
+ index = indexFor(newsize,e->h);
+ if (index == i)
+ {
+ pE = &(e->next);
+ }
+ else
+ {
+ *pE = e->next;
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+ }
+ }
+ h->tablelength = newsize;
+ h->loadlimit = (unsigned int) ceil(newsize * max_load_factor);
+ return -1;
+}
+
+/*****************************************************************************/
+unsigned int
+hashtable_count(struct hashtable *h)
+{
+ return h->entrycount;
+}
+
+/*****************************************************************************/
+int
+hashtable_insert(struct hashtable *h, void *k, void *v)
+{
+ /* This method allows duplicate keys - but they shouldn't be used */
+ unsigned int index;
+ struct entry *e;
+ if (++(h->entrycount) > h->loadlimit)
+ {
+ /* Ignore the return value. If expand fails, we should
+ * still try cramming just this value into the existing table
+ * -- we may not have memory for a larger table, but one more
+ * element may be ok. Next time we insert, we'll try expanding again.*/
+ hashtable_expand(h);
+ }
+ e = (struct entry *)malloc(sizeof(struct entry));
+ if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
+ e->h = hash(h,k);
+ index = indexFor(h->tablelength,e->h);
+ e->k = k;
+ e->v = v;
+ e->next = h->table[index];
+ h->table[index] = e;
+ return -1;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_search(struct hashtable *h, void *k)
+{
+ struct entry *e;
+ unsigned int hashvalue, index;
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hashvalue);
+ e = h->table[index];
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v;
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+void * /* returns value associated with key */
+hashtable_remove(struct hashtable *h, void *k)
+{
+ /* TODO: consider compacting the table when the load factor drops enough,
+ * or provide a 'compact' method. */
+
+ struct entry *e;
+ struct entry **pE;
+ void *v;
+ unsigned int hashvalue, index;
+
+ hashvalue = hash(h,k);
+ index = indexFor(h->tablelength,hash(h,k));
+ pE = &(h->table[index]);
+ e = *pE;
+ while (NULL != e)
+ {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
+ {
+ *pE = e->next;
+ h->entrycount--;
+ v = e->v;
+ freekey(e->k);
+ free(e);
+ return v;
+ }
+ pE = &(e->next);
+ e = e->next;
+ }
+ return NULL;
+}
+
+/*****************************************************************************/
+/* destroy */
+void
+hashtable_destroy(struct hashtable *h, int free_values)
+{
+ unsigned int i;
+ struct entry *e, *f;
+ struct entry **table = h->table;
+ if (free_values)
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k); free(f->v); free(f); }
+ }
+ }
+ else
+ {
+ for (i = 0; i < h->tablelength; i++)
+ {
+ e = table[i];
+ while (NULL != e)
+ { f = e; e = e->next; freekey(f->k); free(f); }
+ }
+ }
+ free(h->table);
+ free(h);
+}
+
+/*
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * 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 OWNER
+ * 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.
+*/
199 c_src/hashtable.h
@@ -0,0 +1,199 @@
+/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#ifndef __HASHTABLE_CWC22_H__
+#define __HASHTABLE_CWC22_H__
+
+struct hashtable;
+
+/* Example of use:
+ *
+ * struct hashtable *h;
+ * struct some_key *k;
+ * struct some_value *v;
+ *
+ * static unsigned int hash_from_key_fn( void *k );
+ * static int keys_equal_fn ( void *key1, void *key2 );
+ *
+ * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn);
+ * k = (struct some_key *) malloc(sizeof(struct some_key));
+ * v = (struct some_value *) malloc(sizeof(struct some_value));
+ *
+ * (initialise k and v to suitable values)
+ *
+ * if (! hashtable_insert(h,k,v) )
+ * { exit(-1); }
+ *
+ * if (NULL == (found = hashtable_search(h,k) ))
+ * { printf("not found!"); }
+ *
+ * if (NULL == (found = hashtable_remove(h,k) ))
+ * { printf("Not found\n"); }
+ *
+ */
+
+/* Macros may be used to define type-safe(r) hashtable access functions, with
+ * methods specialized to take known key and value types as parameters.
+ *
+ * Example:
+ *
+ * Insert this at the start of your file:
+ *
+ * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value);
+ * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value);
+ *
+ * This defines the functions 'insert_some', 'search_some' and 'remove_some'.
+ * These operate just like hashtable_insert etc., with the same parameters,
+ * but their function signatures have 'struct some_key *' rather than
+ * 'void *', and hence can generate compile time errors if your program is
+ * supplying incorrect data as a key (and similarly for value).
+ *
+ * Note that the hash and key equality functions passed to create_hashtable
+ * still take 'void *' parameters instead of 'some key *'. This shouldn't be
+ * a difficult issue as they're only defined and passed once, and the other
+ * functions will ensure that only valid keys are supplied to them.
+ *
+ * The cost for this checking is increased code size and runtime overhead
+ * - if performance is important, it may be worth switching back to the
+ * unsafe methods once your program has been debugged with the safe methods.
+ * This just requires switching to some simple alternative defines - eg:
+ * #define insert_some hashtable_insert
+ *
+ */
+
+/*****************************************************************************
+ * create_hashtable
+
+ * @name create_hashtable
+ * @param minsize minimum initial size of hashtable
+ * @param hashfunction function for hashing keys
+ * @param key_eq_fn function for determining key equality
+ * @return newly created hashtable or NULL on failure
+ */
+
+struct hashtable *
+create_hashtable(unsigned int minsize,
+ unsigned int (*hashfunction) (void*),
+ int (*key_eq_fn) (void*,void*));
+
+/*****************************************************************************
+ * hashtable_insert
+
+ * @name hashtable_insert
+ * @param h the hashtable to insert into
+ * @param k the key - hashtable claims ownership and will free on removal
+ * @param v the value - does not claim ownership
+ * @return non-zero for successful insertion
+ *
+ * This function will cause the table to expand if the insertion would take
+ * the ratio of entries to table size over the maximum load factor.
+ *
+ * This function does not check for repeated insertions with a duplicate key.
+ * The value returned when using a duplicate key is undefined -- when
+ * the hashtable changes size, the order of retrieval of duplicate key
+ * entries is reversed.
+ * If in doubt, remove before insert.
+ */
+
+int
+hashtable_insert(struct hashtable *h, void *k, void *v);
+
+#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \
+int fnname (struct hashtable *h, keytype *k, valuetype *v) \
+{ \
+ return hashtable_insert(h,k,v); \
+}
+
+/*****************************************************************************
+ * hashtable_search
+
+ * @name hashtable_search
+ * @param h the hashtable to search
+ * @param k the key to search for - does not claim ownership
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void *
+hashtable_search(struct hashtable *h, void *k);
+
+#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_search(h,k)); \
+}
+
+/*****************************************************************************
+ * hashtable_remove
+
+ * @name hashtable_remove
+ * @param h the hashtable to remove the item from
+ * @param k the key to search for - does not claim ownership
+ * @return the value associated with the key, or NULL if none found
+ */
+
+void * /* returns value */
+hashtable_remove(struct hashtable *h, void *k);
+
+#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \
+valuetype * fnname (struct hashtable *h, keytype *k) \
+{ \
+ return (valuetype *) (hashtable_remove(h,k)); \
+}
+
+
+/*****************************************************************************
+ * hashtable_count
+
+ * @name hashtable_count
+ * @param h the hashtable
+ * @return the number of items stored in the hashtable
+ */
+unsigned int
+hashtable_count(struct hashtable *h);
+
+
+/*****************************************************************************
+ * hashtable_destroy
+
+ * @name hashtable_destroy
+ * @param h the hashtable
+ * @param free_values whether to call 'free' on the remaining values
+ */
+
+void
+hashtable_destroy(struct hashtable *h, int free_values);
+
+#endif /* __HASHTABLE_CWC22_H__ */
+
+/*
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * 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 OWNER
+ * 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.
+*/
85 c_src/hashtable_private.h
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
+
+#ifndef __HASHTABLE_PRIVATE_CWC22_H__
+#define __HASHTABLE_PRIVATE_CWC22_H__
+
+#include "hashtable.h"
+
+/*****************************************************************************/
+struct entry
+{
+ void *k, *v;
+ unsigned int h;
+ struct entry *next;
+};
+
+struct hashtable {
+ unsigned int tablelength;
+ struct entry **table;
+ unsigned int entrycount;
+ unsigned int loadlimit;
+ unsigned int primeindex;
+ unsigned int (*hashfn) (void *k);
+ int (*eqfn) (void *k1, void *k2);
+};
+
+/*****************************************************************************/
+unsigned int
+hash(struct hashtable *h, void *k);
+
+/*****************************************************************************/
+/* indexFor */
+static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue) {
+ return (hashvalue % tablelength);
+};
+
+/* Only works if tablelength == 2^N */
+/*static inline unsigned int
+indexFor(unsigned int tablelength, unsigned int hashvalue)
+{
+ return (hashvalue & (tablelength - 1u));
+}
+*/
+
+/*****************************************************************************/
+/*#define freekey(X) free(X)*/
+#define freekey(X)
+
+
+/*****************************************************************************/
+
+#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/
+
+/*
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * 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 OWNER
+ * 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.
+*/
310 c_src/sendfile_drv.c
@@ -0,0 +1,310 @@
+/* -*- tab-width: 4;indent-tabs-mode: nil;c-basic-offset: 4 -*- */
+/* ex: ts=4 sw=4 sts=4 et */
+/* */
+/* Copyright 2008 Steve Vinoski. All Rights Reserved. */
+/* Copyright 2010 Tuncer Ayaz. All Rights Reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+/* */
+/* Based on original code from yaws */
+/* */
+/* Interface to sendfile system call for Yaws */
+/* author: vinoski@ieee.org */
+/* Created : 09 Nov 2008 by Steve Vinoski <vinoski@ieee.org> */
+/* */
+/* Renamed to sendfile and modified: Tuncer Ayaz in May 2010 */
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#if defined(__linux__)
+#include <sys/sendfile.h>
+#elif (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__)
+#include <sys/socket.h>
+#include <sys/uio.h>
+#else
+#error "sendfile_drv not supported on this platform"
+#endif
+
+#include "erl_driver.h"
+#ifndef ERL_DRV_NIL
+#include "erl_driver_compat.h"
+#endif
+
+#include "hashtable.h"
+
+#define lshift_index(s, i, shift, t) (((t)((unsigned char*)(s))[i]) << (shift))
+#define lshift32(s, i, shift) lshift_index(s, i, shift, uint32_t)
+#define lshift64(s, i, shift) lshift_index(s, i, shift, uint64_t)
+#define get_int32(s) (lshift32(s,0,24) | lshift32(s,1,16) | lshift32(s,2,8) \
+ | lshift32(s,3,0))
+#define get_int64(s) (lshift64(s,0,56) | lshift64(s,1,48) | lshift64(s,2,40) |\
+ lshift64(s,3,32) | lshift64(s,4,24) | lshift64(s,5,16) |\
+ lshift64(s,6,8) | lshift64(s,7,0))
+
+#define put_shift(i, s, idx, shift) (((unsigned char*)(s))[idx] = \
+ ((unsigned char)((i) >> (shift)) & 0XFF))
+#define put_int32(i, s) do { \
+ put_shift(i, s, 0, 24); \
+ put_shift(i, s, 1, 16); \
+ put_shift(i, s, 2, 8); \
+ put_shift(i, s, 3, 0); \
+ } while(0)
+#define put_int64(i, s) do { \
+ put_shift(i, s, 0, 56); \
+ put_shift(i, s, 1, 48); \
+ put_shift(i, s, 2, 40); \
+ put_shift(i, s, 3, 32); \
+ put_shift(i, s, 4, 24); \
+ put_shift(i, s, 5, 16); \
+ put_shift(i, s, 6, 8); \
+ put_shift(i, s, 7, 0); \
+ } while(0)
+
+typedef union {
+ void* hashkey;
+ ErlDrvEvent ev_data;
+#ifdef _LP64
+ uint64_t socket_fd;
+#else
+ uint32_t socket_fd;
+#endif
+} SocketFd;
+
+typedef struct {
+ off_t offset;
+ size_t count;
+ ssize_t total;
+ int file_fd;
+} Transfer;
+
+typedef struct hashtable* Transfers;
+
+typedef struct {
+ ErlDrvPort port;
+ Transfers xfer_table;
+} Desc;
+
+
+static unsigned int fdhash(void* k)
+{
+ return ((SocketFd*)&k)->socket_fd;
+}
+
+static int fdeq(void* k1, void* k2)
+{
+ return k1 == k2;
+}
+
+static ErlDrvData sendfile_drv_start(ErlDrvPort port, char* buf)
+{
+ Desc* d = (Desc*)driver_alloc(sizeof(Desc));
+ if (d == NULL) return (ErlDrvData) -1;
+ d->port = port;
+ d->xfer_table = create_hashtable(8192, fdhash, fdeq);
+ if (d->xfer_table == NULL) {
+ driver_free(d);
+ return (ErlDrvData) -1;
+ }
+ return (ErlDrvData)d;
+}
+
+static void sendfile_drv_stop(ErlDrvData handle)
+{
+ Desc* d = (Desc*)handle;
+ hashtable_destroy(d->xfer_table, 1);
+ driver_free(d);
+}
+
+typedef union {
+ off_t offset;
+ size_t size;
+ ssize_t count;
+ uint64_t bits;
+ unsigned char bytes[8];
+} U64_t;
+
+typedef union {
+ char* buffer;
+ struct {
+ U64_t offset;
+ U64_t count;
+ uint32_t out_fd;
+ char filename[1];
+ }* args;
+ struct {
+ U64_t count;
+ uint32_t out_fd;
+ unsigned char success;
+ char errno_string[1];
+ }* result;
+} Buffer;
+
+static size_t set_error_buffer(Buffer* b, int socket_fd, int err)
+{
+ char* s, *t;
+ size_t result_size = sizeof *(b->result);
+ memset(b->result, 0, result_size);
+ put_int32(socket_fd, &(b->result->out_fd));
+ s = erl_errno_id(err);
+ if (strcmp(s, "unknown") == 0 && err == EOVERFLOW) {
+ s = "EOVERFLOW";
+ }
+ for (t = b->result->errno_string; *s; s++, t++) {
+ *t = tolower(*s);
+ }
+ *t = '\0';
+ return result_size - 1 + t - b->result->errno_string;
+}
+
+static ssize_t sendfile_call(int out_fd, int in_fd, off_t* offset, size_t count)
+{
+#if defined(__linux__)
+ off_t cur = *offset;
+ ssize_t retval;
+ do {
+ retval = sendfile(out_fd, in_fd, offset, count);
+ } while (retval < 0 && errno == EINTR);
+ if (retval >= 0 && retval != count) {
+ if (*offset == cur) {
+ *offset += retval;
+ }
+ retval = -1;
+ errno = EAGAIN;
+ }
+ return retval;
+#elif defined(__APPLE__) && defined(__MACH__)
+ off_t len = count;
+ int retval;
+ do {
+ retval = sendfile(in_fd, out_fd, *offset, &len, NULL, 0);
+ } while (retval < 0 && errno == EINTR);
+ if (retval < 0 && errno == EAGAIN) {
+ *offset += len;
+ }
+ return retval == 0 ? len : retval;
+#elif defined(__FreeBSD__)
+ off_t len = 0;
+ int retval;
+ do {
+ retval = sendfile(in_fd, out_fd, *offset, count, NULL, &len, 0);
+ } while (retval < 0 && errno == EINTR);
+ if (retval < 0 && errno == EAGAIN) {
+ *offset += len;
+ }
+ return retval == 0 ? len : retval;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static void sendfile_drv_output(ErlDrvData handle, char* buf, int buflen)
+{
+ int fd, socket_fd;
+ Desc* d = (Desc*)handle;
+ Buffer b;
+ b.buffer = buf;
+ socket_fd = get_int32(&(b.args->out_fd));
+ fd = open(b.args->filename, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ int out_buflen = set_error_buffer(&b, socket_fd, errno);
+ driver_output(d->port, buf, out_buflen);
+ } else {
+ Transfer* xfer;
+ SocketFd sfd;
+ sfd.socket_fd = socket_fd;
+ xfer = (Transfer*)hashtable_search(d->xfer_table, sfd.hashkey);
+ if (xfer == NULL) {
+ /* Transfer objects are intentionally not freed until the
+ driver stops, or if an insertion error occurs below. */
+ xfer = (Transfer*)malloc(sizeof(Transfer));
+ if (xfer == NULL) {
+ int out_buflen = set_error_buffer(&b, socket_fd, ENOMEM);
+ driver_output(d->port, buf, out_buflen);
+ return;
+ }
+ if (!hashtable_insert(d->xfer_table, sfd.hashkey, xfer)) {
+ int out_buflen = set_error_buffer(&b, socket_fd, ENOMEM);
+ driver_output(d->port, buf, out_buflen);
+ free(xfer);
+ return;
+ }
+ }
+ xfer->file_fd = fd;
+ xfer->offset = get_int64(&(b.args->offset.offset));
+ xfer->count = get_int64(&(b.args->count.size));
+ xfer->total = 0;
+ driver_select(d->port, sfd.ev_data, DO_WRITE, 1);
+ }
+}
+
+
+static void sendfile_drv_ready_output(ErlDrvData handle, ErlDrvEvent ev)
+{
+ Desc* d = (Desc*)handle;
+ ssize_t result;
+ off_t cur_offset;
+ Transfer* xfer;
+ SocketFd* sfd = (SocketFd*)&ev;
+ xfer = (Transfer*)hashtable_search(d->xfer_table, sfd->hashkey);
+ if (xfer == NULL) {
+ /* fatal error, something is very wrong */
+ driver_failure_atom(d->port, "socket_fd_not_found");
+ return;
+ }
+ cur_offset = xfer->offset;
+ result = sendfile_call(sfd->socket_fd, xfer->file_fd,
+ &xfer->offset, xfer->count);
+ if (result < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ off_t written = xfer->offset - cur_offset;
+ xfer->count -= written;
+ xfer->total += written;
+ } else {
+ int save_errno = errno;
+ int out_buflen;
+ char buf[36];
+ Buffer b;
+ b.buffer = buf;
+ memset(buf, 0, sizeof buf);
+ driver_select(d->port, ev, DO_WRITE, 0);
+ close(xfer->file_fd);
+ if (result < 0) {
+ out_buflen = set_error_buffer(&b, sfd->socket_fd, save_errno);
+ } else {
+ uint64_t total = xfer->total + result;
+ put_int64(total, &(b.result->count.count));
+ put_int32(sfd->socket_fd, &(b.result->out_fd));
+ b.result->success = 1;
+ b.result->errno_string[0] = '\0';
+ out_buflen = sizeof(*b.result);
+ }
+ xfer->file_fd = -1;
+ xfer->offset = xfer->count = xfer->total = 0;
+ driver_output(d->port, buf, out_buflen);
+ }
+}
+
+static ErlDrvEntry sendfile_driver_entry = {
+ NULL,
+ sendfile_drv_start,
+ sendfile_drv_stop,
+ sendfile_drv_output,
+ NULL,
+ sendfile_drv_ready_output,
+ "sendfile_drv",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+DRIVER_INIT(sendfile_drv)
+{
+ return &sendfile_driver_entry;
+}
16 ebin/sendfile.app
@@ -0,0 +1,16 @@
+{application, sendfile,
+ [{description, "sendfile linked-in driver"},
+ {vsn, "0.0.1"},
+ {modules, [
+ sendfile,
+ sendfile_drv
+ ]},
+ {applications, [
+ kernel,
+ stdlib,
+ sasl
+ ]},
+ {registered, []},
+ {env, []}
+ ]}.
+
4 rebar.config
@@ -0,0 +1,4 @@
+{erl_opts, [{platform_define, "(linux|freebsd|darwin)",
+ 'HAVE_SENDFILE'}]}.
+{so_specs, [{"priv/sendfile_drv.so",
+ ["c_src/sendfile_drv.o","c_src/hashtable.o"]}]}.
121 src/sendfile.erl
@@ -0,0 +1,121 @@
+%%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%%% ex: ts=4 sw=4 sts=4 et
+%%%
+%%% Copyright 2008 Steve Vinoski. All Rights Reserved.
+%%% Copyright 2010 Tuncer Ayaz. All Rights Reserved.
+%%% Use of this source code is governed by a BSD-style
+%%% license that can be found in the LICENSE file.
+%%%
+%%% Based on original code from yaws
+%%%
+%%% File : yaws_sendfile_compat.erl
+%%% Author : Claes Wikstrom, klacke@hyber.org
+%%% Description : Conditional OS dependent call to sendfile
+%%% Created : Sat Dec 20 20:00:11 CET 2008
+%%%
+%%% Renamed to sendfile and modified: Tuncer Ayaz in May 2010
+
+-module(sendfile).
+-export([start_link/0, init/1, stop/0, send/2, send/3, send/4, enabled/0]).
+
+-include_lib("kernel/include/file.hrl").
+
+%% TODO: maybe expose as app-config
+-define(CHUNK_SIZE, 10240).
+
+%% will be true for MacOsX, FreeBSD, Linux
+-ifdef(HAVE_SENDFILE).
+
+enabled() ->
+ true.
+start_link() ->
+ sendfile_drv:start_link().
+stop() ->
+ sendfile_drv:stop().
+init(ShLib) ->
+ sendfile_drv:init(ShLib).
+send(Out, FileName) ->
+ case sendfile_drv:send(Out, FileName) of
+ {error, eoverflow} ->
+ compat_send(Out, FileName, 0, all);
+ Other ->
+ Other
+ end.
+send(Out, FileName, Offset) ->
+ case sendfile_drv:send(Out, FileName, Offset) of
+ {error, eoverflow} ->
+ compat_send(Out, FileName, Offset, all);
+ Other ->
+ Other
+ end.
+send(Out, FileName, Offset, Count) ->
+ case sendfile_drv:send(Out, FileName, Offset, Count) of
+ {error, eoverflow} ->
+ compat_send(Out, FileName, Offset, Count);
+ Other ->
+ Other
+ end.
+
+-else.
+
+%% Emulate sendfile, this is true for win32, qnx, solaris. OpenBSD,NetBSD I
+%% still don't know
+
+enabled() ->
+ false.
+start_link() ->
+ ignore.
+stop() ->
+ ok.
+init(_) ->
+ ok.
+send(Out, Filename) ->
+ send(Out, Filename, 0, all).
+send(Out, Filename, Offset) ->
+ send(Out, Filename, Offset, all).
+send(Out, Filename, Offset, Count) ->
+ compat_send(Out, Filename, Offset, Count).
+
+-endif.
+
+compat_send(Out, Filename, Offset, Count) ->
+ case file:open(Filename, [read, binary, raw]) of
+ {ok, Fd} ->
+ file:position(Fd, {bof, Offset}),
+ ChunkSize = ?CHUNK_SIZE,
+ Ret = loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out, Count),
+ file:close(Fd),
+ Ret;
+ Err ->
+ Err
+ end.
+
+loop_send(Fd, ChunkSize, {ok, Bin}, Out, all) ->
+ case gen_tcp:send(Out, Bin) of
+ ok ->
+ loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out, all);
+ Err ->
+ Err
+ end;
+loop_send(_Fd, _ChunkSize, eof, _Out, _) ->
+ ok;
+loop_send(Fd, ChunkSize, {ok, Bin}, Out, Count) ->
+ Sz = size(Bin),
+ if Sz < Count ->
+ case gen_tcp:send(Out, Bin) of
+ ok ->
+ loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize),
+ Out, Count-Sz);
+ Err ->
+ Err
+ end;
+ Sz == Count ->
+ gen_tcp:send(Out, Bin);
+ Sz > Count ->
+ <<Deliver:Count/binary , _/binary>> = Bin,
+ gen_tcp:send(Out, Deliver)
+ end;
+loop_send(_Fd, _, Err, _,_) ->
+ Err.
+
+
119 src/sendfile_drv.erl
@@ -0,0 +1,119 @@
+%%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%%% ex: ts=4 sw=4 sts=4 et
+%%%
+%%% Copyright 2008 Steve Vinoski. All Rights Reserved.
+%%% Copyright 2010 Tuncer Ayaz. All Rights Reserved.
+%%% Use of this source code is governed by a BSD-style
+%%% license that can be found in the LICENSE file.
+%%%
+%%% Based on original code from yaws
+%%%
+%%% File : yaws_sendfile.erl
+%%% Author : Steve Vinoski <vinoski@ieee.org>
+%%% Description : interface to sendfile linked-in driver for Yaws
+%%% Created : 9 Nov 2008 by Steve Vinoski <vinoski@ieee.org>
+%%%
+%%% Renamed to sendfile_drv and modified: Tuncer Ayaz in May 2010
+
+-module(sendfile_drv).
+-export([start_link/0, init/1, stop/0, send/2, send/3, send/4]).
+
+-include_lib("kernel/include/file.hrl").
+
+start_link() ->
+ Shlib = "sendfile_drv",
+ Dir = filename:dirname(code:which(?MODULE)) ++ "/../priv",
+ case erl_ddll:load_driver(Dir, Shlib) of
+ ok -> ok;
+ {error, already_loaded} -> ok;
+ _ -> exit({error, could_not_load_driver})
+ end,
+ {ok, spawn_link(?MODULE, init, [Shlib])}.
+
+init(Shlib) ->
+ register(?MODULE, self()),
+ process_flag(trap_exit, true),
+ Port = open_port({spawn, Shlib}, [binary]),
+ loop(Port).
+
+stop() ->
+ ?MODULE ! stop,
+ unregister(?MODULE),
+ ok.
+
+send(Out, Filename) ->
+ send(Out, Filename, 0, 0).
+send(Out, Filename, Offset) ->
+ send(Out, Filename, Offset, 0).
+send(Out, Filename, Offset, Count) ->
+ Count2 = case Count of
+ 0 ->
+ case file:read_file_info(Filename) of
+ {ok, #file_info{size = Size}} ->
+ Size - Offset;
+ Error ->
+ Error
+ end;
+ _ ->
+ Count
+ end,
+ case Count2 of
+ {error, _}=Error2 ->
+ Error2;
+ _ ->
+ case prim_inet:getfd(Out) of
+ {ok, Socket_fd} ->
+ call_port(
+ Socket_fd,
+ list_to_binary(
+ [<<Offset:64, Count2:64, Socket_fd:32>>,
+ Filename, <<0:8>>]));
+ Error3 ->
+ Error3
+ end
+ end.
+
+call_port(Socket_id, Msg) ->
+ ?MODULE ! {call, self(), Socket_id, Msg},
+ receive
+ {?MODULE, Reply} ->
+ Reply
+ end.
+
+loop(Port) ->
+ receive
+ {call, Caller, Id, Msg} ->
+ put(Id, Caller),
+ erlang:port_command(Port, Msg),
+ loop(Port);
+ {Port, {data, <<Cnt:64, Id:32, Res:8, Err/binary>>}} ->
+ Response = case Res of
+ 1 ->
+ {ok, Cnt};
+ 0 ->
+ {error,
+ list_to_atom(
+ lists:takewhile(fun(El) -> El =/= 0 end,
+ binary_to_list(Err)))}
+ end,
+ Caller = erase(Id),
+ Caller ! {?MODULE, Response},
+ loop(Port);
+ stop ->
+ erlang:port_close(Port),
+ receive {'EXIT', Port, _Reason} -> ok
+ after 0 -> ok
+ end;
+ {'EXIT', _, shutdown} ->
+ erlang:port_close(Port),
+ unregister(?MODULE),
+ exit(shutdown);
+ {'EXIT', Port, Posix_error} ->
+ error_logger:format("Fatal error: sendfile port died, error ~p~n",
+ [Posix_error]),
+ exit(Posix_error);
+ {'EXIT', error, Reason} ->
+ error_logger:format("Fatal error: sendfile driver failure: ~p~n",
+ [Reason]),
+ exit(Reason)
+ end.
92 support/getrebar
@@ -0,0 +1,92 @@
+#!/usr/bin/env escript
+%% -*- mode:erlang;tab-width:2;erlang-indent-level:2;indent-tabs-mode:nil -*-
+%% ex: ft=erlang ts=2 sw=2 et
+-include_lib("kernel/include/file.hrl").
+-author(tuncerayaz).
+
+main(_) ->
+ case os:find_executable("rebar") of
+ false ->
+ start_apps(),
+ case obtain(bin) of
+ {ok,Rebar} -> found(Rebar);
+ _ -> halt(1)
+ end;
+ RebarInPATH -> found(RebarInPATH)
+ end.
+
+found(Rebar) ->
+ io:format("~s", [Rebar]),
+ halt(0).
+
+%%
+%% Internal funs
+%%
+rebar_bin_url() ->
+ "http://bitbucket.org/basho/rebar/downloads/rebar".
+
+obtain(bin) ->
+ SupDir = filename:dirname(escript:script_name()),
+ Rebar = SupDir++"/rebar",
+ case is_escript(Rebar) of
+ true -> {ok, Rebar};
+ false ->
+ ok = idelete(Rebar),
+ try download(rebar_bin_url(), Rebar) of
+ ok ->
+ case is_escript(Rebar) of
+ true ->
+ ok = chmodx(Rebar),
+ {ok, Rebar};
+ false -> false
+ end;
+ _ -> {error, download_failed}
+ catch
+ _:_ -> {error, unknown}
+ end
+ end.
+
+start_apps() ->
+ ok = inets:start(),
+ ok = configure_http().
+
+configure_http() ->
+ case os:getenv("http_proxy") of
+ "http://" ++ Proxy ->
+ [Host, Port] = string:tokens(Proxy, ":"),
+ httpc:setoption([{proxy, {Host,Port}}]);
+ _ -> ok
+ end.
+
+idelete(File) ->
+ case file:delete(File) of
+ ok -> ok;
+ {error, enoent} -> ok
+ end.
+
+download(Url, File) ->
+ HttpOpts = [{autoredirect,true}, {timeout, 30000}],
+ Opts = [{stream, File}],
+ case httpc:request(get, {Url, []}, HttpOpts, Opts) of
+ {ok, saved_to_file} -> ok;
+ {ok, {{_,_Code,_},_,_}} -> false;
+ {ok, {_Code,_}} -> false;
+ _ -> false
+ end.
+
+chmodx(File) ->
+ {ok, FileInfo} = file:read_file_info(File),
+ ok = file:write_file_info(File, FileInfo#file_info{mode=8#700}).
+
+is_escript(File) ->
+ case filelib:is_regular(File) of
+ true ->
+ {ok, IOD} = file:open(File, [read]),
+ Line0 = file:read_line(IOD),
+ ok = file:close(IOD),
+ case Line0 of
+ {ok,"#!/usr/bin/env escript\n"} -> true;
+ _ -> false
+ end;
+ false -> false
+ end.
17 test/sendfile_test.erl
@@ -0,0 +1,17 @@
+%%% Based on original test code from ifile project
+%%%----------------------------------------------------------------------
+%%% Created : 6 Nov 2007 by <tobbe@tornkvist.org>
+%%% Desc. : sendfile test program
+%%%----------------------------------------------------------------------
+-module(sendfile_test).
+-export([test/3]).
+
+%%% Shell 1: nc -l -p 4422 localhost
+%%% or if you nc version is different try
+%%% Shell 1: nc -4 -l localhost 4422
+%%% Eshell 2: sendfile_test:test("localhost", 4422, "/somefile").
+test(Host, Port, Fname) ->
+ io:format("sendfile() enabled: ~p~n", [sendfile:enabled()]),
+ sendfile:start_link(),
+ {ok,Sock} = gen_tcp:connect(Host,Port, [binary,{packet,0}]),
+ sendfile:send(Sock, Fname).

0 comments on commit f028092

Please sign in to comment.
Something went wrong with that request. Please try again.