diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2008, John Meacham
+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 author 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 JOHN MEACHAM ``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 JOHN MEACHAM 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.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,5 @@
+#!/usr/bin/env runhaskell
+> module Main where
+> import Distribution.Simple
+> main :: IO ()
+> main = defaultMain
diff --git a/StringTable/Atom.hsc b/StringTable/Atom.hsc
new file mode 100644
--- /dev/null
+++ b/StringTable/Atom.hsc
@@ -0,0 +1,191 @@
+{-# OPTIONS_GHC -fffi -XTypeSynonymInstances -XDeriveDataTypeable #-}
+module StringTable.Atom(
+    Atom(),
+    ToAtom(..),
+    FromAtom(..),
+    HasHash(..),
+    intToAtom,
+    isValidAtom,
+    unsafeIntToAtom,
+    atomCompare,
+    unsafeByteIndex,
+    dumpTable,
+    dumpToFile,
+    dumpStringTableStats
+    ) where
+
+#include "StringTable_cbits.h"
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Internal as BS
+import qualified Data.ByteString.Unsafe as BS
+import Control.Monad
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import Foreign
+import Foreign.Marshal
+import Data.Word
+import Data.Char
+import Foreign.C
+import Data.Monoid
+import Data.Dynamic
+import Data.Bits
+
+newtype Atom = Atom (#type atom_t)
+    deriving(Typeable,Eq,Ord)
+
+class FromAtom a where
+    fromAtom :: Atom -> a
+    fromAtomIO :: Atom -> IO a
+
+    fromAtomIO a = return (fromAtom a)
+    fromAtom a = unsafePerformIO (fromAtomIO a)
+
+class ToAtom a where
+    toAtom :: a -> Atom
+    toAtomIO :: a -> IO Atom
+
+    toAtomIO a = return (toAtom a)
+    toAtom a = unsafePerformIO (toAtomIO a)
+
+class HasHash a where
+    hash32 :: a -> Word32
+
+instance HasHash Atom where
+    hash32 a = let (x,y) = fromAtom a :: CStringLen in unsafePerformIO $ hash2 0 x (fromIntegral y)
+
+instance HasHash BS.ByteString where
+    hash32 bs = unsafePerformIO $ do
+        BS.unsafeUseAsCStringLen bs $ \ (x,y) -> hash2 0 x (fromIntegral y)
+
+instance HasHash String where
+    hash32 s = unsafePerformIO $ withCStringLen s $ \ (x,y) -> hash2 0 x (fromIntegral y)
+
+instance FromAtom (String -> String) where
+    fromAtom x = shows (fromAtom x :: String)
+
+instance ToAtom Atom where
+    toAtom x = x
+
+instance FromAtom Atom where
+    fromAtom x = x
+
+instance ToAtom Char where
+    toAtom x = toAtom [x]
+
+instance ToAtom CStringLen where
+    toAtomIO (cs,len) = do
+        if (len > (#const MAX_ENTRY_SIZE))
+            then fail "StringTable: atom is too big"
+            else stAdd cs (fromIntegral len)
+
+
+
+instance ToAtom CString where
+    toAtomIO cs = do
+        len <- BS.c_strlen cs
+        toAtomIO (cs,fromIntegral len :: Int)
+
+instance ToAtom String where
+    toAtomIO s = toAtomIO (BS.pack (toUTF s))
+
+instance FromAtom String where
+    fromAtom = fromUTF . BS.unpack . fromAtom
+
+instance ToAtom BS.ByteString where
+    toAtomIO bs = BS.unsafeUseAsCStringLen bs toAtomIO
+
+instance FromAtom CStringLen where
+    fromAtom a@(Atom v) = (stPtr a,fromIntegral $ (v `shiftR` (#const ATOM_LEN_SHIFT)) .&. (#const ATOM_LEN_MASK))
+
+instance FromAtom Word where
+    fromAtom (Atom i) = fromIntegral i
+
+instance FromAtom Int where
+    fromAtom (Atom i) = fromIntegral i
+
+instance FromAtom BS.ByteString where
+    fromAtomIO a = do
+        sl <- fromAtomIO a :: IO CStringLen
+        BS.unsafePackCStringLen sl
+
+instance Monoid Atom where
+    mempty = toAtom BS.empty
+    mappend x y = unsafePerformIO $ atomAppend x y
+
+instance Show Atom where
+    showsPrec _ atom = (fromAtom atom ++)
+
+instance Read Atom where
+    readsPrec _ s = [ (toAtom s,"") ]
+
+intToAtom :: Monad m => Int -> m Atom
+intToAtom i = if isValidAtom i then return (Atom $ fromIntegral i) else fail $ "intToAtom: " ++ show i
+
+isValidAtom :: Int -> Bool
+isValidAtom i = odd i
+
+unsafeIntToAtom :: Int -> Atom
+unsafeIntToAtom x = Atom (fromIntegral x)
+
+unsafeByteIndex :: Atom -> Int -> Word8
+unsafeByteIndex atom off = fromIntegral (unsafePerformIO $ peek (stPtr atom `advancePtr` off))
+
+foreign import ccall unsafe "stringtable_lookup" stAdd :: CString -> CInt -> IO Atom
+foreign import ccall unsafe "stringtable_ptr" stPtr :: Atom -> CString
+foreign import ccall unsafe "stringtable_stats" dumpStringTableStats :: IO ()
+foreign import ccall unsafe "dump_table" dumpTable :: IO ()
+foreign import ccall unsafe "atom_append" atomAppend :: Atom -> Atom -> IO Atom
+foreign import ccall unsafe "lexigraphic_compare" c_atomCompare :: Atom -> Atom -> CInt
+foreign import ccall unsafe "dump_to_file" dumpToFile :: IO ()
+foreign import ccall unsafe hash2  :: Word32 -> CString -> CInt -> IO Word32
+
+atomCompare a b = if c == 0 then EQ else if c > 0 then GT else LT where
+    c = c_atomCompare a b
+
+
+
+-- | Convert Unicode characters to UTF-8.
+toUTF :: String -> [Word8]
+toUTF [] = []
+toUTF (x:xs) | ord x<=0x007F = (fromIntegral $ ord x):toUTF xs
+	     | ord x<=0x07FF = fromIntegral (0xC0 .|. ((ord x `shift` (-6)) .&. 0x1F)):
+			       fromIntegral (0x80 .|. (ord x .&. 0x3F)):
+			       toUTF xs
+	     | otherwise     = fromIntegral (0xE0 .|. ((ord x `shift` (-12)) .&. 0x0F)):
+			       fromIntegral (0x80 .|. ((ord x `shift` (-6)) .&. 0x3F)):
+			       fromIntegral (0x80 .|. (ord x .&. 0x3F)):
+			       toUTF xs
+
+-- | Convert UTF-8 to Unicode.
+
+fromUTF :: [Word8] -> String
+fromUTF xs = fromUTF' (map fromIntegral xs) where
+    fromUTF' [] = []
+    fromUTF' (all@(x:xs))
+	| x<=0x7F = (chr (x)):fromUTF' xs
+	| x<=0xBF = err
+	| x<=0xDF = twoBytes all
+	| x<=0xEF = threeBytes all
+	| otherwise   = err
+    twoBytes (x1:x2:xs) = chr  ((((x1 .&. 0x1F) `shift` 6) .|.
+			       (x2 .&. 0x3F))):fromUTF' xs
+    twoBytes _ = error "fromUTF: illegal two byte sequence"
+
+    threeBytes (x1:x2:x3:xs) = chr ((((x1 .&. 0x0F) `shift` 12) .|.
+				    ((x2 .&. 0x3F) `shift` 6) .|.
+				    (x3 .&. 0x3F))):fromUTF' xs
+    threeBytes _ = error "fromUTF: illegal three byte sequence"
+
+    err = error "fromUTF: illegal UTF-8 character"
+
+instance Binary Atom where
+    get = do
+        x <- getWord8
+        bs <- getBytes (fromIntegral x)
+        return $ toAtom bs
+    put a = do
+        let bs = fromAtom a
+        putWord8 $ fromIntegral $ BS.length bs
+        putByteString bs
diff --git a/cbits/StringTable_cbits.c b/cbits/StringTable_cbits.c
new file mode 100644
--- /dev/null
+++ b/cbits/StringTable_cbits.c
@@ -0,0 +1,441 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <stdbool.h>
+#define NDEBUG 1
+#include <assert.h>
+
+
+#define USE_THREADS 0
+
+#if USE_THREADS
+
+#include <pthread.h>
+
+static pthread_mutex_t mutex_hash = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_string = PTHREAD_MUTEX_INITIALIZER;
+
+#else
+
+#define pthread_mutex_lock(x) ;
+#define pthread_mutex_unlock(x) ;
+
+#endif
+
+#include "StringTable_cbits.h"
+// 23 bits of chunk space to leave one bit for 'valid' flag.
+// valid flag must be set to 1 for it to be a valid atom
+
+static void dieif(bool,char *);
+uint32_t hash2(uint32_t salt,unsigned char *key, int key_len);
+static void print_quoted(FILE *file,unsigned char *s,int len);
+
+// string allocation stuff
+
+#define NUM_CHUNKS 256
+#define CHUNK_SIZE 16384
+
+
+
+#define ATOM_LEN(c)     (((atom_t)(c) >> ATOM_LEN_SHIFT) & ATOM_LEN_MASK)
+#define CHUNK_INDEX(c)  (((atom_t)(c) >> 9)&0xFF)
+#define CHUNK_OFFSET(c) (((atom_t)(c) >> 17) & 0x3FFF)
+
+#define MAKE_ATOM(ci,co,len) ((((((atom_t)len) & ATOM_LEN_MASK) << ATOM_LEN_SHIFT) | ((((atom_t)ci) & 0xff) << 9) | (((atom_t)co & 0x3FFF) << 17)) | VALID_BITMASK)
+
+#define ATOM_PTR(c) ((unsigned char *)&(stringtable_chunks[CHUNK_INDEX(c)][CHUNK_OFFSET(c)]))
+
+#define ATOM_VALID(a) (a)
+
+// ((a) & VALID_BITMASK)
+
+
+
+static unsigned char first_chunk[CHUNK_SIZE];
+static unsigned char *stringtable_chunks[NUM_CHUNKS] = { first_chunk };
+
+static uint16_t current_chunk = 0;
+static uint16_t next_free_offset = 0;
+
+static atom_t
+add_string(unsigned char *cs, int len)
+{
+    pthread_mutex_lock(&mutex_string);
+        //printf("add_string(%c,%c,%i)\n",cs[0],cs[1],len);
+    assert(len >= 0);
+    assert(len < MAX_ENTRY_SIZE);
+    assert(next_free_offset < CHUNK_SIZE);
+    if(next_free_offset + 1 > CHUNK_SIZE - MAX_ENTRY_SIZE) {
+        dieif(current_chunk >= NUM_CHUNKS - 1, "No more chunks");
+        current_chunk++;
+        assert(!stringtable_chunks[current_chunk]);
+        stringtable_chunks[current_chunk] = malloc(CHUNK_SIZE);
+        dieif(!stringtable_chunks[current_chunk], "error alocating memory");
+        next_free_offset = 0;
+    }
+    memcpy(stringtable_chunks[current_chunk] + next_free_offset, cs, len);
+    atom_t r = MAKE_ATOM(current_chunk, next_free_offset, len);
+    assert(CHUNK_INDEX(r) == current_chunk);
+    assert(CHUNK_OFFSET(r) == next_free_offset);
+    assert(ATOM_PTR(r) == stringtable_chunks[current_chunk] + next_free_offset);
+    assert(ATOM_LEN(r) == len);
+    next_free_offset += len;
+    assert(next_free_offset < CHUNK_SIZE);
+    assert(current_chunk < NUM_CHUNKS);
+    pthread_mutex_unlock(&mutex_string);
+    return r;
+}
+
+// hashtable stuff
+
+#define KEEP_HASH 2
+
+#define CUCKOO_HASHES 2U
+#define CUCKOO_BUCKETS 2U
+
+typedef uint32_t hash_t;
+
+struct hentry {
+#if KEEP_HASH
+        hash_t hashes[CUCKOO_HASHES];
+#endif
+        atom_t atom;
+};
+
+#define INIT_SIZE 2
+static uint32_t hsize = INIT_SIZE;
+static struct hentry init_htable[(1 << INIT_SIZE) * CUCKOO_HASHES];
+static struct hentry *htable = init_htable;
+
+#define HASHSIZE  (1 << hsize)
+#define HASHMASK (HASHSIZE - 1)
+
+#define INDEX_HASH(i)  ((i) / HASHSIZE)
+#define HASH_INDEX(h,x) ((h * HASHSIZE) + (HASHMASK & ((uint32_t)x)))
+
+#define HASH_BUCKET(x,b) ((((x) + (b)) % HASHSIZE) + (INDEX_HASH(x)*HASHSIZE))
+
+#define DEPTH_LIMIT 512
+
+static void hash_insert(struct hentry x);
+
+static void
+fast_insert(int t, int tb, struct hentry hb) {
+        hash_insert(hb);
+}
+
+#ifndef NDEBUG
+
+static bool
+atom_exists(atom_t a) {
+        for(int i = 0; i < HASHSIZE*CUCKOO_HASHES; i++) {
+                if(a == htable[i].atom) return true;
+        }
+        return false;
+}
+static bool
+item_exists(unsigned char *cs, int len) {
+        for(int i = 0; i < HASHSIZE*CUCKOO_HASHES; i++) {
+                atom_t a = htable[i].atom;
+                if(ATOM_VALID(a)) {
+                    if(len == ATOM_LEN(a) && !memcmp(ATOM_PTR(a),cs,len))
+                        return true;
+                }
+        }
+        return false;
+}
+
+#endif
+
+void
+dump_to_file(void) {
+        FILE *file = fopen("atom.dump","w");
+        for(int i = 0; i < HASHSIZE*CUCKOO_HASHES; i++) {
+                atom_t a = htable[i].atom;
+                if(ATOM_VALID(a)) {
+                        fprintf(file,"%u:",ATOM_LEN(a));
+                        print_quoted(file, ATOM_PTR(a),ATOM_LEN(a));
+                        fwrite("\n",1,1,file);
+                }
+        }
+}
+
+void
+dump_table(void) {
+        for(int i = 0; i < HASHSIZE*CUCKOO_HASHES; i++) {
+                atom_t a = htable[i].atom;
+                if(ATOM_VALID(a)) {
+                        printf("%p %u: ",ATOM_PTR(a),ATOM_LEN(a));
+                        fwrite(ATOM_PTR(a),1,ATOM_LEN(a),stdout);
+                        fwrite("\n",1,1,stdout);
+                }
+        }
+}
+
+
+static void
+grow_table(void) {
+ //   fprintf(stderr,"grow_table[[[\n");
+        uint32_t os = (1 << hsize++) * CUCKOO_HASHES;
+        struct hentry *ot = htable;
+        htable = calloc(sizeof(struct hentry),CUCKOO_HASHES * (1 << hsize));
+
+        for(int i = 0; i < os; i++) {
+                if(ATOM_VALID(ot[i].atom))
+                        fast_insert(0,0,ot[i]);
+        }
+        if(ot != init_htable) free(ot);
+//    fprintf(stderr,"]]]\n");
+}
+
+#if KEEP_HASH
+#define FHASH(x,i) ((x).hashes[i])
+#else
+#define FHASH(x,i) (hash2(i,ATOM_PTR((x).atom),ATOM_LEN((x).atom)))
+#endif
+
+static void
+hash_insert(struct hentry x) {
+        assert(ATOM_VALID(x.atom));
+//         fprintf(stderr,"hash_insert(%x,%p:%i,%x,%x,[%x,%x]", x.atom, ATOM_PTR(x.atom), ATOM_LEN(x.atom), x.hashes[0], x.hashes[1],HASH_INDEX(0,x.hashes[0]),HASH_INDEX(1,x.hashes[1]));
+        assert(!atom_exists(x.atom));
+        assert(!item_exists(ATOM_PTR(x.atom),ATOM_LEN(x.atom)));
+        atom_t start = x.atom;
+        for(int loop = 0; loop < DEPTH_LIMIT;loop++) {
+                for(int i = 0; i < CUCKOO_HASHES; i++) {
+//                        int e = HASH_INDEX(i,FHASH(x,i));
+                        for(int j = 0; j < CUCKOO_BUCKETS; j++) {
+                                //#struct hentry *b = &(htable[(e + j) & HASHJMASK ]);
+                                //struct hentry *b = &htable[HASH_BUCKET(e,j)];
+                                struct hentry *b = &htable[HASH_INDEX(i,FHASH(x,i) + j)];
+                                if(!ATOM_VALID(b->atom)) {
+                                        *b = x;
+//                                        fprintf(stderr,")\n");
+                                        return;
+                                }
+                                struct hentry tb = x;
+                                x = *b;
+                                *b = tb;
+                        }
+                        // struct hentry *b = &(htable[e]);
+                }
+                if(x.atom == start) {
+                        break;
+                }
+        }
+        grow_table();
+//        fprintf(stderr,"R");
+        return hash_insert(x);
+}
+
+
+
+static void
+print_quoted(FILE *file,unsigned char *s,int len)
+{
+        for(int i = 0;i < len; i ++) {
+                switch(s[i]) {
+                case '\n': fputs("\\n",file); continue;
+                case '\r': fputs("\\r",file); continue;
+                case '\t': fputs("\\t",file); continue;
+                default: ;
+                }
+                if(isprint(s[i]))
+                        fputc(s[i],file);
+                else
+                        fprintf(file,"\\x%2.2X", (unsigned)s[i]);
+        }
+}
+
+
+atom_t
+stringtable_lookup(unsigned char *cs, int len)
+{
+//        static FILE *file = NULL;
+//        if(!file)
+//                file = fopen("atom.lookup","w");
+//        fprintf(file,"stringtable_lookup(");
+//        print_quoted(file,cs,len);
+//        fprintf(file,")\n");
+        pthread_mutex_lock(&mutex_hash);
+        assert(len >= 0);
+        assert(len < MAX_ENTRY_SIZE);
+        hash_t h[CUCKOO_HASHES];
+        for(uint32_t i = 0; i < CUCKOO_HASHES; i++) {
+                h[i] = hash2(i,cs,len);
+                //int e = HASH_INDEX(i,h[i]);
+                for(int j = 0; j < CUCKOO_BUCKETS; j++) {
+                        //struct hentry *b = &htable[(e + i) & HASHJMASK ];
+                        //struct hentry *b = &htable[HASH_BUCKET(e,j)];
+                        struct hentry *b = &htable[HASH_INDEX(i,h[i] + j)];
+#if KEEP_HASH
+                        if (ATOM_VALID(b->atom) && h[i] == b->hashes[i] && len == ATOM_LEN(b->atom) &&  !memcmp(ATOM_PTR(b->atom),cs,len)) {
+                            pthread_mutex_unlock(&mutex_hash);
+                            return b->atom;
+                        }
+#else
+                        if (ATOM_VALID(b->atom) && len == ATOM_LEN(b->atom) && !memcmp(ATOM_PTR(b->atom),cs,len)) {
+                            pthread_mutex_unlock(&mutex_hash);
+                            return b->atom;
+                        }
+#endif
+                }
+        }
+        atom_t na = add_string(cs,len);
+        struct hentry hb;
+        hb.atom = na;
+#if KEEP_HASH
+        memcpy(hb.hashes,h,sizeof hb.hashes);
+#endif
+        hash_insert(hb);
+        pthread_mutex_unlock(&mutex_hash);
+        return na;
+}
+
+
+
+int
+lexigraphic_compare(atom_t x, atom_t y)
+{
+    int xl = ATOM_LEN(x);
+    int yl = ATOM_LEN(y);
+    return memcmp(ATOM_PTR(x),ATOM_PTR(y),xl < yl ? xl : yl) || xl - yl;
+}
+
+
+atom_t
+atom_append(atom_t x,atom_t y)
+{
+    unsigned char *xs,*ys;
+    int xl,yl;
+
+    xl = stringtable_find(x,&xs);
+    yl = stringtable_find(y,&ys);
+
+    unsigned char buf[MAX_ENTRY_SIZE];
+
+    memcpy(buf,xs,xl);
+    memcpy(buf + xl,ys,yl);
+
+    return stringtable_lookup(buf,xl + yl);
+}
+
+unsigned char *
+stringtable_ptr(atom_t cl)
+{
+        assert(ATOM_VALID(cl));
+        return ATOM_PTR(cl);
+}
+
+int
+stringtable_get(atom_t cl, char buf[MAX_ENTRY_SIZE])
+{
+        assert(ATOM_VALID(cl));
+        memcpy(buf,ATOM_PTR(cl),ATOM_LEN(cl));
+        return ATOM_LEN(cl);
+}
+
+int
+stringtable_find(atom_t cl, unsigned char **res)
+{
+        assert(ATOM_VALID(cl));
+        *res = ATOM_PTR(cl);
+        return ATOM_LEN(cl);
+}
+
+void
+stringtable_stats(void)
+{
+    unsigned static_memory = sizeof(stringtable_chunks);
+    printf("Static Memory: %u\n", static_memory);
+    unsigned dynamic_memory = (current_chunk + 1) * CHUNK_SIZE;
+    unsigned data_memory = current_chunk*CHUNK_SIZE + next_free_offset;;
+    printf("Used Chunks: %u/%u - %u bytes\n", current_chunk + 1, NUM_CHUNKS, data_memory);
+    unsigned num_entries = 0;
+    unsigned hash_types[CUCKOO_HASHES];
+    memset(hash_types,0,sizeof hash_types);
+    unsigned num_total = 0;
+    for(int i = 0; i < HASHSIZE * CUCKOO_HASHES; i++) {
+            num_total++;
+            dynamic_memory += sizeof(struct hentry);
+            if(ATOM_VALID(htable[i].atom)) {
+                    num_entries++;
+                    hash_types[i / HASHSIZE]++;
+            }
+    }
+
+    for(int i = 0; i < CUCKOO_HASHES; i++)
+            printf("Hash Table  %i: %u\n", i, hash_types[i]);
+
+    printf("Usage: %u/%u %.3f%%\n", num_entries, num_total, (double)num_entries * 100.0 / num_total);
+
+    printf("Dynamic Memory: %u\n", dynamic_memory);
+    printf("Storage Efficiency: %.3f%%\n", (double)data_memory * 100.0/ dynamic_memory);
+
+}
+
+
+
+
+static void
+dieif(bool w,char *str)
+{
+    if(w) {
+        fprintf(stderr, "stringlib: %s\n", str);
+        exit(1);
+    }
+}
+
+// hash functions
+
+
+uint32_t
+hash2(uint32_t salt, unsigned char *key, int key_len)
+{
+        uint32_t hash = salt;
+        for (int i = 0; i < key_len; i++) {
+                hash += key[i];
+                hash += (hash << 10);
+                hash ^= (hash >> 6);
+        }
+        hash += (hash << 3);
+        hash ^= (hash >> 11);
+        hash += (hash << 15);
+        return hash;
+}
+
+/*
+int
+main(int argc, char *argv[])
+{
+    unsigned char buf[MAX_ENTRY_SIZE];
+    while(fgets((char *)buf,MAX_ENTRY_SIZE,stdin)) {
+        buf[MAX_ENTRY_SIZE - 1] = '\0';
+        unsigned len = strlen((char *)buf);
+        if(!len) continue;
+        if(buf[len - 1] == '\n') buf[--len] = '\0';
+        stringtable_lookup(buf,len);
+        //printf("%x: %s\n", a, buf);
+
+    }
+
+    dump_table();
+    stringtable_stats();
+
+    return 0;
+}
+static hash_t
+hash3(uint32_t salt, unsigned char* str, size_t len)
+{
+        const uint32_t fnv_prime = 0x811C9DC5;
+        unsigned int hash      = salt;
+        for(int i = 0; i < len; i++) {
+                hash *= fnv_prime;
+                hash ^= str[i];
+                hash ^= salt;
+        }
+        return hash;
+}
+*/
diff --git a/cbits/StringTable_cbits.h b/cbits/StringTable_cbits.h
new file mode 100644
--- /dev/null
+++ b/cbits/StringTable_cbits.h
@@ -0,0 +1,22 @@
+#ifndef ST_CBITS_H
+#define ST_CBITS_H
+
+#include <stdint.h>
+
+#define MAX_ENTRY_SIZE 256
+#define VALID_BITMASK 0x1
+
+// always shift, then mask
+#define ATOM_LEN_MASK 0xff
+#define ATOM_LEN_SHIFT 1
+
+typedef uint32_t atom_t;
+
+
+atom_t stringtable_lookup(unsigned char *cs, int len);
+void stringtable_stats(void);
+int stringtable_find(atom_t cl, unsigned char **res);
+unsigned char *stringtable_ptr(atom_t cl);
+
+
+#endif
diff --git a/stringtable-atom.cabal b/stringtable-atom.cabal
new file mode 100644
--- /dev/null
+++ b/stringtable-atom.cabal
@@ -0,0 +1,21 @@
+Name:                stringtable-atom
+Version:             0.0.1
+Category:            Data
+Synopsis:            Maps strings to Atoms for fast equality and ordering comparison
+Description:         Maps strings to Atoms for fast equality and ordering comparison
+License:             BSD3
+License-File:        LICENSE
+Author:              John Meacham
+Maintainer:          audreyt@audreyt.org
+Copyright:           John Meacham, 2008
+Exposed-modules:     StringTable.Atom
+
+Build-Depends:       base, bytestring>=0.9.0.1, binary
+Build-Type:          Simple
+Tested-With:         GHC==6.8.2
+
+extensions:          ForeignFunctionInterface, TypeSynonymInstances, DeriveDataTypeable, FlexibleInstances
+c-sources:           cbits/StringTable_cbits.c
+extra-source-files:  cbits/StringTable_cbits.h
+include-dirs:        cbits
+cc-options:          -O2 -std=c99
