module Halfs.TheBlockMap (TheBlockMap
,mkBlockMap
,allocateBlock
,freeBlock
,freeBlocks
,bmTotalSize
,bmDirty
,newFS
,findAddress
,unitTests)
where
import Halfs.Inode (Inode(metaData), InodeMetadata(level)
,newInode, firstFreeInodeDiskAddr)
import Halfs.TestFramework (Test(..), UnitTests, (~:), (~=?),
hunitToUnitTest)
import Data.Integral ( INInt, intToINInt )
import Halfs.Utils (DiskAddress, BlockNumber,
FileType (File),
blockPointersPerIndirectBlock,
blockPointersPerInode)
import Data.Queue (listToQueue, addToQueue, deQueue, Queue, queueToList, queueLength)
import qualified Data.Set as Set
import Control.Exception(assert)
data TheBlockMap = TheBlockMap {freeBlocks :: Queue DiskAddress
,_freeBlocksSet :: Set.Set DiskAddress
,bmDirty :: Bool
,bmTotalSize :: INInt
}
allocateBlock :: TheBlockMap -> Maybe (DiskAddress, TheBlockMap)
allocateBlock (TheBlockMap inBlockMap inBlockSet _ s)
= case deQueue inBlockMap of
Nothing -> Nothing
Just (addr, newBlockMap)
-> Just ((assert (addr /= 0) addr), TheBlockMap newBlockMap (Set.delete addr inBlockSet) True s)
freeBlock :: TheBlockMap -> DiskAddress -> TheBlockMap
freeBlock (TheBlockMap inBlockMap inBlockSet _ len) addr
= assert (not (Set.member addr inBlockSet)) $
assert (addr > 0 && addr < len) $
TheBlockMap (addToQueue inBlockMap (assert (addr /= 0) addr)) (Set.insert addr inBlockSet) True len
newFS :: INInt
-> TheBlockMap
newFS len
= mkBlockMap (listToQueue [firstFreeInodeDiskAddr .. (len 1)]) True len
mkBlockMap :: Queue DiskAddress -> Bool -> INInt -> TheBlockMap
mkBlockMap free busy size
= assert (queueLength free == Set.size freeSet) $
TheBlockMap free freeSet busy size
where freeSet = Set.fromList (queueToList free)
instance Show TheBlockMap where
show (TheBlockMap free _ de tot) = "TheBlockMap [" ++ showBM (queueToList free) ++ "] " ++ show de ++ " " ++ show tot
where
commas [] = ""
commas xs = foldl1 (\ a b -> a ++ "," ++ b) xs
showBM xs = commas
[ if a == b
then
show a
else show a ++ ".." ++ show b
| (a,b) <- combine [ (x,x) | x <- xs ] ]
combine ((a,b):(c,d):r) | b+1 == c = combine ((a,d):r)
combine (v:r) = v: combine r
combine [] = []
findAddress :: Inode
-> BlockNumber
-> [INInt]
findAddress inode findMe
= addrList (level $ metaData inode)
1
(intToINInt blockPointersPerIndirectBlock)
(intToINInt blockPointersPerInode)
findMe
addrList :: INInt
-> INInt
-> INInt
-> INInt
-> BlockNumber
-> [INInt]
addrList totalLevels currLevel branchingFactor baseBuckets findMe
| findMe >= numBuckets totalLevels branchingFactor baseBuckets
= error "block too big, should have resized"
| currLevel > totalLevels = []
| otherwise = let bucketLoc = absBucket totalLevels currLevel
branchingFactor baseBuckets findMe
prevLevel = currLevel 1
prevBucket = absBucket totalLevels prevLevel
branchingFactor baseBuckets findMe
answer = if currLevel == 1
then bucketLoc
else bucketLoc (branchingFactor * prevBucket)
in answer : (addrList totalLevels (currLevel + 1)
branchingFactor baseBuckets findMe)
numBuckets :: INInt
-> INInt
-> INInt
-> INInt
numBuckets 0 _ _ = error "zero"
numBuckets 1 _ b = b
numBuckets currLevel branchingFactor baseBuckets
= (numBuckets (currLevel 1) branchingFactor baseBuckets) * branchingFactor
absBucket :: INInt
-> INInt
-> INInt
-> INInt
-> BlockNumber
-> INInt
absBucket _ _ _ _ 0 = 0
absBucket totalLevels currLevel branchingFactor baseBuckets findMe =
let maxElems = numBuckets totalLevels branchingFactor baseBuckets
buckets = numBuckets currLevel branchingFactor baseBuckets
itemsPerBucket = maxElems `div` (assert (buckets /= 0) buckets)
in (ceiling $ toRational (findMe+1) / toRational itemsPerBucket) 1
unitTests :: UnitTests
unitTests = hunitToUnitTest hunitTests
hunitTests :: [Test]
hunitTests = let inode' = newInode 0 File
inode = inode'{metaData=(metaData inode'){level=2}}
inode2 = inode'{metaData=(metaData inode'){level=3}}
(maxAddr::INInt) = (16 * (1024 ^ (2::Int))) 1
in
["simple Address List 1" ~: "failed" ~: [1,0,1] ~=? addrList 3 1 3 2 10
,"simple Address List 2" ~: "failed" ~: [1,0,2] ~=? addrList 3 1 3 2 11
,"simple Address List 2" ~: "failed" ~: [1,1,1] ~=? addrList 3 1 3 2 13
,"disk addr list 1" ~: "failed" ~: [0,0] ~=? findAddress inode 0
,"disk addr list 2" ~: "failed" ~: [0,1] ~=? findAddress inode 1
,"disk addr list 3" ~: "failed" ~: [0,1023] ~=? findAddress inode 1023
,"disk addr list 4" ~: "failed" ~: [1,0] ~=? findAddress inode 1024
,"disk addr list 3 lev. 1" ~: "failed" ~: [0,1,0] ~=? findAddress inode2 1024
,"disk addr list 3 lev. 2" ~: "failed"
~: [0,0,1023] ~=? findAddress inode2 1023
,"disk addr list 3 lev. 3" ~: "failed" ~: [0,5,0] ~=? findAddress inode2 5120
,"max addr" ~: "failed" ~: maxAddr ~=? (numBuckets 3 1024 16) 1
,"max addr loc" ~: "failed" ~: [15, 1023, 1023]
~=? findAddress inode2 maxAddr
]