packages feed

bytepatch 0.3.1 → 0.4.0

raw patch · 37 files changed

+2117/−1110 lines, 37 filesdep +binrepdep +blake3dep +heystonedep ~aesondep ~basedep ~generic-opticsPVP ok

version bump matches the API change (PVP)

Dependencies added: binrep, blake3, heystone, memory, refined, singletons, singletons-base, singletons-th, text-short

Dependency ranges changed: aeson, base, generic-optics, optparse-applicative, vinyl, yaml

API changes (from Hackage documentation)

- BytePatch: Aligned :: SeekRep 'RelSeek -> [p] -> Aligned p
- BytePatch: MultiPatch :: a -> [Seek s a] -> MultiPatch s a
- BytePatch: Seek :: SeekRep s -> Maybe a -> Maybe (SeekRep 'FwdSeek) -> Maybe (SeekRep 'FwdSeek) -> Maybe Natural -> Seek s a
- BytePatch: [alignedAlign] :: Aligned p -> SeekRep 'RelSeek
- BytePatch: [alignedPatches] :: Aligned p -> [p]
- BytePatch: [mpAt] :: MultiPatch s a -> [Seek s a]
- BytePatch: [mpData] :: MultiPatch s a -> a
- BytePatch: [sAligned] :: Seek s a -> Maybe Natural
- BytePatch: [sExpected] :: Seek s a -> Maybe a
- BytePatch: [sMaxBytes] :: Seek s a -> Maybe (SeekRep 'FwdSeek)
- BytePatch: [sNullTerminates] :: Seek s a -> Maybe (SeekRep 'FwdSeek)
- BytePatch: [sSeek] :: Seek s a -> SeekRep s
- BytePatch: align :: forall s a ss rs is i r. (SeekRep s ~ Natural, r ~ Const (Meta s), rs ~ RDelete r ss, RElem r ss i, RSubset rs ss is) => Aligned (Patch 'RelSeek ss a) -> Either (Error s) [Patch s rs a]
- BytePatch: cm :: f a -> Rec (Flap a) '[f]
- BytePatch: cmAlign :: SeekRep s ~ Natural => Seek 'RelSeek a -> Rec (Flap a) '[Const (Meta s)]
- BytePatch: cmBin :: Seek s a -> Rec (Flap a) '[Const Meta]
- BytePatch: cmBinStream :: Seek s a -> Rec (Flap a) '[MetaStream]
- BytePatch: convert :: (Seek s a -> Rec (Flap a) fs) -> MultiPatch s a -> [Patch s fs a]
- BytePatch: convertAlign :: forall s a. SeekRep s ~ Natural => MultiPatch 'RelSeek a -> [Patch 'RelSeek '[Const (Meta s)] a]
- BytePatch: convertBin :: forall s a. MultiPatch s a -> [Patch s '[Const Meta, MetaStream] a]
- BytePatch: convertBinAlign :: forall s a. SeekRep s ~ Natural => MultiPatch 'RelSeek a -> [Patch 'RelSeek '[Const (Meta s), Const Meta, MetaStream] a]
- BytePatch: convertEmpty :: forall s a. MultiPatch s a -> [Patch s '[] a]
- BytePatch: data Aligned p
- BytePatch: data MultiPatch s a
- BytePatch: data Seek s a
- BytePatch: instance (Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Patch.SeekRep s), Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (BytePatch.MultiPatch s a)
- BytePatch: instance (Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Patch.SeekRep s), Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (BytePatch.Seek s a)
- BytePatch: instance (Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Patch.SeekRep s), Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (BytePatch.MultiPatch s a)
- BytePatch: instance (Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Patch.SeekRep s), Data.Aeson.Types.ToJSON.ToJSON a) => Data.Aeson.Types.ToJSON.ToJSON (BytePatch.Seek s a)
- BytePatch: instance (GHC.Classes.Eq a, GHC.Classes.Eq (StreamPatch.Patch.SeekRep s)) => GHC.Classes.Eq (BytePatch.MultiPatch s a)
- BytePatch: instance (GHC.Classes.Eq a, GHC.Classes.Eq (StreamPatch.Patch.SeekRep s)) => GHC.Classes.Eq (BytePatch.Seek s a)
- BytePatch: instance (GHC.Show.Show a, GHC.Show.Show (StreamPatch.Patch.SeekRep s)) => GHC.Show.Show (BytePatch.MultiPatch s a)
- BytePatch: instance (GHC.Show.Show a, GHC.Show.Show (StreamPatch.Patch.SeekRep s)) => GHC.Show.Show (BytePatch.Seek s a)
- BytePatch: instance Data.Aeson.Types.FromJSON.FromJSON p => Data.Aeson.Types.FromJSON.FromJSON (BytePatch.Aligned p)
- BytePatch: instance Data.Aeson.Types.ToJSON.ToJSON p => Data.Aeson.Types.ToJSON.ToJSON (BytePatch.Aligned p)
- BytePatch: instance GHC.Classes.Eq p => GHC.Classes.Eq (BytePatch.Aligned p)
- BytePatch: instance GHC.Generics.Generic (BytePatch.Aligned p)
- BytePatch: instance GHC.Generics.Generic (BytePatch.MultiPatch s a)
- BytePatch: instance GHC.Generics.Generic (BytePatch.Seek s a)
- BytePatch: instance GHC.Show.Show p => GHC.Show.Show (BytePatch.Aligned p)
- BytePatch: jsonCfgCamelDrop :: Int -> Options
- BytePatch.HexByteString: HexByteString :: ByteString -> HexByteString
- BytePatch.HexByteString: [unHexByteString] :: HexByteString -> ByteString
- BytePatch.HexByteString: instance Data.Aeson.Types.FromJSON.FromJSON BytePatch.HexByteString.HexByteString
- BytePatch.HexByteString: instance Data.Aeson.Types.ToJSON.ToJSON BytePatch.HexByteString.HexByteString
- BytePatch.HexByteString: instance GHC.Classes.Eq BytePatch.HexByteString.HexByteString
- BytePatch.HexByteString: instance GHC.Show.Show BytePatch.HexByteString.HexByteString
- BytePatch.HexByteString: instance StreamPatch.Patch.Binary.BinRep BytePatch.HexByteString.HexByteString
- BytePatch.HexByteString: newtype HexByteString
- BytePatch.HexByteString: parseHexByteString :: (MonadParsec e s m, Token s ~ Char) => m ByteString
- BytePatch.HexByteString: prettyHexByteString :: ByteString -> Text
- StreamPatch.Apply: applyBinFwd :: forall a m. (MonadFwdInplaceStream m, Chunk m ~ ByteString, BinRep a) => Cfg -> [Patch 'FwdSeek '[MetaStream] a] -> m (Either (Error a) ())
- StreamPatch.Apply: applySimpleFwd :: (MonadFwdInplaceStream m, Chunk m ~ a) => [Patch 'FwdSeek '[] a] -> m ()
- StreamPatch.Apply: runPureFwdBin :: BinRep a => Cfg -> [Patch 'FwdSeek '[MetaStream] a] -> ByteString -> Either (Error a) ByteString
- StreamPatch.Apply: runPureSimpleFwdList :: [Patch 'FwdSeek '[] [a]] -> [a] -> [a]
- StreamPatch.Patch: AbsSeek :: SeekKind
- StreamPatch.Patch: Flap :: f a -> Flap a f
- StreamPatch.Patch: FunctorRec :: Rec (Flap a) fs -> FunctorRec fs a
- StreamPatch.Patch: FwdSeek :: SeekKind
- StreamPatch.Patch: RelSeek :: SeekKind
- StreamPatch.Patch: [getFlap] :: Flap a f -> f a
- StreamPatch.Patch: [getFunctorRec] :: FunctorRec fs a -> Rec (Flap a) fs
- StreamPatch.Patch: data SeekKind
- StreamPatch.Patch: instance (Data.Foldable.Foldable r, Data.Foldable.Foldable (StreamPatch.Patch.FunctorRec rs)) => Data.Foldable.Foldable (StreamPatch.Patch.FunctorRec (r : rs))
- StreamPatch.Patch: instance (Data.Traversable.Traversable r, Data.Traversable.Traversable (StreamPatch.Patch.FunctorRec rs)) => Data.Traversable.Traversable (StreamPatch.Patch.FunctorRec (r : rs))
- StreamPatch.Patch: instance (Data.Vinyl.Core.ReifyConstraint GHC.Show.Show (StreamPatch.Patch.Flap a) fs, Data.Vinyl.Core.RMap fs, Data.Vinyl.Core.RecordToList fs) => GHC.Show.Show (StreamPatch.Patch.FunctorRec fs a)
- StreamPatch.Patch: instance (GHC.Base.Applicative r, GHC.Base.Applicative (StreamPatch.Patch.FunctorRec rs)) => GHC.Base.Applicative (StreamPatch.Patch.FunctorRec (r : rs))
- StreamPatch.Patch: instance (GHC.Base.Functor r, GHC.Base.Functor (StreamPatch.Patch.FunctorRec rs)) => GHC.Base.Functor (StreamPatch.Patch.FunctorRec (r : rs))
- StreamPatch.Patch: instance (GHC.Classes.Eq a, GHC.Classes.Eq (StreamPatch.Patch.SeekRep s), GHC.Classes.Eq (Data.Vinyl.Core.Rec (StreamPatch.Patch.Flap a) fs)) => GHC.Classes.Eq (StreamPatch.Patch.Patch s fs a)
- StreamPatch.Patch: instance (GHC.Show.Show a, GHC.Show.Show (StreamPatch.Patch.SeekRep s), Data.Vinyl.Core.ReifyConstraint GHC.Show.Show (StreamPatch.Patch.Flap a) fs, Data.Vinyl.Core.RMap fs, Data.Vinyl.Core.RecordToList fs) => GHC.Show.Show (StreamPatch.Patch.Patch s fs a)
- StreamPatch.Patch: instance Data.Foldable.Foldable (StreamPatch.Patch.FunctorRec '[])
- StreamPatch.Patch: instance Data.Traversable.Traversable (StreamPatch.Patch.FunctorRec '[])
- StreamPatch.Patch: instance GHC.Base.Applicative (StreamPatch.Patch.FunctorRec '[])
- StreamPatch.Patch: instance GHC.Base.Functor (StreamPatch.Patch.FunctorRec '[])
- StreamPatch.Patch: instance GHC.Base.Functor (StreamPatch.Patch.FunctorRec fs) => GHC.Base.Functor (StreamPatch.Patch.Patch s fs)
- StreamPatch.Patch: instance GHC.Classes.Eq (Data.Vinyl.Core.Rec (StreamPatch.Patch.Flap a) fs) => GHC.Classes.Eq (StreamPatch.Patch.FunctorRec fs a)
- StreamPatch.Patch: instance GHC.Classes.Eq (f a) => GHC.Classes.Eq (StreamPatch.Patch.Flap a f)
- StreamPatch.Patch: instance GHC.Classes.Eq StreamPatch.Patch.SeekKind
- StreamPatch.Patch: instance GHC.Generics.Generic (StreamPatch.Patch.Flap a f)
- StreamPatch.Patch: instance GHC.Generics.Generic (StreamPatch.Patch.FunctorRec fs a)
- StreamPatch.Patch: instance GHC.Generics.Generic StreamPatch.Patch.SeekKind
- StreamPatch.Patch: instance GHC.Show.Show (f a) => GHC.Show.Show (StreamPatch.Patch.Flap a f)
- StreamPatch.Patch: instance GHC.Show.Show StreamPatch.Patch.SeekKind
- StreamPatch.Patch: newtype Flap a f
- StreamPatch.Patch: newtype FunctorRec fs a
- StreamPatch.Patch: type family SeekRep (s :: SeekKind)
- StreamPatch.Patch.Align: ErrorSeekBelow0 :: SeekRep 'RelSeek -> Error s
- StreamPatch.Patch.Align: instance GHC.Classes.Eq (StreamPatch.Patch.SeekRep s) => GHC.Classes.Eq (StreamPatch.Patch.Align.Error s)
- StreamPatch.Patch.Align: instance GHC.Classes.Eq (StreamPatch.Patch.SeekRep s) => GHC.Classes.Eq (StreamPatch.Patch.Align.Meta s)
- StreamPatch.Patch.Align: instance GHC.Generics.Generic (StreamPatch.Patch.Align.Error s)
- StreamPatch.Patch.Align: instance GHC.Generics.Generic (StreamPatch.Patch.Align.Meta s)
- StreamPatch.Patch.Align: instance GHC.Show.Show (StreamPatch.Patch.SeekRep s) => GHC.Show.Show (StreamPatch.Patch.Align.Error s)
- StreamPatch.Patch.Align: instance GHC.Show.Show (StreamPatch.Patch.SeekRep s) => GHC.Show.Show (StreamPatch.Patch.Align.Meta s)
- StreamPatch.Patch.Align: tryIntegerToNatural :: Integer -> Maybe Natural
- StreamPatch.Patch.Binary: Cfg :: Bool -> Cfg
- StreamPatch.Patch.Binary: ErrorBadBinRep :: a -> String -> Error a
- StreamPatch.Patch.Binary: ErrorBinRepTooLong :: ByteString -> Natural -> Error a
- StreamPatch.Patch.Binary: ErrorDidNotMatchExpected :: ByteString -> ByteString -> Error a
- StreamPatch.Patch.Binary: ErrorUnexpectedNonNull :: ByteString -> Error a
- StreamPatch.Patch.Binary: MetaStream :: Maybe (SeekRep 'FwdSeek) -> Maybe a -> MetaStream a
- StreamPatch.Patch.Binary: [cfgAllowPartialExpected] :: Cfg -> Bool
- StreamPatch.Patch.Binary: [mMaxBytes] :: Meta -> Maybe (SeekRep 'FwdSeek)
- StreamPatch.Patch.Binary: [msExpected] :: MetaStream a -> Maybe a
- StreamPatch.Patch.Binary: [msNullTerminates] :: MetaStream a -> Maybe (SeekRep 'FwdSeek)
- StreamPatch.Patch.Binary: check :: BinRep a => Cfg -> ByteString -> MetaStream a -> Either (Error a) ()
- StreamPatch.Patch.Binary: class BinRep a
- StreamPatch.Patch.Binary: data Cfg
- StreamPatch.Patch.Binary: data MetaStream a
- StreamPatch.Patch.Binary: instance Data.Foldable.Foldable StreamPatch.Patch.Binary.MetaStream
- StreamPatch.Patch.Binary: instance Data.Traversable.Traversable StreamPatch.Patch.Binary.MetaStream
- StreamPatch.Patch.Binary: instance GHC.Base.Functor StreamPatch.Patch.Binary.MetaStream
- StreamPatch.Patch.Binary: instance GHC.Classes.Eq StreamPatch.Patch.Binary.Cfg
- StreamPatch.Patch.Binary: instance GHC.Classes.Eq StreamPatch.Patch.Binary.Meta
- StreamPatch.Patch.Binary: instance GHC.Classes.Eq a => GHC.Classes.Eq (StreamPatch.Patch.Binary.MetaStream a)
- StreamPatch.Patch.Binary: instance GHC.Generics.Generic (StreamPatch.Patch.Binary.MetaStream a)
- StreamPatch.Patch.Binary: instance GHC.Generics.Generic StreamPatch.Patch.Binary.Cfg
- StreamPatch.Patch.Binary: instance GHC.Generics.Generic StreamPatch.Patch.Binary.Meta
- StreamPatch.Patch.Binary: instance GHC.Show.Show StreamPatch.Patch.Binary.Cfg
- StreamPatch.Patch.Binary: instance GHC.Show.Show StreamPatch.Patch.Binary.Meta
- StreamPatch.Patch.Binary: instance GHC.Show.Show a => GHC.Show.Show (StreamPatch.Patch.Binary.MetaStream a)
- StreamPatch.Patch.Binary: instance StreamPatch.Patch.Binary.BinRep Data.ByteString.Internal.ByteString
- StreamPatch.Patch.Binary: instance StreamPatch.Patch.Binary.BinRep Data.Text.Internal.Text
- StreamPatch.Patch.Binary: instance StreamPatch.Patch.Binary.BinRep GHC.Base.String
- StreamPatch.Patch.Binary: patchBinRep :: forall a s ss rs is i r. (BinRep a, Traversable (FunctorRec rs), r ~ Const Meta, rs ~ RDelete r ss, RElem r ss i, RSubset rs ss is) => Patch s ss a -> Either (Error a) (Patch s rs ByteString)
- StreamPatch.Patch.Binary: toBinRep :: BinRep a => a -> Either String ByteString
- StreamPatch.Patch.Binary: toBinRep' :: BinRep a => a -> Either (Error a) ByteString
- StreamPatch.Patch.Binary.PascalText: PascalText :: Text -> PascalText (n :: Nat)
- StreamPatch.Patch.Binary.PascalText: [unPascalText] :: PascalText (n :: Nat) -> Text
- StreamPatch.Patch.Binary.PascalText: encodePascalText :: forall n. KnownNat n => PascalText n -> Maybe ByteString
- StreamPatch.Patch.Binary.PascalText: encodeToSizedBE :: (Integral a, Bits a) => Int -> a -> Maybe ByteString
- StreamPatch.Patch.Binary.PascalText: i2be :: (Integral a, Bits a) => a -> ByteString
- StreamPatch.Patch.Binary.PascalText: instance GHC.TypeNats.KnownNat n => StreamPatch.Patch.Binary.BinRep (StreamPatch.Patch.Binary.PascalText.PascalText n)
- StreamPatch.Patch.Binary.PascalText: newtype PascalText (n :: Nat)
- StreamPatch.Patch.Linearize: ErrorOverlap :: SeekRep 'AbsSeek -> Patch 'AbsSeek fs a -> Patch 'AbsSeek fs a -> Error fs a
- StreamPatch.Patch.Linearize: class HasLength a
- StreamPatch.Patch.Linearize: comparePatchSeeks :: Ord (SeekRep s) => Patch s fs a -> Patch s fs a -> Ordering
- StreamPatch.Patch.Linearize: data Error fs a
- StreamPatch.Patch.Linearize: getLength :: HasLength a => a -> Natural
- StreamPatch.Patch.Linearize: instance (GHC.Classes.Eq a, GHC.Classes.Eq (StreamPatch.Patch.Patch 'StreamPatch.Patch.AbsSeek fs a)) => GHC.Classes.Eq (StreamPatch.Patch.Linearize.Error fs a)
- StreamPatch.Patch.Linearize: instance (GHC.Show.Show a, Data.Vinyl.Core.ReifyConstraint GHC.Show.Show (StreamPatch.Patch.Flap a) fs, Data.Vinyl.Core.RMap fs, Data.Vinyl.Core.RecordToList fs) => GHC.Show.Show (StreamPatch.Patch.Linearize.Error fs a)
- StreamPatch.Patch.Linearize: instance Data.Foldable.Foldable (StreamPatch.Patch.Patch 'StreamPatch.Patch.AbsSeek fs) => Data.Foldable.Foldable (StreamPatch.Patch.Linearize.Error fs)
- StreamPatch.Patch.Linearize: instance Data.Traversable.Traversable (StreamPatch.Patch.Patch 'StreamPatch.Patch.AbsSeek fs) => Data.Traversable.Traversable (StreamPatch.Patch.Linearize.Error fs)
- StreamPatch.Patch.Linearize: instance GHC.Base.Functor (StreamPatch.Patch.Patch 'StreamPatch.Patch.AbsSeek fs) => GHC.Base.Functor (StreamPatch.Patch.Linearize.Error fs)
- StreamPatch.Patch.Linearize: instance GHC.Generics.Generic (StreamPatch.Patch.Linearize.Error fs a)
- StreamPatch.Patch.Linearize: instance StreamPatch.Patch.Linearize.HasLength Data.ByteString.Internal.ByteString
- StreamPatch.Patch.Linearize: instance StreamPatch.Patch.Linearize.HasLength Data.Text.Internal.Text
- StreamPatch.Patch.Linearize: instance StreamPatch.Patch.Linearize.HasLength GHC.Base.String
- StreamPatch.Patch.Linearize: linearize :: HasLength a => [Patch 'AbsSeek fs a] -> Either (Error fs a) [Patch 'FwdSeek fs a]
- StreamPatch.Stream: class MonadFwdInplaceStream m => MonadCursorInplaceStream m
- StreamPatch.Stream: class Monad m => MonadFwdInplaceStream m where {
- StreamPatch.Stream: class MonadFwdInplaceStream m => MonadFwdStream m
- StreamPatch.Stream: insert :: MonadFwdStream m => Chunk m -> m ()
- StreamPatch.Stream: instance Control.Monad.IO.Class.MonadIO m => StreamPatch.Stream.MonadCursorInplaceStream (Control.Monad.Trans.Reader.ReaderT GHC.IO.Handle.Types.Handle m)
- StreamPatch.Stream: instance Control.Monad.IO.Class.MonadIO m => StreamPatch.Stream.MonadFwdInplaceStream (Control.Monad.Trans.Reader.ReaderT GHC.IO.Handle.Types.Handle m)
- StreamPatch.Stream: instance GHC.Base.Monad m => StreamPatch.Stream.MonadFwdInplaceStream (Control.Monad.Trans.State.Lazy.StateT (Data.ByteString.Internal.ByteString, Data.ByteString.Builder.Internal.Builder) m)
- StreamPatch.Stream: instance GHC.Base.Monad m => StreamPatch.Stream.MonadFwdInplaceStream (Control.Monad.Trans.State.Lazy.StateT ([a], [a]) m)
- StreamPatch.Stream: move :: MonadCursorInplaceStream m => Integer -> m ()
- StreamPatch.Stream: type family Chunk m :: Type;
+ Binrep.Type.Assembly: ArmV8ThumbLE :: Arch
+ Binrep.Type.Assembly: AsmInstr :: ShortText -> AsmInstr (arch :: Arch)
+ Binrep.Type.Assembly: MachineCode :: ByteString -> MachineCode (arch :: Arch)
+ Binrep.Type.Assembly: MachineInstr :: ShortByteString -> MachineInstr (arch :: Arch)
+ Binrep.Type.Assembly: [getAsmInstr] :: AsmInstr (arch :: Arch) -> ShortText
+ Binrep.Type.Assembly: [getMachineCode] :: MachineCode (arch :: Arch) -> ByteString
+ Binrep.Type.Assembly: [getMachineInstr] :: MachineInstr (arch :: Arch) -> ShortByteString
+ Binrep.Type.Assembly: data Arch
+ Binrep.Type.Assembly: instance Binrep.BLen.BLen (Binrep.Type.Assembly.MachineCode arch)
+ Binrep.Type.Assembly: instance Binrep.Get.Get (Binrep.Type.Assembly.MachineCode arch)
+ Binrep.Type.Assembly: instance Binrep.Put.Put (Binrep.Type.Assembly.MachineCode arch)
+ Binrep.Type.Assembly: instance Data.Aeson.Types.FromJSON.FromJSON (Binrep.Type.Assembly.AsmInstr arch)
+ Binrep.Type.Assembly: instance Data.Aeson.Types.ToJSON.ToJSON (Binrep.Type.Assembly.AsmInstr arch)
+ Binrep.Type.Assembly: instance Data.Data.Data Binrep.Type.Assembly.Arch
+ Binrep.Type.Assembly: instance Data.Typeable.Internal.Typeable arch => Data.Data.Data (Binrep.Type.Assembly.AsmInstr arch)
+ Binrep.Type.Assembly: instance Data.Typeable.Internal.Typeable arch => Data.Data.Data (Binrep.Type.Assembly.MachineCode arch)
+ Binrep.Type.Assembly: instance Data.Typeable.Internal.Typeable arch => Data.Data.Data (Binrep.Type.Assembly.MachineInstr arch)
+ Binrep.Type.Assembly: instance GHC.Classes.Eq (Binrep.Type.Assembly.AsmInstr arch)
+ Binrep.Type.Assembly: instance GHC.Classes.Eq (Binrep.Type.Assembly.MachineCode arch)
+ Binrep.Type.Assembly: instance GHC.Classes.Eq Binrep.Type.Assembly.Arch
+ Binrep.Type.Assembly: instance GHC.Generics.Generic (Binrep.Type.Assembly.AsmInstr arch)
+ Binrep.Type.Assembly: instance GHC.Generics.Generic (Binrep.Type.Assembly.MachineCode arch)
+ Binrep.Type.Assembly: instance GHC.Generics.Generic (Binrep.Type.Assembly.MachineInstr arch)
+ Binrep.Type.Assembly: instance GHC.Generics.Generic Binrep.Type.Assembly.Arch
+ Binrep.Type.Assembly: instance GHC.Show.Show (Binrep.Type.Assembly.AsmInstr arch)
+ Binrep.Type.Assembly: instance GHC.Show.Show (Binrep.Type.Assembly.MachineCode arch)
+ Binrep.Type.Assembly: instance GHC.Show.Show (Binrep.Type.Assembly.MachineInstr arch)
+ Binrep.Type.Assembly: instance GHC.Show.Show Binrep.Type.Assembly.Arch
+ Binrep.Type.Assembly: newtype AsmInstr (arch :: Arch)
+ Binrep.Type.Assembly: newtype MachineCode (arch :: Arch)
+ Binrep.Type.Assembly: newtype MachineInstr (arch :: Arch)
+ Binrep.Type.Assembly.Assemble: assemble :: Assemble arch => [AsmInstr arch] -> Either String (MachineCode arch)
+ Binrep.Type.Assembly.Assemble: assemble' :: MonadIO m => Architecture -> [Mode] -> [String] -> m (Either String ByteString)
+ Binrep.Type.Assembly.Assemble: assemble1 :: forall arch. Assemble arch => AsmInstr arch -> Either String (MachineCode arch)
+ Binrep.Type.Assembly.Assemble: class Assemble arch
+ Binrep.Type.Assembly.Assemble: instance Binrep.Type.Assembly.Assemble.Assemble 'Binrep.Type.Assembly.ArmV8ThumbLE
+ Binrep.Type.Assembly.Assemble: prepInstrs :: forall arch. [AsmInstr arch] -> [String]
+ Binrep.Type.Assembly.Assemble: prepInstrs' :: forall arch. [AsmInstr arch] -> ByteString
+ Binrep.Type.Assembly.Disassemble: class Disassemble arch
+ Binrep.Type.Assembly.Disassemble: disassemble :: Disassemble arch => Natural -> MachineCode arch -> Either String [AsmInstr arch]
+ Raehik.HFunctorMap: LFlap :: Snd fl a -> LFlap a fl
+ Raehik.HFunctorMap: LFunctorList :: Rec (LFlap a) fs -> LFunctorList fs a
+ Raehik.HFunctorMap: [getLFlap] :: LFlap a fl -> Snd fl a
+ Raehik.HFunctorMap: [getLFunctorList] :: LFunctorList fs a -> Rec (LFlap a) fs
+ Raehik.HFunctorMap: instance forall k (a :: k) (fl :: (GHC.Types.Symbol, k -> *)). GHC.Generics.Generic (Raehik.HFunctorMap.LFlap a fl)
+ Raehik.HFunctorMap: instance forall k (a :: k) (fs :: [(GHC.Types.Symbol, k -> *)]). (Data.Vinyl.Core.ReifyConstraint GHC.Show.Show (Raehik.HFunctorMap.LFlap a) fs, Data.Vinyl.Core.RMap fs, Data.Vinyl.Core.RecordToList fs) => GHC.Show.Show (Raehik.HFunctorMap.LFunctorList fs a)
+ Raehik.HFunctorMap: instance forall k (a :: k). Data.Aeson.Types.ToJSON.ToJSON (Raehik.HFunctorMap.LFunctorList '[] a)
+ Raehik.HFunctorMap: instance forall k (f :: k -> *) (a :: k) (l :: GHC.Types.Symbol) (fs :: [(GHC.Types.Symbol, k -> *)]). (Data.Aeson.Types.ToJSON.ToJSON (f a), GHC.TypeLits.KnownSymbol l, Data.Aeson.Types.ToJSON.ToJSON (Raehik.HFunctorMap.LFunctorList fs a)) => Data.Aeson.Types.ToJSON.ToJSON (Raehik.HFunctorMap.LFunctorList ('(l, f) : fs) a)
+ Raehik.HFunctorMap: instance forall k (fl :: (GHC.Types.Symbol, k -> *)) (a :: k). Data.Aeson.Types.ToJSON.ToJSON (Data.Vinyl.TypeLevel.Snd fl a) => Data.Aeson.Types.ToJSON.ToJSON (Raehik.HFunctorMap.LFlap a fl)
+ Raehik.HFunctorMap: instance forall k (fl :: (GHC.Types.Symbol, k -> *)) (a :: k). GHC.Show.Show (Data.Vinyl.TypeLevel.Snd fl a) => GHC.Show.Show (Raehik.HFunctorMap.LFlap a fl)
+ Raehik.HFunctorMap: instance forall k (fs :: [(GHC.Types.Symbol, k -> *)]) (a :: k). GHC.Generics.Generic (Raehik.HFunctorMap.LFunctorList fs a)
+ Raehik.HFunctorMap: lFlap :: forall l a f. f a -> LFlap a '(l, f)
+ Raehik.HFunctorMap: lflgetf :: forall (l :: Symbol) f fs a. HasField Rec l fs fs f f => LFunctorList fs a -> f a
+ Raehik.HFunctorMap: newtype LFlap a fl
+ Raehik.HFunctorMap: newtype LFunctorList fs a
+ Raehik.HFunctorMap: symbolVal'' :: forall l. KnownSymbol l => String
+ StreamPatch.Apply: ErrorBinUnexpectedNonNull :: ByteString -> Error
+ StreamPatch.Apply: ErrorCompare :: String -> Error
+ StreamPatch.Apply: applyBinCompareFwd :: forall v m. (FwdInplaceStream m, Chunk m ~ ByteString, Compare v ByteString, Num (Index m)) => [Patch (Index m) '[Meta v, Meta] ByteString] -> m (Either Error ())
+ StreamPatch.Apply: applyFwd :: (FwdInplaceStream m, Chunk m ~ a) => [Patch (Index m) '[] a] -> m ()
+ StreamPatch.Apply: applyFwdCompare :: forall a v m. (FwdInplaceStream m, Chunk m ~ a, Compare v a, HasLength a, Num (Index m)) => [Patch (Index m) '[Meta v] a] -> m (Either Error ())
+ StreamPatch.Apply: data Error
+ StreamPatch.Apply: instance GHC.Classes.Eq StreamPatch.Apply.Error
+ StreamPatch.Apply: instance GHC.Generics.Generic StreamPatch.Apply.Error
+ StreamPatch.Apply: instance GHC.Show.Show StreamPatch.Apply.Error
+ StreamPatch.Apply: runPureBinCompareFwd :: Compare v ByteString => [Patch Int '[Meta v, Meta] ByteString] -> ByteString -> Either Error ByteString
+ StreamPatch.Apply: runPureFwdCompareString :: Compare v String => [Patch Int '[Meta v] String] -> String -> Either Error String
+ StreamPatch.Apply: runPureFwdList :: [Patch Int '[] [a]] -> [a] -> [a]
+ StreamPatch.Example: ex :: Patch (SIx Natural) '[Meta ('ViaEq 'Exact)] Text
+ StreamPatch.Example: ex2 :: Patch (SIx Natural) '[] Text
+ StreamPatch.HFunctorList: Flap :: f a -> Flap a f
+ StreamPatch.HFunctorList: HFunctorList :: Rec (Flap a) fs -> HFunctorList fs a
+ StreamPatch.HFunctorList: [getFlap] :: Flap a f -> f a
+ StreamPatch.HFunctorList: [getHFunctorList] :: HFunctorList fs a -> Rec (Flap a) fs
+ StreamPatch.HFunctorList: hflGet :: forall f fs a i. RElem f fs i => HFunctorList fs a -> f a
+ StreamPatch.HFunctorList: hflLens :: forall f f' fs fs' a s t. (RecElem Rec f f' fs fs' (RIndex f fs), RElem f fs (RIndex f fs), s ~ HFunctorList fs a, t ~ HFunctorList fs' a) => Lens s t (f a) (f' a)
+ StreamPatch.HFunctorList: hflPut :: forall f f' fs fs' a. RecElem Rec f f' fs fs' (RIndex f fs) => f' a -> HFunctorList fs a -> HFunctorList fs' a
+ StreamPatch.HFunctorList: hflStrip :: forall f fs a fs' b i is. (RElem f fs i, fs' ~ RDelete f fs, RSubset fs' fs is) => (f a -> b) -> HFunctorList fs a -> (b, HFunctorList fs' a)
+ StreamPatch.HFunctorList: instance (Data.Foldable.Foldable r, Data.Foldable.Foldable (StreamPatch.HFunctorList.HFunctorList rs)) => Data.Foldable.Foldable (StreamPatch.HFunctorList.HFunctorList (r : rs))
+ StreamPatch.HFunctorList: instance (Data.Traversable.Traversable r, Data.Traversable.Traversable (StreamPatch.HFunctorList.HFunctorList rs)) => Data.Traversable.Traversable (StreamPatch.HFunctorList.HFunctorList (r : rs))
+ StreamPatch.HFunctorList: instance (GHC.Base.Applicative r, GHC.Base.Applicative (StreamPatch.HFunctorList.HFunctorList rs)) => GHC.Base.Applicative (StreamPatch.HFunctorList.HFunctorList (r : rs))
+ StreamPatch.HFunctorList: instance (GHC.Base.Functor r, GHC.Base.Functor (StreamPatch.HFunctorList.HFunctorList rs)) => GHC.Base.Functor (StreamPatch.HFunctorList.HFunctorList (r : rs))
+ StreamPatch.HFunctorList: instance Data.Foldable.Foldable (StreamPatch.HFunctorList.HFunctorList '[])
+ StreamPatch.HFunctorList: instance Data.Traversable.Traversable (StreamPatch.HFunctorList.HFunctorList '[])
+ StreamPatch.HFunctorList: instance GHC.Base.Applicative (StreamPatch.HFunctorList.HFunctorList '[])
+ StreamPatch.HFunctorList: instance GHC.Base.Functor (StreamPatch.HFunctorList.HFunctorList '[])
+ StreamPatch.HFunctorList: instance forall k (a :: k) (f :: k -> *) (fs :: [k -> *]). (Foreign.Storable.Storable (f a), Foreign.Storable.Storable (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) fs)) => Foreign.Storable.Storable (StreamPatch.HFunctorList.HFunctorList (f : fs) a)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (f :: k -> *). GHC.Classes.Eq (f a) => GHC.Classes.Eq (StreamPatch.HFunctorList.Flap a f)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (f :: k -> *). GHC.Classes.Ord (f a) => GHC.Classes.Ord (StreamPatch.HFunctorList.Flap a f)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (f :: k -> *). GHC.Generics.Generic (StreamPatch.HFunctorList.Flap a f)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (f :: k -> *). GHC.Show.Show (f a) => GHC.Show.Show (StreamPatch.HFunctorList.Flap a f)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (fs :: [k -> *]). (Data.Vinyl.Core.ReifyConstraint GHC.Show.Show (StreamPatch.HFunctorList.Flap a) fs, Data.Vinyl.Core.RMap fs, Data.Vinyl.Core.RecordToList fs) => GHC.Show.Show (StreamPatch.HFunctorList.HFunctorList fs a)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (fs :: [k -> *]). Data.Aeson.Types.ToJSON.ToJSON (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) fs) => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.HFunctorList.HFunctorList fs a)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (fs :: [k -> *]). GHC.Classes.Eq (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) fs) => GHC.Classes.Eq (StreamPatch.HFunctorList.HFunctorList fs a)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (fs :: [k -> *]). GHC.Classes.Ord (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) fs) => GHC.Classes.Ord (StreamPatch.HFunctorList.HFunctorList fs a)
+ StreamPatch.HFunctorList: instance forall k (a :: k) (r :: k -> *) (rs :: [k -> *]). (Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.HFunctorList.Flap a r), GHC.Generics.Generic (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) rs), Data.Aeson.Types.ToJSON.GToJSON' Data.Aeson.Types.Internal.Value Data.Aeson.Types.Generic.Zero (GHC.Generics.Rep (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) rs)), Data.Aeson.Types.ToJSON.GToJSON' Data.Aeson.Encoding.Internal.Encoding Data.Aeson.Types.Generic.Zero (GHC.Generics.Rep (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) rs))) => Data.Aeson.Types.ToJSON.ToJSON (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) (r : rs))
+ StreamPatch.HFunctorList: instance forall k (a :: k). Foreign.Storable.Storable (StreamPatch.HFunctorList.HFunctorList '[] a)
+ StreamPatch.HFunctorList: instance forall k (f :: k -> *) (a :: k). Data.Aeson.Types.FromJSON.FromJSON (f a) => Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.HFunctorList.Flap a f)
+ StreamPatch.HFunctorList: instance forall k (f :: k -> *) (a :: k). Data.Aeson.Types.ToJSON.ToJSON (f a) => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.HFunctorList.Flap a f)
+ StreamPatch.HFunctorList: instance forall k (f :: k -> *) (a :: k). Foreign.Storable.Storable (f a) => Foreign.Storable.Storable (StreamPatch.HFunctorList.Flap a f)
+ StreamPatch.HFunctorList: instance forall k (fs :: [k -> *]) (a :: k). GHC.Generics.Generic (StreamPatch.HFunctorList.HFunctorList fs a)
+ StreamPatch.HFunctorList: newtype Flap a f
+ StreamPatch.HFunctorList: newtype HFunctorList fs a
+ StreamPatch.Patch: instance (Data.Aeson.Types.FromJSON.FromJSON a, Data.Aeson.Types.FromJSON.FromJSON s, Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.HFunctorList.HFunctorList fs a)) => Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Patch.Patch s fs a)
+ StreamPatch.Patch: instance (Data.Aeson.Types.ToJSON.ToJSON a, Data.Aeson.Types.ToJSON.ToJSON s, Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.HFunctorList.HFunctorList fs a)) => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Patch.Patch s fs a)
+ StreamPatch.Patch: instance (Data.Data.Data a, Data.Data.Data s, Data.Data.Data (StreamPatch.HFunctorList.HFunctorList fs a), Data.Typeable.Internal.Typeable fs) => Data.Data.Data (StreamPatch.Patch.Patch s fs a)
+ StreamPatch.Patch: instance (GHC.Classes.Eq a, GHC.Classes.Eq s, GHC.Classes.Eq (StreamPatch.HFunctorList.HFunctorList fs a)) => GHC.Classes.Eq (StreamPatch.Patch.Patch s fs a)
+ StreamPatch.Patch: instance (GHC.Show.Show a, GHC.Show.Show s, GHC.Show.Show (StreamPatch.HFunctorList.HFunctorList fs a)) => GHC.Show.Show (StreamPatch.Patch.Patch s fs a)
+ StreamPatch.Patch: instance Data.Foldable.Foldable (StreamPatch.HFunctorList.HFunctorList fs) => Data.Foldable.Foldable (StreamPatch.Patch.Patch s fs)
+ StreamPatch.Patch: instance Data.Traversable.Traversable (StreamPatch.HFunctorList.HFunctorList fs) => Data.Traversable.Traversable (StreamPatch.Patch.Patch s fs)
+ StreamPatch.Patch: instance GHC.Base.Functor (StreamPatch.HFunctorList.HFunctorList fs) => GHC.Base.Functor (StreamPatch.Patch.Patch s fs)
+ StreamPatch.Patch.Align: ErrorAlignedToNegative :: Integer -> Error st
+ StreamPatch.Patch.Align: instance GHC.Classes.Eq st => GHC.Classes.Eq (StreamPatch.Patch.Align.Error st)
+ StreamPatch.Patch.Align: instance GHC.Classes.Eq st => GHC.Classes.Eq (StreamPatch.Patch.Align.Meta st)
+ StreamPatch.Patch.Align: instance GHC.Generics.Generic (StreamPatch.Patch.Align.Error st)
+ StreamPatch.Patch.Align: instance GHC.Generics.Generic (StreamPatch.Patch.Align.Meta st)
+ StreamPatch.Patch.Align: instance GHC.Show.Show st => GHC.Show.Show (StreamPatch.Patch.Align.Error st)
+ StreamPatch.Patch.Align: instance GHC.Show.Show st => GHC.Show.Show (StreamPatch.Patch.Align.Meta st)
+ StreamPatch.Patch.Binary: ErrorBinRepOverlong :: BLenT -> BLenT -> a -> Maybe ByteString -> Error a
+ StreamPatch.Patch.Binary: MetaPrep :: Maybe Natural -> MetaPrep
+ StreamPatch.Patch.Binary: [mNullTerminates] :: Meta a -> Maybe Natural
+ StreamPatch.Patch.Binary: [mpMaxBytes] :: MetaPrep -> Maybe Natural
+ StreamPatch.Patch.Binary: binRepify :: forall a s ss is r rs. (Put a, BLen a, Traversable (HFunctorList rs), r ~ Const MetaPrep, rs ~ RDelete r ss, RElem r ss (RIndex r ss), RSubset rs ss is) => Patch s ss a -> Either (Error a) (Patch s rs ByteString)
+ StreamPatch.Patch.Binary: binRepifyNull :: forall ec s r1 r2 ss rs is. (r1 ~ Meta, r2 ~ Meta ('ViaEq ec), rs ~ RDelete r1 ss, RElem r1 ss (RIndex r1 ss), RSubset rs ss is, RElem r2 rs (RIndex r2 rs), RecElem Rec r2 r2 rs rs (RIndex r2 rs)) => Patch s ss ByteString -> Patch s rs ByteString
+ StreamPatch.Patch.Binary: data MetaPrep
+ StreamPatch.Patch.Binary: instance Data.Foldable.Foldable StreamPatch.Patch.Binary.Meta
+ StreamPatch.Patch.Binary: instance Data.Traversable.Traversable StreamPatch.Patch.Binary.Meta
+ StreamPatch.Patch.Binary: instance GHC.Base.Functor StreamPatch.Patch.Binary.Meta
+ StreamPatch.Patch.Binary: instance GHC.Classes.Eq StreamPatch.Patch.Binary.MetaPrep
+ StreamPatch.Patch.Binary: instance GHC.Generics.Generic StreamPatch.Patch.Binary.MetaPrep
+ StreamPatch.Patch.Binary: instance GHC.Show.Show StreamPatch.Patch.Binary.MetaPrep
+ StreamPatch.Patch.Binary: instance forall k (a :: k). GHC.Classes.Eq (StreamPatch.Patch.Binary.Meta a)
+ StreamPatch.Patch.Binary: instance forall k (a :: k). GHC.Generics.Generic (StreamPatch.Patch.Binary.Meta a)
+ StreamPatch.Patch.Binary: instance forall k (a :: k). GHC.Show.Show (StreamPatch.Patch.Binary.Meta a)
+ StreamPatch.Patch.Compare: B3 :: HashFunc
+ StreamPatch.Patch.Compare: Digest :: a -> Digest (h :: HashFunc) a
+ StreamPatch.Patch.Compare: Exact :: EqualityCheck
+ StreamPatch.Patch.Compare: MD5 :: HashFunc
+ StreamPatch.Patch.Compare: Meta :: Maybe (CompareRep v a) -> Meta (v :: Via) a
+ StreamPatch.Patch.Compare: PrefixOf :: EqualityCheck
+ StreamPatch.Patch.Compare: SHA256 :: HashFunc
+ StreamPatch.Patch.Compare: ViaDigest :: HashFunc -> Via
+ StreamPatch.Patch.Compare: ViaEq :: EqualityCheck -> Via
+ StreamPatch.Patch.Compare: ViaSize :: Via
+ StreamPatch.Patch.Compare: [SB3] :: SHashFunc (B3 :: HashFunc)
+ StreamPatch.Patch.Compare: [SExact] :: SEqualityCheck (Exact :: EqualityCheck)
+ StreamPatch.Patch.Compare: [SMD5] :: SHashFunc (MD5 :: HashFunc)
+ StreamPatch.Patch.Compare: [SPrefixOf] :: SEqualityCheck (PrefixOf :: EqualityCheck)
+ StreamPatch.Patch.Compare: [SSHA256] :: SHashFunc (SHA256 :: HashFunc)
+ StreamPatch.Patch.Compare: [SViaDigest] :: forall (n_aIuw :: HashFunc). () => Sing n_aIuw -> SVia (ViaDigest n_aIuw :: Via)
+ StreamPatch.Patch.Compare: [SViaEq] :: forall (n_aIuu :: EqualityCheck). () => Sing n_aIuu -> SVia (ViaEq n_aIuu :: Via)
+ StreamPatch.Patch.Compare: [SViaSize] :: SVia (ViaSize :: Via)
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679159439Sym0KindInference] :: SameKind (Apply ShowsPrec_6989586621679159439Sym0 arg_aCVO) (ShowsPrec_6989586621679159439Sym1 arg_aCVO) => ShowsPrec_6989586621679159439Sym0 a6989586621679159449
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679159439Sym1KindInference] :: SameKind (Apply (ShowsPrec_6989586621679159439Sym1 a6989586621679159449) arg_aCVO) (ShowsPrec_6989586621679159439Sym2 a6989586621679159449 arg_aCVO) => ShowsPrec_6989586621679159439Sym1 a6989586621679159449 a6989586621679159450
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679159439Sym2KindInference] :: SameKind (Apply (ShowsPrec_6989586621679159439Sym2 a6989586621679159449 a6989586621679159450) arg_aCVO) (ShowsPrec_6989586621679159439Sym3 a6989586621679159449 a6989586621679159450 arg_aCVO) => ShowsPrec_6989586621679159439Sym2 a6989586621679159449 a6989586621679159450 a6989586621679159451
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679178563Sym0KindInference] :: SameKind (Apply ShowsPrec_6989586621679178563Sym0 arg_aHUi) (ShowsPrec_6989586621679178563Sym1 arg_aHUi) => ShowsPrec_6989586621679178563Sym0 a6989586621679178575
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679178563Sym1KindInference] :: SameKind (Apply (ShowsPrec_6989586621679178563Sym1 a6989586621679178575) arg_aHUi) (ShowsPrec_6989586621679178563Sym2 a6989586621679178575 arg_aHUi) => ShowsPrec_6989586621679178563Sym1 a6989586621679178575 a6989586621679178576
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679178563Sym2KindInference] :: SameKind (Apply (ShowsPrec_6989586621679178563Sym2 a6989586621679178575 a6989586621679178576) arg_aHUi) (ShowsPrec_6989586621679178563Sym3 a6989586621679178575 a6989586621679178576 arg_aHUi) => ShowsPrec_6989586621679178563Sym2 a6989586621679178575 a6989586621679178576 a6989586621679178577
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679180783Sym0KindInference] :: SameKind (Apply ShowsPrec_6989586621679180783Sym0 arg_aIu6) (ShowsPrec_6989586621679180783Sym1 arg_aIu6) => ShowsPrec_6989586621679180783Sym0 a6989586621679180795
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679180783Sym1KindInference] :: SameKind (Apply (ShowsPrec_6989586621679180783Sym1 a6989586621679180795) arg_aIu6) (ShowsPrec_6989586621679180783Sym2 a6989586621679180795 arg_aIu6) => ShowsPrec_6989586621679180783Sym1 a6989586621679180795 a6989586621679180796
+ StreamPatch.Patch.Compare: [ShowsPrec_6989586621679180783Sym2KindInference] :: SameKind (Apply (ShowsPrec_6989586621679180783Sym2 a6989586621679180795 a6989586621679180796) arg_aIu6) (ShowsPrec_6989586621679180783Sym3 a6989586621679180795 a6989586621679180796 arg_aIu6) => ShowsPrec_6989586621679180783Sym2 a6989586621679180795 a6989586621679180796 a6989586621679180797
+ StreamPatch.Patch.Compare: [TFHelper_6989586621679159975Sym0KindInference] :: SameKind (Apply TFHelper_6989586621679159975Sym0 arg_aD4n) (TFHelper_6989586621679159975Sym1 arg_aD4n) => TFHelper_6989586621679159975Sym0 a6989586621679159980
+ StreamPatch.Patch.Compare: [TFHelper_6989586621679159975Sym1KindInference] :: SameKind (Apply (TFHelper_6989586621679159975Sym1 a6989586621679159980) arg_aD4n) (TFHelper_6989586621679159975Sym2 a6989586621679159980 arg_aD4n) => TFHelper_6989586621679159975Sym1 a6989586621679159980 a6989586621679159981
+ StreamPatch.Patch.Compare: [TFHelper_6989586621679178583Sym0KindInference] :: SameKind (Apply TFHelper_6989586621679178583Sym0 arg_aHUv) (TFHelper_6989586621679178583Sym1 arg_aHUv) => TFHelper_6989586621679178583Sym0 a6989586621679178588
+ StreamPatch.Patch.Compare: [TFHelper_6989586621679178583Sym1KindInference] :: SameKind (Apply (TFHelper_6989586621679178583Sym1 a6989586621679178588) arg_aHUv) (TFHelper_6989586621679178583Sym2 a6989586621679178588 arg_aHUv) => TFHelper_6989586621679178583Sym1 a6989586621679178588 a6989586621679178589
+ StreamPatch.Patch.Compare: [TFHelper_6989586621679180807Sym0KindInference] :: SameKind (Apply TFHelper_6989586621679180807Sym0 arg_aIun) (TFHelper_6989586621679180807Sym1 arg_aIun) => TFHelper_6989586621679180807Sym0 a6989586621679180812
+ StreamPatch.Patch.Compare: [TFHelper_6989586621679180807Sym1KindInference] :: SameKind (Apply (TFHelper_6989586621679180807Sym1 a6989586621679180812) arg_aIun) (TFHelper_6989586621679180807Sym2 a6989586621679180812 arg_aIun) => TFHelper_6989586621679180807Sym1 a6989586621679180812 a6989586621679180813
+ StreamPatch.Patch.Compare: [ViaDigestSym0KindInference] :: SameKind (Apply ViaDigestSym0 arg_aItQ) (ViaDigestSym1 arg_aItQ) => ViaDigestSym0 a6989586621679180779
+ StreamPatch.Patch.Compare: [ViaEqSym0KindInference] :: SameKind (Apply ViaEqSym0 arg_aItN) (ViaEqSym1 arg_aItN) => ViaEqSym0 a6989586621679180776
+ StreamPatch.Patch.Compare: [getDigest] :: Digest (h :: HashFunc) a -> a
+ StreamPatch.Patch.Compare: [mCompare] :: Meta (v :: Via) a -> Maybe (CompareRep v a)
+ StreamPatch.Patch.Compare: class Compare (v :: Via) a
+ StreamPatch.Patch.Compare: class SwapCompare a (vFrom :: Via) (vTo :: Via)
+ StreamPatch.Patch.Compare: compare' :: Compare v a => CompareRep v a -> CompareRep v a -> Maybe String
+ StreamPatch.Patch.Compare: compareTo :: forall v a. Compare v a => CompareRep v a -> a -> Maybe String
+ StreamPatch.Patch.Compare: data EqualityCheck
+ StreamPatch.Patch.Compare: data HashFunc
+ StreamPatch.Patch.Compare: data Meta (v :: Via) a
+ StreamPatch.Patch.Compare: data SEqualityCheck :: EqualityCheck -> Type
+ StreamPatch.Patch.Compare: data SHashFunc :: HashFunc -> Type
+ StreamPatch.Patch.Compare: data SVia :: Via -> Type
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679159439Sym0 :: (~>) Natural ((~>) EqualityCheck ((~>) Symbol Symbol))
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679159439Sym1 (a6989586621679159449 :: Natural) :: (~>) EqualityCheck ((~>) Symbol Symbol)
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679159439Sym2 (a6989586621679159449 :: Natural) (a6989586621679159450 :: EqualityCheck) :: (~>) Symbol Symbol
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679178563Sym0 :: (~>) Natural ((~>) HashFunc ((~>) Symbol Symbol))
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679178563Sym1 (a6989586621679178575 :: Natural) :: (~>) HashFunc ((~>) Symbol Symbol)
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679178563Sym2 (a6989586621679178575 :: Natural) (a6989586621679178576 :: HashFunc) :: (~>) Symbol Symbol
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679180783Sym0 :: (~>) Natural ((~>) Via ((~>) Symbol Symbol))
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679180783Sym1 (a6989586621679180795 :: Natural) :: (~>) Via ((~>) Symbol Symbol)
+ StreamPatch.Patch.Compare: data ShowsPrec_6989586621679180783Sym2 (a6989586621679180795 :: Natural) (a6989586621679180796 :: Via) :: (~>) Symbol Symbol
+ StreamPatch.Patch.Compare: data TFHelper_6989586621679159975Sym0 :: (~>) EqualityCheck ((~>) EqualityCheck Bool)
+ StreamPatch.Patch.Compare: data TFHelper_6989586621679159975Sym1 (a6989586621679159980 :: EqualityCheck) :: (~>) EqualityCheck Bool
+ StreamPatch.Patch.Compare: data TFHelper_6989586621679178583Sym0 :: (~>) HashFunc ((~>) HashFunc Bool)
+ StreamPatch.Patch.Compare: data TFHelper_6989586621679178583Sym1 (a6989586621679178588 :: HashFunc) :: (~>) HashFunc Bool
+ StreamPatch.Patch.Compare: data TFHelper_6989586621679180807Sym0 :: (~>) Via ((~>) Via Bool)
+ StreamPatch.Patch.Compare: data TFHelper_6989586621679180807Sym1 (a6989586621679180812 :: Via) :: (~>) Via Bool
+ StreamPatch.Patch.Compare: data Via
+ StreamPatch.Patch.Compare: data ViaDigestSym0 :: (~>) HashFunc Via
+ StreamPatch.Patch.Compare: data ViaEqSym0 :: (~>) EqualityCheck Via
+ StreamPatch.Patch.Compare: hashB3 :: ByteString -> ByteString
+ StreamPatch.Patch.Compare: hashFuncLabel :: forall h l. (l ~ HashFuncLabel h, KnownSymbol l) => Text
+ StreamPatch.Patch.Compare: instance (Data.Eq.Singletons.SEq StreamPatch.Patch.Compare.EqualityCheck, Data.Eq.Singletons.SEq StreamPatch.Patch.Compare.HashFunc) => Data.Eq.Singletons.SEq StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance (Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.EqualityCheck, Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.HashFunc) => Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance (Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.EqualityCheck, Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.HashFunc) => Data.Type.Coercion.TestCoercion StreamPatch.Patch.Compare.SVia
+ StreamPatch.Patch.Compare: instance (Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.EqualityCheck, Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.HashFunc) => Data.Type.Equality.TestEquality StreamPatch.Patch.Compare.SVia
+ StreamPatch.Patch.Compare: instance (Data.Singletons.ShowSing.ShowSing StreamPatch.Patch.Compare.EqualityCheck, Data.Singletons.ShowSing.ShowSing StreamPatch.Patch.Compare.HashFunc) => GHC.Show.Show (StreamPatch.Patch.Compare.SVia z)
+ StreamPatch.Patch.Compare: instance (GHC.Classes.Eq a, GHC.Show.Show a) => StreamPatch.Patch.Compare.Compare ('StreamPatch.Patch.Compare.ViaEq 'StreamPatch.Patch.Compare.Exact) a
+ StreamPatch.Patch.Compare: instance (Text.Show.Singletons.SShow StreamPatch.Patch.Compare.EqualityCheck, Text.Show.Singletons.SShow StreamPatch.Patch.Compare.HashFunc) => Text.Show.Singletons.SShow StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance (l GHC.Types.~ StreamPatch.Patch.Compare.HashFuncLabel h, GHC.TypeLits.KnownSymbol l) => Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Patch.Compare.Digest h Data.ByteString.Internal.ByteString)
+ StreamPatch.Patch.Compare: instance (l GHC.Types.~ StreamPatch.Patch.Compare.HashFuncLabel h, GHC.TypeLits.KnownSymbol l) => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Patch.Compare.Digest h Data.ByteString.Internal.ByteString)
+ StreamPatch.Patch.Compare: instance Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Patch.Compare.CompareRep v a) => Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Patch.Compare.Meta v a)
+ StreamPatch.Patch.Compare: instance Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Patch.Compare.CompareRep v a) => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Patch.Compare.Meta v a)
+ StreamPatch.Patch.Compare: instance Data.Data.Data StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance Data.Data.Data StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance Data.Data.Data StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance Data.Eq.Singletons.PEq StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance Data.Eq.Singletons.PEq StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance Data.Eq.Singletons.PEq StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance Data.Eq.Singletons.SEq StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance Data.Eq.Singletons.SEq StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance Data.Singletons.Decide.SDecide StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI 'StreamPatch.Patch.Compare.B3
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI 'StreamPatch.Patch.Compare.Exact
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI 'StreamPatch.Patch.Compare.MD5
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI 'StreamPatch.Patch.Compare.PrefixOf
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI 'StreamPatch.Patch.Compare.SHA256
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI 'StreamPatch.Patch.Compare.ViaSize
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI StreamPatch.Patch.Compare.ViaDigestSym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI StreamPatch.Patch.Compare.ViaEqSym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI n => Data.Singletons.SingI ('StreamPatch.Patch.Compare.ViaDigest n)
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI n => Data.Singletons.SingI ('StreamPatch.Patch.Compare.ViaEq n)
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI v => Data.Foldable.Foldable (StreamPatch.Patch.Compare.Meta v)
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI v => Data.Traversable.Traversable (StreamPatch.Patch.Compare.Meta v)
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI v => GHC.Base.Functor (StreamPatch.Patch.Compare.Meta v)
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI1 'StreamPatch.Patch.Compare.ViaDigest
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingI1 'StreamPatch.Patch.Compare.ViaEq
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingKind StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingKind StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance Data.Singletons.SingKind StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.ShowsPrec_6989586621679159439Sym1 a6989586621679159449)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.ShowsPrec_6989586621679159439Sym2 a6989586621679159449 a6989586621679159450)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.ShowsPrec_6989586621679178563Sym1 a6989586621679178575)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.ShowsPrec_6989586621679178563Sym2 a6989586621679178575 a6989586621679178576)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.ShowsPrec_6989586621679180783Sym1 a6989586621679180795)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.ShowsPrec_6989586621679180783Sym2 a6989586621679180795 a6989586621679180796)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.TFHelper_6989586621679159975Sym1 a6989586621679159980)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.TFHelper_6989586621679178583Sym1 a6989586621679178588)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings (StreamPatch.Patch.Compare.TFHelper_6989586621679180807Sym1 a6989586621679180812)
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.ShowsPrec_6989586621679159439Sym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.ShowsPrec_6989586621679178563Sym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.ShowsPrec_6989586621679180783Sym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.TFHelper_6989586621679159975Sym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.TFHelper_6989586621679178583Sym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.TFHelper_6989586621679180807Sym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.ViaDigestSym0
+ StreamPatch.Patch.Compare: instance Data.Singletons.TH.SuppressUnusedWarnings.SuppressUnusedWarnings StreamPatch.Patch.Compare.ViaEqSym0
+ StreamPatch.Patch.Compare: instance Data.Type.Coercion.TestCoercion StreamPatch.Patch.Compare.SEqualityCheck
+ StreamPatch.Patch.Compare: instance Data.Type.Coercion.TestCoercion StreamPatch.Patch.Compare.SHashFunc
+ StreamPatch.Patch.Compare: instance Data.Type.Equality.TestEquality StreamPatch.Patch.Compare.SEqualityCheck
+ StreamPatch.Patch.Compare: instance Data.Type.Equality.TestEquality StreamPatch.Patch.Compare.SHashFunc
+ StreamPatch.Patch.Compare: instance GHC.Classes.Eq (StreamPatch.Patch.Compare.CompareRep v a) => GHC.Classes.Eq (StreamPatch.Patch.Compare.Meta v a)
+ StreamPatch.Patch.Compare: instance GHC.Classes.Eq StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance GHC.Classes.Eq StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance GHC.Classes.Eq StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance GHC.Classes.Eq a => GHC.Classes.Eq (StreamPatch.Patch.Compare.Digest h a)
+ StreamPatch.Patch.Compare: instance GHC.Generics.Generic (StreamPatch.Patch.Compare.Digest h a)
+ StreamPatch.Patch.Compare: instance GHC.Generics.Generic (StreamPatch.Patch.Compare.Meta v a)
+ StreamPatch.Patch.Compare: instance GHC.Generics.Generic StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance GHC.Generics.Generic StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance GHC.Generics.Generic StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance GHC.Show.Show (StreamPatch.Patch.Compare.CompareRep v a) => GHC.Show.Show (StreamPatch.Patch.Compare.Meta v a)
+ StreamPatch.Patch.Compare: instance GHC.Show.Show (StreamPatch.Patch.Compare.SEqualityCheck z)
+ StreamPatch.Patch.Compare: instance GHC.Show.Show (StreamPatch.Patch.Compare.SHashFunc z)
+ StreamPatch.Patch.Compare: instance GHC.Show.Show StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance GHC.Show.Show StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance GHC.Show.Show StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance GHC.Show.Show a => GHC.Show.Show (StreamPatch.Patch.Compare.Digest h a)
+ StreamPatch.Patch.Compare: instance StreamPatch.Patch.Compare.Compare ('StreamPatch.Patch.Compare.ViaDigest 'StreamPatch.Patch.Compare.B3) Data.ByteString.Internal.ByteString
+ StreamPatch.Patch.Compare: instance StreamPatch.Patch.Compare.Compare ('StreamPatch.Patch.Compare.ViaEq 'StreamPatch.Patch.Compare.PrefixOf) Data.ByteString.Internal.ByteString
+ StreamPatch.Patch.Compare: instance StreamPatch.Patch.Compare.SwapCompare Data.ByteString.Internal.ByteString ('StreamPatch.Patch.Compare.ViaEq 'StreamPatch.Patch.Compare.Exact) ('StreamPatch.Patch.Compare.ViaDigest 'StreamPatch.Patch.Compare.B3)
+ StreamPatch.Patch.Compare: instance StreamPatch.Patch.Compare.SwapCompare a v v
+ StreamPatch.Patch.Compare: instance Text.Show.Singletons.PShow StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance Text.Show.Singletons.PShow StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: instance Text.Show.Singletons.PShow StreamPatch.Patch.Compare.Via
+ StreamPatch.Patch.Compare: instance Text.Show.Singletons.SShow StreamPatch.Patch.Compare.EqualityCheck
+ StreamPatch.Patch.Compare: instance Text.Show.Singletons.SShow StreamPatch.Patch.Compare.HashFunc
+ StreamPatch.Patch.Compare: newtype Digest (h :: HashFunc) a
+ StreamPatch.Patch.Compare: parseDigest :: forall h l e s m. (l ~ HashFuncLabel h, KnownSymbol l, MonadParsec e s m, Token s ~ Char, Tokens s ~ Text) => m (Digest h ByteString)
+ StreamPatch.Patch.Compare: prettyDigest :: forall h a l. (l ~ HashFuncLabel h, KnownSymbol l) => (a -> [Word8]) -> Digest h a -> Text
+ StreamPatch.Patch.Compare: swapCompare :: SwapCompare a vFrom vTo => CompareRep vFrom a -> Either String (CompareRep vTo a)
+ StreamPatch.Patch.Compare: toCompareRep :: Compare v a => a -> CompareRep v a
+ StreamPatch.Patch.Compare: type Digest' h = Digest h ByteString
+ StreamPatch.Patch.Compare: type family HashFuncLabel (h :: HashFunc)
+ StreamPatch.Patch.Compile: compileCompareMeta :: forall v a. Compare v a => Meta ('ViaEq 'Exact) a -> Meta v a
+ StreamPatch.Patch.Compile: compilePatch :: forall v a s f f' fs fs' i. (Compare v a, f ~ Meta ('ViaEq 'Exact), f' ~ Meta v, RElem f fs i, RecElem Rec f f' fs fs' i, i ~ RIndex f fs) => Patch s fs a -> Patch s fs' a
+ StreamPatch.Patch.Linearize.Common: comparePatchSeeks :: Ord s => Patch s fs a -> Patch s fs a -> Ordering
+ StreamPatch.Patch.Linearize.InPlace: ErrorOverlap :: Len -> Patch Len fs a -> Patch Len fs a -> Error fs a
+ StreamPatch.Patch.Linearize.InPlace: class HasLength a
+ StreamPatch.Patch.Linearize.InPlace: data Error fs a
+ StreamPatch.Patch.Linearize.InPlace: getLength :: HasLength a => a -> Len
+ StreamPatch.Patch.Linearize.InPlace: instance (GHC.Classes.Eq a, GHC.Classes.Eq (Data.Vinyl.Core.Rec (StreamPatch.HFunctorList.Flap a) fs)) => GHC.Classes.Eq (StreamPatch.Patch.Linearize.InPlace.Error fs a)
+ StreamPatch.Patch.Linearize.InPlace: instance (GHC.Show.Show a, Data.Vinyl.Core.ReifyConstraint GHC.Show.Show (StreamPatch.HFunctorList.Flap a) fs, Data.Vinyl.Core.RMap fs, Data.Vinyl.Core.RecordToList fs) => GHC.Show.Show (StreamPatch.Patch.Linearize.InPlace.Error fs a)
+ StreamPatch.Patch.Linearize.InPlace: instance Data.Foldable.Foldable (StreamPatch.HFunctorList.HFunctorList fs) => Data.Foldable.Foldable (StreamPatch.Patch.Linearize.InPlace.Error fs)
+ StreamPatch.Patch.Linearize.InPlace: instance Data.Traversable.Traversable (StreamPatch.HFunctorList.HFunctorList fs) => Data.Traversable.Traversable (StreamPatch.Patch.Linearize.InPlace.Error fs)
+ StreamPatch.Patch.Linearize.InPlace: instance GHC.Base.Functor (StreamPatch.HFunctorList.HFunctorList fs) => GHC.Base.Functor (StreamPatch.Patch.Linearize.InPlace.Error fs)
+ StreamPatch.Patch.Linearize.InPlace: instance GHC.Generics.Generic (StreamPatch.Patch.Linearize.InPlace.Error fs a)
+ StreamPatch.Patch.Linearize.InPlace: instance StreamPatch.Patch.Linearize.InPlace.HasLength Data.ByteString.Internal.ByteString
+ StreamPatch.Patch.Linearize.InPlace: instance StreamPatch.Patch.Linearize.InPlace.HasLength Data.Text.Internal.Text
+ StreamPatch.Patch.Linearize.InPlace: instance StreamPatch.Patch.Linearize.InPlace.HasLength GHC.Base.String
+ StreamPatch.Patch.Linearize.InPlace: linearizeInPlace :: forall a fs. HasLength a => [Patch Len fs a] -> Either (Error fs a) [Patch Len fs a]
+ StreamPatch.Patch.Linearize.InPlace: type Len = Int
+ StreamPatch.Patch.Linearize.Insert: linearize :: (Integral sf, Integral st) => (a -> sf) -> (st -> a -> b) -> [a] -> Either (a, a) (Maybe (a, [b]))
+ StreamPatch.Patch.Linearize.Insert: linearize' :: (Integral sf, Integral st) => [sf] -> Either (sf, sf) (Maybe (sf, [st]))
+ StreamPatch.Seek: SIx :: a -> SIx a
+ StreamPatch.Seek: [unSIx] :: SIx a -> a
+ StreamPatch.Seek: class Seek (s :: Type) where {
+ StreamPatch.Seek: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Seek.SIx a)
+ StreamPatch.Seek: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Seek.SIx a)
+ StreamPatch.Seek: instance Data.Data.Data a => Data.Data.Data (StreamPatch.Seek.SIx a)
+ StreamPatch.Seek: instance GHC.Classes.Eq a => GHC.Classes.Eq (StreamPatch.Seek.SIx a)
+ StreamPatch.Seek: instance GHC.Classes.Ord a => GHC.Classes.Ord (StreamPatch.Seek.SIx a)
+ StreamPatch.Seek: instance GHC.Generics.Generic (StreamPatch.Seek.SIx a)
+ StreamPatch.Seek: instance GHC.Show.Show a => GHC.Show.Show (StreamPatch.Seek.SIx a)
+ StreamPatch.Seek: mkSeek :: Seek s => SeekRep s -> s
+ StreamPatch.Seek: newtype SIx a
+ StreamPatch.Seek: type SeekRep s :: Type;
+ StreamPatch.Seek: unSeek :: Seek s => s -> SeekRep s
+ StreamPatch.Seek: }
+ StreamPatch.Simple: Aligned :: Integer -> [p] -> Aligned p
+ StreamPatch.Simple: MultiPatch :: a -> s -> Maybe (CompareRep v a) -> Maybe Natural -> Maybe Natural -> Maybe Integer -> MultiPatch s (v :: Via) a
+ StreamPatch.Simple: [alignedAlign] :: Aligned p -> Integer
+ StreamPatch.Simple: [alignedPatches] :: Aligned p -> [p]
+ StreamPatch.Simple: [mpAligned] :: MultiPatch s (v :: Via) a -> Maybe Integer
+ StreamPatch.Simple: [mpCompare] :: MultiPatch s (v :: Via) a -> Maybe (CompareRep v a)
+ StreamPatch.Simple: [mpData] :: MultiPatch s (v :: Via) a -> a
+ StreamPatch.Simple: [mpMaxBytes] :: MultiPatch s (v :: Via) a -> Maybe Natural
+ StreamPatch.Simple: [mpNullTerminates] :: MultiPatch s (v :: Via) a -> Maybe Natural
+ StreamPatch.Simple: [mpSeek] :: MultiPatch s (v :: Via) a -> s
+ StreamPatch.Simple: align :: forall st a ss rs is i r. (r ~ Const (Meta st), Num st, Eq st, rs ~ RDelete r ss, RElem r ss i, RSubset rs ss is) => Aligned (Patch Integer ss a) -> Either (Error st) [Patch st rs a]
+ StreamPatch.Simple: cm :: f a -> Rec (Flap a) '[f]
+ StreamPatch.Simple: cmAlign :: MultiPatch Integer c a -> Rec (Flap a) '[Const (Meta Int)]
+ StreamPatch.Simple: cmBin :: MultiPatch s c a -> Rec (Flap a) '[Meta]
+ StreamPatch.Simple: cmBinPrep :: MultiPatch s c a -> Rec (Flap a) '[Const MetaPrep]
+ StreamPatch.Simple: cmCompare :: MultiPatch s v a -> Rec (Flap a) '[Meta v]
+ StreamPatch.Simple: convert :: (MultiPatch s v a -> Rec (Flap a) fs) -> MultiPatch s v a -> Patch s fs a
+ StreamPatch.Simple: convertAlign :: forall a v. MultiPatch Integer v a -> Patch Integer '[Const (Meta Int)] a
+ StreamPatch.Simple: convertBackBin :: forall v s a. Patch s '[Meta v, Meta] a -> MultiPatch s v a
+ StreamPatch.Simple: convertBin :: forall s a v. MultiPatch s v a -> Patch s '[Const MetaPrep, Meta v, Meta] a
+ StreamPatch.Simple: convertBinAlign :: forall a v. MultiPatch Integer v a -> Patch Integer '[Const (Meta Int), Const MetaPrep, Meta v, Meta] a
+ StreamPatch.Simple: convertEmpty :: forall s a v. MultiPatch s v a -> Patch s '[] a
+ StreamPatch.Simple: data Aligned p
+ StreamPatch.Simple: data MultiPatch s (v :: Via) a
+ StreamPatch.Simple: instance (Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Patch.Compare.CompareRep v a), Data.Aeson.Types.FromJSON.FromJSON a, Data.Aeson.Types.FromJSON.FromJSON s) => Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Simple.MultiPatch s v a)
+ StreamPatch.Simple: instance (Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Patch.Compare.CompareRep v a), Data.Aeson.Types.ToJSON.ToJSON a, Data.Aeson.Types.ToJSON.ToJSON s) => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Simple.MultiPatch s v a)
+ StreamPatch.Simple: instance (GHC.Classes.Eq a, GHC.Classes.Eq s, GHC.Classes.Eq (StreamPatch.Patch.Compare.CompareRep v a)) => GHC.Classes.Eq (StreamPatch.Simple.MultiPatch s v a)
+ StreamPatch.Simple: instance (GHC.Show.Show a, GHC.Show.Show s, GHC.Show.Show (StreamPatch.Patch.Compare.CompareRep v a)) => GHC.Show.Show (StreamPatch.Simple.MultiPatch s v a)
+ StreamPatch.Simple: instance Data.Aeson.Types.FromJSON.FromJSON p => Data.Aeson.Types.FromJSON.FromJSON (StreamPatch.Simple.Aligned p)
+ StreamPatch.Simple: instance Data.Aeson.Types.ToJSON.ToJSON p => Data.Aeson.Types.ToJSON.ToJSON (StreamPatch.Simple.Aligned p)
+ StreamPatch.Simple: instance GHC.Base.Functor (StreamPatch.Simple.MultiPatch s ('StreamPatch.Patch.Compare.ViaDigest h))
+ StreamPatch.Simple: instance GHC.Base.Functor (StreamPatch.Simple.MultiPatch s ('StreamPatch.Patch.Compare.ViaEq ec))
+ StreamPatch.Simple: instance GHC.Classes.Eq p => GHC.Classes.Eq (StreamPatch.Simple.Aligned p)
+ StreamPatch.Simple: instance GHC.Generics.Generic (StreamPatch.Simple.Aligned p)
+ StreamPatch.Simple: instance GHC.Generics.Generic (StreamPatch.Simple.MultiPatch s v a)
+ StreamPatch.Simple: instance GHC.Show.Show p => GHC.Show.Show (StreamPatch.Simple.Aligned p)
+ StreamPatch.Simple: jsonCfgCamelDrop :: Int -> Options
+ StreamPatch.Simple: metaEmpty :: HFunctorList '[] a
+ StreamPatch.Simple: metaWrap1 :: f a -> HFunctorList '[f] a
+ StreamPatch.Stream: -- <tt>Natural</tt> is the most mathematically honest. I leave the
+ StreamPatch.Stream: -- but is often used internally; <a>Integer</a> also gets some use;
+ StreamPatch.Stream: -- decision up to the instance to allow them to be as efficient as
+ StreamPatch.Stream: -- possible.
+ StreamPatch.Stream: -- | The unsigned integral type used for indexing. <a>Int</a> has a sign,
+ StreamPatch.Stream: class Monad m => FwdInplaceStream m where {
+ StreamPatch.Stream: class FwdInplaceStream m => FwdStream m
+ StreamPatch.Stream: delete :: FwdStream m => Index m -> m ()
+ StreamPatch.Stream: getCursor :: FwdInplaceStream m => m (Index m)
+ StreamPatch.Stream: instance Control.Monad.IO.Class.MonadIO m => StreamPatch.Stream.FwdInplaceStream (Control.Monad.Trans.Reader.ReaderT GHC.IO.Handle.Types.Handle m)
+ StreamPatch.Stream: instance GHC.Base.Monad m => StreamPatch.Stream.FwdInplaceStream (Control.Monad.Trans.State.Lazy.StateT (Data.ByteString.Internal.ByteString, Data.ByteString.Builder.Internal.Builder, GHC.Types.Int) m)
+ StreamPatch.Stream: instance GHC.Base.Monad m => StreamPatch.Stream.FwdInplaceStream (Control.Monad.Trans.State.Lazy.StateT ([a], [a], GHC.Types.Int) m)
+ StreamPatch.Stream: instance GHC.Base.Monad m => StreamPatch.Stream.FwdStream (Control.Monad.Trans.State.Lazy.StateT (Data.ByteString.Internal.ByteString, Data.ByteString.Builder.Internal.Builder, GHC.Types.Int) m)
+ StreamPatch.Stream: type Chunk m :: Type;
+ StreamPatch.Stream: type Index m :: Type;
+ StreamPatch.Stream: write :: FwdStream m => Chunk m -> m ()
- StreamPatch.Patch: Patch :: a -> SeekRep s -> FunctorRec fs a -> Patch s fs a
+ StreamPatch.Patch: Patch :: a -> s -> HFunctorList fs a -> Patch s fs a
- StreamPatch.Patch: [patchMeta] :: Patch s fs a -> FunctorRec fs a
+ StreamPatch.Patch: [patchMeta] :: Patch s fs a -> HFunctorList fs a
- StreamPatch.Patch: [patchSeek] :: Patch s fs a -> SeekRep s
+ StreamPatch.Patch: [patchSeek] :: Patch s fs a -> s
- StreamPatch.Patch.Align: ErrorDoesntMatchExpected :: SeekRep s -> SeekRep s -> Error s
+ StreamPatch.Patch.Align: ErrorDoesntMatchExpected :: st -> st -> Error st
- StreamPatch.Patch.Align: Meta :: Maybe (SeekRep s) -> Meta s
+ StreamPatch.Patch.Align: Meta :: Maybe st -> Meta st
- StreamPatch.Patch.Align: [mExpected] :: Meta s -> Maybe (SeekRep s)
+ StreamPatch.Patch.Align: [mExpected] :: Meta st -> Maybe st
- StreamPatch.Patch.Align: align :: forall s a ss rs is i r. (SeekRep s ~ Natural, r ~ Const (Meta s), rs ~ RDelete r ss, RElem r ss i, RSubset rs ss is) => SeekRep 'RelSeek -> Patch 'RelSeek ss a -> Either (Error s) (Patch s rs a)
+ StreamPatch.Patch.Align: align :: forall sf st a ss is r rs. (Integral sf, Num st, Eq st, r ~ Const (Meta st), rs ~ RDelete r ss, RElem r ss (RIndex r ss), RSubset rs ss is) => Integer -> Patch sf ss a -> Either (Error st) (Patch st rs a)
- StreamPatch.Patch.Align: data Error s
+ StreamPatch.Patch.Align: data Error st
- StreamPatch.Patch.Align: data Meta s
+ StreamPatch.Patch.Align: data Meta st
- StreamPatch.Patch.Binary: Meta :: Maybe (SeekRep 'FwdSeek) -> Meta
+ StreamPatch.Patch.Binary: Meta :: Maybe Natural -> Meta a
- StreamPatch.Patch.Binary: data Meta
+ StreamPatch.Patch.Binary: data Meta a
- StreamPatch.Stream: advance :: MonadFwdInplaceStream m => Natural -> m ()
+ StreamPatch.Stream: advance :: FwdInplaceStream m => Index m -> m ()
- StreamPatch.Stream: overwrite :: MonadFwdInplaceStream m => Chunk m -> m ()
+ StreamPatch.Stream: overwrite :: FwdInplaceStream m => Chunk m -> m ()
- StreamPatch.Stream: readahead :: MonadFwdInplaceStream m => Natural -> m (Chunk m)
+ StreamPatch.Stream: readahead :: FwdInplaceStream m => Index m -> m (Chunk m)

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+## 0.4.0 (2022-07-08)+  * lots of refactoring, breaking, removing+  * clean up FunctorRec into standalone HFunctorList module+  * clean up HexBytestring into standalone module+  * add assembly patching (just ARMv8 Thumb LE for now)+ ## 0.3.1 (2021-12-21)   * packaging improvements (documentation, CI) 
README.md view
@@ -1,34 +1,60 @@ # bytepatch-A Haskell library and CLI tool for patching data in a stream, primarily aimed at-handling byte-representable data. Write **patchscripts** (in-place edits to a-file) using a convenient YAML format, and apply them with a safe patching-algorithm. On the library side, define explicit patches over any type, apply-them to pure/impure streams, and write further patch types and streams to extend-as you desire.+A Haskell library and CLI tool for writing declarative patches over streams.+Write **patchscripts** (in-place edits to a file) using a configurable YAML+schema, and apply them with a safe patching algorithm. On the library side,+define patches of various forms over any type, apply them to pure/impure+streams, and write further patch data types and streams to extend as you like. -The executable is intended as a general tool for making simple binary editing-more manageable. The codebase is really a fun investigation into some type-level-Haskell features. Regardless of which you're interested in, I hope you might-find some use from this project!+bytepatch works with many data types: +  * read pretty binary `00 FF 1234`, parse to bytes+  * read text, re-encode, null-terminate to generate a C-style string+  * read assembly, assemble down to machine code++The schema itself is highly configurable:++  * align: add a value (set in patch file) to each patch offset+  * compare: bytepatch can check expected data against actual during patching,+    to assert that you're patching the expected file. You can select how this+    check works: compare equality (exact, or match prefix), hashes+  * seek type: what the patch offsets mean. You'll likely want absolute+    offsets - but you may also write them as linear offsets, forward from the+    previous.++You may also ask bytepatch to *compile* a patchscript, which reads it and then+outputs a YAML file with a simplified schema: pre-aligned, compare via hashes,+forward seeks.++The bytepatch executable aims to be a general tool for reverse engineers, hoping+to make simple binary & assembly editing more manageable. The Haskell library is+quite sprawling, and has personally made for a fun investigation into type-level+Haskell.+ ## What? If you're modifying binaries, you often end up needing to make edits in a hex editor. This is fun for a very short while, then you realise how easy it is to-mess up. bytepatch provides a simple, human read-writeable format for defining-such edits, and can apply them for you.+mess up. -bytepatch is intended as a developer tool for writing a *static patchscript*.-It's not a binary patch tool like IPS, BPS: these take an input and a result-file, and generate a patch file that can be applied to the input in order to-recreate the result. By itself, the patch file isn't useful, being instructions-to the tool telling how to edit the input file. bytepatch uses a human-readable-patch file format that describes in plain terms the edits to make, so developers-can read and write patches in a structured, useable format.+bytepatch is primarily intended as a developer tool for writing a+*human-friendly static patchscript*. It provides a nice schema for defining+edits to a binary file, so that developers can read and write patches in a+structured, readable format. If one wanted, they could very easily read the+patch file and use it to make the changes manually (though bytepatch wants to do+this for you). +bytepatch is not aimed at replacing binary patch tools such as IPS, BPS: these+take an input and a result file, and generate a patch file that can be applied+to the input in order to recreate the result. You need the result file in the+first place. These tools both generate the patch file and apply it; bytepatch+only applies the patch file, and leaves the writing to the user.+ ## When might I want to use this? You might find bytepatch useful if you want to define edits to be made on binary-files (especially executables) that are *static* and *in-place*.+files (especially executables) that are *static* and *in-place*. Examples are: +  * instruction patching (via assembly)+  * string patching (via text)+ ### Less relevant use cases You can't use bytepatch directly for edits which are dynamic in nature (e.g. you need to read a file header in order to know what to change). *(You could@@ -42,18 +68,6 @@ offset, or do we implicitly shift them so they still write to the "same place"? There *is* some support in the library for such edits, however, and it would be interesting to explore.--### Original rationale-I'm working on some binary files, and need to patch strings, machine code and-other data. I make some changes, I test them, they work. Then I note down the-changes I made. It's tedious and repetitive, but I really don't want to lose-track of what got changed how, and it's the clearest way to share my work-others. But if I want to test more stuff, I may have to make those changes-again. I'm gonna make a mistake eventually.--Instead, I described my patches in YAML and wrote a tool to apply them. Now my-changes and my notes (thanks YAML comments!) are the same file, which can be-checked for correctness and applied automatically.  ## License Provided under the MIT license. Please see `LICENSE` for the full license text.
+ app/BytePatch.hs view
@@ -0,0 +1,346 @@+{-# LANGUAGE AllowAmbiguousTypes, OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module BytePatch where++import BytePatch.Config+import Raehik.CLI.Stream+import BytePatch.CLI qualified as CLI+import StreamPatch.Patch.Compile qualified as Compile++import GHC.Generics ( Generic )++import StreamPatch.Patch+import StreamPatch.HFunctorList ( Flap, HFunctorList )+import StreamPatch.Patch.Align qualified as Align+import StreamPatch.Patch.Linearize.InPlace qualified as Linear+import StreamPatch.Patch.Binary qualified as Bin+import StreamPatch.Patch.Compare qualified as Compare+import StreamPatch.Patch.Compare ( Via(..), SVia(..), SEqualityCheck(..), HashFunc(..), SHashFunc(..), Compare, CompareRep )+import StreamPatch.Apply qualified as Apply+import StreamPatch.Simple as Simple++import Binrep.Extra.HexByteString++import Binrep.Type.Assembly qualified as Asm+import Binrep.Type.Assembly.Assemble qualified as Asm+import Binrep.Type.Assembly.Assemble ( Assemble )+import Binrep.Type.Text qualified as BR.Text+import Binrep.Type.ByteString qualified as BR.ByteString++import Binrep+import Binrep.Type.ByteString ( AsByteString )++import Refined++import qualified System.Exit as System+import Data.Vinyl+import Data.Vinyl.TypeLevel+import Data.Functor.Const+import Data.Either.Combinators ( mapLeft )+import Control.Monad.IO.Class+import Control.Monad.Reader+import Control.Monad.Except+import Data.ByteString qualified as B+import Data.ByteString.Lazy qualified as BL+import Data.Yaml qualified as Yaml+import Data.Yaml.Pretty qualified as Yaml.Pretty+import Data.Aeson ( ToJSON, FromJSON )+import Data.Text ( Text )+import Optics+import Data.Generics.Product.Any++import Data.Singletons ( withSomeSing, Sing, SingI )++type Bytes = B.ByteString++--------------------------------------------------------------------------------++-- Errors that can occur during patchscript processing. Everything except+-- reading the patchscript, and writing out the successfully patched file.+data Error+  = ErrorYaml     Yaml.ParseException+  | ErrorAlign    String+  | ErrorLinear   String+  | Error         String+  | ErrorUnimplemented+  | ErrorProcessBinRep String -- can't do Bin.Error, has a typevar (until we put the typevar in this data, which will happen eventually)+  | ErrorProcessAssemble String+  | ErrorProcessApply String+    deriving (Generic, Show)++quit :: MonadIO m => Error -> m a+quit err = do+    liftIO $ putStrLn $ "bytepatch: error: " <> errStr+    liftIO $ System.exitWith $ System.ExitFailure errExitCode+  where+    (errExitCode, errStr) = case err of+      ErrorYaml     e -> (1, "while parsing YAML: "<>show e)+      ErrorAlign    e -> (2, "while aligning: "<>e)+      ErrorProcessBinRep e -> (3, "while converting to binary representation: "<>e)+      ErrorLinear   e -> (4, "while linearizing: "<>e)+      ErrorProcessApply e -> (5, "while applying patch: "<>e)+      ErrorProcessAssemble e -> (6, "while assembling: "<>e)+      ErrorUnimplemented -> (10, "feature not yet implemented")+      Error         e -> (20, e)++logWarn :: MonadIO m => String -> m ()+logWarn msg = liftIO $ putStrLn $ "bytepatch: warning: " <> msg++liftProcessError :: MonadIO m => ExceptT Error m a -> m a+liftProcessError action =+    runExceptT action >>= \case+      Left  e -> quit e+      Right a -> return a++liftMapProcessError :: MonadError Error m => (e -> Error) -> Either e a -> m a+liftMapProcessError f = liftEither . mapLeft f++--------------------------------------------------------------------------------++processDecode :: forall a m. (MonadError Error m, FromJSON a) => Bytes -> m a+processDecode = liftMapProcessError ErrorYaml . Yaml.decodeEither'++processAlign+    :: forall (v :: Via) a m r rs ss is+    .  ( r ~ Const (Align.Meta Int)+       , rs ~ RDelete r ss+       , RElem r ss (RIndex r ss)+       , RSubset rs ss is+       , MonadError Error m )+    => (MultiPatch Integer v a -> Patch Integer ss a)+    -> [Aligned (MultiPatch Integer v a)]+    -> m [Patch Int rs a]+processAlign f = liftMapProcessError (ErrorAlign . show) . fmap concat . traverse (wrapAlign f)++processLinearize+    :: forall a m fs+    .  ( Linear.HasLength a+       , Show a, ReifyConstraint Show (Flap a) fs, RMap fs, RecordToList fs+       , MonadError Error m )+    => [Patch Linear.Len fs a]+    -> m [Patch Linear.Len fs a]+processLinearize = liftMapProcessError (ErrorLinear . show) . Linear.linearizeInPlace++processAsm+    :: forall (arch :: Asm.Arch) s fs m+    .  (MonadError Error m, Assemble arch, Traversable (HFunctorList fs))+    => [Patch s fs (Asm.AsmInstr arch)]+    -> m [Patch s fs (Asm.MachineCode arch)]+processAsm =+      liftMapProcessError ErrorProcessAssemble+    . traverse (traverse (Asm.assemble1 @arch))++processAsms+    :: forall (arch :: Asm.Arch) s fs m+    .  (MonadError Error m, Assemble arch, Traversable (HFunctorList fs))+    => [Patch s fs [Asm.AsmInstr arch]]+    -> m [Patch s fs (Asm.MachineCode arch)]+processAsms =+      liftMapProcessError ErrorProcessAssemble+    . traverse (traverse (Asm.assemble @arch))++processBin+    :: forall a s ss is r rs m+    .  ( MonadError Error m+       , Put a, BLen a+       , r ~ Const Bin.MetaPrep+       , rs ~ RDelete r ss+       , RElem r ss (RIndex r ss)+       , RSubset rs ss is+       , Traversable (HFunctorList rs)+       , Show a+       )+    => [Patch s ss a]+    -> m [Patch s rs Bytes]+processBin = liftMapProcessError (ErrorProcessBinRep . show) . traverse Bin.binRepify++--------------------------------------------------------------------------------++main :: IO ()+main = CLI.parse >>= runReaderT run++run :: forall m. (MonadIO m, MonadReader C m) => m ()+run = readPatchscriptBs >>= liftProcessError . usePatchscriptBs+  where+    readPatchscriptBs = asks cPsPath >>= liftIO . B.readFile . unPath+    usePatchscriptBs bs = do+        cfg <- ask+        let cpf = cfg.cPsFmt+        withSomeSing cpf.cPsFmtCompare (process cfg bs)++-- using singletons simply to automatically bring type into scope+process+    :: forall (v :: Via) m+    .  ( MonadIO m, MonadReader C m )+    => C -> Bytes -> Sing v -> ExceptT Error m ()+process cfg bs = \case+      SViaEq SExact -> do+        ps <- prep @v cfg.cPsFmt bs+        case cfg.cCmd of+          CCmdPatch'   cfg'  -> cmdPatchBinCompareFwd cfg' ps+          CCmdCompile' _cfg' ->+            let ps'  = map (Compile.compilePatch @('ViaDigest 'B3)) ps+                ps'' = asPrettyYamlHexMultiPatch convertBackBin ps'+             in liftIO $ B.putStr ps''+      SViaDigest SB3 -> do+        ps <- prep @v cfg.cPsFmt bs+        case cfg.cCmd of+          CCmdPatch'   cfg'  -> cmdPatchBinCompareFwd cfg' ps+          CCmdCompile' _cfg' ->+              let ps' = asPrettyYamlHexMultiPatch convertBackBin ps+               in liftIO $ B.putStr ps'+      SViaEq SPrefixOf -> do+        ps <- prep @v cfg.cPsFmt bs+        case cfg.cCmd of+          CCmdPatch'   cfg'  -> cmdPatchBinCompareFwd cfg' ps+          CCmdCompile' _cfg' -> throwError $ ErrorUnimplemented+      _ -> throwError $ ErrorUnimplemented++patchPureBinCompareFwd+    :: forall v s m+    .  (MonadIO m, Compare v Bytes)+    => Stream 'In s+    -> [Patch Int '[Compare.Meta v, Bin.Meta] Bytes]+    -> m Bytes+patchPureBinCompareFwd si ps = do+    bsIn <- readStream si+    case Apply.runPureBinCompareFwd ps bsIn of+      Left  e     -> quit $ ErrorProcessApply $ show e+      Right bsOut -> return $ BL.toStrict bsOut++cmdPatchBinCompareFwd+    :: forall v m+    .  (MonadIO m, Compare v Bytes)+    => CCmdPatch+    -> [Patch Int '[Compare.Meta v, Bin.Meta] Bytes]+    -> m ()+cmdPatchBinCompareFwd c ps = do+    bs <- patchPureBinCompareFwd c.cCmdPatchStreamIn ps+    case c.cCmdPatchStreamOut of+      Path' (Path fp) -> liftIO $ B.writeFile fp bs+      Std      -> case c.cCmdPatchPrintBinary of+        True  -> liftIO $ B.putStr bs+        False -> liftIO $ logWarn $+                     "refusing to print binary to stdout:"+                  <> " write to a file with --out-file FILE"+                  <> " or use --print-binary flag to override"++-- TODO fix all of this, it got weird with seekrep removal+wrapAlign+    :: ( r ~ Const (Align.Meta Int)+       , rs ~ RDelete r ss+       , RElem r ss i+       , RSubset rs ss is )+    => (MultiPatch Integer v a -> Patch Integer ss a)+    -> Aligned (MultiPatch Integer v a)+    -> Either (Align.Error Int) [Patch Int rs a]+wrapAlign f = Simple.align . over (the @"alignedPatches") (map f)++readStream :: forall s m. MonadIO m => Stream 'In s -> m Bytes+readStream = liftIO . \case Std             -> B.getContents+                            Path' (Path fp) -> B.readFile fp++-- Parse and prepare/normalize a binrep-compare patchscript, polymorphic on the+-- comparison strategy. We can't handle that in here, because you need it when+-- processing the command, since different comparison strategies require+-- different handling, and some are invalid.+prep+    :: forall v m+    -- grr+     . ( MonadError Error m+       , FromJSON (CompareRep v Text)+       , FromJSON (CompareRep v HexByteString)+       , FromJSON (CompareRep v (Asm.AsmInstr 'Asm.ArmV8ThumbLE))+       , FromJSON (CompareRep v [Asm.AsmInstr 'Asm.ArmV8ThumbLE])+       , Show     (CompareRep v Bytes)+       -- , Traversable (Compare.Meta v)+       , SingI v+       )+    => CPsFmt+    -> Bytes+    -> m [Patch Int '[Compare.Meta v, Bin.Meta] Bytes]+prep c bs = case c.cPsFmtData of+  CDataBytes -> prep' @HexByteString c (return . fmap unHexPatch) bs+  CDataTextBin BR.Text.UTF8 BR.ByteString.C ->+      prep' @Text @(AsByteString 'BR.ByteString.C) c (binTextifyPatches @'BR.Text.UTF8) bs+  CDataAsm Asm.ArmV8ThumbLE -> prep' c (processAsm @'Asm.ArmV8ThumbLE) bs+  CDataText -> throwError ErrorUnimplemented+  cDataX -> throwError $ Error $ "can't yet handle patchscript data type: "<>show cDataX++unHexPatch+    :: Functor (HFunctorList fs)+    => Patch s fs HexByteString+    -> Patch s fs B.ByteString+unHexPatch = fmap unHex++binTextifyPatches+    :: forall (enc :: BR.Text.Encoding) (rep :: BR.ByteString.Rep) s fs m+    .  ( Predicate enc Text, BR.Text.Encode enc, Predicate rep B.ByteString+       , MonadError Error m, Traversable (HFunctorList fs) )+    => [Patch s fs Text]+    -> m [Patch s fs (AsByteString rep)]+binTextifyPatches = traverse (binTextifyPatch @enc)++binTextifyPatch+    :: forall (enc :: BR.Text.Encoding) (rep :: BR.ByteString.Rep) s fs m+    .  ( Predicate enc Text, BR.Text.Encode enc, Predicate rep B.ByteString+       , MonadError Error m, Traversable (HFunctorList fs) )+    => Patch s fs Text+    -> m (Patch s fs (AsByteString rep))+binTextifyPatch p = liftEither $ do+    pTextEnc <- liftMapProcessError (Error . show) $ traverse (refine @enc) p+    pBs <- liftMapProcessError (Error . show) $ traverse (BR.Text.encodeToRep @rep) pTextEnc+    return pBs++-- Binrep-compare, parsing @a@ and failably converting to @b@, In many cases,+-- you may want to parse and binrep the same type -- in such cases, use 'pure'.+prep'+    :: forall a b v m+    .  ( FromJSON a, Put b, BLen b, Show b+       , FromJSON (CompareRep v a)+       , SingI v+       , Show     (CompareRep v Bytes)+       , MonadError Error m+       )+    => CPsFmt+    -> (forall s fs. Traversable (HFunctorList fs) => [Patch s fs a] -> m [Patch s fs b])+    -> Bytes+    -> m [Patch Int '[Compare.Meta v, Bin.Meta] Bytes]+prep' c fBin bs =+    case c.cPsFmtAlign of+      CAlign ->     processDecode bs+                >>= processAlign @v Simple.convertBinAlign+                >>= fBin+                >>= processBin @b+                >>= processLinearize+      CNoAlign ->     processDecode bs+                  >>= return . map Simple.convertBin+                  >>= fBin+                  >>= processBin @b+                  >>= processLinearize++asPrettyYamlHexMultiPatch+    :: forall v s a fs+    .  ( ToJSON (CompareRep v (Hex a))+       , ToJSON (Hex a)+       , ToJSON s+       , Functor (MultiPatch s v)+       )+    => (Patch s fs a -> MultiPatch s v a)+    -> [Patch s fs a]+    -> B.ByteString+asPrettyYamlHexMultiPatch f ps =+      let psSimple    = fmap f ps+          psSimpleHex = fmap (fmap Hex) psSimple+       in Yaml.Pretty.encodePretty yamlPrettyCfg psSimpleHex++-- silly stuff+yamlPrettyCfg :: Yaml.Pretty.Config+yamlPrettyCfg = Yaml.Pretty.setConfCompare cmp $ Yaml.Pretty.setConfDropNull True $ Yaml.Pretty.defConfig+  where+    cmp "data" _  = LT+    cmp _  "data" = GT+    cmp "seek" _ = LT+    cmp _ "seek" = GT+    cmp k1     k2 = Prelude.compare k1 k2
+ app/BytePatch/CLI.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE OverloadedRecordDot #-}++module BytePatch.CLI ( parse ) where++import BytePatch.Config+import Raehik.CLI.Stream++import Options.Applicative+import Control.Monad.IO.Class+import StreamPatch.Patch.Compare qualified as Compare++import Binrep.Type.Assembly qualified as BR.Asm+import Binrep.Type.ByteString qualified as BR.ByteString+import Binrep.Type.Text qualified as BR.Text++parse :: MonadIO m => m C+parse = execParserWithDefaults desc pC+  where desc = "Patch data in a stream."++pC :: Parser C+pC = C <$> pCPsFmt <*> pPathIn <*> pCCmd++pCPsFmt :: Parser CPsFmt+pCPsFmt = CPsFmt <$> pCData+                 <*> pCAlign+                 <*> pCCompareVia+                 <*> pure ()++pCCmd :: Parser CCmd+pCCmd = hsubparser $+       cmd' "patch"   descPatch   (CCmdPatch'   <$> pCCmdPatch)+    <> cmd' "compile" descCompile (CCmdCompile' <$> pCCmdCompile)+  where+    descPatch   = "Apply patchscript to a stream."+    descCompile = "\"Compile\" patchscript to a processed form."++pCCmdPatch :: Parser CCmdPatch+pCCmdPatch = CCmdPatch <$> pStreamIn <*> pStreamOut <*> pPrintBinary++pCCmdCompile :: Parser CCmdCompile+pCCmdCompile = pure CCmdCompile++pCData :: Parser CData+pCData = option (maybeReader mapper) $+       long "type"+    <> short 't'+    <> help "Patch data meaning (see docs for full help)"+    <> metavar "PATCH_TYPE"+  where mapper = \case "bin"              -> Just CDataBytes+                       "text-bin:utf8,c"  -> Just $ CDataTextBin BR.Text.UTF8 BR.ByteString.C+                       "asm:armv8thumble" -> Just $ CDataAsm BR.Asm.ArmV8ThumbLE+                       "text-plain"       -> Just CDataText+                       _                  -> Nothing++pCAlign :: Parser CAlign+pCAlign = flag CNoAlign CAlign $+       long "aligned"+    <> help "Parse alignment data"++pPrintBinary :: Parser Bool+pPrintBinary = switch $+       long "print-binary"+    <> help "Force print binary to stdout"++pCCompareVia :: Parser Compare.Via+pCCompareVia = option (maybeReader mapper) $+       long "compare"+    <> short 'c'+    <> help "Comparison strategy (equal/prefix/size/hashB3)"+    <> metavar "COMPARISON_STRATEGY"+  where mapper = \case "equal"  -> Just $ Compare.ViaEq Compare.Exact+                       "prefix" -> Just $ Compare.ViaEq Compare.PrefixOf+                       "size"   -> Just Compare.ViaSize+                       "hashB3" -> Just $ Compare.ViaDigest Compare.B3+                       _        -> Nothing+++--------------------------------------------------------------------------------++-- | Execute a 'Parser' with decent defaults.+execParserWithDefaults :: MonadIO m => String -> Parser a -> m a+execParserWithDefaults desc p = liftIO $ customExecParser+    (prefs $ showHelpOnError)+    (info (helper <*> p) (progDesc desc))++-- | Shorthand for the way I always write commands.+cmd' :: String -> String -> Parser a -> Mod CommandFields a+cmd' name desc p = command name (info p (progDesc desc))
+ app/BytePatch/Config.hs view
@@ -0,0 +1,62 @@+module BytePatch.Config where++import GHC.Generics ( Generic )+import Data.Data ( Typeable, Data )+import StreamPatch.Patch.Compare qualified as Compare++import Binrep.Type.Text qualified as BR.Text+import Binrep.Type.ByteString qualified as BR.ByteString+import Binrep.Type.Assembly qualified as BR.Asm++import Raehik.CLI.Stream++data C = C+  { cPsFmt  :: CPsFmt+  , cPsPath :: Path 'In "patchscript file"+  , cCmd    :: CCmd+  } deriving stock (Generic, Typeable, Data, Show, Eq)++data CPsFmt = CPsFmt+  { cPsFmtData    :: CData+  , cPsFmtAlign   :: CAlign+  , cPsFmtCompare :: Compare.Via+  , cPsFmtSeek    :: ()+  } deriving stock (Generic, Typeable, Data, Show, Eq)++-- | What the patch data indicates (base and target representation).+data CData+  = CDataBytes+  -- ^ Raw bytes (via hex), patched directly over a bytestring.++  | CDataTextBin BR.Text.Encoding BR.ByteString.Rep+  -- ^ Text, which is to be encoded to the given character encoding and placed+  --   in the given bytestring representation, then patched over a bytestring.++  | CDataAsm BR.Asm.Arch+  -- ^ Assembly instructions for the given architecture, which is to be+  --   assembled into machine code for the architecture, then patched over a+  --   bytestring.++  | CDataText+  -- ^ Text, which is to be patched directly over @Text@.+    deriving stock (Generic, Typeable, Data, Show, Eq)++data CCmd+  = CCmdPatch' CCmdPatch+  | CCmdCompile' CCmdCompile+    deriving stock (Generic, Typeable, Data, Show, Eq)++data CCmdPatch = CCmdPatch+  { cCmdPatchStreamIn    :: Stream 'In  "stream to patch"+  , cCmdPatchStreamOut   :: Stream 'Out "stream"+  , cCmdPatchPrintBinary :: Bool+  } deriving stock (Generic, Typeable, Data, Show, Eq)++-- TODO+data CCmdCompile = CCmdCompile+    deriving stock (Generic, Typeable, Data, Show, Eq)++data CAlign+  = CAlign   -- ^ Patch has alignment data.+  | CNoAlign -- ^ Patch does not have alignment data.+    deriving stock (Generic, Typeable, Data, Show, Eq)
− app/CLI.hs
@@ -1,66 +0,0 @@-module CLI ( parse ) where--import           Config-import           Options.Applicative-import           Control.Monad.IO.Class-import qualified StreamPatch.Patch.Binary as Bin--parse :: MonadIO m => m Config-parse = execParserWithDefaults desc pConfig-  where desc = "Patch bytestrings (TODO)."--pConfig :: Parser Config-pConfig = Config-    <$> strArgument (metavar "PATCHSCRIPT" <> help "Path to patchscript")-    <*> pCStreamPair-    <*> pCPatchFormat-    <*> pPrintBinary--pCStreamPair :: Parser CStreamPair-pCStreamPair = CStreamPair <$> pCSIn <*> pCSOut-  where-    pCSIn    = pFileArg <|> pStdin-    pCSOut   = pFileOpt <|> pure CStreamStd-    pFileArg = CStreamFile <$> strArgument (metavar "FILE" <> help "Input file")-    pFileOpt = CStreamFile <$> strOption (metavar "FILE" <> long "out-file" <> short 'o' <> help "Output file")-    pStdin   = flag' CStreamStd (long "stdin"  <> help "Use stdin")--pCPatchFormat :: Parser CPatchFormat-pCPatchFormat = CPatchFormat <$> pCPatchDataType <*> pCPatchAlign <*> pBinCfg--pCPatchDataType :: Parser CPatchDataType-pCPatchDataType = option (maybeReader mapper) $-       long "type"-    <> short 't'-    <> help "How to interpret & use patch data (binary/text/text-only)"-    <> metavar "PATCH_TYPE"-  where mapper = \case "text-only" -> Just CTextPatch-                       "bin"       -> Just CBinPatch-                       "binary"    -> Just CBinPatch-                       "text"      -> Just CTextBinPatch-                       _           -> Nothing--pCPatchAlign :: Parser CPatchAlign-pCPatchAlign = flag CNoAlignPatch CAlignPatch $-       long "aligned"-    <> help "Parse alignment data"--pBinCfg :: Parser Bin.Cfg-pBinCfg = Bin.Cfg <$> pExactMatch-  where-    pExactMatch = flag True False $-           long "match-exact"-        <> help "Expected binary data must match exactly (rather than be a prefix)"--pPrintBinary :: Parser Bool-pPrintBinary = switch $-       long "print-binary"-    <> help "Force print binary to stdout"-------------------------------------------------------------------------------------- | Execute a 'Parser' with decent defaults.-execParserWithDefaults :: MonadIO m => String -> Parser a -> m a-execParserWithDefaults desc p = liftIO $ customExecParser-    (prefs $ showHelpOnError)-    (info (helper <*> p) (progDesc desc))
− app/Config.hs
@@ -1,39 +0,0 @@-module Config where--import           GHC.Generics ( Generic )-import qualified StreamPatch.Patch.Binary as Bin--data Config = Config-  { cfgPatchscript :: FilePath-  , cfgStreamPair  :: CStreamPair-  , cfgPatchFormat :: CPatchFormat-  , cfgPrintBinary :: Bool-  } deriving (Eq, Show, Generic)--data CStreamPair = CStreamPair-  { cStreamPairIn  :: CStream-  , cStreamPairOut :: CStream-  } deriving (Eq, Show, Generic)---- | "Single file" stream.-data CStream-  = CStreamFile FilePath-  | CStreamStd-    deriving (Eq, Show, Generic)--data CPatchFormat = CPatchFormat-  { cpfDataType :: CPatchDataType-  , cpfAlign    :: CPatchAlign-  , cpfBinCfg   :: Bin.Cfg-  } deriving (Eq, Show, Generic)--data CPatchAlign-  = CAlignPatch     -- ^ Patch has alignment data.-  | CNoAlignPatch   -- ^ Patch does not have alignment data.-    deriving (Eq, Show, Generic)--data CPatchDataType-  = CTextPatch      -- ^ Patch 'Text' over a 'Text' stream.-  | CBinPatch       -- ^ Patch bytestrings over a bytestream.-  | CTextBinPatch   -- ^ Patch 'Text' over a bytestream.-    deriving (Eq, Show, Generic)
app/Main.hs view
@@ -1,190 +1,6 @@-{-# LANGUAGE AllowAmbiguousTypes #-}--module Main ( main ) where--import           Config-import qualified CLI--import           StreamPatch.Patch-import qualified StreamPatch.Patch.Align  as Align-import qualified StreamPatch.Patch.Linearize as Linear-import qualified StreamPatch.Patch.Binary as Bin-import           StreamPatch.Patch.Binary ( BinRep )-import qualified StreamPatch.Apply as Apply-import           BytePatch as BP-import qualified BytePatch.HexByteString as HexBS-import           BytePatch.HexByteString ( HexByteString )--import qualified System.Exit as System-import           Numeric.Natural-import           Data.Vinyl-import           Data.Vinyl.TypeLevel-import           Data.Functor.Const-import           Data.Either.Combinators ( mapLeft )-import           Control.Monad.IO.Class-import           Control.Monad.Reader-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BL-import qualified Data.Yaml as Yaml-import           Data.Yaml ( FromJSON )-import qualified Data.Text as Text-import qualified Data.Text.Encoding as Text-import           Data.Text ( Text )-import           Optics-import           Data.Generics.Product.Any--type Bytes = BS.ByteString+module Main where -data Error-  = ErrorYaml     Yaml.ParseException-  | ErrorAlign    String-  | ErrorBin      String-  | ErrorLinear   String-  | ErrorBinPatch String-  | Error         String-    deriving (Show)+import BytePatch qualified  main :: IO ()-main = CLI.parse >>= runReaderT run--run :: forall m. (MonadIO m, MonadReader Config m) => m ()-run = readPatchscript >>= processPatchScript >>= writePatched-  where-    readPatchscript = asks cfgPatchscript >>= readStream . CStreamFile--    processPatchScript psBs = do-        cpf <- asks cfgPatchFormat-        case cpfDataType cpf of-          CTextPatch    -> processText psBs-          CBinPatch     -> processBin' @HexByteString psBs-          CTextBinPatch -> processBin' @Text          psBs--    processText :: Bytes -> m Bytes-    processText psBs = do-        cpf <- asks cfgPatchFormat-        case processAny @Text psBs (cpfAlign cpf) of-          Left err -> quit err-          Right ps -> patchTextAwkward ps--    processBin' :: forall a. (BinRep a, FromJSON a, Show a) => Bytes -> m Bytes-    processBin' psBs = do-        cpf <- asks cfgPatchFormat-        case processBin @a psBs (cpfAlign cpf) of-          Left err -> quit err-          Right ps -> patchBin ps--    patchBin ps = do-        bsIn <- asks (cStreamPairIn . cfgStreamPair) >>= readStream-        binCfg <- asks $ cpfBinCfg . cfgPatchFormat-        case Apply.runPureFwdBin binCfg ps bsIn of-          Left err -> quit $ ErrorBinPatch (showErrorBin err)-          Right bsOut -> return $ BL.toStrict bsOut--    patchTextAwkward :: [Patch 'FwdSeek '[] Text] -> m Bytes-    patchTextAwkward ps = do-        bsIn <- asks (cStreamPairIn . cfgStreamPair) >>= readStream-        let psStr  = map (fmap Text.unpack) ps-            strIn  = Text.unpack $ Text.decodeUtf8 bsIn-            strOut = Apply.runPureSimpleFwdList psStr strIn-            bsOut  = Text.encodeUtf8 $ Text.pack strOut-        return bsOut--    writePatched bs =-        asks (cStreamPairOut . cfgStreamPair) >>= \case-          CStreamFile fp -> liftIO $ BS.writeFile fp bs-          CStreamStd     -> asks (cpfDataType . cfgPatchFormat) >>= \case-            CTextPatch -> liftIO $ BS.putStr bs-            _          -> asks cfgPrintBinary >>= \case-              True  -> liftIO $ BS.putStr bs-              False -> liftIO $ logWarn $-                     "refusing to print binary to stdout:"-                  <> " write to a file with --out-file FILE"-                  <> " or use --print-binary flag to override"--logWarn :: MonadIO m => String -> m ()-logWarn msg = liftIO $ putStrLn $ "bytepatch: warning: " <> msg---- | Parses your bytes into some binreppable patch form (aligned or not,---   depending on flag), converts it to the internal patch form, binreps it, and---   linearizes. The binrep type is consumed inside the function, making it---   ambiguous, so you need to pass it a type via @processBin \@Text@.------ Note that we linearize *after* binrepping. This is important -- it means the--- seeks are byte offsets, not using the pre-binrepped type.-processBin-    :: forall a. (BinRep a, FromJSON a, Show a) => Bytes -> CPatchAlign-    -> Either Error [Patch 'FwdSeek '[Bin.MetaStream] Bytes]-processBin bs = \case-  CAlignPatch   ->     mapLeft ErrorYaml (Yaml.decodeEither' @[Aligned (MultiPatch 'RelSeek a)] bs)-                   >>= mapLeft (ErrorAlign . show) . fmap concat . traverse (wrapAlign (BP.convertBinAlign @'AbsSeek))-                   >>= mapLeft (ErrorBin . showErrorBin) . traverse Bin.patchBinRep-                   >>= mapLeft (ErrorLinear . show) . Linear.linearize-  CNoAlignPatch ->     mapLeft ErrorYaml (Yaml.decodeEither' @[MultiPatch 'AbsSeek a] bs)-                   >>= mapLeft (ErrorBin . showErrorBin) . traverse Bin.patchBinRep . concat . map BP.convertBin-                   >>= mapLeft (ErrorLinear . show) . Linear.linearize---- | Like 'processBin' but no binrepping. Similarly beautiful and ambiguous.-processAny-    :: forall a. (Show a, FromJSON a, Linear.HasLength a) => Bytes -> CPatchAlign-    -> Either Error [Patch 'FwdSeek '[] a]-processAny bs = \case-  CAlignPatch   ->     mapLeft ErrorYaml (Yaml.decodeEither' @[Aligned (MultiPatch 'RelSeek a)] bs)-                   >>= mapLeft (ErrorAlign . show) . fmap concat . traverse (wrapAlign (BP.convertAlign @'AbsSeek))-                   >>= mapLeft (ErrorLinear . show) . Linear.linearize-  CNoAlignPatch ->     mapLeft ErrorYaml (Yaml.decodeEither' @[MultiPatch 'AbsSeek a] bs)-                   >>= mapLeft (ErrorLinear . show) . Linear.linearize . concat . map BP.convertEmpty--wrapAlign-    :: ( SeekRep s ~ Natural-       , r ~ Const (Align.Meta s)-       , rs ~ RDelete r ss-       , RElem r ss i-       , RSubset rs ss is )-    => (MultiPatch 'RelSeek a -> [Patch 'RelSeek ss a])-    -> Aligned (MultiPatch 'RelSeek a)-    -> Either (Align.Error s) [Patch s rs a]-wrapAlign f = align . over (the @"alignedPatches") (concat . map f)--quit :: MonadIO m => Error -> m a-quit err = do-    liftIO $ putStrLn $ "bytepatch: error: " <> errStr-    liftIO $ System.exitWith $ System.ExitFailure errExitCode-  where-    (errExitCode, errStr) = case err of-      ErrorYaml     e -> (1, "while parsing YAML: " <> show e)-      ErrorAlign    e -> (2, "while aligning: "     <> e)-      ErrorBin      e -> (3, "while converting to binary representation: " <> e)-      ErrorLinear   e -> (4, "while linearizing: " <> e)-      ErrorBinPatch e -> (5, "while applying binaryized patch: " <> e)-      Error         e -> (10, e)--readStream :: MonadIO m => CStream -> m Bytes-readStream s = liftIO $-    case s of-      CStreamStd     -> BS.getContents-      CStreamFile fp -> BS.readFile fp---- stupid but how else do I do this without pluggin HexByteString into Binary--- guess I just gotta write ACTUAL error handling. ffs-showErrorBin :: Show a => Bin.Error a -> String-showErrorBin = \case-  Bin.ErrorBadBinRep a s -> "ErrorBadBinRep " <> show a <> " " <> s-  Bin.ErrorUnexpectedNonNull bs -> "ErrorUnexpectedNonNull " <> go bs-  Bin.ErrorDidNotMatchExpected bs1 bs2 -> "ErrorDidNotMatchExpected " <> go bs1 <> " " <> go bs2-  Bin.ErrorBinRepTooLong bs n -> "ErrorBinRepTooLong " <> go bs <> " " <> show n-  where go bs = "\"" <> Text.unpack (HexBS.prettyHexByteString bs) <> "\""--{---writeStream :: MonadIO m => Bytes -> CStream -> m ()-writeStream bs s = liftIO $-    case s of-      CStreamStd     -> BS.putStr bs-      CStreamFile fp -> BS.writeFile fp bs--tryDecodeYaml-    :: forall a m. (FromJSON a, MonadIO m)-    => FilePath -> m (Either Yaml.ParseException a)-tryDecodeYaml fp = Yaml.decodeEither' <$> liftIO (BS.readFile fp)---}+main = BytePatch.main
+ app/Raehik/CLI/Stream.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE AllowAmbiguousTypes #-}++module Raehik.CLI.Stream where++import GHC.Generics ( Generic )+import Data.Data ( Typeable, Data )+import GHC.TypeLits ( Symbol, KnownSymbol, symbolVal' )+import GHC.Exts ( proxy#, Proxy# )++import Options.Applicative++import Data.Char qualified as Char++data Stream (d :: Direction) (s :: Symbol)+  = Path' (Path d s)+  | Std+    deriving stock (Generic, Typeable, Data, Show, Eq)++newtype Path (d :: Direction) (s :: Symbol)+  = Path { unPath :: FilePath }+    deriving stock (Generic, Typeable, Data)+    deriving (Show, Eq) via FilePath++data Direction = In | Out+    deriving stock (Generic, Typeable, Data, Show, Eq)++-- | Either a positional filepath, or standalone @--stdin@ switch.+pStreamIn :: forall s. KnownSymbol s => Parser (Stream 'In s)+pStreamIn = (Path' <$> pPathIn) <|> pStdinOpt+  where+    pStdinOpt = flag' Std $  long "stdin"+                          <> help ("Get "<>sym @s<>" from stdin")++-- | Either an @--out-file X@ option, or default to stdout.+pStreamOut :: forall s. KnownSymbol s => Parser (Stream 'Out s)+pStreamOut = (Path' <$> pPathOut) <|> pure Std+  where pPathOut = Path <$> strOption (modFileOut (sym @s))++-- | Positional filepath.+pPathIn :: forall s. KnownSymbol s => Parser (Path 'In s)+pPathIn = Path <$> strArgument (modFileIn (sym @s))++--------------------------------------------------------------------------------++-- | Generate a base 'Mod' for a file type using the given descriptive+--   name (the "type" of input, e.g. file format) and the given direction.+modFile :: HasMetavar f => String -> String -> Mod f a+modFile dir desc =  metavar "FILE" <> help (dir<>" "<>desc)++modFileIn :: HasMetavar f => String -> Mod f a+modFileIn = modFile "Input"++modFileOut :: (HasMetavar f, HasName f) => String -> Mod f a+modFileOut s = modFile "Output" s <> long "out-file" <> short 'o'++metavarify :: String -> String+metavarify = map $ Char.toUpper . spaceToUnderscore+  where spaceToUnderscore = \case ' ' -> '_'; ch -> ch++--------------------------------------------------------------------------------++-- | More succint 'symbolVal' via type application.+sym :: forall s. KnownSymbol s => String+sym = symbolVal' (proxy# :: Proxy# s)
bytepatch.cabal view
@@ -1,12 +1,12 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.5.+-- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack  name:           bytepatch-version:        0.3.1-synopsis:       Patch byte-representable data in a bytestream.+version:        0.4.0+synopsis:       Patch byte-representable data in a bytestream description:    Please see README.md. category:       CLI homepage:       https://github.com/raehik/bytepatch#readme@@ -17,7 +17,7 @@ license-file:   LICENSE build-type:     Simple tested-with:-    GHC >= 8.10 && < 9.2+    GHC ==9.2.3 extra-source-files:     README.md     CHANGELOG.md@@ -28,14 +28,23 @@  library   exposed-modules:-      BytePatch-      BytePatch.HexByteString+      Binrep.Type.Assembly+      Binrep.Type.Assembly.Assemble+      Binrep.Type.Assembly.Disassemble+      Raehik.HFunctorMap       StreamPatch.Apply+      StreamPatch.Example+      StreamPatch.HFunctorList       StreamPatch.Patch       StreamPatch.Patch.Align       StreamPatch.Patch.Binary-      StreamPatch.Patch.Binary.PascalText-      StreamPatch.Patch.Linearize+      StreamPatch.Patch.Compare+      StreamPatch.Patch.Compile+      StreamPatch.Patch.Linearize.Common+      StreamPatch.Patch.Linearize.InPlace+      StreamPatch.Patch.Linearize.Insert+      StreamPatch.Seek+      StreamPatch.Simple       StreamPatch.Stream       StreamPatch.Util   other-modules:@@ -44,99 +53,130 @@       src   default-extensions:       EmptyCase-      FlexibleContexts-      FlexibleInstances-      InstanceSigs-      MultiParamTypeClasses       LambdaCase+      InstanceSigs+      BangPatterns+      ExplicitNamespaces+      DerivingStrategies+      DerivingVia+      StandaloneDeriving+      DeriveAnyClass       DeriveGeneric-      DeriveFoldable+      DeriveDataTypeable       DeriveFunctor-      DeriveGeneric-      DeriveLift+      DeriveFoldable       DeriveTraversable-      DerivingStrategies-      GeneralizedNewtypeDeriving-      StandaloneDeriving-      BangPatterns-      KindSignatures-      TypeOperators+      DeriveLift+      FlexibleContexts+      FlexibleInstances+      MultiParamTypeClasses+      GADTs+      PolyKinds+      RoleAnnotations+      RankNTypes       TypeApplications-      DataKinds+      DefaultSignatures       TypeFamilies-      UndecidableInstances+      DataKinds+      MagicHash+      ImportQualifiedPost       StandaloneKindSignatures-      RankNTypes       ScopedTypeVariables+      TypeOperators+  ghc-options: -Wall   build-depends:-      aeson >=1.5 && <2.1-    , base >=4.12 && <4.17+      aeson >=2.0.2.0 && <2.1+    , base >=4.12 && <5+    , binrep+    , blake3 ==0.2.*     , bytestring >=0.10 && <0.12     , either >=5.0.1.1 && <5.1-    , generic-optics >=2.1 && <2.4+    , generic-optics >=2.2.1.0 && <2.3+    , heystone >=0.1.0 && <0.2     , megaparsec >=9.0 && <9.3+    , memory ==0.17.*     , mtl >=2.2.2 && <2.3     , optics >=0.3 && <0.5+    , singletons ==3.0.*+    , singletons-base ==3.1.*+    , singletons-th ==3.1.*     , text >=1.2.4.1 && <1.3-    , vinyl >=0.13.3 && <0.14+    , text-short >=0.1.5 && <0.2+    , vinyl >=0.14.1 && <0.15   default-language: Haskell2010  executable bytepatch   main-is: Main.hs   other-modules:-      CLI-      Config+      BytePatch+      BytePatch.CLI+      BytePatch.Config+      Raehik.CLI.Stream       Paths_bytepatch   hs-source-dirs:       app   default-extensions:       EmptyCase-      FlexibleContexts-      FlexibleInstances-      InstanceSigs-      MultiParamTypeClasses       LambdaCase+      InstanceSigs+      BangPatterns+      ExplicitNamespaces+      DerivingStrategies+      DerivingVia+      StandaloneDeriving+      DeriveAnyClass       DeriveGeneric-      DeriveFoldable+      DeriveDataTypeable       DeriveFunctor-      DeriveGeneric-      DeriveLift+      DeriveFoldable       DeriveTraversable-      DerivingStrategies-      GeneralizedNewtypeDeriving-      StandaloneDeriving-      BangPatterns-      KindSignatures-      TypeOperators+      DeriveLift+      FlexibleContexts+      FlexibleInstances+      MultiParamTypeClasses+      GADTs+      PolyKinds+      RoleAnnotations+      RankNTypes       TypeApplications-      DataKinds+      DefaultSignatures       TypeFamilies-      UndecidableInstances+      DataKinds+      MagicHash+      ImportQualifiedPost       StandaloneKindSignatures-      RankNTypes       ScopedTypeVariables+      TypeOperators   ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N   build-depends:-      aeson >=1.5 && <2.1-    , base >=4.12 && <4.17+      aeson >=2.0.2.0 && <2.1+    , base >=4.12 && <5+    , binrep+    , blake3 ==0.2.*     , bytepatch     , bytestring >=0.10 && <0.12     , either >=5.0.1.1 && <5.1-    , generic-optics >=2.1 && <2.4+    , generic-optics >=2.2.1.0 && <2.3+    , heystone >=0.1.0 && <0.2     , megaparsec >=9.0 && <9.3+    , memory ==0.17.*     , mtl >=2.2.2 && <2.3     , optics >=0.3 && <0.5-    , optparse-applicative >=0.16.1 && <0.17+    , optparse-applicative ==0.17.*+    , refined ==0.7.*+    , singletons ==3.0.*+    , singletons-base ==3.1.*+    , singletons-th ==3.1.*     , text >=1.2.4.1 && <1.3-    , vinyl >=0.13.3 && <0.14-    , yaml >=0.11.7 && <0.12+    , text-short >=0.1.5 && <0.2+    , vinyl >=0.14.1 && <0.15+    , yaml >=0.11.6.0 && <0.12   default-language: Haskell2010  test-suite spec   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:-      BytePatch.HexByteStringSpec       StreamPatch.ApplySpec       StreamPatch.Patch.AlignSpec       StreamPatch.Patch.LinearizeSpec@@ -146,44 +186,59 @@       test   default-extensions:       EmptyCase-      FlexibleContexts-      FlexibleInstances-      InstanceSigs-      MultiParamTypeClasses       LambdaCase+      InstanceSigs+      BangPatterns+      ExplicitNamespaces+      DerivingStrategies+      DerivingVia+      StandaloneDeriving+      DeriveAnyClass       DeriveGeneric-      DeriveFoldable+      DeriveDataTypeable       DeriveFunctor-      DeriveGeneric-      DeriveLift+      DeriveFoldable       DeriveTraversable-      DerivingStrategies-      GeneralizedNewtypeDeriving-      StandaloneDeriving-      BangPatterns-      KindSignatures-      TypeOperators+      DeriveLift+      FlexibleContexts+      FlexibleInstances+      MultiParamTypeClasses+      GADTs+      PolyKinds+      RoleAnnotations+      RankNTypes       TypeApplications-      DataKinds+      DefaultSignatures       TypeFamilies-      UndecidableInstances+      DataKinds+      MagicHash+      ImportQualifiedPost       StandaloneKindSignatures-      RankNTypes       ScopedTypeVariables+      TypeOperators+  ghc-options: -Wall   build-tool-depends:       hspec-discover:hspec-discover >=2.7 && <2.10   build-depends:       QuickCheck >=2.14.2 && <2.15-    , aeson >=1.5 && <2.1-    , base >=4.12 && <4.17+    , aeson >=2.0.2.0 && <2.1+    , base >=4.12 && <5+    , binrep+    , blake3 ==0.2.*     , bytepatch     , bytestring >=0.10 && <0.12     , either >=5.0.1.1 && <5.1-    , generic-optics >=2.1 && <2.4+    , generic-optics >=2.2.1.0 && <2.3+    , heystone >=0.1.0 && <0.2     , hspec >=2.7 && <2.10     , megaparsec >=9.0 && <9.3+    , memory ==0.17.*     , mtl >=2.2.2 && <2.3     , optics >=0.3 && <0.5+    , singletons ==3.0.*+    , singletons-base ==3.1.*+    , singletons-th ==3.1.*     , text >=1.2.4.1 && <1.3-    , vinyl >=0.13.3 && <0.14+    , text-short >=0.1.5 && <0.2+    , vinyl >=0.14.1 && <0.15   default-language: Haskell2010
+ src/Binrep/Type/Assembly.hs view
@@ -0,0 +1,31 @@+module Binrep.Type.Assembly where++import GHC.Generics ( Generic )+import Data.Data ( Typeable, Data )++import Data.ByteString.Short ( ShortByteString )+import Data.ByteString ( ByteString )+import Data.Text.Short ( ShortText )+import Data.Aeson ( ToJSON, FromJSON )++import Binrep.Extra.HexByteString++import Binrep++data Arch+  = ArmV8ThumbLE+    deriving stock (Generic, Typeable, Data, Show, Eq)++newtype MachineInstr (arch :: Arch) = MachineInstr { getMachineInstr :: ShortByteString }+    deriving stock (Generic, Typeable, Data)+    deriving Show via (Hex ShortByteString)++newtype MachineCode  (arch :: Arch) = MachineCode  { getMachineCode  :: ByteString }+    deriving stock (Generic, Typeable, Data)+    deriving Eq via ByteString+    deriving Show via (Hex ByteString)+    deriving (BLen, Put, Get) via ByteString++newtype AsmInstr     (arch :: Arch) = AsmInstr     { getAsmInstr     :: ShortText }+    deriving stock (Generic, Typeable, Data, Show, Eq)+    deriving (ToJSON, FromJSON) via ShortText
+ src/Binrep/Type/Assembly/Assemble.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE OverloadedStrings #-}++module Binrep.Type.Assembly.Assemble where++import Binrep.Type.Assembly++import Heystone qualified as Keystone+import System.IO.Unsafe ( unsafeDupablePerformIO )+import Control.Monad.IO.Class+import Data.ByteString qualified as BS+import Data.Text.Short qualified as Text.Short+import Data.Text qualified as Text+import Data.List qualified as List++class Assemble arch where+    assemble :: [AsmInstr arch] -> Either String (MachineCode arch)++assemble1+    :: forall arch. Assemble arch+    => AsmInstr arch -> Either String (MachineCode arch)+assemble1 inst = assemble [inst]++instance Assemble 'ArmV8ThumbLE where+    assemble =+          fmap MachineCode+        . unsafeDupablePerformIO+        . assemble' Keystone.ArchArm modeFlags+        . prepInstrs+      where+        modeFlags =+            [Keystone.ModeV8, Keystone.ModeThumb, Keystone.ModeLittleEndian]++-- | TODO This is stupid because the assembler takes a '[String]'. Great for+--   end-user, poor for performance. I want the option to give it a+--   'BS.ByteString' that I've already prepared (as is the interface).+prepInstrs :: forall arch. [AsmInstr arch] -> [String]+prepInstrs =+      List.singleton+    . Text.unpack+    . Text.intercalate (Text.pack ";")+    . map (Text.Short.toText . getAsmInstr)++-- | Ideally, the assembler takes a raw 'BS.ByteString'. A+--   'BS.Short.ShortByteString' isn't appropriate, because it could be quite+--   large. But this way, this function is basically "compose a bunch of short+--   bytestrings into one big one".+prepInstrs' :: forall arch. [AsmInstr arch] -> BS.ByteString+prepInstrs' =+      BS.intercalate ";"+    . map (Text.Short.toByteString . getAsmInstr)++assemble'+    :: MonadIO m+    => Keystone.Architecture -> [Keystone.Mode]+    -> [String]+    -> m (Either String BS.ByteString)+assemble' arch modes instrs = do+    let as' = Keystone.open arch modes+    liftIO (Keystone.runAssembler as') >>= \case+      Left  e  -> err $ "failed to obtain assembler: "<>show e+      Right as -> do+        let out' = Keystone.assemble as instrs Nothing+        -- TODO have to inspect engine to find error. probably say if x=1 OK, if+        -- x>1 weird error, if x<1 check errno->strerror+        liftIO (Keystone.runAssembler out') >>= \case+          Left e -> err $ "error while assembling: "<>show e+          Right (mc, _count) -> return $ Right mc+  where err = return . Left
+ src/Binrep/Type/Assembly/Disassemble.hs view
@@ -0,0 +1,8 @@+module Binrep.Type.Assembly.Disassemble where++import Binrep.Type.Assembly++import Numeric.Natural ( Natural )++class Disassemble arch where+    disassemble :: Natural -> (MachineCode arch) -> Either String [AsmInstr arch]
− src/BytePatch.hs
@@ -1,133 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE OverloadedStrings #-}--module BytePatch where--import           StreamPatch.Patch-import qualified StreamPatch.Patch.Binary as Bin-import qualified StreamPatch.Patch.Align  as Align--import           Numeric.Natural-import           Data.Functor.Const-import           Data.Vinyl-import           Data.Vinyl.TypeLevel-import           Data.Aeson-import           GHC.Generics ( Generic )--data MultiPatch s a = MultiPatch-  { mpData :: a-  , mpAt :: [Seek s a]-  } deriving (Generic)--deriving instance (Eq   a, Eq   (SeekRep s)) => Eq   (MultiPatch s a)-deriving instance (Show a, Show (SeekRep s)) => Show (MultiPatch s a)--data Seek s a = Seek-  { sSeek :: SeekRep s-  , sExpected :: Maybe a-  , sNullTerminates :: Maybe (SeekRep 'FwdSeek)-  , sMaxBytes :: Maybe (SeekRep 'FwdSeek) -- TODO confirm meaning, maybe improve name-  , sAligned :: Maybe Natural-  } deriving (Generic)--deriving instance (Eq   a, Eq   (SeekRep s)) => Eq   (Seek s a)-deriving instance (Show a, Show (SeekRep s)) => Show (Seek s a)---- TODO write a Text newtype for Haskell-style negated integers -- allowing--- negative hex integers!-data Aligned p = Aligned-  { alignedAlign   :: SeekRep 'RelSeek-  , alignedPatches :: [p]-  } deriving (Eq, Show, Generic)------------------------------------------------------------------------------------jsonCfgCamelDrop :: Int -> Options-jsonCfgCamelDrop x = defaultOptions-  { fieldLabelModifier = camelTo2 '_' . drop x-  , rejectUnknownFields = False }--instance (ToJSON   (SeekRep s), ToJSON   a) => ToJSON   (MultiPatch s a) where-    toJSON     = genericToJSON     $ jsonCfgCamelDrop 2-    toEncoding = genericToEncoding $ jsonCfgCamelDrop 2-instance (FromJSON (SeekRep s), FromJSON a) => FromJSON (MultiPatch s a) where-    parseJSON  = genericParseJSON  $ jsonCfgCamelDrop 2--instance (ToJSON   (SeekRep s), ToJSON   a) => ToJSON   (Seek s a) where-    toJSON     = genericToJSON     $ jsonCfgCamelDrop 1-    toEncoding = genericToEncoding $ jsonCfgCamelDrop 1-instance (FromJSON (SeekRep s), FromJSON a) => FromJSON (Seek s a) where-    parseJSON  = genericParseJSON  $ jsonCfgCamelDrop 1--instance (ToJSON   p) => ToJSON   (Aligned p) where-    toJSON     = genericToJSON     $ jsonCfgCamelDrop 7-    toEncoding = genericToEncoding $ jsonCfgCamelDrop 7-instance (FromJSON p) => FromJSON (Aligned p) where-    parseJSON  = genericParseJSON  $ jsonCfgCamelDrop 7------------------------------------------------------------------------------------convert :: (Seek s a -> Rec (Flap a) fs) -> MultiPatch s a -> [Patch s fs a]-convert f p = map go (mpAt p)-  where go s = Patch { patchData = mpData p-                     , patchSeek = sSeek s-                     , patchMeta = FunctorRec $ f s }---- TODO how to clean up (it's like liftA2 over n instead of 2. like a fold)--- likely 'ap'!-convertBinAlign-    :: forall s a-    .  SeekRep s ~ Natural-    => MultiPatch 'RelSeek a-    -> [Patch 'RelSeek '[Const (Align.Meta s), Const Bin.Meta, Bin.MetaStream] a]-convertBinAlign p = convert go p-  where go s = cmAlign s <+> cmBin s <+> cmBinStream s--convertBin-    :: forall s a-    .  MultiPatch s a-    -> [Patch s '[Const Bin.Meta, Bin.MetaStream] a]-convertBin p = convert go p-  where go s = cmBin s <+> cmBinStream s--convertAlign-    :: forall s a-    .  SeekRep s ~ Natural-    => MultiPatch 'RelSeek a-    -> [Patch 'RelSeek '[Const (Align.Meta s)] a]-convertAlign p = convert cmAlign p--convertEmpty-    :: forall s a-    .  MultiPatch s a-    -> [Patch s '[] a]-convertEmpty p = convert (const RNil) p------------------------------------------------------------------------------------align-    :: forall s a ss rs is i r-    .  ( SeekRep s ~ Natural-       , r ~ Const (Align.Meta s)-       , rs ~ RDelete r ss-       , RElem r ss i-       , RSubset rs ss is )-    => Aligned (Patch 'RelSeek ss a)-    -> Either (Align.Error s) [Patch s rs a]-align (Aligned a ps) = traverse (Align.align a) ps------------------------------------------------------------------------------------cmBin :: Seek s a -> Rec (Flap a) '[Const Bin.Meta]-cmBin s = cm $ Const $ Bin.Meta { Bin.mMaxBytes = sMaxBytes s }--cmBinStream :: Seek s a -> Rec (Flap a) '[Bin.MetaStream]-cmBinStream s = cm $ Bin.MetaStream-    { Bin.msNullTerminates = sNullTerminates s-    , Bin.msExpected = sExpected s }--cmAlign :: SeekRep s ~ Natural => Seek 'RelSeek a -> Rec (Flap a) '[Const (Align.Meta s)]-cmAlign s = cm $ Const $ Align.Meta { Align.mExpected = sAligned s }--cm :: f a -> Rec (Flap a) '[f]-cm fa = Flap fa :& RNil
− src/BytePatch/HexByteString.hs
@@ -1,68 +0,0 @@-{- |-A 'BS.ByteString' newtype wrapper indicating a human-readable bytestring, to be-displayed in hex form (e.g. 00 12 AB FF).--}--module BytePatch.HexByteString-  ( HexByteString(..)-  , parseHexByteString-  , prettyHexByteString-  ) where--import           StreamPatch.Patch.Binary ( BinRep(..) )--import           Data.Void-import           Text.Megaparsec-import qualified Text.Megaparsec.Char       as MC-import qualified Data.ByteString            as BS-import qualified Data.Char                  as Char-import           Data.Word-import qualified Data.Text                  as Text-import           Data.Text                  ( Text )-import           Data.List                  as List-import           Data.Aeson--newtype HexByteString = HexByteString { unHexByteString :: BS.ByteString }-    deriving (Eq)--instance Show HexByteString where-    show = Text.unpack . prettyHexByteString . unHexByteString--instance BinRep HexByteString where-    toBinRep = Right . unHexByteString--instance FromJSON HexByteString where-    parseJSON = withText "hex bytestring" $ \t ->-        case parseMaybe @Void parseHexByteString t of-          Nothing -> fail "failed to parse hex bytestring (TODO)"-          Just t' -> pure (HexByteString t')-instance ToJSON   HexByteString where-    toJSON = String . prettyHexByteString . unHexByteString---- | A hex bytestring looks like this: @00 01 89 8a   FEff@. You can mix and--- match capitalization and spacing, but I prefer to space each byte, full caps.-parseHexByteString :: (MonadParsec e s m, Token s ~ Char) => m BS.ByteString-parseHexByteString = BS.pack <$> parseHexByte `sepBy` MC.hspace---- | Parse a byte formatted as two hex digits e.g. EF. You _must_ provide both--- nibbles e.g. @0F@, not @F@. They cannot be spaced e.g. @E F@ is invalid.------ Returns a value 0-255, so can fit in any Num type that can store that.-parseHexByte :: (MonadParsec e s m, Token s ~ Char, Num a) => m a-parseHexByte = do-    c1 <- MC.hexDigitChar-    c2 <- MC.hexDigitChar-    return $ 0x10 * fromIntegral (Char.digitToInt c1) + fromIntegral (Char.digitToInt c2)--prettyHexByteString :: BS.ByteString -> Text-prettyHexByteString =-    Text.concat . List.intersperse (Text.singleton ' ') . fmap (f . prettyHexByte) . BS.unpack-  where-    f :: (Char, Char) -> Text-    f (c1, c2) = Text.cons c1 $ Text.singleton c2--prettyHexByte :: Word8 -> (Char, Char)-prettyHexByte w = (prettyNibble h, prettyNibble l)-  where-    (h,l) = fromIntegral w `divMod` 0x10-    prettyNibble = Char.toUpper . Char.intToDigit
+ src/Raehik/HFunctorMap.hs view
@@ -0,0 +1,187 @@+{-# LANGUAGE AllowAmbiguousTypes, UndecidableInstances #-}++module Raehik.HFunctorMap where++import Data.Kind+import GHC.TypeLits+import Data.Vinyl+import Data.Vinyl.TypeLevel+import GHC.Generics ( Generic )+import Data.Aeson+import GHC.Exts+import Data.Aeson.KeyMap qualified as KM+import Data.Aeson.Key qualified as K++-- labelled flipped application+type LFlap :: k -> (Symbol, k -> Type) -> Type+newtype LFlap a fl = LFlap { getLFlap :: (Snd fl) a }+    deriving stock (Generic)+deriving stock    instance Show   (Snd fl a) => Show   (LFlap a fl)+deriving anyclass instance ToJSON (Snd fl a) => ToJSON (LFlap a fl)++{-+newtype Flap a f = Flap { getFlap :: f a }+    deriving stock   (Generic, Show, Eq, Ord)+    deriving Storable via (f a)+    deriving (ToJSON, FromJSON) via (f a)+-}++lFlap :: forall l a f. f a -> LFlap a '(l, f)+lFlap = LFlap++-- labelled functor list+newtype LFunctorList fs a = LFunctorList { getLFunctorList :: Rec (LFlap a) fs }+    deriving stock (Generic)+deriving stock    instance+    (ReifyConstraint Show (LFlap a) fs, RMap fs, RecordToList fs)+      => Show (LFunctorList fs a)++instance ToJSON (LFunctorList '[] a) where+    toJSON (LFunctorList RNil) = Object mempty+instance (ToJSON (f a), KnownSymbol l, ToJSON (LFunctorList fs a)) => ToJSON (LFunctorList ('(l, f) ': fs) a) where+    toJSON (LFunctorList (LFlap fa :& fs)) =+        let Object os = toJSON $ LFunctorList fs+            label = symbolVal'' @l+            o = toJSON fa+        in  Object $ KM.insert (K.fromString label) o os++symbolVal'' :: forall l. KnownSymbol l => String+symbolVal'' = symbolVal' (proxy# :: Proxy# l)++-- use with visible type applications+lflgetf+    :: forall (l :: Symbol) f fs a+    .  ( HasField Rec l fs fs f f )+    => LFunctorList fs a -> f a+lflgetf = getLFlap . rget @(l ::: f) . getLFunctorList++{-++import Data.Vinyl+import Data.Vinyl.TypeLevel ( RDelete, RIndex )+import Control.Applicative  ( liftA2 )+import GHC.Generics         ( Generic, Rep )+import Foreign.Storable     ( Storable )++import Optics++import Data.Aeson++instance ( ToJSON (Flap a r), Generic (Rec (Flap a) rs)+         , GToJSON' Value Zero (Rep (Rec (Flap a) rs))+         , GToJSON' Encoding Zero (Rep (Rec (Flap a) rs))+         ) => ToJSON (Rec (Flap a) (r ': rs))++-- | A list of functors parametric over a "shared" 'a', where each functor+--   stores a single value 'f a'.+--+-- Just a wrapper on top of Vinyl with some types swap around.+newtype HFunctorList fs a = HFunctorList { getHFunctorList :: Rec (Flap a) fs }+    deriving stock (Generic)++deriving via (Rec (Flap a) fs) instance ToJSON (Rec (Flap a) fs) => ToJSON (HFunctorList fs a)++deriving instance (ReifyConstraint Show (Flap a) fs, RMap fs, RecordToList fs) => Show (HFunctorList fs a)+deriving instance Eq        (Rec (Flap a) fs) => Eq        (HFunctorList fs a)+deriving instance Ord       (Rec (Flap a) fs) => Ord       (HFunctorList fs a)++-- Right. I only partly get this. As I understand, I'm leveraging deriving via+-- to generate the instance bodies, since they look the same as Rec but with a+-- set functor. So I just have to assure it that you can make it Storable in the+-- same way, given that @Flap a@ is storable (which is handled similarly at its+-- own definition).+deriving via (Rec (Flap a) '[])       instance Storable (HFunctorList '[] a)+deriving via (Rec (Flap a) (f ': fs)) instance (Storable (f a), Storable (Rec (Flap a) fs)) => Storable (HFunctorList (f ': fs) a)++-- It appears we can't do the same for 'Functor' etc., because the @a@ type+-- variable isn't bound, but must be for us to say what type to derive via. I+-- wonder if there is a workaround, but I can't figure it out.+instance Functor (HFunctorList '[]) where+  fmap _ (HFunctorList RNil) = HFunctorList RNil+instance (Functor r, Functor (HFunctorList rs)) => Functor (HFunctorList (r ': rs)) where+  fmap f (HFunctorList (Flap r :& rs)) =+    HFunctorList (Flap (fmap f r) :& getHFunctorList (fmap f (HFunctorList rs)))++instance Applicative (HFunctorList '[]) where+  pure _ = HFunctorList RNil+  HFunctorList RNil <*> HFunctorList RNil = HFunctorList RNil+instance (Applicative r, Applicative (HFunctorList rs)) => Applicative (HFunctorList (r ': rs)) where+  pure a = HFunctorList (Flap (pure a) :& getHFunctorList (pure a))+  HFunctorList (Flap f :& fs) <*> HFunctorList (Flap a :& as) =+    HFunctorList (Flap (f <*> a) :& getHFunctorList (HFunctorList fs <*> HFunctorList as))++instance Foldable (HFunctorList '[]) where+  foldr _ z (HFunctorList RNil) = z+instance (Foldable r, Foldable (HFunctorList rs)) => Foldable (HFunctorList (r ': rs)) where+    -- only foldmap because foldr is harder looool+    foldMap f (HFunctorList (Flap r :& rs)) = foldMap f r <> foldMap f (HFunctorList rs)++-- this took me ages because I'm stupid T_T+instance Traversable (HFunctorList '[]) where+  traverse _ (HFunctorList RNil) = pure (HFunctorList RNil)+instance (Traversable r, Traversable (HFunctorList rs)) => Traversable (HFunctorList (r ': rs)) where+  traverse+      :: forall f a b. Applicative f+      => (a -> f b)+      -> (HFunctorList (r ': rs)) a+      -> f (HFunctorList (r ': rs) b)+  traverse f (HFunctorList (Flap (r :: r a) :& rs)) =+      HFunctorList <$> rBoth+    where+      rBoth :: f (Rec (Flap b) (r ': rs))+      rBoth = liftA2 (:&) rHead rTail+      rHead :: f (Flap b r)+      rHead = Flap <$> traverse f r+      rTail :: f (Rec (Flap b) rs)+      rTail = getHFunctorList <$> traverse f (HFunctorList rs)++-- | Flipped apply: a single value at 'f a', but with "flipped" type arguments.+--   Very useless - has no Functor nor Contravariant nor HFunctor instance.+newtype Flap a f = Flap { getFlap :: f a }+    deriving stock   (Generic, Show, Eq, Ord)+    deriving Storable via (f a)+    deriving (ToJSON, FromJSON) via (f a)++--------------------------------------------------------------------------------++-- | Get the value at a type in an HFunctorList.+hflGet+    :: forall f fs a i+    .  RElem f fs i+    => HFunctorList fs a+    -> f a+hflGet = getFlap . rget . getHFunctorList++-- | Put a value at a type in an HFunctorList.+hflPut+    :: forall f f' fs fs' a+    .  RecElem Rec f f' fs fs' (RIndex f fs)+    => f' a+    -> HFunctorList fs a+    -> HFunctorList fs' a+hflPut x = HFunctorList . rput' @_ @f (Flap x) . getHFunctorList++-- | Get a lens to the value at a type in an HFunctorList.+hflLens+    :: forall f f' fs fs' a s t+    .  ( RecElem Rec f f' fs fs' (RIndex f fs)+       , RElem f fs (RIndex f fs)+       , s ~ HFunctorList fs  a+       , t ~ HFunctorList fs' a )+    => Lens s t (f a) (f' a)+hflLens = lens hflGet (\hfl x -> hflPut @f x hfl)++-- | Use the value at a type in an HFunctorList, and remove it from the list.+hflStrip+    :: forall f fs a fs' b i is+    .  ( RElem f fs i+       , fs' ~ RDelete f fs+       , RSubset fs' fs is )+    => (f a -> b)+    -> HFunctorList fs a+    -> (b, HFunctorList fs' a)+hflStrip f hfl =+    let hfl' = HFunctorList $ rcast $ getHFunctorList hfl+     in (f (hflGet hfl), hfl')++-}
src/StreamPatch/Apply.hs view
@@ -1,60 +1,121 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+ module StreamPatch.Apply where -import           StreamPatch.Stream-import           StreamPatch.Patch-import qualified StreamPatch.Patch.Binary   as Bin-import           StreamPatch.Patch.Binary   ( BinRep )+import GHC.Generics ( Generic ) -import           Data.Vinyl-import qualified Data.ByteString            as BS-import qualified Data.ByteString.Builder    as BB-import qualified Data.ByteString.Lazy       as BL-import           Control.Monad.State-import           StreamPatch.Util           ( traverseM_ )+import StreamPatch.Patch+import StreamPatch.Stream+import StreamPatch.HFunctorList+import StreamPatch.Patch.Binary qualified as Bin+import StreamPatch.Patch.Compare qualified as Compare+import StreamPatch.Patch.Compare ( Compare(..), compareTo )+import StreamPatch.Patch.Linearize.InPlace ( HasLength, getLength ) --- TODO how to clean up, use Either monad inside m? (lift didn't work)-applyBinFwd-    :: forall a m. (MonadFwdInplaceStream m, Chunk m ~ BS.ByteString, BinRep a)-    => Bin.Cfg-    -> [Patch 'FwdSeek '[Bin.MetaStream] a]-    -> m (Either (Bin.Error a) ())-applyBinFwd cfg = traverseM_ $ \(Patch a s (FunctorRec (Flap m :& RNil))) -> do-    case Bin.toBinRep' a of-      Left err -> return $ Left err-      Right bs -> do-        advance s-        bsStream <- readahead $ fromIntegral $ BS.length bs-        case Bin.check cfg bsStream m of-          Left err -> return $ Left err-          Right () -> do-            overwrite bs-            return $ Right ()+import Data.Vinyl+import Data.ByteString qualified as BS+import Data.ByteString.Builder qualified as BB+import Data.ByteString.Lazy qualified as BL+import Control.Monad.State+import StreamPatch.Util ( traverseM_ ) -runPureFwdBin-    :: BinRep a-    => Bin.Cfg-    -> [Patch 'FwdSeek '[Bin.MetaStream] a]+import Control.Monad.Except++data Error+  = ErrorCompare String+  | ErrorBinUnexpectedNonNull BS.ByteString+    deriving (Generic, Eq, Show)++applyBinCompareFwd+    :: forall v m+    .  ( FwdInplaceStream m, Chunk m ~ BS.ByteString+       , Compare v BS.ByteString, Num (Index m) )+    => [Patch (Index m) '[Compare.Meta v, Bin.Meta] BS.ByteString]+    -> m (Either Error ())+applyBinCompareFwd = traverseM_ $ \(Patch bs s (HFunctorList (Flap cm :& Flap bm :& RNil))) -> runExceptT $ do+    -- advance to patch location+    lift $ advance s++    -- read same number of bytes as patch data+    bsStream  <- lift $ readahead $ fromIntegral $ getLength bs++    -- check for & strip expected terminating nulls+    bsStream' <- doNullTermCheck bsStream (Bin.mNullTerminates bm)++    -- compare with expected data+    doCompare bsStream' $ Compare.mCompare cm++    -- if that was all successful, write patch in-place+    lift $ overwrite bs+  where+    err = throwError+    doCompare bs' = \case+      Nothing   -> return ()+      Just cmp -> do+        case compareTo @v cmp bs' of+          Nothing -> return ()+          Just e -> err $ ErrorCompare e+    doNullTermCheck bs' = \case+      Nothing -> return bs'+      Just nt ->+        let (bs'', bsNulls) = BS.splitAt (fromIntegral nt) bs'+         in if   bsNulls == BS.replicate (BS.length bsNulls) 0x00+            then return bs''+            else err $ ErrorBinUnexpectedNonNull bs'++runPureBinCompareFwd+    :: (Compare v BS.ByteString)+    => [Patch Int '[Compare.Meta v, Bin.Meta] BS.ByteString]     -> BS.ByteString-    -> Either (Bin.Error a) BL.ByteString-runPureFwdBin cfg ps bs =-    let (mErr, (bsRemaining, bbPatched)) = runState (applyBinFwd cfg ps) (bs, mempty)+    -> Either Error BL.ByteString+runPureBinCompareFwd ps bs =+    let initState = (bs, mempty :: BB.Builder, 0 :: Int)+        (mErr, (bsRemaining, bbPatched, _)) = runState (applyBinCompareFwd ps) initState         bbPatched' = bbPatched <> BB.byteString bsRemaining      in case mErr of-          Left err -> Left err+          Left  e  -> Left e           Right () -> Right $ BB.toLazyByteString bbPatched' -applySimpleFwd-    :: (MonadFwdInplaceStream m, Chunk m ~ a)-    => [Patch 'FwdSeek '[] a]+applyFwd+    :: (FwdInplaceStream m, Chunk m ~ a)+    => [Patch (Index m) '[] a]     -> m ()-applySimpleFwd =-    mapM_ $ \(Patch a s (FunctorRec RNil)) -> advance s >> overwrite a+applyFwd =+    mapM_ $ \(Patch a s (HFunctorList RNil)) ->+        advance s >> overwrite a --- stupid because no monotraversable :<-runPureSimpleFwdList-    :: [Patch 'FwdSeek '[] [a]]+runPureFwdList+    :: [Patch Int '[] [a]]     -> [a]     -> [a]-runPureSimpleFwdList ps start =-    let ((), (remaining, patched)) = runState (applySimpleFwd ps) (start, mempty)+runPureFwdList ps start =+    let ((), (remaining, patched, _)) = runState (applyFwd ps) (start, mempty, 0 :: Int)      in patched <> remaining++applyFwdCompare+    :: forall a v m+    .  ( FwdInplaceStream m, Chunk m ~ a+       , Compare v a, HasLength a, Num (Index m) )+    => [Patch (Index m) '[Compare.Meta v] a]+    -> m (Either Error ())+applyFwdCompare = traverseM_ $ \(Patch a s (HFunctorList (Flap cm :& RNil))) -> do+    advance s+    aStream <- readahead $ fromIntegral $ getLength a+    case Compare.mCompare cm of+      Nothing   -> do+        x <- overwrite a+        return $ Right x+      Just aCmp -> case compareTo @v aCmp aStream of+                     Nothing -> return $ Right ()+                     Just e  -> return $ Left $ ErrorCompare e++runPureFwdCompareString+    :: Compare v String+    => [Patch Int '[Compare.Meta v] String]+    -> String+    -> Either Error String+runPureFwdCompareString ps start =+    let (r, (remaining, patched, _)) = runState (applyFwdCompare ps) (start, "", 0 :: Int)+    in  case r of+          Left err -> Left err+          Right () -> Right $ patched <> remaining
+ src/StreamPatch/Example.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE OverloadedStrings #-}++module StreamPatch.Example where++import StreamPatch.Patch+import StreamPatch.Seek+import StreamPatch.Patch.Compare qualified as Compare+import StreamPatch.Patch.Compare ( Via(..), EqualityCheck(..) )+import StreamPatch.HFunctorList+import Data.Vinyl++import Data.Text ( Text )+import Numeric.Natural++ex :: Patch (SIx Natural) '[Compare.Meta ('ViaEq 'Exact)] Text+ex = Patch "no way this works LMAO" (SIx 0) $ HFunctorList $ Flap (Compare.Meta Nothing) :& RNil++ex2 :: Patch (SIx Natural) '[] Text+ex2 = Patch "no way this works LMAO" (SIx 0) $ HFunctorList RNil
+ src/StreamPatch/HFunctorList.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE AllowAmbiguousTypes, UndecidableInstances #-}++module StreamPatch.HFunctorList where++import Data.Vinyl+import Data.Vinyl.TypeLevel ( RDelete, RIndex )+import Control.Applicative  ( liftA2 )+import GHC.Generics         ( Generic, Rep )+import Foreign.Storable     ( Storable )++import Optics++import Data.Aeson++instance ( ToJSON (Flap a r), Generic (Rec (Flap a) rs)+         , GToJSON' Value Zero (Rep (Rec (Flap a) rs))+         , GToJSON' Encoding Zero (Rep (Rec (Flap a) rs))+         ) => ToJSON (Rec (Flap a) (r ': rs))++-- | A list of functors parametric over a "shared" 'a', where each functor+--   stores a single value 'f a'.+--+-- Just a wrapper on top of Vinyl with some types swap around.+newtype HFunctorList fs a = HFunctorList { getHFunctorList :: Rec (Flap a) fs }+    deriving stock (Generic)++deriving via (Rec (Flap a) fs) instance ToJSON (Rec (Flap a) fs) => ToJSON (HFunctorList fs a)++deriving instance (ReifyConstraint Show (Flap a) fs, RMap fs, RecordToList fs) => Show (HFunctorList fs a)+deriving instance Eq        (Rec (Flap a) fs) => Eq        (HFunctorList fs a)+deriving instance Ord       (Rec (Flap a) fs) => Ord       (HFunctorList fs a)++-- Right. I only partly get this. As I understand, I'm leveraging deriving via+-- to generate the instance bodies, since they look the same as Rec but with a+-- set functor. So I just have to assure it that you can make it Storable in the+-- same way, given that @Flap a@ is storable (which is handled similarly at its+-- own definition).+deriving via (Rec (Flap a) '[])       instance Storable (HFunctorList '[] a)+deriving via (Rec (Flap a) (f ': fs)) instance (Storable (f a), Storable (Rec (Flap a) fs)) => Storable (HFunctorList (f ': fs) a)++-- It appears we can't do the same for 'Functor' etc., because the @a@ type+-- variable isn't bound, but must be for us to say what type to derive via. I+-- wonder if there is a workaround, but I can't figure it out.+instance Functor (HFunctorList '[]) where+  fmap _ (HFunctorList RNil) = HFunctorList RNil+instance (Functor r, Functor (HFunctorList rs)) => Functor (HFunctorList (r ': rs)) where+  fmap f (HFunctorList (Flap r :& rs)) =+    HFunctorList (Flap (fmap f r) :& getHFunctorList (fmap f (HFunctorList rs)))++instance Applicative (HFunctorList '[]) where+  pure _ = HFunctorList RNil+  HFunctorList RNil <*> HFunctorList RNil = HFunctorList RNil+instance (Applicative r, Applicative (HFunctorList rs)) => Applicative (HFunctorList (r ': rs)) where+  pure a = HFunctorList (Flap (pure a) :& getHFunctorList (pure a))+  HFunctorList (Flap f :& fs) <*> HFunctorList (Flap a :& as) =+    HFunctorList (Flap (f <*> a) :& getHFunctorList (HFunctorList fs <*> HFunctorList as))++instance Foldable (HFunctorList '[]) where+  foldr _ z (HFunctorList RNil) = z+instance (Foldable r, Foldable (HFunctorList rs)) => Foldable (HFunctorList (r ': rs)) where+    -- only foldmap because foldr is harder looool+    foldMap f (HFunctorList (Flap r :& rs)) = foldMap f r <> foldMap f (HFunctorList rs)++-- this took me ages because I'm stupid T_T+instance Traversable (HFunctorList '[]) where+  traverse _ (HFunctorList RNil) = pure (HFunctorList RNil)+instance (Traversable r, Traversable (HFunctorList rs)) => Traversable (HFunctorList (r ': rs)) where+  traverse+      :: forall f a b. Applicative f+      => (a -> f b)+      -> (HFunctorList (r ': rs)) a+      -> f (HFunctorList (r ': rs) b)+  traverse f (HFunctorList (Flap (r :: r a) :& rs)) =+      HFunctorList <$> rBoth+    where+      rBoth :: f (Rec (Flap b) (r ': rs))+      rBoth = liftA2 (:&) rHead rTail+      rHead :: f (Flap b r)+      rHead = Flap <$> traverse f r+      rTail :: f (Rec (Flap b) rs)+      rTail = getHFunctorList <$> traverse f (HFunctorList rs)++-- | Flipped apply: a single value at 'f a', but with "flipped" type arguments.+--   Very useless - has no Functor nor Contravariant nor HFunctor instance.+newtype Flap a f = Flap { getFlap :: f a }+    deriving stock   (Generic, Show, Eq, Ord)+    deriving Storable via (f a)+    deriving (ToJSON, FromJSON) via (f a)++--------------------------------------------------------------------------------++-- | Get the value at a type in an HFunctorList.+hflGet+    :: forall f fs a i+    .  RElem f fs i+    => HFunctorList fs a+    -> f a+hflGet = getFlap . rget . getHFunctorList++-- | Put a value at a type in an HFunctorList.+hflPut+    :: forall f f' fs fs' a+    .  RecElem Rec f f' fs fs' (RIndex f fs)+    => f' a+    -> HFunctorList fs a+    -> HFunctorList fs' a+hflPut x = HFunctorList . rput' @_ @f (Flap x) . getHFunctorList++-- | Get a lens to the value at a type in an HFunctorList.+hflLens+    :: forall f f' fs fs' a s t+    .  ( RecElem Rec f f' fs fs' (RIndex f fs)+       , RElem f fs (RIndex f fs)+       , s ~ HFunctorList fs  a+       , t ~ HFunctorList fs' a )+    => Lens s t (f a) (f' a)+hflLens = lens hflGet (\hfl x -> hflPut @f x hfl)++-- | Use the value at a type in an HFunctorList, and remove it from the list.+hflStrip+    :: forall f fs a fs' b i is+    .  ( RElem f fs i+       , fs' ~ RDelete f fs+       , RSubset fs' fs is )+    => (f a -> b)+    -> HFunctorList fs a+    -> (b, HFunctorList fs' a)+hflStrip f hfl =+    let hfl' = HFunctorList $ rcast $ getHFunctorList hfl+     in (f (hflGet hfl), hfl')
src/StreamPatch/Patch.hs view
@@ -1,78 +1,37 @@--- | Core patch type definitions: patches, seeks, metadata.--module StreamPatch.Patch where--import           Data.Kind-import           GHC.Generics ( Generic )-import           GHC.Natural-import           Data.Vinyl-import           Control.Applicative ( liftA2 )--type Patch :: SeekKind -> [Type -> Type] -> Type -> Type-data Patch s fs a = Patch-  { patchData :: a-  , patchSeek :: SeekRep s-  , patchMeta :: FunctorRec fs a-  } deriving (Generic)+{-# LANGUAGE UndecidableInstances #-} -deriving instance (Eq   a, Eq   (SeekRep s), Eq (Rec (Flap a) fs))  => Eq (Patch s fs a)-deriving instance (Show a, Show (SeekRep s), ReifyConstraint Show (Flap a) fs, RMap fs, RecordToList fs) => Show (Patch s fs a)-deriving instance (Functor (FunctorRec fs)) => Functor (Patch s fs)+{- | Core patch type: patches, seeks, metadata container. --- Taken from vinyl-plus. Functor and Applicative instances were provided.-newtype Flap a f = Flap { getFlap :: f a } deriving (Eq, Show, Generic)-newtype FunctorRec fs a = FunctorRec { getFunctorRec :: Rec (Flap a) fs } deriving (Generic)-deriving instance (ReifyConstraint Show (Flap a) fs, RMap fs, RecordToList fs) => Show (FunctorRec fs a)-deriving instance Eq (Rec (Flap a) fs) => Eq (FunctorRec fs a)+This library is based around patches to streams i.e. containers indexed by the+natural numbers (or integers, depending on your view). As such, we restrict what+a seek can look like. Parts of the codebase could be generalized to work over+any seek kind, so you could e.g. write a text patcher that uses line and column+positions to seek through the data. But you can't transform line and column to+byte position, at least not without parsing the file. So it would need a lot of+thought and careful design to generalize in that direction.+-} -instance Functor (FunctorRec '[]) where-  fmap _ (FunctorRec RNil) = FunctorRec RNil-instance (Functor r, Functor (FunctorRec rs)) => Functor (FunctorRec (r ': rs)) where-  fmap f (FunctorRec (Flap r :& rs)) =-    FunctorRec (Flap (fmap f r) :& getFunctorRec (fmap f (FunctorRec rs)))+module StreamPatch.Patch where -instance Applicative (FunctorRec '[]) where-  pure _ = FunctorRec RNil-  FunctorRec RNil <*> FunctorRec RNil = FunctorRec RNil-instance (Applicative r, Applicative (FunctorRec rs)) => Applicative (FunctorRec (r ': rs)) where-  pure a = FunctorRec (Flap (pure a) :& getFunctorRec (pure a))-  FunctorRec (Flap f :& fs) <*> FunctorRec (Flap a :& as) =-    FunctorRec (Flap (f <*> a) :& getFunctorRec (FunctorRec fs <*> FunctorRec as))+import StreamPatch.HFunctorList -instance Foldable (FunctorRec '[]) where-  foldr _ z (FunctorRec RNil) = z-instance (Foldable r, Foldable (FunctorRec rs)) => Foldable (FunctorRec (r ': rs)) where-    -- TODO foldr is harder lol-    foldMap f (FunctorRec (Flap r :& rs)) = foldMap f r <> foldMap f (FunctorRec rs)+import GHC.Generics ( Generic )+import Data.Data ( Data, Typeable )+import Data.Aeson --- I am shit at this LOL-instance Traversable (FunctorRec '[]) where-  traverse _ (FunctorRec RNil) = pure (FunctorRec RNil)-instance (Traversable r, Traversable (FunctorRec rs)) => Traversable (FunctorRec (r ': rs)) where-  traverse-      :: forall f a b. Applicative f-      => (a -> f b)-      -> (FunctorRec (r ': rs)) a-      -> f (FunctorRec (r ': rs) b)-  traverse f (FunctorRec (Flap (r :: r a) :& rs)) =-      FunctorRec <$> rBoth-    where-      rBoth :: f (Rec (Flap b) (r ': rs))-      rBoth = liftA2 (:&) rHead rTail-      rHead :: f (Flap b r)-      rHead = Flap <$> traverse f r-      rTail :: f (Rec (Flap b) rs)-      rTail = getFunctorRec <$> traverse f (FunctorRec rs)+-- | A single patch on a stream of 'a'.+data Patch s fs a = Patch+  { patchData :: a+  , patchSeek :: s+  , patchMeta :: HFunctorList fs a+  } deriving stock (Generic) --- | What a patch seek value means.-data SeekKind-  = FwdSeek -- ^ seeks only move cursor forward-  | RelSeek -- ^ seeks are relative e.g. to a universal base, or a stream cursor-  | AbsSeek -- ^ seeks specify an exact offset in stream-    deriving (Eq, Show, Generic)+deriving stock instance (Eq   a, Eq   s, Eq   (HFunctorList fs a)) => Eq   (Patch s fs a)+deriving stock instance (Show a, Show s, Show (HFunctorList fs a)) => Show (Patch s fs a)+deriving stock instance (Data a, Data s, Data (HFunctorList fs a), Typeable fs) => Data (Patch s fs a)+deriving stock instance Functor     (HFunctorList fs) => Functor     (Patch s fs)+deriving stock instance Foldable    (HFunctorList fs) => Foldable    (Patch s fs)+deriving stock instance Traversable (HFunctorList fs) => Traversable (Patch s fs) --- | Get the representation for a 'SeekKind'. Allows us a bit more safety.-type family SeekRep (s :: SeekKind) where-    SeekRep 'FwdSeek = Natural-    SeekRep 'RelSeek = Integer-    SeekRep 'AbsSeek = Natural+instance (ToJSON   a, ToJSON   s, ToJSON   (HFunctorList fs a)) => ToJSON   (Patch s fs a)+instance (FromJSON a, FromJSON s, FromJSON (HFunctorList fs a)) => FromJSON (Patch s fs a)
src/StreamPatch/Patch/Align.hs view
@@ -1,59 +1,49 @@ module StreamPatch.Patch.Align where -import           StreamPatch.Patch+import StreamPatch.Patch+import StreamPatch.HFunctorList ( hflStrip ) -import           GHC.Generics ( Generic )-import           Numeric.Natural-import           GHC.Natural ( naturalFromInteger )-import           Data.Vinyl-import           Data.Vinyl.TypeLevel-import           Data.Functor.Const+import GHC.Generics ( Generic )+import Data.Vinyl+import Data.Vinyl.TypeLevel+import Data.Functor.Const -data Meta s = Meta-  { mExpected :: Maybe (SeekRep s)+data Meta st = Meta+  { mExpected :: Maybe st   -- ^ Absolute stream offset for edit. Used for checking against actual offset.-  } deriving (Generic)--deriving instance (Eq   (SeekRep s)) => Eq   (Meta s)-deriving instance (Show (SeekRep s)) => Show (Meta s)--data Error s-  = ErrorSeekBelow0 (SeekRep 'RelSeek)-  | ErrorDoesntMatchExpected (SeekRep s) (SeekRep s) -- expected, then actual-    deriving (Generic)+  } deriving (Generic, Show, Eq) -deriving instance (Eq   (SeekRep s)) => Eq   (Error s)-deriving instance (Show (SeekRep s)) => Show (Error s)+data Error st+  = ErrorAlignedToNegative Integer -- guaranteed negative+  | ErrorDoesntMatchExpected st st+    deriving (Generic, Show, Eq)  -- | Attempt to align the given patch to 0 using the given base.+--+-- The resulting seek is guaranteed to be non-negative, so you may use+-- natural-like types safely.+--+-- TODO Complicated. align-    :: forall s a ss rs is i r-    .  ( SeekRep s ~ Natural-       , r ~ Const (Meta s)+    :: forall sf st a ss is r rs+    .  ( Integral sf, Num st, Eq st+       , r ~ Const (Meta st)        , rs ~ RDelete r ss-       , RElem r ss i+       , RElem r ss (RIndex r ss)        , RSubset rs ss is )-    => SeekRep 'RelSeek-    -> Patch 'RelSeek ss a-    -> Either (Error s) (Patch s rs a)-align sBase (Patch a s ms) =-    case tryIntegerToNatural sAligned of-      Nothing          -> Left $ ErrorSeekBelow0 sAligned-      Just sAlignedNat ->-        case mExpected m of-          Nothing        -> reform sAlignedNat-          Just sExpected ->-            if   sExpected == sAlignedNat-            then reform sAlignedNat-            else Left $ ErrorDoesntMatchExpected sExpected sAlignedNat+    => Integer+    -> Patch sf ss a+    -> Either (Error st) (Patch st rs a)+align sBase (Patch a s ms)+  | s' < 0 = Left $ ErrorAlignedToNegative s'+  | otherwise =+      case mExpected m of+        Nothing        -> Right $ Patch a s'' ms'+        Just sExpected ->+          if   sExpected == s''+          then Right $ Patch a s'' ms'+          else Left $ ErrorDoesntMatchExpected sExpected s''   where-    sAligned = sBase + s-    -- TODO require a visible type application here, unsure why. even without-    -- the SeekRep indirection-    m = getConst @(Meta s) $ getFlap $ rget $ getFunctorRec ms-    ms' = FunctorRec $ rcast @rs $ getFunctorRec ms-    reform s' = Right $ Patch a s' ms'--tryIntegerToNatural :: Integer -> Maybe Natural-tryIntegerToNatural n | n < 0     = Nothing-                      | otherwise = Just $ naturalFromInteger n+    s' = sBase + toInteger s+    s'' = fromInteger s'+    (m, ms') = hflStrip getConst ms
src/StreamPatch/Patch/Binary.hs view
@@ -1,136 +1,109 @@ {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE AllowAmbiguousTypes #-}  -- TODO rewrite patch/check bits (some overlap)+-- TODO rewrite, rename checks (one for process, one for apply) -module StreamPatch.Patch.Binary-  ( Meta(..)-  , MetaStream(..)-  , Cfg(..)-  , Error(..)-  , patchBinRep-  , BinRep(..)-  , toBinRep'-  , check-  ) where+module StreamPatch.Patch.Binary where -import           StreamPatch.Patch+import StreamPatch.Patch+import StreamPatch.HFunctorList -import           GHC.Generics       ( Generic )-import           GHC.Natural-import qualified Data.ByteString    as BS-import qualified Data.Text.Encoding as Text-import qualified Data.Text          as Text-import           Data.Text          ( Text )-import           Data.Either.Combinators-import           Data.Vinyl-import           Data.Functor.Const-import           Data.Vinyl.TypeLevel+import Binrep -data Meta = Meta-  { mMaxBytes :: Maybe (SeekRep 'FwdSeek)-  -- ^ Maximum number of bytes permitted to write at the associated position.-  } deriving (Eq, Show, Generic)+import GHC.Generics       ( Generic )+import GHC.Natural+import Data.ByteString qualified as BS+import Data.Vinyl+import Data.Functor.Const+import Data.Vinyl.TypeLevel -data MetaStream a = MetaStream-  { msNullTerminates :: Maybe (SeekRep 'FwdSeek)-  -- ^ Stream segment should be null bytes (0x00) only from this index onwards.+import StreamPatch.Patch.Compare qualified as Compare+import StreamPatch.Patch.Compare hiding ( Meta ) -  , msExpected       :: Maybe a-  -- ^ Stream segment should be this.-  } deriving (Eq, Show, Generic, Functor, Foldable, Traversable)+import Optics -data Cfg = Cfg-  { cfgAllowPartialExpected :: Bool-  -- ^ If enabled, allow partial expected bytes checking. If disabled, then even-  --   if the expected bytes are a prefix of the actual, fail.+data Meta a = Meta+  { mNullTerminates :: Maybe Natural+  -- ^ Stream segment should be null bytes (0x00) only from this index onwards.+  } deriving (Generic, Eq, Show, Functor, Foldable, Traversable)++data MetaPrep = MetaPrep+  { mpMaxBytes :: Maybe Natural+  -- ^ Maximum bytelength of binrepped data.+  --+  -- Though binrepping is a safe operation, this is a useful sanity check in+  -- cases where you know the maximum space available.+  --+  -- Note that this is only available for the patch data, not other meta data.+  -- (If you want that, you'll need to shove this field into the patch type.)+  -- itself. Probably not very useful.)   } deriving (Eq, Show, Generic)  data Error a-  = ErrorBadBinRep a String-  | ErrorUnexpectedNonNull BS.ByteString-  | ErrorDidNotMatchExpected BS.ByteString BS.ByteString-  | ErrorBinRepTooLong BS.ByteString Natural+  = ErrorBinRepOverlong BLenT BLenT a (Maybe BS.ByteString)+  -- ^ If the value was serialized, it's given in the 'Maybe'.     deriving (Eq, Show, Generic, Functor, Foldable, Traversable) -patchBinRep-    :: forall a s ss rs is i r-    .  ( BinRep a-       , Traversable (FunctorRec rs)-       , r ~ Const Meta+-- Note that via binrep's 'BLen' typeclass, we can check sizing "for free",+-- without serializing. TODO, I could make another function that serializes,+-- then checks. That way, we can return the serialization attempt to the user,+-- and not require 'BLen'. In exchange, it's less efficient on errors. Both have+-- the same main path complexity.+binRepify+    :: forall a s ss is r rs+    .  ( Put a, BLen a+       , Traversable (HFunctorList rs)+       , r ~ Const MetaPrep        , rs ~ RDelete r ss-       , RElem r ss i+       , RElem r ss (RIndex r ss)        , RSubset rs ss is )     => Patch s ss a     -> Either (Error a) (Patch s rs BS.ByteString)-patchBinRep (Patch a s ms) = do-    a' <- toBinRep' a-    () <- case mMaxBytes m of-            Nothing       -> return ()-            Just maxBytes -> if   BS.length a' > fromIntegral maxBytes-                             then Left $ ErrorBinRepTooLong a' maxBytes-                             else return ()-    let msDroppedMeta = FunctorRec $ rcast @rs $ getFunctorRec ms-    ms' <- traverse toBinRep' msDroppedMeta-    return $ Patch a' s ms'-  where m = getConst @Meta $ getFlap $ rget $ getFunctorRec ms+binRepify (Patch a s ms) = do+    let (m, ms') = hflStrip getConst ms+    checkMeta m+    let bs = runPut a+        bsms = fmap runPut ms'+    return $ Patch bs s bsms+  where+    checkMeta m =+        case mpMaxBytes m of+          Nothing       -> return ()+          Just maxBytes ->+            let maxBytes' = natToBLen maxBytes+             in if   blen a > maxBytes'+                then Left $ ErrorBinRepOverlong (blen a) maxBytes' a Nothing+                else return () --- | Type has a binary representation for using in patchscripts.------ Patchscripts are parsed parameterized over the type to edit. That type needs--- to become a bytestring for eventual patch application. We're forced into--- newtypes and typeclasses by Aeson already, so this just enables us to define--- some important patch generation behaviour in one place. Similarly to Aeson,--- if you require custom behaviour for existing types (e.g. length-prefixed--- strings instead of C-style null terminated), define a newtype over it.+-- | Treat the nulls field as a "this is how many extra nulls there are", and+--   amend the compare meta for a patch by appending those nulls, and strip that+--   stream-time bin meta. ----- Some values may not have valid patch representations, for example if you're--- patching a 1-byte length-prefixed string and your string is too long (>255--- encoded bytes). Thus, 'toPatchRep' is failable.-class BinRep a where-    toBinRep :: a -> Either String BS.ByteString--toBinRep' :: BinRep a => a -> Either (Error a) BS.ByteString-toBinRep' a = mapLeft (ErrorBadBinRep a) $ toBinRep a---- | Bytestrings are copied as-is.-instance BinRep BS.ByteString where-    toBinRep = Right . id---- | Text is converted to UTF-8 bytes and null-terminated.-instance BinRep Text where-    toBinRep = Right . flip BS.snoc 0x00 . Text.encodeUtf8---- | String is the same but goes the long way round, through Text.-instance BinRep String where-    toBinRep = toBinRep . Text.pack--check :: BinRep a => Cfg -> BS.ByteString -> MetaStream a -> Either (Error a) ()-check cfg bs meta = do-    case msExpected meta of-      Nothing -> Right ()-      Just aExpected -> do-        bsExpected <- checkInner aExpected Nothing -- cheating a bit here-        case msNullTerminates meta of-          Nothing -> check' bs bsExpected-          Just nullsFrom ->-            let (bs', bsNulls) = BS.splitAt (fromIntegral nullsFrom) bs-             in if   bsNulls == BS.replicate (BS.length bsNulls) 0x00-                then check' bs' bsExpected-                else Left $ ErrorUnexpectedNonNull bs+-- Correct thing to do, but needs lots of changes elsewhere too. Dang.+binRepifyNull+    :: forall ec s r1 r2 ss rs is+    .  ( r1 ~ Meta+       , r2 ~ Compare.Meta ('ViaEq ec)+       , rs ~ RDelete r1 ss+       , RElem r1 ss (RIndex r1 ss)+       , RSubset rs ss is+       , RElem r2 rs (RIndex r2 rs)+       , RecElem Rec r2 r2 rs rs (RIndex r2 rs)+       )+    => Patch s ss BS.ByteString+    -> Patch s rs BS.ByteString+binRepifyNull (Patch a s ms) = do+    let (mNT, ms') = hflStrip @r1 mNullTerminates ms+    case mNT of+      Nothing -> Patch a s ms'+      Just nt ->+        let mCmpLens = hflLens @r2 @r2 @rs @rs+            ms'' = over mCmpLens (\(Compare.Meta x) -> go nt x) ms'+         in Patch a s ms''   where-    check' bs' bsExpected =-        case checkExpected bs' bsExpected of-          True  -> Right ()-          False -> Left $ ErrorDidNotMatchExpected bs' bsExpected-    checkInner a mn = do-        bs' <- toBinRep' a-        case mn of-          Nothing -> Right bs'-          Just n  ->-            if   fromIntegral (BS.length bs') > n-            then Left $ ErrorBinRepTooLong bs' n-            else Right bs'-    checkExpected bs' bsExpected =-        case cfgAllowPartialExpected cfg of-          True  -> BS.isPrefixOf bs' bsExpected-          False -> bs' == bsExpected+    go nt = \case+      Nothing  -> Compare.Meta Nothing+      Just cmp ->+        let cmp' = cmp <> BS.replicate (fromIntegral nt) 0x00+         in Compare.Meta $ Just cmp'
− src/StreamPatch/Patch/Binary/PascalText.hs
@@ -1,48 +0,0 @@-{-| Newtype for manipulating length-prefixed strings.--This type is for UTF-8 'Text' that you intend to write out to a length-prefixed-bytestring. The size of the length field is static. You essentially have to-decide the maximum bytesize of the string on creation.--}--module StreamPatch.Patch.Binary.PascalText where--import           StreamPatch.Patch.Binary ( BinRep(..) )--import qualified Data.Text.Encoding         as Text-import           Data.Text                  ( Text )-import qualified Data.ByteString            as BS-import           GHC.TypeNats-import           Data.Proxy-import           Data.Bits--newtype PascalText (n :: Nat) = PascalText { unPascalText :: Text }--instance KnownNat n => BinRep (PascalText n) where-    toBinRep t =-        case encodePascalText t of-          Nothing -> Left "UTF-8 encoded text too long for length prefix field"-          Just bs -> Right bs--encodePascalText :: forall n. KnownNat n => PascalText n -> Maybe BS.ByteString-encodePascalText t = do-    lenBs <- encodeToSizedBE (fromIntegral (natVal (Proxy @n))) (BS.length bs)-    return $ lenBs <> bs-  where-    bs = Text.encodeUtf8 . unPascalText $ t--encodeToSizedBE :: (Integral a, Bits a) => Int -> a -> Maybe BS.ByteString-encodeToSizedBE byteSize x =-    let bs = i2be x-        nulls = byteSize - BS.length bs-     in if   nulls < 0-        then Nothing-        else Just $ BS.replicate nulls 0x00 <> bs---- | Re-encode an 'Integer' to a little-endian integer stored as a---   'BS.ByteString' using the fewest bytes needed to represent it.------ adapated from crypto-api 0.13.3, Crypto.Util.i2bs_unsized-i2be :: (Integral a, Bits a) => a -> BS.ByteString-i2be 0 = BS.singleton 0-i2be i = BS.reverse $ BS.unfoldr (\i' -> if i' <= 0 then Nothing else Just (fromIntegral i', (i' `shiftR` 8))) i
+ src/StreamPatch/Patch/Compare.hs view
@@ -0,0 +1,208 @@+{- TODO+  * Via -> Strategy (? I do like the brevity of Via)+  * Rename Compare (but not sure what to...)+-}++{-# LANGUAGE AllowAmbiguousTypes, UndecidableInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++module StreamPatch.Patch.Compare where++import GHC.Generics ( Generic )+import Data.Data ( Typeable, Data )++import Numeric.Natural+import Data.ByteString qualified as BS+import Data.Text qualified as Text+import Data.Text ( Text )++import Data.Void+import Control.Monad ( void )++import GHC.Exts ( Proxy#, proxy# )+import GHC.TypeLits ( KnownSymbol, symbolVal' )++import Binrep.Extra.HexByteString++import Data.Aeson qualified as Aeson+import Data.Aeson ( ToJSON(..), FromJSON(..) )++import Text.Megaparsec+import Text.Megaparsec.Char qualified as MC+import Text.Megaparsec.Char.Lexer qualified as MCL++import BLAKE3 qualified as B3+import Data.ByteArray qualified as BA+import Data.ByteString qualified as B+import Data.Word ( Word8 )++import Data.Singletons.TH+-- required for deriving instances (seems like bug)+import Prelude.Singletons hiding ( AbsSym0, Compare )+import Data.Singletons.Base.TH ( FromString, sFromString )++$(singletons [d|+    -- | What sort of equality check to do.+    data EqualityCheck+      = Exact -- ^ "Exact equality" is defined as whatever the 'Eq' class does. (lol)+      | PrefixOf+        deriving stock (Show, Eq)+    |])+deriving stock instance Generic  EqualityCheck+deriving stock instance Typeable EqualityCheck+deriving stock instance Data     EqualityCheck++$(singletons [d|+    data HashFunc+      = B3+      | SHA256+      | MD5+        deriving stock (Show, Eq)+    |])+deriving stock instance Generic  HashFunc+deriving stock instance Typeable HashFunc+deriving stock instance Data     HashFunc++$(singletons [d|+    -- | How should we compare two values?+    data Via+      -- | Are they equal in some way?+      = ViaEq EqualityCheck++      -- | Do they have the same size?+      | ViaSize++      -- | Do they have the same digest under the given hash function?+      | ViaDigest HashFunc+        deriving stock (Show, Eq)+    |])+deriving stock instance Generic  Via+deriving stock instance Typeable Via+deriving stock instance Data     Via++data Meta (v :: Via) a = Meta+  { mCompare :: Maybe (CompareRep v a)+  } deriving stock (Generic)++deriving stock instance Eq   (CompareRep v a) => Eq   (Meta v a)+deriving stock instance Show (CompareRep v a) => Show (Meta v a)++deriving anyclass instance ToJSON   (CompareRep v a) => ToJSON   (Meta v a)+deriving anyclass instance FromJSON (CompareRep v a) => FromJSON (Meta v a)++instance SingI v => Functor     (Meta v) where+    fmap f (Meta c) = case sing @v of+      SViaEq     _ -> Meta $ fmap f c+      SViaSize     -> Meta c+      SViaDigest _ -> Meta c++instance SingI v => Foldable    (Meta v) where+    foldMap f (Meta c) = case sing @v of+      SViaEq     _ -> foldMap f c+      SViaSize     -> mempty+      SViaDigest _ -> mempty++instance SingI v => Traversable (Meta v) where+    traverse f (Meta c) = case sing @v of+      SViaEq     _ -> Meta <$> traverse f c+      SViaSize     -> pure $ Meta c+      SViaDigest _ -> pure $ Meta c++type family CompareRep (v :: Via) a where+    CompareRep ('ViaEq _)     a = a+    CompareRep 'ViaSize       _ = Natural+    CompareRep ('ViaDigest h) _ = Digest h B.ByteString++-- | The resulting digest from hashing some data using the given hash function.+--+-- TODO+-- As of 2022, most good cryptographic hash functions produce digest sizes+-- between 256-512 bits. That's 32-64 bytes. So I want to use a ShortByteString,+-- but the BLAKE3 library uses the memory library, which I can't figure out. I+-- bet it'd be more efficient. So, I'm polymorphising in preparation.+newtype Digest (h :: HashFunc) a = Digest { getDigest :: a }+    deriving stock (Generic, Eq, Show)++type Digest' h = Digest h B.ByteString++type family HashFuncLabel (h :: HashFunc) where+    HashFuncLabel 'B3     = "b3"+    HashFuncLabel 'SHA256 = "sha256"+    HashFuncLabel 'MD5    = "md5"++hashFuncLabel :: forall h l. (l ~ HashFuncLabel h, KnownSymbol l) => Text+hashFuncLabel = Text.pack (symbolVal' (proxy# :: Proxy# l))++-- | Add a @digest:@ prefix to better separate from regular text.+instance (l ~ HashFuncLabel h, KnownSymbol l) => ToJSON   (Digest h B.ByteString) where+    toJSON = Aeson.String . Text.append "digest:" . prettyDigest B.unpack++instance (l ~ HashFuncLabel h, KnownSymbol l) => FromJSON (Digest h B.ByteString) where+    parseJSON = Aeson.withText "hex hash digest" $ \t ->+        case parseMaybe @Void parseDigest t of+          Nothing   -> fail "failed to parse hex hash digest"+          Just hash -> pure hash++-- | Pretty print a hash like @hashfunc:123abc@.+prettyDigest+    :: forall h a l. (l ~ HashFuncLabel h, KnownSymbol l)+    => (a -> [Word8]) -> Digest h a -> Text+prettyDigest unpack (Digest d) =+       hashFuncLabel @h+    <> Text.singleton ':'+    <> prettyHexByteStringCompact unpack d++-- Bad naming, these maybe aren't symbols/lexemes in the expected sense+-- (horizontal spacing is optional).+parseDigest+    :: forall h l e s m+    .  (l ~ HashFuncLabel h, KnownSymbol l, MonadParsec e s m, Token s ~ Char, Tokens s ~ Text)+    => m (Digest h B.ByteString)+parseDigest = do+    symbol "digest"+    symbol ":"+    symbol $ hashFuncLabel @h+    symbol ":"+    Digest <$> parseHexByteString B.pack+  where symbol = void . MCL.lexeme MC.hspace . chunk++class Compare (v :: Via) a where+    compare'     :: CompareRep v a -> CompareRep v a -> Maybe String+    toCompareRep :: a -> CompareRep v a++compareTo :: forall v a. Compare v a => CompareRep v a -> a -> Maybe String+compareTo cmp = compare' @v @a cmp . toCompareRep @v @a++-- Free instance: Compare for exact equality via 'Eq'.+-- TODO show is bandaid. no need to handle it here.+instance (Eq a, Show a) => Compare ('ViaEq 'Exact) a where+    toCompareRep = id+    compare' c1 c2 | c1 == c2  = Nothing+                   | otherwise = Just $ "values not equal: "<>show c1<>" /= "<>show c2++instance Compare ('ViaDigest 'B3) BS.ByteString where+    toCompareRep = Digest . hashB3+    compare' c1 c2 | c1 == c2  = Nothing+                   | otherwise = Just "digests not equal"++-- TODO I need to define the compare class better lol, I'm confused which is+-- real and which is test.+instance Compare ('ViaEq 'PrefixOf) BS.ByteString where+    toCompareRep = id+    compare' c1 c2 | c2 `B.isPrefixOf` c1 = Nothing+                   | otherwise = Just $ "prefix compare check fail: "<>show c1<>" vs. "<>show c2++-- I unpack to '[Word8]' then repack to 'ByteString' because the memory library+-- is very keen on complicated unsafe IO. cheers no thanks+hashB3 :: BS.ByteString -> BS.ByteString+hashB3 bs = BS.pack $ BA.unpack $ B3.hash @B3.DEFAULT_DIGEST_LEN [bs]++class SwapCompare a (vFrom :: Via) (vTo :: Via) where+    swapCompare :: CompareRep vFrom a -> Either String (CompareRep vTo a)++instance SwapCompare a v v where+    swapCompare = Right++instance SwapCompare BS.ByteString ('ViaEq 'Exact) ('ViaDigest 'B3) where+    swapCompare = Right . Digest . hashB3
+ src/StreamPatch/Patch/Compile.hs view
@@ -0,0 +1,35 @@+-- | Compiling/normalizing patches using 'Compare'.++{-# LANGUAGE AllowAmbiguousTypes #-}++module StreamPatch.Patch.Compile where++import StreamPatch.Patch+import StreamPatch.Patch.Compare qualified as Compare+import StreamPatch.Patch.Compare ( Compare(..), Via(..), EqualityCheck(..) )++import StreamPatch.HFunctorList+import Optics+import Data.Generics.Product.Any+import Data.Vinyl+import Data.Vinyl.TypeLevel ( RIndex )++compilePatch+    :: forall v a s f f' fs fs' i+    .  ( Compare v a+       , f  ~ Compare.Meta ('ViaEq 'Exact)+       , f' ~ Compare.Meta v+       , RElem f fs i+       , RecElem Rec f f' fs fs' i+       , i ~ RIndex f fs+       )+    => Patch s fs  a+    -> Patch s fs' a+compilePatch = over ((the @"patchMeta") % hflLens) (compileCompareMeta @v)++compileCompareMeta+    :: forall v a. Compare v a+    => Compare.Meta ('ViaEq 'Exact) a+    -> Compare.Meta v a+compileCompareMeta (Compare.Meta cmp) =+    Compare.Meta $ fmap (toCompareRep @v) cmp
− src/StreamPatch/Patch/Linearize.hs
@@ -1,58 +0,0 @@-module StreamPatch.Patch.Linearize where--import           StreamPatch.Patch--import           GHC.Generics ( Generic )-import           Numeric.Natural-import           GHC.Natural ( minusNaturalMaybe )-import           Data.Vinyl--import           Control.Monad.State-import qualified Data.List              as List-import qualified Data.ByteString        as BS-import qualified Data.Text              as Text-import           Data.Text              ( Text )-import           StreamPatch.Util       ( traverseM )--class HasLength a where-    getLength :: a -> Natural--instance HasLength BS.ByteString where-    getLength = fromIntegral . BS.length-instance HasLength Text where-    getLength = fromIntegral . Text.length-instance HasLength String where-    getLength = fromIntegral . List.length--data Error fs a-  = ErrorOverlap -- ^ Two edits wrote to the same offset.-        (SeekRep 'AbsSeek)    -- ^ absolute position in stream-        (Patch 'AbsSeek fs a) -- ^ overlapping patch-        (Patch 'AbsSeek fs a) -- ^ previous patch-    deriving (Generic)--deriving instance (Eq a, Eq (Patch 'AbsSeek fs a)) => Eq (Error fs a)-deriving instance (Show a, ReifyConstraint Show (Flap a) fs, RMap fs, RecordToList fs) => Show (Error fs a)-deriving instance (Functor     (Patch 'AbsSeek fs)) => Functor     (Error fs)-deriving instance (Foldable    (Patch 'AbsSeek fs)) => Foldable    (Error fs)-deriving instance (Traversable (Patch 'AbsSeek fs)) => Traversable (Error fs)--linearize-    :: HasLength a-    => [Patch 'AbsSeek fs a]-    -> Either (Error fs a) [Patch 'FwdSeek fs a]-linearize ps = evalState (traverseM go (List.sortBy comparePatchSeeks ps)) (0, undefined)-  where-    go p@(Patch a s _)  = do-        (cursor, pPrev) <- get-        case s `minusNaturalMaybe` cursor of-          -- next absolute seek is before cursor: current patch overlaps prev-          Nothing -> return $ Left $ ErrorOverlap cursor p pPrev-          Just skip -> do-            let cursor' = cursor + skip + getLength a-                p' = p { patchSeek = skip }-            put (cursor', p)-            return $ Right p'--comparePatchSeeks :: Ord (SeekRep s) => Patch s fs a -> Patch s fs a -> Ordering-comparePatchSeeks p1 p2 = compare (patchSeek p1) (patchSeek p2)
+ src/StreamPatch/Patch/Linearize/Common.hs view
@@ -0,0 +1,6 @@+module StreamPatch.Patch.Linearize.Common where++import StreamPatch.Patch++comparePatchSeeks :: Ord s => Patch s fs a -> Patch s fs a -> Ordering+comparePatchSeeks p1 p2 = compare (patchSeek p1) (patchSeek p2)
+ src/StreamPatch/Patch/Linearize/InPlace.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE UndecidableInstances #-}++module StreamPatch.Patch.Linearize.InPlace where++import StreamPatch.Patch+import StreamPatch.HFunctorList+import StreamPatch.Patch.Linearize.Common++import GHC.Generics ( Generic )+import Data.Vinyl++import Control.Monad.State+import qualified Data.List              as List+import qualified Data.ByteString        as BS+import qualified Data.Text              as Text+import Data.Text              ( Text )+import StreamPatch.Util       ( traverseM )++type Len = Int++class HasLength a where+    -- | Returns non-negative values only.+    getLength :: a -> Len++instance HasLength BS.ByteString where getLength = BS.length+instance HasLength Text          where getLength = Text.length+instance HasLength String        where getLength = List.length++data Error fs a+  = ErrorOverlap -- ^ Two edits wrote to the same offset.+        Len -- ^ absolute position in stream+        (Patch Len fs a) -- ^ overlapping patch+        (Patch Len fs a) -- ^ previous patch+    deriving (Generic)++deriving instance (Eq a, Eq (Rec (Flap a) fs)) => Eq (Error fs a)+deriving instance (Show a, ReifyConstraint Show (Flap a) fs, RMap fs, RecordToList fs) => Show (Error fs a)+deriving instance Functor     (HFunctorList fs) => Functor     (Error fs)+deriving instance Foldable    (HFunctorList fs) => Foldable    (Error fs)+deriving instance Traversable (HFunctorList fs) => Traversable (Error fs)++linearizeInPlace+    :: forall a fs. HasLength a+    => [Patch Len fs a]+    -> Either (Error fs a) [Patch Len fs a]+linearizeInPlace ps = evalState (traverseM go (List.sortBy comparePatchSeeks ps)) (0, undefined)+  where+    go p@(Patch a s _)  = do+        (cursor, pPrev) <- get+        let skip = s - cursor+        if skip < 0 then do+            -- next absolute seek is before cursor: current patch overlaps prev+            return $ Left $ ErrorOverlap cursor p pPrev+        else do+            let cursor' = cursor + skip + getLength a+                p' = p { patchSeek = skip }+            put (cursor', p)+            return $ Right p'
+ src/StreamPatch/Patch/Linearize/Insert.hs view
@@ -0,0 +1,41 @@+module StreamPatch.Patch.Linearize.Insert where++import Control.Monad.State+import Data.List qualified as List+import StreamPatch.Util ( traverseM )++{-+-- Result seeks are non-negative, so natural-like types are safe to use.+linearizeInsert+    :: (Integral sf, Integral st)+    => [Patch sf fs a] -> Either (Error (Patch sf fs a)) [Patch st fs a]+linearizeInsert = linearize patchSeek (\s p -> p { patchSeek = s })+-}++-- | Linearize some list of @a@s.+--+-- For non-empty lists, the result is a tuple of the first @a@, followed by the+-- linearized @a@s (converted to @b@). Linearized values are non-negative, so+-- natural-like types are safe to use for @st@.+linearize+    :: (Integral sf, Integral st)+    => (a -> sf)+    -> (st -> a -> b)+    -> [a] -> Either (a, a) (Maybe (a, [b]))+linearize f g as =+    case List.sortBy cmp as of+      []      -> Right Nothing+      (a:as') -> do+        bs <- evalState (traverseM go as') a+        Right $ Just (a, bs)+  where+    cmp a1 a2 = compare (f a1) (f a2)+    go a = do+        a' <- get+        let s' = fromIntegral $ f a - f a'+        if s' == 0+        then return $ Left (a, a')+        else put a >> return (Right (g s' a))++linearize' :: (Integral sf, Integral st) => [sf] -> Either (sf, sf) (Maybe (sf, [st]))+linearize' = linearize id const
+ src/StreamPatch/Seek.hs view
@@ -0,0 +1,19 @@+module StreamPatch.Seek where++import Data.Kind+import GHC.Generics ( Generic )+import Data.Data ( Data )+import Data.Aeson++class Seek (s :: Type) where+    type SeekRep s :: Type+    unSeek :: s -> SeekRep s+    mkSeek :: SeekRep s -> s++-- | Seek to stream index. Relative to a base, which can either move during+--   patching (actual relative) or stay constant ("absolute"). We abstract over+--   the internal type to allow using natural types where we want to disallow+--   negatives, and possibly allow sized types for efficiency.+newtype SIx a = SIx { unSIx :: a }+    deriving stock (Generic, Data, Show, Eq, Ord)+    deriving (ToJSON, FromJSON) via a
+ src/StreamPatch/Simple.hs view
@@ -0,0 +1,148 @@+-- | TODO remove ASAP. figure out Aeson with Vinyl++{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-}++module StreamPatch.Simple where++import StreamPatch.Patch+import StreamPatch.HFunctorList+import StreamPatch.Patch.Binary qualified as Bin+import StreamPatch.Patch.Compare qualified as Compare+import StreamPatch.Patch.Compare ( Via(..), CompareRep )+import StreamPatch.Patch.Align qualified as Align++import Numeric.Natural+import Data.Functor.Const+import Data.Vinyl+import Data.Vinyl.TypeLevel+import Data.Aeson+import GHC.Generics++data MultiPatch s (v :: Via) a = MultiPatch+  { mpData           :: a+  , mpSeek           :: s+  , mpCompare        :: Maybe (CompareRep v a)+  , mpNullTerminates :: Maybe Natural+  , mpMaxBytes       :: Maybe Natural -- TODO confirm meaning, maybe improve name+  , mpAligned        :: Maybe Integer+  } deriving (Generic)++instance Functor (MultiPatch s ('ViaEq ec)) where+    fmap f (MultiPatch d s c n m a) = MultiPatch (f d) s (fmap f c) n m a+instance Functor (MultiPatch s ('ViaDigest h)) where+    fmap f (MultiPatch d s c n m a) = MultiPatch (f d) s c          n m a++deriving instance (Eq   a, Eq   s, Eq   (CompareRep v a)) => Eq   (MultiPatch s v a)+deriving instance (Show a, Show s, Show (CompareRep v a)) => Show (MultiPatch s v a)++data Aligned p = Aligned+  { alignedAlign   :: Integer+  , alignedPatches :: [p]+  } deriving (Eq, Show, Generic)++--------------------------------------------------------------------------------++jsonCfgCamelDrop :: Int -> Options+jsonCfgCamelDrop x = defaultOptions+  { fieldLabelModifier = camelTo2 '_' . drop x+  , rejectUnknownFields = True }++instance (ToJSON   (CompareRep v a), ToJSON   a, ToJSON   s) => ToJSON   (MultiPatch s v a) where+    toJSON     = genericToJSON     $ jsonCfgCamelDrop 2+    toEncoding = genericToEncoding $ jsonCfgCamelDrop 2+instance (FromJSON (CompareRep v a), FromJSON a, FromJSON s) => FromJSON (MultiPatch s v a) where+    parseJSON  = genericParseJSON  $ jsonCfgCamelDrop 2++instance (ToJSON   p) => ToJSON   (Aligned p) where+    toJSON     = genericToJSON     $ jsonCfgCamelDrop 7+    toEncoding = genericToEncoding $ jsonCfgCamelDrop 7+instance (FromJSON p) => FromJSON (Aligned p) where+    parseJSON  = genericParseJSON  $ jsonCfgCamelDrop 7++--------------------------------------------------------------------------------++convert :: (MultiPatch s v a -> Rec (Flap a) fs) -> MultiPatch s v a -> Patch s fs a+convert f p = Patch { patchData = mpData p+                    , patchSeek = mpSeek p+                    , patchMeta = HFunctorList $ f p }++-- TODO how to clean up (it's like liftA2 over n instead of 2. like a fold)+-- likely 'ap'!+convertBinAlign+    :: forall a v+    .  MultiPatch Integer v a+    -> Patch Integer '[Const (Align.Meta Int), Const Bin.MetaPrep, Compare.Meta v, Bin.Meta] a+convertBinAlign = convert go+  where go p = cmAlign p <+> cmBinPrep p <+> cmCompare p <+> cmBin p++convertBin+    :: forall s a v+    .  MultiPatch s v a+    -> Patch s '[Const Bin.MetaPrep, Compare.Meta v, Bin.Meta] a+convertBin p = convert go p+  where go s = cmBinPrep s <+> cmCompare s <+> cmBin s++convertAlign+    :: forall a v+    .  MultiPatch Integer v a+    -> Patch Integer '[Const (Align.Meta Int)] a+convertAlign p = convert cmAlign p++convertEmpty+    :: forall s a v+    .  MultiPatch s v a+    -> Patch s '[] a+convertEmpty p = convert (const RNil) p++--------------------------------------------------------------------------------++align+    :: forall st a ss rs is i r+    .  ( r ~ Const (Align.Meta st)+       , Num st, Eq st+       , rs ~ RDelete r ss+       , RElem r ss i+       , RSubset rs ss is )+    => Aligned (Patch Integer ss a)+    -> Either (Align.Error st) [Patch st rs a]+align (Aligned a ps) = traverse (Align.align a) ps++--------------------------------------------------------------------------------++cmBin :: MultiPatch s c a -> Rec (Flap a) '[Bin.Meta]+cmBin p = cm $ Bin.Meta { Bin.mNullTerminates = mpNullTerminates p }++cmCompare :: MultiPatch s v a -> Rec (Flap a) '[Compare.Meta v]+cmCompare p = cm $ Compare.Meta { Compare.mCompare = mpCompare p }++cmBinPrep :: MultiPatch s c a -> Rec (Flap a) '[Const Bin.MetaPrep]+cmBinPrep p = cm $ Const $ Bin.MetaPrep { Bin.mpMaxBytes = mpMaxBytes p }++cmAlign :: MultiPatch Integer c a -> Rec (Flap a) '[Const (Align.Meta Int)]+cmAlign p = cm $ Const $ Align.Meta { Align.mExpected = fromInteger <$> mpAligned p }++cm :: f a -> Rec (Flap a) '[f]+cm fa = Flap fa :& RNil++--------------------------------------------------------------------------------++-- | Convenience function to wrap a single meta into an 'HFunctorList'.+metaWrap1 :: f a -> HFunctorList '[f] a+metaWrap1 = HFunctorList . cm++-- | Convenience function for the empty 'HFunctorList'.+metaEmpty :: HFunctorList '[] a+metaEmpty = HFunctorList RNil++--------------------------------------------------------------------------------++convertBackBin :: forall v s a. Patch s '[Compare.Meta v, Bin.Meta] a -> MultiPatch s v a+convertBackBin p = MultiPatch { mpData           = patchData p+                              , mpSeek           = patchSeek p+                              , mpCompare        = Compare.mCompare @v $ hflGet $ patchMeta p+                              , mpNullTerminates = Bin.mNullTerminates $ hflGet $ patchMeta p+                              , mpMaxBytes       = Nothing+                              , mpAligned        = Nothing+                              }
src/StreamPatch/Stream.hs view
@@ -1,78 +1,131 @@-module StreamPatch.Stream where+{- | Stream monads. -import           GHC.Natural-import           Data.Kind-import qualified Data.ByteString            as BS-import qualified Data.ByteString.Builder    as BB-import           Control.Monad.State-import           Control.Monad.Reader-import           System.IO                  ( Handle, SeekMode(..), hSeek )+By stream, I mean a container indexed by the naturals(/integers). Note that not+all parts of streampatch are limited to this, but it's an extremely useful+invariant, without which patch linearization & application gets a whole lot more+complex. So for now, streams it is. -import qualified Data.List                  as List+These are designed to support easy pure and impure implementations. That's the+reasoning behind forward-only streams, and separating overwrites (easy impure,+mid pure) from inserts (hard impure, ease pure).+-} --- TODO also require reporting cursor position (for error reporting)-class Monad m => MonadFwdInplaceStream m where+module StreamPatch.Stream where++import Data.Kind+import Data.ByteString qualified as B+import Data.ByteString.Builder qualified as BB+import Control.Monad.State+import Control.Monad.Reader+import System.IO qualified as IO+import Data.List qualified as List++-- | Streams supporting forward seeking and in-place edits (length never+--   changes).+class Monad m => FwdInplaceStream m where     type Chunk m :: Type -    -- | Read a number of bytes without advancing the cursor.-    readahead :: Natural -> m (Chunk m)+    -- | The unsigned integral type used for indexing. 'Int' has a sign, but is+    --   often used internally; 'Integer' also gets some use; 'Natural' is the+    --   most mathematically honest. I leave the decision up to the instance to+    --   allow them to be as efficient as possible.+    type Index m :: Type -    -- | Advance cursor without reading.-    advance :: Natural -> m ()+    -- | Read a number of elements forward without moving the cursor.+    --+    -- Argument must be positive.+    readahead :: Index m -> m (Chunk m) -    -- | Insert bytes into the stream at the cursor position, overwriting-    --   existing bytes.+    -- | Overlay a chunk onto the stream at the current cursor position,+    --   overwriting existing elements.+    --+    -- Moves the cursor to the right by the length of the chunk.     overwrite :: Chunk m -> m () --- TODO Need MonoTraversable to define for Text, ByteString etc easily. Bleh. I--- think Snoyman's advice is to reimplement. Also bleh.-instance Monad m => MonadFwdInplaceStream (StateT ([a], [a]) m) where-    type Chunk (StateT ([a], [a]) m) = [a]-    readahead n = List.take (fromIntegral n) <$> gets fst-    advance n = do-        (src, out) <- get-        let (bs, src') = List.splitAt (fromIntegral n) src-        put (src', out <> bs)+    -- | Move cursor forwards without reading. Must be positive.+    advance :: Index m -> m ()++    -- | Get the current cursor position.+    --+    -- Intended for error messages.+    getCursor :: m (Index m)++instance Monad m => FwdInplaceStream (StateT (B.ByteString, BB.Builder, Int) m) where+    type Chunk (StateT (B.ByteString, BB.Builder, Int) m) = B.ByteString+    type Index (StateT (B.ByteString, BB.Builder, Int) m) = Int+    readahead n = get >>= \(src, _, _) -> return $ B.take n src     overwrite bs = do-        (src, out) <- get-        let (_, src') = List.splitAt (List.length bs) src-        put (src', out <> bs)+        (src, out, pos) <- get+        let (_, src') = B.splitAt (B.length bs) src+            out' = out <> BB.byteString bs+            pos' = pos + B.length bs+        put (src', out', pos')+    advance n = do+        (src, out, pos) <- get+        let (bs, src') = B.splitAt n src+            out' = out <> BB.byteString bs+            pos' = pos + n+        put (src', out', pos')+    getCursor = get >>= \(_, _, pos) -> return pos -instance MonadIO m => MonadFwdInplaceStream (ReaderT Handle m) where-    type Chunk (ReaderT Handle m) = BS.ByteString+instance MonadIO m => FwdInplaceStream (ReaderT IO.Handle m) where+    type Chunk (ReaderT IO.Handle m) = B.ByteString+    type Index (ReaderT IO.Handle m) = Integer     readahead n = do         hdl <- ask-        bs <- liftIO $ BS.hGet hdl (fromIntegral n)-        liftIO $ hSeek hdl RelativeSeek (- fromIntegral n)-        return bs+        bs <- liftIO $ B.hGet hdl $ fromInteger n -- TODO Integer -> Int :(+        liftIO $ IO.hSeek hdl IO.RelativeSeek (-n)+        pure bs+    overwrite bs = do+        hdl <- ask+        liftIO $ B.hPut hdl bs     advance n = do         hdl <- ask-        liftIO $ hSeek hdl RelativeSeek (fromIntegral n)-    overwrite bs = do+        liftIO $ IO.hSeek hdl IO.RelativeSeek n+    getCursor = do         hdl <- ask-        liftIO $ BS.hPut hdl bs+        pos <- liftIO $ IO.hTell hdl+        return $ fromInteger pos -instance Monad m => MonadFwdInplaceStream (StateT (BS.ByteString, BB.Builder) m) where-    type Chunk (StateT (BS.ByteString, BB.Builder) m) = BS.ByteString-    readahead n = BS.take (fromIntegral n) <$> gets fst-    advance n = do-        (src, out) <- get-        let (bs, src') = BS.splitAt (fromIntegral n) src-        put (src', out <> BB.byteString bs)+-- TODO Need MonoTraversable to define for Text, ByteString etc easily. Bleh. I+-- think Snoyman's advice is to reimplement. Also bleh.+instance Monad m => FwdInplaceStream (StateT ([a], [a], Int) m) where+    type Chunk (StateT ([a], [a], Int) m) = [a]+    type Index (StateT ([a], [a], Int) m) = Int+    readahead n = get >>= \(src, _, _) -> return $ List.take n src     overwrite bs = do-        (src, out) <- get-        let (_, src') = BS.splitAt (BS.length bs) src-        put (src', out <> BB.byteString bs)+        (src, out, pos) <- get+        let (_, src') = List.splitAt (List.length bs) src+            out' = out <> bs+            pos' = pos + List.length bs+        put (src', out', pos')+    advance n = do+        (src, out, pos) <- get+        let (bs, src') = List.splitAt (fromIntegral n) src+            out' = out <> bs+            pos' = pos + n+        put (src', out', pos')+    getCursor = get >>= \(_, _, pos) -> return pos --- | A forward stream, but backward too.-class MonadFwdInplaceStream m => MonadCursorInplaceStream m where-    -- | Move cursor.-    move :: Integer -> m ()+-- | Streams supporting forward seeking and arbitrary edits.+class FwdInplaceStream m => FwdStream m where+    -- | Write a chunk into the stream at the current cursor position.+    --+    -- Moves the cursor to the right by the length of the chunk.+    write :: Chunk m -> m () -instance MonadIO m => MonadCursorInplaceStream (ReaderT Handle m) where-    move n = do-        hdl <- ask-        liftIO $ hSeek hdl RelativeSeek (fromIntegral n)+    -- | Delete a sized chunk at the current cursor position.+    --+    -- Argument must be positive.+    delete :: Index m -> m () -class MonadFwdInplaceStream m => MonadFwdStream m where-    insert :: Chunk m -> m ()+instance Monad m => FwdStream (StateT (B.ByteString, BB.Builder, Int) m) where+    write bs = do+        (src, out, pos) <- get+        let out' = out <> BB.byteString bs+            pos' = pos + B.length bs+        put (src, out', pos')+    delete n = do+        (src, out, pos) <- get+        let src' = B.drop n src+        put (src', out, pos)
− test/BytePatch/HexByteStringSpec.hs
@@ -1,23 +0,0 @@-module BytePatch.HexByteStringSpec ( spec ) where--import           BytePatch.HexByteString-import           Test.Hspec-import           Util--import qualified Data.ByteString                as BS--spec :: Spec-spec = do-    let parse = parseFromCharStream parseHexByteString-        bs = BS.pack-    it "parses valid hex bytestrings" $ do-      parse "00" `shouldBe` Just (bs [0x00])-      parse "FF" `shouldBe` Just (bs [0xFF])-      parse "1234" `shouldBe` Just (bs [0x12, 0x34])-      parse "01 9A FE" `shouldBe` Just (bs [0x01, 0x9A, 0xFE])-      parse "FFFFFFFF" `shouldBe` Just (BS.replicate 4 0xFF)-      parse "12 34    AB CD" `shouldBe` Just (bs [0x12, 0x34, 0xAB, 0xCD])-    it "fails to parse invalid hex bytestrings" $ do-      parse "-00" `shouldBe` Nothing-      parse "FG" `shouldBe` Nothing-      parse "0x1234" `shouldBe` Nothing
test/StreamPatch/ApplySpec.hs view
@@ -1,19 +1,19 @@ module StreamPatch.ApplySpec ( spec ) where -import           StreamPatch.Apply-import           Test.Hspec-import           Test.Hspec.QuickCheck-import           Test.QuickCheck-import           Util+import StreamPatch.Apply+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Util -import           StreamPatch.Patch-import           Data.Functor.Const-import           Data.Vinyl-import           GHC.Natural+import StreamPatch.Patch+import Data.Functor.Const+import Data.Vinyl+import GHC.Natural  spec :: Spec spec = do-    let patch' ps = runPureSimpleFwdList $ makePatchscript ps+    let patch' ps = runPureFwdList $ makePatchscript ps     it "applies valid simple forward in-place patches" $ do       patch' []           "1234567890" `shouldBe` "1234567890"       patch' [(0, "XXX")] "1234567890" `shouldBe` "XXX4567890"@@ -30,7 +30,7 @@ -- nah I cba lol just trust me --manualPatch :: [Patch 'FwdSeek '[] [a]] -> [a] -> [a] -genPatchList :: Arbitrary a => Natural -> Gen [Patch 'FwdSeek '[] [a]]+genPatchList :: Arbitrary a => Natural -> Gen [Patch Natural '[] [a]] genPatchList = skip []   where     skip ps n = do
test/StreamPatch/Patch/AlignSpec.hs view
@@ -1,23 +1,24 @@ module StreamPatch.Patch.AlignSpec ( spec ) where -import           StreamPatch.Patch.Align-import           Test.Hspec-import           Util+import StreamPatch.Patch.Align+import Test.Hspec+import Util -import           StreamPatch.Patch-import           Data.Vinyl-import           Data.Functor.Const-import           GHC.Natural+import StreamPatch.Patch+import StreamPatch.HFunctorList+import Data.Vinyl+import Data.Functor.Const+import GHC.Natural  makeUnalignedPatch     :: [(Integer, a)]-    -> [Patch 'RelSeek '[Const (Meta 'FwdSeek)] a]+    -> [Patch Integer '[Const (Meta Natural)] a] makeUnalignedPatch = map go-  where go (n, a) = Patch a n $ FunctorRec $ Flap (Const (Meta Nothing)) :& RNil+  where go (n, a) = Patch a n $ HFunctorList $ Flap (Const (Meta Nothing)) :& RNil  align'     :: Integer -> [(Integer, String)]-    -> Either (Error 'FwdSeek) [Patch 'FwdSeek '[] String]+    -> Either (Error Natural) [Patch Natural '[] String] align' sBase = traverse (align sBase) . makeUnalignedPatch  spec :: Spec
test/StreamPatch/Patch/LinearizeSpec.hs view
@@ -1,19 +1,23 @@ module StreamPatch.Patch.LinearizeSpec ( spec ) where -import           StreamPatch.Patch.Linearize-import           Test.Hspec-import           Util+import StreamPatch.Patch.Linearize.InPlace+import StreamPatch.Patch.Linearize.Insert+import Test.Hspec+import Util -import           StreamPatch.Patch-import           Data.Functor.Const-import           Data.Either.Combinators-import           GHC.Natural-import           Optics-import           Data.Generics.Product.Any+import StreamPatch.Patch+import Data.Functor.Const+import Data.Either.Combinators+import GHC.Natural+import Optics+import Data.Generics.Product.Any  spec :: Spec spec = do-    let p  = makePatch' @'AbsSeek-        p' = makePatch' @'FwdSeek-    it "linearizes a simple valid patch" $ do-      linearize [p "1" 1, p "5" 5, p "2" 2] `shouldBe` Right [p' "1" 1, p' "2" 0, p' "5" 2]+    let p  = makePatch+    it "linearizes a simple valid in-place patch" $ do+      linearizeInPlace   [p "1" 1, p "5" 5, p "2" 2]+        `shouldBe` Right [p "1" 1, p "2" 0, p "5" 2]+    it "TODO" $ do+      linearize'  [1,2,3,4,5] `shouldBe` Right (Just ( 1, [1,1,1,1]))+      linearize' [-1,2,3,4,5] `shouldBe` Right (Just (-1, [3,1,1,1]))
test/Util.hs view
@@ -2,28 +2,20 @@  module Util where -import           StreamPatch.Patch-import           Data.Vinyl-import           Text.Megaparsec-import           Data.Void-import           GHC.Natural-import           Test.QuickCheck----parseFromCharStream :: (MonadParsec e s m, Token s ~ Char) => m a -> s -> Maybe a-parseFromCharStream :: (Stream s, Token s ~ Char) => Parsec Void s a -> s -> Maybe a-parseFromCharStream parser text = parseMaybe parser text+import StreamPatch.Patch+import StreamPatch.HFunctorList+import Data.Vinyl+import GHC.Natural+import Test.QuickCheck  -- TODO this is in quickcheck-instances instance Arbitrary Natural where   arbitrary = arbitrarySizedNatural   shrink    = shrinkIntegral -makePatch :: a -> Natural -> Patch 'FwdSeek '[] a-makePatch a n = Patch a n $ FunctorRec RNil--makePatch' :: forall s a. a -> SeekRep s -> Patch s '[] a-makePatch' a n = Patch a n $ FunctorRec RNil+makePatch :: a -> s -> Patch s '[] a+makePatch a s = Patch a s $ HFunctorList RNil -makePatchscript :: [(Natural, a)] -> [Patch 'FwdSeek '[] a]+makePatchscript :: [(s, a)] -> [Patch s '[] a] makePatchscript = map go   where go (n, a) = makePatch a n