In [1]:
:set -XOverloadedStrings
In [2]:
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Attoparsec.ByteString as AB
import Data.Attoparsec.ByteString.Char8 (anyChar)
import Prelude hiding (take)
import Numeric
import Data.ByteString.UTF8 (toString, fromString)
import Data.ByteString.Base16 (encode, decode)
import Data.List (intercalate)
In [3]:
let index = B.readFile "../.git/index"
In [4]:
word8s = string . B.pack
fromBytes = foldl addByte 0
where addByte num d = (256*num) + d
toBytes n = case divMod n 256 of
(0, i) -> [i]
(x, y) -> toBytes x ++ toBytes y
fromPack :: B.ByteString -> Int
fromPack = fromBytes . map (fromIntegral . toInteger) . B.unpack
toPack len num = let bytes = toBytes num :: [Int]
pNum = len - length bytes
padding = replicate pNum 0
in B.pack $ map fromIntegral (padding ++ bytes)
In [5]:
data Index = Index
{ indexVersion :: Int
, indexEntries :: [IndexEntry]
, indexExtra :: B.ByteString}
data IndexEntry = IndexEntry
{ metadataChanged :: Int
, metadataFraction :: Int
, dataChanged :: Int
, dataFraction :: Int
, dev :: Int
, ino :: Int
, mode :: Int
, uid :: Int
, gid :: Int
, fileSize :: Int
, sha1 :: B.ByteString
, flags :: Int
, name :: B.ByteString}
deriving (Eq)
instance Show IndexEntry where
show (IndexEntry mChanged mFraction dChanged dFraction dev ino mode uid gid size sha1 flags name) = intercalate " " components
where components = (map show [mChanged, mFraction, dChanged, dFraction, dev, ino, mode, uid, gid, size]) ++ [toString sha1, show flags, toString name]
parseIndexEntry 2 = IndexEntry
<$> (fromPack <$> take 4)
<*> (fromPack <$> take 4)
<*> (fromPack <$> take 4)
<*> (fromPack <$> take 4) -- dataFraction
<*> (fromPack <$> take 4) -- dev
<*> (fromPack <$> take 4) -- ino
<*> (fromPack <$> take 4) -- mode
<*> (fromPack <$> take 4) -- uid
<*> (fromPack <$> take 4) -- gid
<*> (fromPack <$> take 4) -- fileSize
<*> (encode <$> take 20) -- sha1
<*> (fromPack <$> take 2) -- flags
<*> (do name <- takeTill (==0)
count (8 - ((6 + B.length name) `mod` 8)) $ string "\NUL"
return name)
parseIndexEntry _ = undefined
parseIndex = do
string "DIRC"
version <- fromPack <$> (choice $ map word8s [[0,0,0,2], [0,0,0,3], [0,0,0,4]])
noOfEntries <- fromPack <$> take 4
entries <- count noOfEntries $ parseIndexEntry version
rest <- takeByteString
return $ Index version entries rest
parsedIndex <- either error id . parseOnly parseIndex <$> index
entries = indexEntries parsedIndex
In [6]:
unparseIndexEntry 2 entry = B.concat components
where components = [ toPack 4 $ metadataChanged entry
, toPack 4 $ metadataFraction entry
, toPack 4 $ dataChanged entry
, toPack 4 $ dataFraction entry
, toPack 4 $ dev entry
, toPack 4 $ ino entry
, toPack 4 $ mode entry
, toPack 4 $ uid entry
, toPack 4 $ gid entry
, toPack 4 $ fileSize entry
, fst $ decode $ sha1 entry
, toPack 2 $ flags entry
, name entry
, B.concat $ replicate (8 - ((6 + B.length (name entry)) `mod` 8)) "\NUL"]
In [7]:
reparse e = either error id $ parseOnly (parseIndexEntry 2) (unparseIndexEntry 2 e)
checkEqual e = reparse e == e
In [8]:
all checkEqual entries
In [9]:
toBytes 1460985245
In [10]:
map name entries