<commit_before>{-# LANGUAGE
      MultiParamTypeClasses
    , FlexibleInstances
    , TypeFamilies
    , RecordWildCards #-}
module Data.CapNProto.Blob
    ( Blob(..)
    , MutBlob(..)
    , Slice(..)
    , BlobSlice(..)
    , lengthInWords
    , indexWord
    , writeWord
    )
  where

import Prelude hiding (length)

import Control.Monad (when, forM_)
import Control.Monad.Catch (MonadThrow(..))
import Control.Monad.Primitive (PrimMonad, PrimState)
import qualified Data.ByteString as BS
import Data.Bits
import Data.Word
import Data.Primitive.ByteArray
    ( MutableByteArray
    , sizeofMutableByteArray
    , copyMutableByteArray
    , fillByteArray
    , newByteArray
    , readByteArray
    , writeByteArray
    , ByteArray
    , sizeofByteArray
    , indexByteArray
    )

import Data.CapNProto.Bits
    ( WordCount(..)
    , ByteCount(..)
    , wordsToBytes
    , bytesToWordsFloor
    )
import qualified Data.CapNProto.Errors as E

-- TODO: be clearer about error handling re: these classes. The general notion
-- is to either throw (in a monad that supports it), or just not check the
-- inputs at all, relying on the caller not to violate the invariants.
-- Preference would be for the former, of course.

-- | A 'Blob' is an array of bytes, which may be accessed
-- inside of a Monad.
class Blob m a where
    -- | Return the length of the blob, in words.
    length :: a -> m ByteCount
    -- | @index b i@ returns the ith byte in the blob @b@. Typically,
    -- instances will include a MonadThrow constraint, throwing on out
    -- of bounds errors.
    index :: a -> ByteCount -> m Word8

-- | A mutable 'Blob'.
class Blob m a => MutBlob m a where
    -- | @write b i value@ writes @value@ to the ith position in the blob.
    write :: a -> ByteCount -> Word8 -> m ()
    -- | @grow b amount@ grows the blob @b@ by @amount@ words. In an instance
    -- of PrimMonad, this may modify or destroy the original blob.
    grow :: a -> ByteCount -> m a

-- A slice affords efficiently extracting sub-ranges.
class Slice m a where
    -- | @slice s offset len@ extracts the range [offset, offset+len) from @s@.
    -- Complexity is O(1).
   slice :: a -> ByteCount -> ByteCount -> m a

-- | A slice of a blob.
--
-- This wraps an instance of blob, exposing a sub-range of it, and allowing
-- further slicing operations. The resulting value is itself a blob.
data BlobSlice a = BlobSlice
    { blob :: a
    , offset :: ByteCount
    , sliceLen :: ByteCount
    } deriving(Show)

-- | @lengthInWords blob@ is The length of @blob@, in 64-bit words, rounded down.
lengthInWords :: (Monad m, Blob m b) => b -> m WordCount
lengthInWords = fmap bytesToWordsFloor . length<commit_msg>Data.CapNProto.Blob: haddock comment for 'Slice'<commit_after>{-# LANGUAGE
      MultiParamTypeClasses
    , FlexibleInstances
    , TypeFamilies
    , RecordWildCards #-}
module Data.CapNProto.Blob
    ( Blob(..)
    , MutBlob(..)
    , Slice(..)
    , BlobSlice(..)
    , lengthInWords
    , indexWord
    , writeWord
    )
  where

import Prelude hiding (length)

import Control.Monad (when, forM_)
import Control.Monad.Catch (MonadThrow(..))
import Control.Monad.Primitive (PrimMonad, PrimState)
import qualified Data.ByteString as BS
import Data.Bits
import Data.Word
import Data.Primitive.ByteArray
    ( MutableByteArray
    , sizeofMutableByteArray
    , copyMutableByteArray
    , fillByteArray
    , newByteArray
    , readByteArray
    , writeByteArray
    , ByteArray
    , sizeofByteArray
    , indexByteArray
    )

import Data.CapNProto.Bits
    ( WordCount(..)
    , ByteCount(..)
    , wordsToBytes
    , bytesToWordsFloor
    )
import qualified Data.CapNProto.Errors as E

-- TODO: be clearer about error handling re: these classes. The general notion
-- is to either throw (in a monad that supports it), or just not check the
-- inputs at all, relying on the caller not to violate the invariants.
-- Preference would be for the former, of course.

-- | A 'Blob' is an array of bytes, which may be accessed
-- inside of a Monad.
class Blob m a where
    -- | Return the length of the blob, in words.
    length :: a -> m ByteCount
    -- | @index b i@ returns the ith byte in the blob @b@. Typically,
    -- instances will include a MonadThrow constraint, throwing on out
    -- of bounds errors.
    index :: a -> ByteCount -> m Word8

-- | A mutable 'Blob'.
class Blob m a => MutBlob m a where
    -- | @write b i value@ writes @value@ to the ith position in the blob.
    write :: a -> ByteCount -> Word8 -> m ()
    -- | @grow b amount@ grows the blob @b@ by @amount@ words. In an instance
    -- of PrimMonad, this may modify or destroy the original blob.
    grow :: a -> ByteCount -> m a

-- | A slice affords efficiently extracting sub-ranges.
class Slice m a where
    -- | @slice s offset len@ extracts the range [offset, offset+len) from @s@.
    -- Complexity is O(1).
   slice :: a -> ByteCount -> ByteCount -> m a

-- | A slice of a blob.
--
-- This wraps an instance of blob, exposing a sub-range of it, and allowing
-- further slicing operations. The resulting value is itself a blob.
data BlobSlice a = BlobSlice
    { blob :: a
    , offset :: ByteCount
    , sliceLen :: ByteCount
    } deriving(Show)

-- | @lengthInWords blob@ is The length of @blob@, in 64-bit words, rounded down.
lengthInWords :: (Monad m, Blob m b) => b -> m WordCount
lengthInWords = fmap bytesToWordsFloor . length
{'fraction_non_alphanumeric': 0.10156971375807941, 'fraction_numerical': 0.0018467220683287165, 'mean_word_length': 2.7506925207756234, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3805260', 'n_tokens_mistral': 1697, 'n_tokens_neox': 1651, 'n_words': 649}