{-# LANGUAGE Rank2Types #-} {-# LANGUAGE FlexibleContexts #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Bits.Lens -- Copyright : (C) 2012-16 Edward Kmett -- License : BSD-style (see the file LICENSE) -- Maintainer : Edward Kmett <ekmett@gmail.com> -- Stability : experimental -- Portability : LiberalTypeSynonyms -- ---------------------------------------------------------------------------- module Data.Bits.Lens ( (.|.~), (.&.~), (<.|.~), (<.&.~), (<<.|.~), (<<.&.~) , (.|.=), (.&.=), (<.|.=), (<.&.=), (<<.|.=), (<<.&.=) , bitAt , bits , byteAt , bytewise ) where import Prelude () import Control.Lens import Control.Lens.Internal.Prelude import Control.Monad.State import Data.Bits import Data.Word -- $setup -- >>> :set -XNoOverloadedStrings -- >>> import Data.Word infixr 4 .|.~, .&.~, <.|.~, <.&.~, <<.|.~, <<.&.~ infix 4 .|.=, .&.=, <.|.=, <.&.=, <<.|.=, <<.&.= -- | Bitwise '.|.' the target(s) of a 'Lens' or 'Setter'. -- -- >>> _2 .|.~ 6 $ ("hello",3) -- ("hello",7) -- -- @ -- ('.|.~') :: 'Bits' a => 'Setter' s t a a -> a -> s -> t -- ('.|.~') :: 'Bits' a => 'Iso' s t a a -> a -> s -> t -- ('.|.~') :: 'Bits' a => 'Lens' s t a a -> a -> s -> t -- ('.|.~') :: ('Data.Monoid.Monoid' a, 'Bits' a) => 'Traversal' s t a a -> a -> s -> t -- @ (.|.~):: Bits a => ASetter s t a a -> a -> s -> t l .|.~ n = over l (.|. n) {-# INLINE (.|.~) #-} -- | Bitwise '.&.' the target(s) of a 'Lens' or 'Setter'. -- -- >>> _2 .&.~ 7 $ ("hello",254) -- ("hello",6) -- -- @ -- ('.&.~') :: 'Bits' a => 'Setter' s t a a -> a -> s -> t -- ('.&.~') :: 'Bits' a => 'Iso' s t a a -> a -> s -> t -- ('.&.~') :: 'Bits' a => 'Lens' s t a a -> a -> s -> t -- ('.&.~') :: ('Data.Monoid.Monoid' a, 'Bits' a) => 'Traversal' s t a a -> a -> s -> t -- @ (.&.~) :: Bits a => ASetter s t a a -> a -> s -> t l .&.~ n = over l (.&. n) {-# INLINE (.&.~) #-} -- | Modify the target(s) of a 'Lens'', 'Setter'' or 'Traversal'' by computing its bitwise '.&.' with another value. -- -- >>> execState (do _1 .&.= 15; _2 .&.= 3) (7,7) -- (7,3) -- -- @ -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Setter'' s a -> a -> m () -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Iso'' s a -> a -> m () -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a -> a -> m () -- ('.&.=') :: ('MonadState' s m, 'Bits' a) => 'Traversal'' s a -> a -> m () -- @ (.&.=):: (MonadState s m, Bits a) => ASetter' s a -> a -> m () l .&.= a = modify (l .&.~ a) {-# INLINE (.&.=) #-} -- | Modify the target(s) of a 'Lens'', 'Setter' or 'Traversal' by computing its bitwise '.|.' with another value. -- -- >>> execState (do _1 .|.= 15; _2 .|.= 3) (7,7) -- (15,7) -- -- @ -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Setter'' s a -> a -> m () -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Iso'' s a -> a -> m () -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a -> a -> m () -- ('.|.=') :: ('MonadState' s m, 'Bits' a) => 'Traversal'' s a -> a -> m () -- @ (.|.=) :: (MonadState s m, Bits a) => ASetter' s a -> a -> m () l .|.= a = modify (l .|.~ a) {-# INLINE (.|.=) #-} -- | Bitwise '.|.' the target(s) of a 'Lens' (or 'Traversal'), returning the result -- (or a monoidal summary of all of the results). -- -- >>> _2 <.|.~ 6 $ ("hello",3) -- (7,("hello",7)) -- -- @ -- ('<.|.~') :: 'Bits' a => 'Iso' s t a a -> a -> s -> (a, t) -- ('<.|.~') :: 'Bits' a => 'Lens' s t a a -> a -> s -> (a, t) -- ('<.|.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal' s t a a -> a -> s -> (a, t) -- @ (<.|.~):: Bits a => LensLike ((,) a) s t a a -> a -> s -> (a, t) l <.|.~ n = l <%~ (.|. n) {-# INLINE (<.|.~) #-} -- | Bitwise '.&.' the target(s) of a 'Lens' or 'Traversal', returning the result -- (or a monoidal summary of all of the results). -- -- >>> _2 <.&.~ 7 $ ("hello",254) -- (6,("hello",6)) -- -- @ -- ('<.&.~') :: 'Bits' a => 'Iso' s t a a -> a -> s -> (a, t) -- ('<.&.~') :: 'Bits' a => 'Lens' s t a a -> a -> s -> (a, t) -- ('<.&.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal' s t a a -> a -> s -> (a, t) -- @ (<.&.~) :: Bits a => LensLike ((,) a) s t a a -> a -> s -> (a, t) l <.&.~ n = l <%~ (.&. n) {-# INLINE (<.&.~) #-} -- | Modify the target(s) of a 'Lens'' (or 'Traversal'') by computing its bitwise '.&.' with another value, -- returning the result (or a monoidal summary of all of the results traversed). -- -- >>> runState (_1 <.&.= 15) (31,0) -- (15,(15,0)) -- -- @ -- ('<.&.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a -> a -> m a -- ('<.&.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a -- @ (<.&.=):: (MonadState s m, Bits a) => LensLike' ((,)a) s a -> a -> m a l <.&.= b = l <%= (.&. b) {-# INLINE (<.&.=) #-} -- | Modify the target(s) of a 'Lens'', (or 'Traversal') by computing its bitwise '.|.' with another value, -- returning the result (or a monoidal summary of all of the results traversed). -- -- >>> runState (_1 <.|.= 7) (28,0) -- (31,(31,0)) -- -- @ -- ('<.|.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a -> a -> m a -- ('<.|.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a -- @ (<.|.=) :: (MonadState s m, Bits a) => LensLike' ((,)a) s a -> a -> m a l <.|.= b = l <%= (.|. b) {-# INLINE (<.|.=) #-} -- | Bitwise '.&.' the target(s) of a 'Lens' or 'Traversal', and return the -- original value, or a monoidal summary of the original values. -- -- When you do not need the old value, ('.&.~') is more flexible. -- -- >>> _2 <<.&.~ 7 $ ("hello", 254) -- (254,("hello",6)) -- -- @ -- ('<<.&.~') :: 'Bits' a => 'Iso' s t a a -> a -> s -> (a, t) -- ('<<.&.~') :: 'Bits' a => 'Lens' s t a a -> a -> s -> (a, t) -- ('<<.&.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal' s t a a -> a -> s -> (a, t) -- @ (<<.&.~) :: Bits a => Optical' (->) q ((,)a) s a -> a -> q s (a, s) l <<.&.~ b = l $ \a -> (a, a .&. b) {-# INLINE (<<.&.~) #-} -- | Bitwise '.|.' the target(s) of a 'Lens' or 'Traversal', and return the -- original value, or a monoidal summary of the original values. -- -- When you do not need the old value, ('.|.~') is more flexible. -- -- >>> _2 <<.|.~ 6 $ ("hello", 3) -- (3,("hello",7)) -- -- @ -- ('<<.|.~') :: 'Bits' a => 'Iso' s t a a -> a -> s -> (a, t) -- ('<<.|.~') :: 'Bits' a => 'Lens' s t a a -> a -> s -> (a, t) -- ('<<.|.~') :: ('Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal' s t a a -> a -> s -> (a, t) -- @ (<<.|.~) :: Bits a => Optical' (->) q ((,)a) s a -> a -> q s (a, s) l <<.|.~ b = l $ \a -> (a, a .|. b) {-# INLINE (<<.|.~) #-} -- | Modify the target(s) of a 'Lens'', (or 'Traversal'') by computing its -- bitwise '.&.' with another value, returning the original value (or a -- monoidal summary of all the original values). -- -- When you do not need the old value, ('.&.=') is more flexible. -- -- >>> runState (_1 <<.&.= 15) (31,0) -- (31,(15,0)) -- -- @ -- ('<<.&.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a -> a -> m a -- ('<<.&.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a -- @ (<<.&.=) :: (MonadState s m, Bits a) => LensLike' ((,) a) s a -> a -> m a l <<.&.= b = l %%= \a -> (a, a .&. b) {-# INLINE (<<.&.=) #-} -- | Modify the target(s) of a 'Lens'', (or 'Traversal'') by computing its -- bitwise '.|.' with another value, returning the original value (or a -- monoidal summary of all the original values). -- -- When you do not need the old value, ('.|.=') is more flexible. -- -- >>> runState (_1 <<.|.= 7) (28,0) -- (28,(31,0)) -- -- @ -- ('<<.|.=') :: ('MonadState' s m, 'Bits' a) => 'Lens'' s a -> a -> m a -- ('<<.|.=') :: ('MonadState' s m, 'Bits' a, 'Data.Monoid.Monoid' a) => 'Traversal'' s a -> a -> m a -- @ (<<.|.=) :: (MonadState s m, Bits a) => LensLike' ((,) a) s a -> a -> m a l <<.|.= b = l %%= \a -> (a, a .|. b) {-# INLINE (<<.|.=) #-} -- | This 'Lens' can be used to access the value of the nth bit in a number. -- -- @'bitAt' n@ is only a legal 'Lens' into @b@ if @0 '<=' n '<' 'bitSize' ('undefined' :: b)@. -- -- >>> 16^.bitAt 4 -- True -- -- >>> 15^.bitAt 4 -- False -- -- >>> 15 & bitAt 4 .~ True -- 31 -- -- >>> 16 & bitAt 4 .~ False -- 0 bitAt :: Bits b => Int -> IndexedLens' Int b Bool bitAt n f b = indexed f n (testBit b n) <&> \x -> if x then setBit b n else clearBit b n {-# INLINE bitAt #-} -- | Get the nth byte, counting from the low end. -- -- @'byteAt' n@ is a legal 'Lens' into @b@ iff @0 '<=' n '<' 'div' ('bitSize' ('undefined' :: b)) 8@ -- -- >>> (0xff00 :: Word16)^.byteAt 0 -- 0 -- -- >>> (0xff00 :: Word16)^.byteAt 1 -- 255 -- -- >>> byteAt 1 .~ 0 $ 0xff00 :: Word16 -- 0 -- -- >>> byteAt 0 .~ 0xff $ 0 :: Word16 -- 255 byteAt :: (Integral b, Bits b) => Int -> IndexedLens' Int b Word8 byteAt i f b = back <$> indexed f i (forward b) where back w8 = (fromIntegral w8 `shiftL` (i * 8)) .|. (complement (255 `shiftL` (i * 8)) .&. b) forward = fromIntegral . (.&.) 0xff . flip shiftR (i * 8) -- | Traverse over all bits in a numeric type. -- -- The bit position is available as the index. -- -- >>> toListOf bits (5 :: Word8) -- [True,False,True,False,False,False,False,False] -- -- If you supply this an 'Integer', the result will be an infinite 'Traversal', which -- can be productively consumed, but not reassembled. bits :: (Num b, Bits b) => IndexedTraversal' Int b Bool bits f b = foldr step 0 <$> traverse g bs where g n = (,) n <$> indexed f n (testBit b n) bs = takeWhile hasBit [0..] hasBit n = complementBit b n /= b -- test to make sure that complementing this bit actually changes the value step (n,True) r = setBit r n step _ r = r {-# INLINE bits #-} -- | Traverse over all the bytes in an integral type, from the low end. -- -- The byte position is available as the index. -- -- >>> toListOf bytewise (1312301580 :: Word32) -- [12,34,56,78] -- -- If you supply this an 'Integer', the result will be an infinite 'Traversal', -- which can be productively consumed, but not reassembled. -- -- Why isn't this function called @bytes@ to match 'bits'? Alas, there -- is already a function by that name in "Data.ByteString.Lens". bytewise :: (Integral b, Bits b) => IndexedTraversal' Int b Word8 bytewise f b = foldr step 0 <$> traverse g bs where g n = (,) n <$> indexed f n (fromIntegral $ b `shiftR` (n*8)) bs = takeWhile hasByte [0..] hasByte n = complementBit b (n*8) /= b step (n,x) r = r .|. (fromIntegral x `shiftL` (n*8)) {-# INLINE bytewise #-}