{-# LANGUAGE MultiParamTypeClasses #-}
module Darcs.Util.Index
( openIndex
, updateIndexFrom
, indexFormatValid
, treeFromIndex
, listFileIDs
, Index
, filter
, getFileID
, IndexEntry(..)
, dumpIndex
, align
) where
import Darcs.Prelude hiding ( readFile, writeFile, filter )
import Darcs.Util.ByteString ( readSegment, decodeLocale )
import qualified Darcs.Util.File ( getFileStatus )
import Darcs.Util.Global ( debugMessage )
import Darcs.Util.Hash ( Hash(..), mkHash, rawHash, sha256 )
import Darcs.Util.Tree
import Darcs.Util.Tree.Hashed ( darcsTreeHash )
import Darcs.Util.Path
( AnchoredPath(..)
, realPath
, anchoredRoot
, Name
, rawMakeName
, appendPath
, flatten
)
import Darcs.Util.Progress ( beginTedious, endTedious, finishedOneIO )
import Control.Monad( when )
import Control.Exception( catch, throw, SomeException, Exception )
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import Data.ByteString.Unsafe( unsafeHead, unsafeDrop )
import Data.ByteString.Internal
( c2w
, fromForeignPtr
, nullForeignPtr
, toForeignPtr
)
import qualified Data.ByteString.Short.Internal as BS
import Data.Int( Int64, Int32 )
import Data.Word( Word8 )
import Data.IORef( )
import Data.Maybe( fromJust, isJust, isNothing )
import Data.Typeable( Typeable )
import Foreign.Marshal.Utils ( copyBytes )
import Foreign.Storable
import Foreign.ForeignPtr( ForeignPtr, withForeignPtr, castForeignPtr )
import Foreign.Ptr( Ptr, plusPtr )
import System.IO ( hPutStrLn, stderr )
import System.IO.MMap( mmapFileForeignPtr, mmapWithFilePtr, Mode(..) )
import System.Directory( doesFileExist, getCurrentDirectory )
import System.Directory( renameFile )
import System.FilePath( (<.>) )
import qualified System.Posix.Files as F ( fileID )
import System.FilePath ( (</>) )
import qualified System.Posix.Files as F
( modificationTimeHiRes, fileSize, isDirectory, isSymbolicLink
, FileStatus
)
import System.Posix.Types ( FileID, FileOffset )
data Item = Item { Item -> Ptr ()
iBase :: !(Ptr ())
, Item -> ByteString
iHashAndDescriptor :: !B.ByteString
} deriving Int -> Item -> ShowS
[Item] -> ShowS
Item -> String
(Int -> Item -> ShowS)
-> (Item -> String) -> ([Item] -> ShowS) -> Show Item
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Item -> ShowS
showsPrec :: Int -> Item -> ShowS
$cshow :: Item -> String
show :: Item -> String
$cshowList :: [Item] -> ShowS
showList :: [Item] -> ShowS
Show
index_version :: B.ByteString
index_version :: ByteString
index_version = String -> ByteString
BC.pack String
"HSI7"
index_endianness_indicator :: Int32
index_endianness_indicator :: Int32
index_endianness_indicator = Int32
1
size_header, size_magic, size_endianness_indicator :: Int
size_magic :: Int
size_magic = Int
4
size_endianness_indicator :: Int
size_endianness_indicator = Int
4
= Int
size_magic Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_endianness_indicator
size_dsclen, size_hash, size_size, size_aux, size_fileid :: Int
size_size :: Int
size_size = Int
8
size_aux :: Int
size_aux = Int
8
size_fileid :: Int
size_fileid = Int
8
size_dsclen :: Int
size_dsclen = Int
4
size_hash :: Int
size_hash = Int
32
size_type, size_null :: Int
size_type :: Int
size_type = Int
1
size_null :: Int
size_null = Int
1
off_size, off_aux, off_hash, off_dsc, off_dsclen, off_fileid :: Int
off_size :: Int
off_size = Int
0
off_aux :: Int
off_aux = Int
off_size Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_size
off_fileid :: Int
off_fileid = Int
off_aux Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_aux
off_dsclen :: Int
off_dsclen = Int
off_fileid Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_fileid
off_hash :: Int
off_hash = Int
off_dsclen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_dsclen
off_dsc :: Int
off_dsc = Int
off_hash Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_hash
itemAllocSize :: AnchoredPath -> Int
itemAllocSize :: AnchoredPath -> Int
itemAllocSize AnchoredPath
apath = Int -> Int -> Int
forall a. Integral a => a -> a -> a
align Int
4 (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$
Int
size_size Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_aux Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_fileid Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_dsclen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_hash Int -> Int -> Int
forall a. Num a => a -> a -> a
+
Int
size_type Int -> Int -> Int
forall a. Num a => a -> a -> a
+ ByteString -> Int
B.length (AnchoredPath -> ByteString
flatten AnchoredPath
apath) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_null
itemSize :: Item -> Int
itemSize :: Item -> Int
itemSize Item
i =
Int
size_size Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_aux Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_fileid Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_dsclen Int -> Int -> Int
forall a. Num a => a -> a -> a
+
(ByteString -> Int
B.length (ByteString -> Int) -> ByteString -> Int
forall a b. (a -> b) -> a -> b
$ Item -> ByteString
iHashAndDescriptor Item
i) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_null
itemNext :: Item -> Int
itemNext :: Item -> Int
itemNext Item
i = Int -> Int -> Int
forall a. Integral a => a -> a -> a
align Int
4 (Item -> Int
itemSize Item
i)
iHash, iDescriptor :: Item -> B.ByteString
iDescriptor :: Item -> ByteString
iDescriptor = Int -> ByteString -> ByteString
unsafeDrop Int
size_hash (ByteString -> ByteString)
-> (Item -> ByteString) -> Item -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Item -> ByteString
iHashAndDescriptor
iHash :: Item -> ByteString
iHash = Int -> ByteString -> ByteString
B.take Int
size_hash (ByteString -> ByteString)
-> (Item -> ByteString) -> Item -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Item -> ByteString
iHashAndDescriptor
iPath :: Item -> FilePath
iPath :: Item -> String
iPath = ByteString -> String
decodeLocale (ByteString -> String) -> (Item -> ByteString) -> Item -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> ByteString -> ByteString
unsafeDrop Int
1 (ByteString -> ByteString)
-> (Item -> ByteString) -> Item -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Item -> ByteString
iDescriptor
iSize, iAux :: Item -> Ptr Int64
iSize :: Item -> Ptr Int64
iSize Item
i = Ptr () -> Int -> Ptr Int64
forall a b. Ptr a -> Int -> Ptr b
plusPtr (Item -> Ptr ()
iBase Item
i) Int
off_size
iAux :: Item -> Ptr Int64
iAux Item
i = Ptr () -> Int -> Ptr Int64
forall a b. Ptr a -> Int -> Ptr b
plusPtr (Item -> Ptr ()
iBase Item
i) Int
off_aux
iFileID :: Item -> Ptr FileID
iFileID :: Item -> Ptr FileID
iFileID Item
i = Ptr () -> Int -> Ptr FileID
forall a b. Ptr a -> Int -> Ptr b
plusPtr (Item -> Ptr ()
iBase Item
i) Int
off_fileid
itemIsDir :: Item -> Bool
itemIsDir :: Item -> Bool
itemIsDir Item
i = ByteString -> Word8
unsafeHead (Item -> ByteString
iDescriptor Item
i) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Char -> Word8
c2w Char
'D'
type FileStatus = Maybe F.FileStatus
modificationTime :: FileStatus -> Int64
modificationTime :: FileStatus -> Int64
modificationTime = Int64 -> (FileStatus -> Int64) -> FileStatus -> Int64
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Int64
0 (POSIXTime -> Int64
forall b. Integral b => POSIXTime -> b
forall a b. (RealFrac a, Integral b) => a -> b
truncate (POSIXTime -> Int64)
-> (FileStatus -> POSIXTime) -> FileStatus -> Int64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (POSIXTime -> POSIXTime -> POSIXTime
forall a. Num a => a -> a -> a
*POSIXTime
1e9) (POSIXTime -> POSIXTime)
-> (FileStatus -> POSIXTime) -> FileStatus -> POSIXTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FileStatus -> POSIXTime
F.modificationTimeHiRes)
fileSize :: FileStatus -> FileOffset
fileSize :: FileStatus -> FileOffset
fileSize = FileOffset
-> (FileStatus -> FileOffset) -> FileStatus -> FileOffset
forall b a. b -> (a -> b) -> Maybe a -> b
maybe FileOffset
0 FileStatus -> FileOffset
F.fileSize
fileExists :: FileStatus -> Bool
fileExists :: FileStatus -> Bool
fileExists = Bool -> (FileStatus -> Bool) -> FileStatus -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (Bool -> FileStatus -> Bool
forall a b. a -> b -> a
const Bool
True)
isDirectory :: FileStatus -> Bool
isDirectory :: FileStatus -> Bool
isDirectory = Bool -> (FileStatus -> Bool) -> FileStatus -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False FileStatus -> Bool
F.isDirectory
fileID :: FileStatus -> FileID
fileID :: FileStatus -> FileID
fileID = FileID -> (FileStatus -> FileID) -> FileStatus -> FileID
forall b a. b -> (a -> b) -> Maybe a -> b
maybe FileID
0 FileStatus -> FileID
F.fileID
createItem :: ItemType -> AnchoredPath -> ForeignPtr () -> Int -> IO Item
createItem :: ItemType -> AnchoredPath -> ForeignPtr () -> Int -> IO Item
createItem ItemType
typ AnchoredPath
apath ForeignPtr ()
fp Int
off = do
let dsc :: ByteString
dsc =
[ByteString] -> ByteString
B.concat
[ Char -> ByteString
BC.singleton (Char -> ByteString) -> Char -> ByteString
forall a b. (a -> b) -> a -> b
$ if ItemType
typ ItemType -> ItemType -> Bool
forall a. Eq a => a -> a -> Bool
== ItemType
TreeType then Char
'D' else Char
'F'
, AnchoredPath -> ByteString
flatten AnchoredPath
apath
, Word8 -> ByteString
B.singleton Word8
0
]
(ForeignPtr Word8
dsc_fp, Int
dsc_start, Int
dsc_len) = ByteString -> (ForeignPtr Word8, Int, Int)
toForeignPtr ByteString
dsc
ForeignPtr () -> (Ptr () -> IO Item) -> IO Item
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ()
fp ((Ptr () -> IO Item) -> IO Item) -> (Ptr () -> IO Item) -> IO Item
forall a b. (a -> b) -> a -> b
$ \Ptr ()
p ->
ForeignPtr Word8 -> (Ptr Word8 -> IO Item) -> IO Item
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr Word8
dsc_fp ((Ptr Word8 -> IO Item) -> IO Item)
-> (Ptr Word8 -> IO Item) -> IO Item
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
dsc_p -> do
Ptr () -> Int -> Int32 -> IO ()
forall b. Ptr b -> Int -> Int32 -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr ()
p (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
off_dsclen) (Int -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dsc_len :: Int32)
Ptr (ZonkAny 1) -> Ptr (ZonkAny 1) -> Int -> IO ()
forall a. Ptr a -> Ptr a -> Int -> IO ()
copyBytes
(Ptr () -> Int -> Ptr (ZonkAny 1)
forall a b. Ptr a -> Int -> Ptr b
plusPtr Ptr ()
p (Int -> Ptr (ZonkAny 1)) -> Int -> Ptr (ZonkAny 1)
forall a b. (a -> b) -> a -> b
$ Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
off_dsc)
(Ptr Word8 -> Int -> Ptr (ZonkAny 1)
forall a b. Ptr a -> Int -> Ptr b
plusPtr Ptr Word8
dsc_p Int
dsc_start)
(Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dsc_len)
ForeignPtr () -> Int -> IO Item
peekItem ForeignPtr ()
fp Int
off
peekItem :: ForeignPtr () -> Int -> IO Item
peekItem :: ForeignPtr () -> Int -> IO Item
peekItem ForeignPtr ()
fp Int
off =
ForeignPtr () -> (Ptr () -> IO Item) -> IO Item
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr ()
fp ((Ptr () -> IO Item) -> IO Item) -> (Ptr () -> IO Item) -> IO Item
forall a b. (a -> b) -> a -> b
$ \Ptr ()
p -> do
nl' :: Int32 <- Ptr () -> Int -> IO Int32
forall b. Ptr b -> Int -> IO Int32
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr ()
p (Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
off_dsclen)
when (nl' <= 2) $ fail "Descriptor too short in peekItem!"
let nl = Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
nl'
dsc =
ForeignPtr Word8 -> Int -> Int -> ByteString
fromForeignPtr
(ForeignPtr () -> ForeignPtr Word8
forall a b. ForeignPtr a -> ForeignPtr b
castForeignPtr ForeignPtr ()
fp)
(Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
off_hash)
(Int
size_hash Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
nl Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
size_null)
return $! Item {iBase = plusPtr p off, iHashAndDescriptor = dsc}
updateItem :: Item -> Int64 -> Hash -> IO ()
updateItem :: Item -> Int64 -> Hash -> IO ()
updateItem Item
item Int64
size Hash
hash =
do Ptr Int64 -> Int64 -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke (Item -> Ptr Int64
iSize Item
item) Int64
size
ByteString -> ByteString -> IO ()
unsafePokeBS (Item -> ByteString
iHash Item
item) (Hash -> ByteString
rawHash Hash
hash)
updateFileID :: Item -> FileID -> IO ()
updateFileID :: Item -> FileID -> IO ()
updateFileID Item
item FileID
fileid = Ptr FileID -> FileID -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke (Item -> Ptr FileID
iFileID Item
item) FileID
fileid
updateAux :: Item -> Int64 -> IO ()
updateAux :: Item -> Int64 -> IO ()
updateAux Item
item Int64
aux = Ptr Int64 -> Int64 -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke (Item -> Ptr Int64
iAux Item
item) Int64
aux
updateTime :: Item -> Int64 -> IO ()
updateTime :: Item -> Int64 -> IO ()
updateTime Item
item Int64
mtime = Item -> Int64 -> IO ()
updateAux Item
item Int64
mtime
iHash' :: Item -> Maybe Hash
iHash' :: Item -> Maybe Hash
iHash' Item
i = let ih :: ByteString
ih = Item -> ByteString
iHash Item
i in if ByteString
ih ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
nullHash then Maybe Hash
forall a. Maybe a
Nothing else Hash -> Maybe Hash
forall a. a -> Maybe a
Just (ByteString -> Hash
mkHash ByteString
ih)
nullHash :: B.ByteString
nullHash :: ByteString
nullHash = Int -> Word8 -> ByteString
B.replicate Int
size_hash Word8
0
mmapIndex :: forall a. FilePath -> Int -> IO (ForeignPtr a, Int)
mmapIndex :: forall a. String -> Int -> IO (ForeignPtr a, Int)
mmapIndex String
indexpath Int
req_size = do
act_size <- FileOffset -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (FileOffset -> Int)
-> (FileStatus -> FileOffset) -> FileStatus -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FileStatus -> FileOffset
fileSize (FileStatus -> Int) -> IO FileStatus -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO FileStatus
Darcs.Util.File.getFileStatus String
indexpath
let size = case Int
req_size Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 of
Bool
True -> Int
req_size
Bool
False | Int
act_size Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
size_header -> Int
act_size Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
size_header
| Bool
otherwise -> Int
0
case size of
Int
0 -> (ForeignPtr a, Int) -> IO (ForeignPtr a, Int)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ForeignPtr Word8 -> ForeignPtr a
forall a b. ForeignPtr a -> ForeignPtr b
castForeignPtr ForeignPtr Word8
nullForeignPtr, Int
size)
Int
_ -> do (x, _, _) <- String -> Mode -> Maybe (Int64, Int) -> IO (ForeignPtr a, Int, Int)
forall a.
String -> Mode -> Maybe (Int64, Int) -> IO (ForeignPtr a, Int, Int)
mmapFileForeignPtr String
indexpath
Mode
ReadWriteEx ((Int64, Int) -> Maybe (Int64, Int)
forall a. a -> Maybe a
Just (Int64
0, Int
size Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_header))
return (x, size)
data IndexM m = Index { forall (m :: * -> *). IndexM m -> ForeignPtr ()
mmap :: (ForeignPtr ())
, forall (m :: * -> *). IndexM m -> String
basedir :: FilePath
, forall (m :: * -> *).
IndexM m -> AnchoredPath -> TreeItem m -> Bool
predicate :: AnchoredPath -> TreeItem m -> Bool }
| EmptyIndex
type Index = IndexM IO
data State = State
{ State -> Int
dirlength :: !Int
, State -> AnchoredPath
path :: !AnchoredPath
, State -> Int
start :: !Int
}
data Result = Result
{ Result -> Bool
changed :: !Bool
, Result -> Int
next :: !Int
, Result -> Maybe (TreeItem IO)
treeitem :: !(Maybe (TreeItem IO))
, Result -> Item
resitem :: !Item
}
readItem :: String -> Index -> State -> IO Result
readItem :: String -> Index -> State -> IO Result
readItem String
progressKey Index
index State
state = do
item <- ForeignPtr () -> Int -> IO Item
peekItem (Index -> ForeignPtr ()
forall (m :: * -> *). IndexM m -> ForeignPtr ()
mmap Index
index) (State -> Int
start State
state)
res' <- if itemIsDir item
then readDir item
else readFile item
finishedOneIO progressKey (iPath item)
return res'
where
readDir :: Item -> IO Result
readDir Item
item = do
following <- Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int64 -> Int) -> IO Int64 -> IO Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr Int64 -> IO Int64
forall a. Storable a => Ptr a -> IO a
peek (Item -> Ptr Int64
iAux Item
item)
st <- getFileStatus (iPath item)
let exists = FileStatus -> Bool
fileExists FileStatus
st Bool -> Bool -> Bool
&& FileStatus -> Bool
isDirectory FileStatus
st
fileid <- peek $ iFileID item
when (fileid == 0) $ updateFileID item (fileID st)
let substate = Item -> State -> State
substateof Item
item State
state
want =
Bool
exists Bool -> Bool -> Bool
&& (Index -> AnchoredPath -> TreeItem IO -> Bool
forall (m :: * -> *).
IndexM m -> AnchoredPath -> TreeItem m -> Bool
predicate Index
index) (State -> AnchoredPath
path State
substate) (IO (Tree IO) -> Maybe Hash -> TreeItem IO
forall (m :: * -> *). m (Tree m) -> Maybe Hash -> TreeItem m
Stub IO (Tree IO)
forall a. HasCallStack => a
undefined Maybe Hash
forall a. Maybe a
Nothing)
oldhash = Item -> Maybe Hash
iHash' Item
item
subs Int
off =
case Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
off Int
following of
Ordering
LT -> do
result <- String -> Index -> State -> IO Result
readItem String
progressKey Index
index (State -> IO Result) -> State -> IO Result
forall a b. (a -> b) -> a -> b
$ State
substate { start = off }
rest <- subs $ next result
return $! (nameof (resitem result) substate, result) : rest
Ordering
EQ -> [(Maybe Name, Result)] -> IO [(Maybe Name, Result)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []
Ordering
GT ->
String -> IO [(Maybe Name, Result)]
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO [(Maybe Name, Result)])
-> String -> IO [(Maybe Name, Result)]
forall a b. (a -> b) -> a -> b
$
String
"Offset mismatch at " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
off String -> ShowS
forall a. [a] -> [a] -> [a]
++
String
" (ends at " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
following String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
inferiors <- if want then subs $ start substate
else return []
let we_changed = [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
or [ Result -> Bool
changed Result
x | (Maybe Name
_, Result
x) <- [(Maybe Name, Result)]
inferiors ] Bool -> Bool -> Bool
|| Bool
nullleaf
nullleaf = [(Maybe Name, Result)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Maybe Name, Result)]
inferiors Bool -> Bool -> Bool
&& Maybe Hash -> Bool
forall a. Maybe a -> Bool
isNothing Maybe Hash
oldhash
tree' =
[(Name, TreeItem IO)] -> Tree IO
forall (m :: * -> *). [(Name, TreeItem m)] -> Tree m
makeTree
[ (Name
n, Maybe (TreeItem IO) -> TreeItem IO
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe (TreeItem IO) -> TreeItem IO)
-> Maybe (TreeItem IO) -> TreeItem IO
forall a b. (a -> b) -> a -> b
$ Result -> Maybe (TreeItem IO)
treeitem Result
s)
| (Just Name
n, Result
s) <- [(Maybe Name, Result)]
inferiors, Maybe (TreeItem IO) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (TreeItem IO) -> Bool) -> Maybe (TreeItem IO) -> Bool
forall a b. (a -> b) -> a -> b
$ Result -> Maybe (TreeItem IO)
treeitem Result
s ]
treehash = if Bool
we_changed then Hash -> Maybe Hash
forall a. a -> Maybe a
Just (Tree IO -> Hash
forall (m :: * -> *). Tree m -> Hash
darcsTreeHash Tree IO
tree') else Maybe Hash
oldhash
tree = Tree IO
tree' { treeHash = treehash }
when (exists && we_changed) $
updateItem item 0 (fromJust treehash)
return $ Result { changed = not exists || we_changed
, next = following
, treeitem = if want then Just $ SubTree tree
else Nothing
, resitem = item }
readFile :: Item -> IO Result
readFile Item
item = do
st <- String -> IO FileStatus
getFileStatus (Item -> String
iPath Item
item)
mtime <- fromIntegral <$> (peek $ iAux item)
size <- peek $ iSize item
fileid <- peek $ iFileID item
let mtime' = FileStatus -> Int64
modificationTime FileStatus
st
size' = FileOffset -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (FileOffset -> Int64) -> FileOffset -> Int64
forall a b. (a -> b) -> a -> b
$ FileStatus -> FileOffset
fileSize FileStatus
st
readblob = FileSegment -> IO ByteString
readSegment (Index -> String
forall (m :: * -> *). IndexM m -> String
basedir Index
index String -> ShowS
</> (Item -> String
iPath Item
item), Maybe (Int64, Int)
forall a. Maybe a
Nothing)
exists = FileStatus -> Bool
fileExists FileStatus
st Bool -> Bool -> Bool
&& Bool -> Bool
not (FileStatus -> Bool
isDirectory FileStatus
st)
we_changed = Int64
mtime Int64 -> Int64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Int64
mtime' Bool -> Bool -> Bool
|| Int64
size Int64 -> Int64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Int64
size'
hash = Item -> Maybe Hash
iHash' Item
item
when (exists && we_changed) $
do hash' <- sha256 `fmap` readblob
updateItem item size' hash'
updateTime item mtime'
when (fileid == 0) $ updateFileID item (fileID st)
return $ Result { changed = not exists || we_changed
, next = start state + itemNext item
, treeitem =
if exists
then Just $ File $ Blob readblob hash
else Nothing
, resitem = item }
data CorruptIndex = CorruptIndex String deriving (CorruptIndex -> CorruptIndex -> Bool
(CorruptIndex -> CorruptIndex -> Bool)
-> (CorruptIndex -> CorruptIndex -> Bool) -> Eq CorruptIndex
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CorruptIndex -> CorruptIndex -> Bool
== :: CorruptIndex -> CorruptIndex -> Bool
$c/= :: CorruptIndex -> CorruptIndex -> Bool
/= :: CorruptIndex -> CorruptIndex -> Bool
Eq, Typeable)
instance Exception CorruptIndex
instance Show CorruptIndex where show :: CorruptIndex -> String
show (CorruptIndex String
s) = String
s
nameof :: Item -> State -> Maybe Name
nameof :: Item -> State -> Maybe Name
nameof Item
item State
state
| Item -> ByteString
iDescriptor Item
item ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== String -> ByteString
BC.pack String
"D." = Maybe Name
forall a. Maybe a
Nothing
| Bool
otherwise =
case ByteString -> Either String Name
rawMakeName (ByteString -> Either String Name)
-> ByteString -> Either String Name
forall a b. (a -> b) -> a -> b
$ Int -> ByteString -> ByteString
B.drop (State -> Int
dirlength State
state Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Item -> ByteString
iDescriptor Item
item of
Left String
msg -> CorruptIndex -> Maybe Name
forall a e. (HasCallStack, Exception e) => e -> a
throw (String -> CorruptIndex
CorruptIndex String
msg)
Right Name
name -> Name -> Maybe Name
forall a. a -> Maybe a
Just Name
name
maybeAppendName :: AnchoredPath -> Maybe Name -> AnchoredPath
maybeAppendName :: AnchoredPath -> Maybe Name -> AnchoredPath
maybeAppendName AnchoredPath
parent = AnchoredPath
-> (Name -> AnchoredPath) -> Maybe Name -> AnchoredPath
forall b a. b -> (a -> b) -> Maybe a -> b
maybe AnchoredPath
parent (AnchoredPath
parent AnchoredPath -> Name -> AnchoredPath
`appendPath`)
substateof :: Item -> State -> State
substateof :: Item -> State -> State
substateof Item
item State
state =
State
state
{ start = start state + itemNext item
, path = path state `maybeAppendName` myname
, dirlength =
case myname of
Maybe Name
Nothing ->
State -> Int
dirlength State
state
Just Name
_ ->
ByteString -> Int
B.length (Item -> ByteString
iDescriptor Item
item)
}
where
myname :: Maybe Name
myname = Item -> State -> Maybe Name
nameof Item
item State
state
data ResultF = ResultF
{ ResultF -> Int
nextF :: !Int
, ResultF -> Item
resitemF :: !Item
, ResultF -> [((AnchoredPath, ItemType), FileID)]
_fileIDs :: [((AnchoredPath, ItemType), FileID)]
}
listFileIDs :: Index -> IO ([((AnchoredPath, ItemType), FileID)])
listFileIDs :: Index -> IO [((AnchoredPath, ItemType), FileID)]
listFileIDs Index
EmptyIndex = [((AnchoredPath, ItemType), FileID)]
-> IO [((AnchoredPath, ItemType), FileID)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []
listFileIDs Index
index =
do let initial :: State
initial = State { start :: Int
start = Int
size_header
, dirlength :: Int
dirlength = Int
0
, path :: AnchoredPath
path = AnchoredPath
anchoredRoot }
res <- Index -> State -> IO ResultF
readItemFileIDs Index
index State
initial
return $ _fileIDs res
readItemFileIDs :: Index -> State -> IO ResultF
readItemFileIDs :: Index -> State -> IO ResultF
readItemFileIDs Index
index State
state = do
item <- ForeignPtr () -> Int -> IO Item
peekItem (Index -> ForeignPtr ()
forall (m :: * -> *). IndexM m -> ForeignPtr ()
mmap Index
index) (State -> Int
start State
state)
res' <- if itemIsDir item
then readDirFileIDs index state item
else readFileFileID index state item
return res'
readDirFileIDs :: Index -> State -> Item -> IO ResultF
readDirFileIDs :: Index -> State -> Item -> IO ResultF
readDirFileIDs Index
index State
state Item
item =
do fileid <- Ptr FileID -> IO FileID
forall a. Storable a => Ptr a -> IO a
peek (Ptr FileID -> IO FileID) -> Ptr FileID -> IO FileID
forall a b. (a -> b) -> a -> b
$ Item -> Ptr FileID
iFileID Item
item
following <- fromIntegral <$> peek (iAux item)
let substate = Item -> State -> State
substateof Item
item State
state
subs Int
off =
case Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
off Int
following of
Ordering
LT -> do
result <- Index -> State -> IO ResultF
readItemFileIDs Index
index (State -> IO ResultF) -> State -> IO ResultF
forall a b. (a -> b) -> a -> b
$ State
substate {start = off}
rest <- subs $ nextF result
return $! (nameof (resitemF result) substate, result) : rest
Ordering
EQ -> [(Maybe Name, ResultF)] -> IO [(Maybe Name, ResultF)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []
Ordering
GT ->
String -> IO [(Maybe Name, ResultF)]
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO [(Maybe Name, ResultF)])
-> String -> IO [(Maybe Name, ResultF)]
forall a b. (a -> b) -> a -> b
$
String
"Offset mismatch at " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
off String -> ShowS
forall a. [a] -> [a] -> [a]
++
String
" (ends at " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
following String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
inferiors <- subs $ start substate
return $ ResultF { nextF = following
, resitemF = item
, _fileIDs = (((path substate, TreeType), fileid):concatMap (_fileIDs . snd) inferiors) }
readFileFileID :: Index -> State -> Item -> IO ResultF
readFileFileID :: Index -> State -> Item -> IO ResultF
readFileFileID Index
_ State
state Item
item =
do fileid' <- Ptr FileID -> IO FileID
forall a. Storable a => Ptr a -> IO a
peek (Ptr FileID -> IO FileID) -> Ptr FileID -> IO FileID
forall a b. (a -> b) -> a -> b
$ Item -> Ptr FileID
iFileID Item
item
let myname = Item -> State -> Maybe Name
nameof Item
item State
state
return $ ResultF { nextF = start state + itemNext item
, resitemF = item
, _fileIDs = [((path state `maybeAppendName` myname, BlobType), fileid')] }
openIndex :: FilePath -> IO Index
openIndex :: String -> IO Index
openIndex String
indexpath = do
(mmap_ptr, mmap_size) <- String -> Int -> IO (ForeignPtr (), Int)
forall a. String -> Int -> IO (ForeignPtr a, Int)
mmapIndex String
indexpath Int
0
base <- getCurrentDirectory
return $ if mmap_size == 0 then EmptyIndex
else Index { mmap = mmap_ptr
, basedir = base
, predicate = \AnchoredPath
_ TreeItem IO
_ -> Bool
True }
formatIndex :: ForeignPtr () -> Tree IO -> Tree IO -> IO ()
formatIndex :: ForeignPtr () -> Tree IO -> Tree IO -> IO ()
formatIndex ForeignPtr ()
mmap_ptr Tree IO
old Tree IO
reference =
do _ <- TreeItem IO -> AnchoredPath -> Int -> IO Int
forall {m :: * -> *}. TreeItem m -> AnchoredPath -> Int -> IO Int
create (Tree IO -> TreeItem IO
forall (m :: * -> *). Tree m -> TreeItem m
SubTree Tree IO
reference) (AnchoredPath
anchoredRoot) Int
size_header
unsafePokeBS magic index_version
withForeignPtr mmap_ptr $ \Ptr ()
ptr ->
Ptr () -> Int -> Int32 -> IO ()
forall b. Ptr b -> Int -> Int32 -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr ()
ptr Int
size_magic Int32
index_endianness_indicator
where magic :: ByteString
magic = ForeignPtr Word8 -> Int -> Int -> ByteString
fromForeignPtr (ForeignPtr () -> ForeignPtr Word8
forall a b. ForeignPtr a -> ForeignPtr b
castForeignPtr ForeignPtr ()
mmap_ptr) Int
0 Int
4
create :: TreeItem m -> AnchoredPath -> Int -> IO Int
create (File Blob m
_) AnchoredPath
path' Int
off =
do i <- ItemType -> AnchoredPath -> ForeignPtr () -> Int -> IO Item
createItem ItemType
BlobType AnchoredPath
path' ForeignPtr ()
mmap_ptr Int
off
st <- getFileStatus (iPath i)
updateFileID i (fileID st)
case find old path' of
Maybe (TreeItem IO)
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just TreeItem IO
ti -> do let hash :: Maybe Hash
hash = TreeItem IO -> Maybe Hash
forall (m :: * -> *). TreeItem m -> Maybe Hash
itemHash TreeItem IO
ti
mtime :: Int64
mtime = FileStatus -> Int64
modificationTime FileStatus
st
size :: FileOffset
size = FileStatus -> FileOffset
fileSize FileStatus
st
Item -> Int64 -> Hash -> IO ()
updateItem Item
i (FileOffset -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral FileOffset
size) (Maybe Hash -> Hash
forall a. HasCallStack => Maybe a -> a
fromJust Maybe Hash
hash)
Item -> Int64 -> IO ()
updateTime Item
i Int64
mtime
return $ off + itemNext i
create (SubTree Tree m
s) AnchoredPath
path' Int
off =
do i <- ItemType -> AnchoredPath -> ForeignPtr () -> Int -> IO Item
createItem ItemType
TreeType AnchoredPath
path' ForeignPtr ()
mmap_ptr Int
off
st <- getFileStatus (iPath i)
updateFileID i (fileID st)
case find old path' of
Maybe (TreeItem IO)
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just TreeItem IO
ti ->
case TreeItem IO -> Maybe Hash
forall (m :: * -> *). TreeItem m -> Maybe Hash
itemHash TreeItem IO
ti of
Maybe Hash
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just Hash
h -> Item -> Int64 -> Hash -> IO ()
updateItem Item
i Int64
0 Hash
h
let subs [] = Int -> IO Int
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> IO Int) -> Int -> IO Int
forall a b. (a -> b) -> a -> b
$ Int
off Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Item -> Int
itemNext Item
i
subs ((Name
name,TreeItem m
x):[(Name, TreeItem m)]
xs) = do
let path'' :: AnchoredPath
path'' = AnchoredPath
path' AnchoredPath -> Name -> AnchoredPath
`appendPath` Name
name
noff <- [(Name, TreeItem m)] -> IO Int
subs [(Name, TreeItem m)]
xs
create x path'' noff
lastOff <- subs (listImmediate s)
poke (iAux i) (fromIntegral lastOff)
return lastOff
create (Stub m (Tree m)
_ Maybe Hash
_) AnchoredPath
path' Int
_ =
String -> IO Int
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO Int) -> String -> IO Int
forall a b. (a -> b) -> a -> b
$ String
"Cannot create index from stubbed Tree at " String -> ShowS
forall a. [a] -> [a] -> [a]
++ AnchoredPath -> String
forall a. Show a => a -> String
show AnchoredPath
path'
updateIndexFrom :: FilePath -> Tree IO -> IO Index
updateIndexFrom :: String -> Tree IO -> IO Index
updateIndexFrom String
indexpath Tree IO
ref =
do String -> IO ()
debugMessage String
"Updating the index ..."
old_tree <- Index -> IO (Tree IO)
treeFromIndex (Index -> IO (Tree IO)) -> IO Index -> IO (Tree IO)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< String -> IO Index
openIndex String
indexpath
reference <- expand ref
let len_root = AnchoredPath -> Int
itemAllocSize AnchoredPath
anchoredRoot
len = Int
len_root Int -> Int -> Int
forall a. Num a => a -> a -> a
+ [Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [ AnchoredPath -> Int
itemAllocSize AnchoredPath
p | (AnchoredPath
p, TreeItem IO
_) <- Tree IO -> [(AnchoredPath, TreeItem IO)]
forall (m :: * -> *). Tree m -> [(AnchoredPath, TreeItem m)]
list Tree IO
reference ]
exist <- doesFileExist indexpath
when exist $ renameFile indexpath (indexpath <.> "old")
(mmap_ptr, _) <- mmapIndex indexpath len
formatIndex mmap_ptr old_tree reference
debugMessage "Done updating the index, reopening it ..."
openIndex indexpath
treeFromIndex :: Index -> IO (Tree IO)
treeFromIndex :: Index -> IO (Tree IO)
treeFromIndex Index
EmptyIndex = Tree IO -> IO (Tree IO)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Tree IO
forall (m :: * -> *). Tree m
emptyTree
treeFromIndex Index
index =
do let initial :: State
initial = State { start :: Int
start = Int
size_header
, dirlength :: Int
dirlength = Int
0
, path :: AnchoredPath
path = AnchoredPath
anchoredRoot }
progressKey :: String
progressKey = String
"Updating the index"
String -> IO ()
beginTedious String
progressKey
res <- String -> Index -> State -> IO Result
readItem String
progressKey Index
index State
initial
endTedious progressKey
case treeitem res of
Just (SubTree Tree IO
tree) -> Tree IO -> IO (Tree IO)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Tree IO -> IO (Tree IO)) -> Tree IO -> IO (Tree IO)
forall a b. (a -> b) -> a -> b
$ (AnchoredPath -> TreeItem IO -> Bool) -> Tree IO -> Tree IO
forall (a :: (* -> *) -> *) (m :: * -> *).
FilterTree a m =>
(AnchoredPath -> TreeItem m -> Bool) -> a m -> a m
filter (Index -> AnchoredPath -> TreeItem IO -> Bool
forall (m :: * -> *).
IndexM m -> AnchoredPath -> TreeItem m -> Bool
predicate Index
index) Tree IO
tree
Maybe (TreeItem IO)
_ -> String -> IO (Tree IO)
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Unexpected failure in treeFromIndex!"
indexFormatValid :: FilePath -> IO Bool
indexFormatValid :: String -> IO Bool
indexFormatValid String
path' =
do
(start, _, _) <- String
-> Mode
-> Maybe (Int64, Int)
-> IO (ForeignPtr (ZonkAny 2), Int, Int)
forall a.
String -> Mode -> Maybe (Int64, Int) -> IO (ForeignPtr a, Int, Int)
mmapFileForeignPtr String
path' Mode
ReadOnly ((Int64, Int) -> Maybe (Int64, Int)
forall a. a -> Maybe a
Just (Int64
0, Int
size_header))
let magic = ForeignPtr Word8 -> Int -> Int -> ByteString
fromForeignPtr (ForeignPtr (ZonkAny 2) -> ForeignPtr Word8
forall a b. ForeignPtr a -> ForeignPtr b
castForeignPtr ForeignPtr (ZonkAny 2)
start) Int
0 Int
4
endianness_indicator <- withForeignPtr start $ \Ptr (ZonkAny 2)
ptr -> Ptr (ZonkAny 2) -> Int -> IO Int32
forall b. Ptr b -> Int -> IO Int32
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr (ZonkAny 2)
ptr Int
4
return $
index_version == magic && index_endianness_indicator == endianness_indicator
IO Bool -> (SomeException -> IO Bool) -> IO Bool
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` \(SomeException
_::SomeException) -> Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
instance FilterTree IndexM IO where
filter :: (AnchoredPath -> TreeItem IO -> Bool) -> Index -> Index
filter AnchoredPath -> TreeItem IO -> Bool
_ Index
EmptyIndex = Index
forall (m :: * -> *). IndexM m
EmptyIndex
filter AnchoredPath -> TreeItem IO -> Bool
p Index
index = Index
index { predicate = \AnchoredPath
a TreeItem IO
b -> Index -> AnchoredPath -> TreeItem IO -> Bool
forall (m :: * -> *).
IndexM m -> AnchoredPath -> TreeItem m -> Bool
predicate Index
index AnchoredPath
a TreeItem IO
b Bool -> Bool -> Bool
&& AnchoredPath -> TreeItem IO -> Bool
p AnchoredPath
a TreeItem IO
b }
getFileID :: AnchoredPath -> IO (Maybe FileID)
getFileID :: AnchoredPath -> IO (Maybe FileID)
getFileID AnchoredPath
p = (FileStatus -> FileID) -> FileStatus -> Maybe FileID
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FileStatus -> FileID
F.fileID (FileStatus -> Maybe FileID) -> IO FileStatus -> IO (Maybe FileID)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO FileStatus
getFileStatus (AnchoredPath -> String
realPath AnchoredPath
p)
unsafePokeBS :: BC.ByteString -> BC.ByteString -> IO ()
unsafePokeBS :: ByteString -> ByteString -> IO ()
unsafePokeBS ByteString
to ByteString
from =
do let (ForeignPtr Word8
fp_to, Int
off_to, Int
len_to) = ByteString -> (ForeignPtr Word8, Int, Int)
toForeignPtr ByteString
to
(ForeignPtr Word8
fp_from, Int
off_from, Int
len_from) = ByteString -> (ForeignPtr Word8, Int, Int)
toForeignPtr ByteString
from
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
len_to Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
len_from) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Length mismatch in unsafePokeBS: from = "
String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
len_from String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" /= to = " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
len_to
ForeignPtr Word8 -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr Word8
fp_from ((Ptr Word8 -> IO ()) -> IO ()) -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
p_from ->
ForeignPtr Word8 -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
withForeignPtr ForeignPtr Word8
fp_to ((Ptr Word8 -> IO ()) -> IO ()) -> (Ptr Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Ptr Word8
p_to ->
Ptr (ZonkAny 0) -> Ptr (ZonkAny 0) -> Int -> IO ()
forall a. Ptr a -> Ptr a -> Int -> IO ()
copyBytes
(Ptr Word8 -> Int -> Ptr (ZonkAny 0)
forall a b. Ptr a -> Int -> Ptr b
plusPtr Ptr Word8
p_to Int
off_to)
(Ptr Word8 -> Int -> Ptr (ZonkAny 0)
forall a b. Ptr a -> Int -> Ptr b
plusPtr Ptr Word8
p_from Int
off_from)
(Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len_to)
align :: Integral a => a -> a -> a
align :: forall a. Integral a => a -> a -> a
align a
boundary a
i = case a
i a -> a -> a
forall a. Integral a => a -> a -> a
`rem` a
boundary of
a
0 -> a
i
a
x -> a
i a -> a -> a
forall a. Num a => a -> a -> a
+ a
boundary a -> a -> a
forall a. Num a => a -> a -> a
- a
x
{-# INLINE align #-}
getFileStatus :: FilePath -> IO FileStatus
getFileStatus :: String -> IO FileStatus
getFileStatus String
path = do
mst <- String -> IO FileStatus
Darcs.Util.File.getFileStatus String
path
case mst of
Just FileStatus
st
| FileStatus -> Bool
F.isSymbolicLink FileStatus
st -> do
Handle -> String -> IO ()
hPutStrLn Handle
stderr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Warning: ignoring symbolic link " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
path
FileStatus -> IO FileStatus
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return FileStatus
forall a. Maybe a
Nothing
FileStatus
_ -> FileStatus -> IO FileStatus
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return FileStatus
mst
data IndexEntry = IndexEntry
{ IndexEntry -> Int64
ieSize :: Int64
, IndexEntry -> Int64
ieAux :: Int64
, IndexEntry -> FileID
ieFileID :: FileID
, IndexEntry -> Maybe Hash
ieHash :: Maybe Hash
, IndexEntry -> Char
ieType :: Char
, IndexEntry -> AnchoredPath
iePath :: AnchoredPath
}
dumpIndex :: FilePath -> IO [IndexEntry]
dumpIndex :: String -> IO [IndexEntry]
dumpIndex String
indexpath =
String
-> Mode
-> Maybe (Int64, Int)
-> ((Ptr (), Int) -> IO [IndexEntry])
-> IO [IndexEntry]
forall a.
String
-> Mode -> Maybe (Int64, Int) -> ((Ptr (), Int) -> IO a) -> IO a
mmapWithFilePtr String
indexpath Mode
ReadOnly Maybe (Int64, Int)
forall a. Maybe a
Nothing (((Ptr (), Int) -> IO [IndexEntry]) -> IO [IndexEntry])
-> ((Ptr (), Int) -> IO [IndexEntry]) -> IO [IndexEntry]
forall a b. (a -> b) -> a -> b
$ \(Ptr ()
ptr, Int
size) -> do
magic <- Ptr () -> Int -> IO ShortByteString
forall a. Ptr a -> Int -> IO ShortByteString
BS.createFromPtr Ptr ()
ptr Int
4
when (magic /= BS.toShort index_version) $ fail "index format is invalid"
readEntries (size - size_header) (ptr `plusPtr` size_header)
where
readEntries :: Int -> Ptr b -> IO [IndexEntry]
readEntries Int
s Ptr b
_ | Int
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< (Int -> Int
next Int
0) = [IndexEntry] -> IO [IndexEntry]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []
readEntries Int
s Ptr b
p = do
(entry, fwd) <- Ptr b -> IO (IndexEntry, Int)
forall {a}. Ptr a -> IO (IndexEntry, Int)
readEntry Ptr b
p
entries <- readEntries (s - fwd) (p `plusPtr` fwd)
return (entry : entries)
readEntry :: Ptr a -> IO (IndexEntry, Int)
readEntry Ptr a
p = do
ieSize <- Ptr a -> Int -> IO Int64
forall b. Ptr b -> Int -> IO Int64
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr a
p Int
off_size
ieAux <- peekByteOff p off_aux
ieFileID <- peekByteOff p off_fileid
ieHash <- do
h <- BS.createFromPtr (p `plusPtr` off_hash) size_hash
return $ if h == shortNullHash then Nothing else Just (SHA256 h)
dsclen :: Int32 <- peekByteOff p off_dsclen
ieType <- b2c <$> peekByteOff p off_dsc
path <-
BS.fromShort <$>
BS.createFromPtr (p `plusPtr` off_path) (fromIntegral dsclen - size_type - size_null)
iePath <-
either fail return $ AnchoredPath <$> mapM rawMakeName (BC.split '/' (fixRoot path))
return (IndexEntry {..}, next (B.length path))
b2c :: Word8 -> Char
b2c :: Word8 -> Char
b2c = Int -> Char
forall a. Enum a => Int -> a
toEnum (Int -> Char) -> (Word8 -> Int) -> Word8 -> Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral
off_path :: Int
off_path = Int
off_dsc Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_type
next :: Int -> Int
next Int
pathlen =
Int -> Int -> Int
forall a. Integral a => a -> a -> a
align Int
4 (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ Int
size_size Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_aux Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_fileid Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_hash Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_dsclen
Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_type Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
pathlen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size_null
fixRoot :: ByteString -> ByteString
fixRoot ByteString
s | ByteString
s ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== String -> ByteString
BC.pack String
"." = ByteString
BC.empty
fixRoot ByteString
s = ByteString
s
shortNullHash :: ShortByteString
shortNullHash = ByteString -> ShortByteString
BS.toShort ByteString
nullHash