indexed-write.tri (1991B)
1 !import io_v2 as IO 2 !import base as B 3 !export hash 4 !export write 5 !export publish 6 !export roundTrip 7 8 hash limit tree = IO.io (IO.hashIndexedTree limit tree) 9 10 -- In-place transport, deliberately not atomic replacement. Failed preflight does 11 -- not touch this already-open writer; opening an absent update file creates it. 12 write path limit digest tree = IO.io <| IO.scope (owner : do IO.onOk 13 writer <- IO.openUpdate owner path 14 IO.writeIndexedTree writer limit digest tree) 15 16 -- Source owns naming/publication and exact winner verification. "exists" means 17 -- byte-verified at observation time, not crash durability or future immutability. 18 publish directory limit tree = IO.io <| IO.scope (owner : do IO.onOk 19 info <- IO.hashIndexedTree limit tree 20 case info of 21 [size digest] = do IO.onOk 22 stage <- IO.openStage owner directory 23 IO.writeIndexedTree stage limit digest tree 24 IO.sealStage stage 25 state <- IO.publishNew stage (B.append directory (B.append "/" digest)) 26 case state of 27 "published" = IO.pure (B.ok [state size digest] t) 28 "exists" = do IO.onOk 29 reader <- IO.openReader owner (B.append directory (B.append "/" digest)) 30 IO.verifyIndexedTree reader limit tree 31 end <- IO.readSome reader 1 32 case end of 33 ("eof", t) = IO.pure (B.ok [state size digest] t) 34 _ = IO.pure (B.err ["invalid-data" "indexed-file/verify" "trailing bytes" "unknown"] t) 35 _ = IO.fail "Invalid publication reply" 36 _ = IO.fail "Invalid indexed hash reply") 37 38 -- A shared DAG enters and leaves without ever becoming an expanded source byte 39 -- list. This fixture expects canonical input; the writer checks its digest again. 40 roundTrip input output directory limit size digest = IO.io <| IO.scope (owner : do IO.onOk 41 reader <- IO.openReader owner input 42 tree <- IO.readIndexedTree reader directory limit size digest 43 writer <- IO.openWriter owner output 44 IO.writeIndexedTree writer limit digest tree)