packages feed

bitarray-bs-0.1.0.0: src/Data/ByteString/BitArray.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Data.ByteString.BitArray (

	-- * BIT ARRAY

	B, null, length, empty,

	-- * FROM/TO BYTE STRING

	fromByteString, toByteString,

	-- * APPEND

	append, appendByteString,

	-- * SPLIT

	pop, popBits, splitAt, byteBoundary,

	-- * CONVERT

	toBits, toBits'

	) where

import Prelude hiding (null, length, splitAt)
import Control.Arrow
import Data.Bits
import Data.Bool
import Data.ByteString qualified as BS
import Data.ByteString.Bit qualified as Bit

data B = B { unB :: [B1] } deriving Show

empty :: B
empty = B []

null :: B -> Bool
null = \case (B []) -> True; _ -> False

length :: B -> Int
length  = sum . (length1 <$>) . unB

data B1 = B1 { zero :: Int, length1 :: Int, body :: BS.ByteString }
	deriving Show

empty1 :: B1
empty1 = B1 { zero = 0, length1 = 0, body = "" }

null1 :: B1 -> Bool
null1 = \case B1 { length1 = 0 } -> True; _ -> False

fromByteString :: BS.ByteString -> B
fromByteString "" = B []
fromByteString bs = B [B1 { zero = 0, length1 = 8 * BS.length bs, body = bs }]

toByteString :: B -> Either B BS.ByteString
toByteString b@(B b1s) = case b1s of
	[] -> Right ""
	[b1] -> either (Left . B . (: [])) Right $ toByteString1 b1
	_ -> Left b

toByteString1 :: B1 -> Either B1 BS.ByteString
toByteString1 ba@B1 { zero = z, length1 = ln, body = bs } =
	case (z, ln `mod` 8) of (0, 0) -> Right bs; _ -> Left ba

append :: B -> B -> B
B b1 `append` B b2 = B . normalize $ b1 ++ b2

appendByteString :: B -> BS.ByteString -> B
appendByteString b = append b . fromByteString

normalize :: [B1] -> [B1]
normalize = go empty1 . (normalize1 <$>)
	where
	go B1 { length1 = 0 } = \case
		[] -> []
		b1' : b1s -> go b1' b1s
	go b1@B1 { zero = z1, length1 = ln1, body = bs1 } = \case
		[]	| ln1 > 0 -> [b1]
			| otherwise -> []
		B1 { length1 = 0 } : b1s -> go b1 b1s
		b1'@B1 { zero = z2, length1 = ln2, body = bs2 } : b1s
			| (z1 + ln1) `mod` 8 == 0, z2 == 0 ->
				go B1 {	zero = z1, length1 = ln1 + ln2,
					body = bs1 `BS.append` bs2 } b1s
			| (z1 + ln1 - z2) `mod` 8 == 0 ->
				go B1 { zero = z1, length1 = ln1 + ln2,
					body = BS.init bs1 `BS.append` (
						(	BS.last bs1 .&. bitMask 0 (z2 - 1) .|.
							BS.head bs2 .&. bitMask z2 7) `BS.cons`
						BS.tail bs2 ) } b1s
			| otherwise -> b1 : go b1' b1s

bitMask :: Bits n => Int -> Int -> n
bitMask i j = foldl setBit zeroBits [i .. j]

normalize1 :: B1 -> B1
normalize1 B1 { zero = z, length1 = ln, body = bs }
	| 0 <= z = B1 {
		zero = z',
		length1 = ln, body = BS.take t $ BS.drop (z `div` 8) bs }
	| otherwise = error "normalize: bad"
	where z' = z `mod` 8; t = (ln + z' - 1) `div` 8 + 1

pop :: B -> Maybe (Bit.B, B)
pop (B []) = Nothing
pop (B (b1 : b1s)) = case pop1 b1 of
	Nothing -> error $ "never occur: " ++ show b1 ++ " " ++ show b1s
	Just (b, b1') -> Just (b, B . normalize $ b1' : b1s )

pop1 :: B1 -> Maybe (Bit.B, B1)
pop1 B1 { zero = z, length1 = ln, body = bs } = case (z, ln) of
	(_, 0) -> Nothing
	(7, _) -> case BS.uncons bs of
		Just (b, bs') -> Just (
			bool Bit.O Bit.I (b `testBit` 7),
			B1 { zero = 0, length1 = ln - 1, body = bs' } )
		Nothing -> error "never occur"
	_ -> Just (
		bool Bit.O Bit.I $ BS.head bs `testBit` z,
		B1 { zero = z + 1, length1 = ln - 1, body = bs } )

splitAt :: Int -> B -> Maybe (B, B)
splitAt n0 = ((B *** B) <$>) . go n0 . unB
	where
	go 0 b1s = Just ([], b1s)
	go _ [] = Nothing
	go n (b1@B1 { length1 = ln } : b1s)
		| n < ln = case splitAt1 n b1 of
			Nothing -> error "never occur"
			Just (b1t, b1d) -> Just ([b1t], b1d : b1s)
		| n == ln = Just ([b1], b1s)
		| otherwise = ((b1 :) `first`) <$> go (n - ln) b1s

splitAt1 :: Int -> B1 -> Maybe (B1, B1)
splitAt1 n B1 { zero = z, length1 = ln, body = bs }
	| ln < n = Nothing
	| otherwise = Just (
		normalize1 B1 { zero = z, length1 = n, body = bs },
		normalize1 B1 { zero = z + n, length1 = ln - n, body = bs } )

byteBoundary :: B -> Either (B, B) B
byteBoundary (B b) = case go b of
	([], b2) -> Right $ B b2; b12 -> Left $ (B *** B) b12
	where
	go [] = ([], [])
	go (b1 : b1s) = case byteBoundary1 b1 of
		Nothing -> (b1 :) `first` go b1s
		Just (b1t, b1d)
			| null1 b1t -> ([], b1d : b1s)
			| otherwise -> ([b1t], b1d : b1s)

byteBoundary1 :: B1 -> Maybe (B1, B1)
byteBoundary1 b1@B1 { zero = z, length1 = ln, body = bs } = case z of
	0 -> Just (empty1, b1)
	_	| z + ln <= 8 -> Nothing
		| otherwise -> Just (
			B1 { zero = z, length1 = 8 - z, body = BS.take 1 bs },
			B1 { zero = 0, length1 = ln - 8 + z, body = BS.tail bs } )

popBits :: Bits n => Int -> B -> Maybe (n, B)
popBits ln b0
	| length b0 < ln = Nothing
	| otherwise = Just $ go 0 ln b0
	where
	go i j b = case (j, pop b) of
		(0, _) -> (zeroBits, b)
		(_, Nothing) -> error "never occur"
		(_, Just (bt, bts)) -> Bit.bit id (`setBit` i) bt `first` go (i + 1) (j - 1) bts

toBits :: Bits n => B -> n
toBits = go zeroBits
	where go i b = case pop b of
		Nothing -> zeroBits
		Just (bt, bts) -> Bit.bit id (`setBit` i) bt $ go (i + 1) bts

toBits' :: forall n . FiniteBits n => B -> Maybe n
toBits' b0
	| length b0 <= finiteBitSize (undefined :: n) = Just $ go 0 b0
	| otherwise = Nothing
	where go i b = case pop b of
		Nothing -> zeroBits
		Just (bt, bts) -> Bit.bit id (`setBit` i) bt $ go (i + 1) bts