packages feed

stringtable-atom (empty) → 0.0.1

raw patch · 6 files changed

+704/−0 lines, 6 filesdep +basedep +binarydep +bytestringsetup-changed

Dependencies added: base, binary, bytestring

Files

+ LICENSE view
@@ -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.
+ Setup.lhs view
@@ -0,0 +1,5 @@+#!/usr/bin/env runhaskell+> module Main where+> import Distribution.Simple+> main :: IO ()+> main = defaultMain
+ StringTable/Atom.hsc view
@@ -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
+ cbits/StringTable_cbits.c view
@@ -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;+}+*/
+ cbits/StringTable_cbits.h view
@@ -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
+ stringtable-atom.cabal view
@@ -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