packages feed

combobuffer (empty) → 0.1

raw patch · 11 files changed

+516/−0 lines, 11 filesdep +basedep +containersdep +template-haskellsetup-changed

Dependencies added: base, containers, template-haskell, vector, vector-space

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright John W. Lato 2011++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 John W. Lato nor the names of other+      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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ combobuffer.cabal view
@@ -0,0 +1,35 @@+Name:                combobuffer+Version:             0.1+Synopsis:            Various buffer implementations+Description:         Various buffer implementations+Homepage:            https://github.com/JohnLato/combobuffer+License:             BSD3+License-file:        LICENSE+Author:              John W. Lato+Maintainer:          jwlato@gmail.com+Category:            Data+Build-type:          Simple+Cabal-version:       >=1.6++Library+  Hs-Source-Dirs:     src+  Exposed-modules:    Data.RingBuffer+                     ,Data.RingBuffer.Chord+                     ,Data.RingBuffer.Class+                     ,Data.RingBuffer.ComboBuffer+                     ,Data.RingBuffer.MapBuffer+                     ,Data.RingBuffer.SeqBuffer+                     ,Data.RingBuffer.SVec+                     ,Data.RingBuffer.TGen++  Build-depends:      base         >= 3      && < 5+                     ,containers   >= 0.3    && < 0.6+                     ,template-haskell >= 2.6 && < 2.9+                     -- ,type-level   >= 0.2    && < 0.3+                     -- ,tuple        >= 0.2    && < 0.3+                     ,vector       >= 0.5    && < 0.11+                     ,vector-space >= 0.7    && < 0.9+  +source-repository head+  type:                git+  location:            git@github.com:JohnLato/combobuffer.git
+ src/Data/RingBuffer.hs view
@@ -0,0 +1,17 @@+module Data.RingBuffer (+  Initializable (..)+ ,RingBuffer (..)+ ,El+ ,SeqBuffer (..)+ ,ComboBuffer (..)+ ,MapBuffer   (..)+ ,module X+)++where++import Data.RingBuffer.Class+import Data.RingBuffer.ComboBuffer+import Data.RingBuffer.MapBuffer+import Data.RingBuffer.SeqBuffer+import Data.RingBuffer.SVec as X
+ src/Data/RingBuffer/Chord.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -Wall #-}+module Data.RingBuffer.Chord+(+  Chord+, emptyChord+, cToVec+)++where++import Prelude hiding (length)+import Data.RingBuffer.Class+import qualified Data.Vector.Unboxed as V+import           Data.Vector.Unboxed (Vector, Unbox)++-- A structure for pushing elements+-- params:+--   partial chunk fill+--   partial chunk+--   full chunk quantity+--   full chunks+data Chord a = Chord !Int [a] !Int [Vector a]+  deriving (Show, Eq, Ord)++-- | an empty chord.+emptyChord :: a -> Chord a+emptyChord _ = Chord 0 [] 0 []++cToVec :: Unbox a => Chord a -> Vector a+cToVec (Chord pf pc _ cs) = V.concat (V.fromListN pf pc : cs)+{-# INLINE cToVec #-}++type instance (El (Chord a)) = a++instance V.Unbox a => RingBuffer (Chord a) where+  {-# INLINE length #-}+  length (Chord pfill _ c _) = 32*c + pfill+  {-# INLINE push #-}+  push   (Chord 31 pc n cs) el =+     let new = V.fromListN 32 (el:pc)+     in  new `seq` Chord 0 [] (n+1) (new:cs)+  push   (Chord pf pc n cs) el =+     Chord (pf+1) (el : pc) n cs+  {-# INLINE (!) #-}+  (Chord pf pc _ cs) ! ix+     | ix <= pf  = pc !! ix+     | otherwise = let (major,minor) = (ix - pf) `divMod` 32+                   in (cs !! major) `V.unsafeIndex` minor
+ src/Data/RingBuffer/Class.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE TypeFamilies, GADTs #-}++{-# OPTIONS_GHC -Wall #-}+-- | The RingBuffer interface.+--+-- A RingBuffer is a fixed-length buffer that supports lookups anywhere in+-- the structure and pushing new elements onto the front.  When a new value is+-- pushed, the oldest value will be dropped.+--+-- This module provides an implementation based on+-- 'Data.Vector.Unboxed.Vector', with O(1) lookups and O(n) pushes.  Other+-- modules provide implementations with different performance profiles.+module Data.RingBuffer.Class (+  El+ ,RingBuffer (..)+ ,Initializable (..)+)++where++import Prelude hiding (length)+import qualified Data.Vector.Unboxed as V++type family El c :: *++-- | Create a new 'Initializable' with the given value at every position.+-- Essentially a generalized 'Prelude.replicate'+class Initializable c where+  newInit :: El c -> Int -> c++-- | The RingBuffer interface.+--+-- Instances must define 'length', 'push', and '(!)'.  Instances may define+-- 'slice' for better performance.+class RingBuffer c where+  length    :: c -> Int+  push      :: c -> El c -> c+  (!)       :: c -> Int  -> El c+  slice     :: c -> Int  -> Int -> [El c]+  {-# INLINE slice #-}+  slice c start num = [ c ! ix | ix <- [start .. start+num]]+++type instance El (V.Vector el) = el++instance V.Unbox el => Initializable (V.Vector el) where+  {-# INLINE newInit #-}+  newInit = flip V.replicate++instance V.Unbox el => RingBuffer (V.Vector el) where+  {-# INLINE length #-}+  length      = V.length+  {-# INLINE (!) #-}+  (!)         = (V.!)+  {-# INLINE push #-}+  push vec el = V.cons el $ V.unsafeInit vec+  {-# INLINE slice #-}+  slice vec start num = V.toList $ V.slice start num vec
+ src/Data/RingBuffer/ComboBuffer.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -Wall #-}+-- | Generally the best-performing implementation in this package.+--+-- 'ComboBuffer' supports amortized O(1) push and O(1) length.  lookups at 'ix+-- >= n/2' and 'ix=0' are O(1), while lookups at '0 <= ix < n/2' are no more+-- than O(n).  If you need to do many lookups in the first half, and+-- particularly around n/4, another structure may be a better choice.+module Data.RingBuffer.ComboBuffer (+  ComboBuffer (..)+)++where++import           Prelude hiding (length)++import           Data.RingBuffer.Class+import           Data.RingBuffer.Chord+import qualified Data.Vector.Unboxed as V++data ComboBuffer a =+   CB {-# UNPACK #-} !Int          -- Half Size+      {-# UNPACK #-} !Int          -- buffer position+                     !(V.Vector a) -- newer vector+                     !(V.Vector a) -- older vector+      (Chord a)                    -- updates+ | CBOdd {-# UNPACK #-} !Int          -- Half (Size + 1)+         {-# UNPACK #-} !Int          -- buffer position+                        !(V.Vector a) -- newer vector+                        !(V.Vector a) -- older vector+         (Chord a)                    -- updates+ deriving (Eq, Ord, Show)++type instance El (ComboBuffer a) = a++instance V.Unbox a => Initializable (ComboBuffer a) where+  {-# INLINE newInit #-}+  newInit = newInit'++newInit' :: V.Unbox a => a -> Int -> ComboBuffer a+newInit' el sz+  | sz <= 0 = error "Can't initialize ComboBuffer with size <= 0"+  | sz `rem` 2 == 0 = let half = sz `div` 2+                          iv   = V.replicate half el+                      in  CB half 0 iv iv (emptyChord el)+  | otherwise       = let half = (1+sz) `div` 2+                          iv   = V.replicate half el+                      in  CBOdd half 0 iv iv (emptyChord el)+{-# INLINE newInit' #-}++instance V.Unbox a => RingBuffer (ComboBuffer a) where+  {-# INLINE length #-}+  length (CB sz _ _ _ _)    = 2*sz+  length (CBOdd sz _ _ _ _) = 2*sz-1+  {-# INLINE (!) #-}+  (!) = at+  {-# INLINE push #-}+  push = pushB+  {-# INLINE slice #-}+  slice = sliceB++at :: V.Unbox a => ComboBuffer a -> Int -> a+at (CB sz pos v1 v2 upds) ix+  | ix < 0    = error $ "ComboBuffer: index out of range: " ++ show ix+  | ix < pos  = upds ! ix+  | ix < 2*sz = let ix' = ix - pos in if ix' < sz+                  then v1 `V.unsafeIndex` ix'+                  else v2 `V.unsafeIndex` (ix' - sz)+  | otherwise = error $ "ComboBuffer: index out of range: " ++ show (ix,2*sz)+at (CBOdd sz pos v1 v2 upds) ix+  | ix < 0      = error $ "ComboBuffer: index out of range: " ++ show ix+  | ix < pos    = upds ! ix+  | ix < 2*sz-1 = let ix' = ix - pos in if ix' < sz+                    then v1 `V.unsafeIndex` ix'+                    else v2 `V.unsafeIndex` (ix' - sz)+  | otherwise = error $ "ComboBuffer: index out of range: " ++ show (ix,2*sz-1)+{-# INLINE at #-}++sliceB :: V.Unbox a => ComboBuffer a -> Int -> Int -> [a]+sliceB (CB sz pos v1 v2 upds) start num+  = let ix' = start - pos+        fstTake = sz - (ix'+num)+    in if ix' < sz+         then slice upds start num+              ++ V.toList (V.slice ix' (min 0 fstTake) v1)+              ++ V.toList (V.slice 0 (min 0 (negate fstTake)) v2)+         else V.toList (V.slice (ix'-sz) num v2)+sliceB (CBOdd sz pos v1 v2 upds) start num+  = let ix' = start - pos+        fstTake = sz - (ix'+num)+    in if ix' < sz+         then slice upds start num+              ++ V.toList (V.slice ix' (min 0 fstTake) v1)+              ++ V.toList (V.slice 0 (min 0 (negate fstTake)) v2)+         else V.toList (V.slice (ix'-sz) num v2)+{-# INLINE sliceB #-}++pushB :: V.Unbox a => ComboBuffer a -> a -> ComboBuffer a+pushB (CB sz pos v1 v2 upds) el+  | pos == sz-1 = CB sz 0 (cToVec $ push upds el) v1+                          (emptyChord el)+  | otherwise   = CB sz (pos+1) v1 v2 (push upds el)+pushB (CBOdd sz pos v1 v2 upds) el+  | pos == sz-1 = CBOdd sz 0 (cToVec $ push upds el) v1+                           (emptyChord el)+  | otherwise   = CBOdd sz (pos+1) v1 v2 (push upds el)+{-# INLINE pushB #-}
+ src/Data/RingBuffer/MapBuffer.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeSynonymInstances #-}++{-# OPTIONS_GHC -Wall #-}+-- | A 'RingBuffer' implementation based on IntMaps.  Operations have the same+-- complexity as the underlying IntMap+module Data.RingBuffer.MapBuffer (+  Initializable (..)+ ,RingBuffer (..)+ ,MapBuffer (..)+)++where++import           Prelude hiding (length)++import           Data.RingBuffer.Class+import           Data.IntMap (IntMap)+import qualified Data.IntMap as M++data MapBuffer a = MB {-# UNPACK #-} !Int !(IntMap a) +  deriving (Eq, Show, Ord)++type instance El (MapBuffer el) = el++instance Initializable (MapBuffer el) where+  {-# INLINE newInit #-}+  newInit el sz = MB 0 $ M.fromDistinctAscList $ map (,el) [0..sz-1]++instance RingBuffer (MapBuffer el) where+  {-# INLINE length #-}+  length (MB _ vec) = M.size vec+  {-# INLINE (!) #-}+  (MB pos vec) ! ix = vec M.! ((pos-ix-1) `mod` M.size vec)+  {-# INLINE push #-}+  push = pushE+  {-# INLINE slice #-}+  slice = sliceB++pushE :: MapBuffer a -> a -> MapBuffer a+pushE (MB pos vec) el = +  let newPos = if pos == M.size vec - 1 then 0 else pos + 1+      vec' = M.insert pos el vec+  in  MB newPos vec'+{-# INLINE pushE #-}++sliceB :: MapBuffer a -> Int -> Int -> [a]+sliceB (MB pos vec) start num = -- V.toList $ V.slice (pos+start) num vec+  let ix1 = pos+start+      top = snd $ M.split (ix1-1) vec+  in  map snd . M.toAscList . fst $ M.split (ix1+num) top+{-# INLINE sliceB #-}
+ src/Data/RingBuffer/SVec.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE DeriveFoldable #-}++{-# OPTIONS_GHC -funbox-strict-fields #-}++-- | Strict vectors of doubles to length == 30+module Data.RingBuffer.SVec++where++import Prelude hiding (length)+import Data.RingBuffer.Class+import Data.RingBuffer.TGen+import Language.Haskell.TH (Type(..),mkName, TyVarBndr(..))++import Data.Foldable++$(mkVecFromTo 1 30 (ConT ''Double) [] "")++-- | Generate a 32-element polymorphic vector, maybe I should just use+-- Data.Vector?+$(mkVecFromTo 32 32 (VarT (mkName "a")) [PlainTV (mkName "a")] "Vec")++deriving instance Show a => Show (TVec32 a)+deriving instance Eq a => Eq (TVec32 a)+deriving instance Ord a => Ord (TVec32 a)+deriving instance Foldable (TVec32)
+ src/Data/RingBuffer/SeqBuffer.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -Wall #-}+-- | A 'RingBuffer' based on 'Data.Sequence.Seq'.  Asymptotic behavior is quite+-- good in all cases, but constant factors are very high.+module Data.RingBuffer.SeqBuffer (+  SeqBuffer+ ,RingBuffer (..)+ ,new+)++where++import           Prelude hiding (length, (!!))+import           Data.RingBuffer.Class++import qualified Data.Sequence as S+import           Data.Foldable++newtype SeqBuffer a = RB (S.Seq a) deriving (Eq, Ord, Show)++type instance El (SeqBuffer a) = a++instance Initializable (SeqBuffer a) where+  {-# INLINE newInit #-}+  newInit = newInit'++instance RingBuffer (SeqBuffer a) where+  {-# INLINE length #-}+  length  = length'+  {-# INLINE push #-}+  push    = push'+  {-# INLINE (!) #-}+  (!)     = (!!)+  {-# INLINE slice #-}+  slice (RB sq) start num = toList . S.take num $ S.drop start sq++-- | Create a new SeqBuffer, initialized to all 0's, of the given size+new :: (Num a) => Int -> SeqBuffer a+new = newInit' 0+{-# INLINE new #-}++-- | Create a new SeqBuffer from a given initial value+newInit' :: a -> Int -> SeqBuffer a+newInit' _ sz | sz <= 0 = error "can't make empty ringbuffer"+newInit' i sz           = RB (S.replicate sz i)+{-# INLINE newInit' #-}++-- | Get the total size of a SeqBuffer.+length' :: SeqBuffer a -> Int+length' (RB vec) = S.length vec+{-# INLINE length' #-}++-- | Look up a value in a SeqBuffer.+(!!) :: SeqBuffer a -> Int -> a+(!!) (RB vec) = S.index vec+{-# INLINE (!!) #-}++-- | Push a new value into a SeqBuffer.  The following will hold:+--     NewSeqBuffer ! 0 === added element+--     NewSeqBuffer ! 1 === OldSeqBuffer ! 0+push' :: SeqBuffer a -> a -> SeqBuffer a+push' (RB vec) el   = case S.viewr vec of+  v' S.:> _ -> RB $ el S.<| v'+  _         -> error "internal error"+{-# INLINE push' #-}
+ src/Data/RingBuffer/TGen.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}++-- | Template Haskell splices to create  constant-sized vectors and RingBuffer+-- instances for them+module Data.RingBuffer.TGen (+  mkVecFromTo+ ,mkVec+)++where++import Prelude hiding (length)+import Data.RingBuffer.Class++import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Control.Applicative+import Control.Monad++mkVecFromTo start stop elname binders prefix =+  concat <$> mapM (mkVec elname binders prefix) [start .. stop]++mkVec elname binders prefix sz = do+  let nm = mkName $ 'T':prefix ++ show sz+  let tname = case binders of+        []   -> ConT nm+        [PlainTV b1] -> AppT (ConT nm) (VarT b1)+        _    -> error "can't handle types with more than 1 type variable, or non-* kinded types"+  d1 <- decTN sz nm elname binders+  d2 <- mkElInst tname elname+  d3 <- mkInitInst sz nm (return tname)+  d4 <- mkRbInst sz nm (return tname)+  return $ concat [d1,d2,d3,d4]++decTN sz nm elname binders =+  let fields = replicate sz (IsStrict, elname)+  in return [DataD [] nm binders [NormalC nm fields] []]++mkElInst tname elname = return [TySynInstD ''El [tname] (elname) ]++mkInitInst vsz nm tname = let nmStr = show nm in [d| instance Initializable $(tname) where {-# INLINE newInit #-}; newInit el sz | sz >= 0 && sz <= vsz = $(appsE $ conE nm:replicate vsz [| el |]) ; newInit el sz = error ("cannot initialize " ++ nmStr ++ " with size: " ++ show sz) |]++mkRbInst vsz nm tname = [d| instance RingBuffer $(tname) where {-# INLINE length #-}; length = const vsz; {-# INLINE (!) #-}; (!) = $(mkLookup vsz nm); {-# INLINE push #-}; push = $(mkPush vsz nm) |]++mkLookup vsz nm = do+  nms <- mapM (newName . ('v':) . show) [1 .. vsz]+  ixNm <- newName "ix"+  let lhs1 = conP nm (map varP nms)+      lhs2 = varP ixNm+      matches = map (\ix -> match (litP $ integerL (fromIntegral ix)) +                                (normalB $ varE (nms !! ix) )+                                [] )+                    [0..vsz-1]+                ++ [match (varP (mkName "ix"))+                          (normalB [| error ("TGen: index out of bounds: " ++ show $(varE $ mkName "ix")) |])+                          [] ]+      rhs  = caseE (varE ixNm) matches+  lamE [lhs1,lhs2] rhs++mkPush vsz nm = do+  nms <- mapM (newName . ('v':) . show) [1 .. vsz]+  elNm <- newName "el"+  let lhs1 = conP nm (map varP nms)+      lhs2 = varP elNm+      rhs  = appsE $ conE nm : varE elNm : map varE (init nms)+  lamE [lhs1, lhs2] rhs