commit 693b42b8545855fc153b50045e183ddd17d6335e
parent b643c73b7b10e8444fb1b9294e889383e10dc0fe
Author: James Eversole <james@eversole.co>
Date: Thu, 7 May 2026 12:28:14 -0500
Add Arborix section directory byte readers
Diffstat:
| M | lib/arborix.tri | | | 55 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | test/Spec.hs | | | 122 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 177 insertions(+), 0 deletions(-)
diff --git a/lib/arborix.tri b/lib/arborix.tri
@@ -20,3 +20,58 @@ readArborixHeader = (bs :
(pair majorVersion
(pair minorVersion sectionCount))
afterSectionCount)))))
+
+readSectionRecord = (bs :
+ bindResult (readBytes 2 bs)
+ (sectionId afterSectionId :
+ bindResult (readBytes 4 afterSectionId)
+ (offset afterOffset :
+ bindResult (readBytes 4 afterOffset)
+ (length afterLength :
+ ok
+ (pair sectionId
+ (pair offset length))
+ afterLength))))
+
+readSectionDirectory_ = y (self sectionCount i bs acc :
+ matchBool
+ (ok (reverse acc) bs)
+ (bindResult (readSectionRecord bs)
+ (sectionRecord afterSectionRecord :
+ self sectionCount (succ i) afterSectionRecord (pair sectionRecord acc)))
+ (equal? i sectionCount))
+
+readSectionDirectory = (sectionCount bs : readSectionDirectory_ sectionCount 0 bs t)
+
+sectionRecordId = (sectionRecord :
+ matchPair
+ (sectionId _ : sectionId)
+ sectionRecord)
+
+sectionRecordOffset = (sectionRecord :
+ matchPair
+ (_ payload :
+ matchPair
+ (offset _ : offset)
+ payload)
+ sectionRecord)
+
+sectionRecordLength = (sectionRecord :
+ matchPair
+ (_ payload :
+ matchPair
+ (_ length : length)
+ payload)
+ sectionRecord)
+
+lookupSectionRecord = y (self sectionId directory :
+ matchList
+ nothing
+ (sectionRecord rest :
+ matchBool
+ (just sectionRecord)
+ (self sectionId rest)
+ (bytesEq? sectionId (sectionRecordId sectionRecord)))
+ directory)
+
+byteSlice = (offset length bytes : bytesTake length (bytesDrop offset bytes))
diff --git a/test/Spec.hs b/test/Spec.hs
@@ -1554,4 +1554,126 @@ binaryReaderTests = testGroup "Binary Reader Tests"
library <- evaluateFile "./lib/arborix.tri"
let env = evalTricu library (parseTricu input)
result env @?= errT eofT (bytesT [65,82])
+
+ -- ------------------------------------------------------------------------
+ -- Arborix section directory record parsing
+ -- ------------------------------------------------------------------------
+
+ , testCase "readSectionRecord: parses raw section id offset and length" $ do
+ let input = "readSectionRecord [(0) (2) (0) (0) (0) (16) (0) (0) (0) (32)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= okT
+ (pairT (bytesT [0,2])
+ (pairT (bytesT [0,0,0,16])
+ (bytesT [0,0,0,32])))
+ (bytesT [])
+
+ , testCase "readSectionRecord: preserves trailing bytes" $ do
+ let input = "readSectionRecord [(0) (2) (0) (0) (0) (16) (0) (0) (0) (32) (9) (8)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= okT
+ (pairT (bytesT [0,2])
+ (pairT (bytesT [0,0,0,16])
+ (bytesT [0,0,0,32])))
+ (bytesT [9,8])
+
+ , testCase "readSectionRecord: empty input returns EOF" $ do
+ let input = "readSectionRecord []"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= errT eofT (bytesT [])
+
+ , testCase "readSectionRecord: short section id returns EOF preserving input" $ do
+ let input = "readSectionRecord [(0)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= errT eofT (bytesT [0])
+
+ , testCase "readSectionRecord: missing offset returns EOF preserving unread offset bytes" $ do
+ let input = "readSectionRecord [(0) (2)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= errT eofT (bytesT [])
+
+ , testCase "readSectionRecord: short offset returns EOF preserving unread offset bytes" $ do
+ let input = "readSectionRecord [(0) (2) (0) (0) (0)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= errT eofT (bytesT [0,0,0])
+
+ , testCase "readSectionRecord: missing length returns EOF preserving unread length bytes" $ do
+ let input = "readSectionRecord [(0) (2) (0) (0) (0) (16)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= errT eofT (bytesT [])
+
+ , testCase "readSectionRecord: short length returns EOF preserving unread length bytes" $ do
+ let input = "readSectionRecord [(0) (2) (0) (0) (0) (16) (0) (0) (0)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= errT eofT (bytesT [0,0,0])
+
+ -- ------------------------------------------------------------------------
+ -- Arborix section directory parsing
+ -- ------------------------------------------------------------------------
+
+ , testCase "readSectionDirectory: zero records preserves input" $ do
+ let input = "readSectionDirectory 0 [(9) (8)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= okT (ofList []) (bytesT [9,8])
+
+ , testCase "readSectionDirectory: reads requested records and preserves trailing bytes" $ do
+ let input = "readSectionDirectory 2 [(0) (1) (0) (0) (0) (10) (0) (0) (0) (20) (0) (2) (0) (0) (0) (30) (0) (0) (0) (40) (9)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= okT
+ (ofList
+ [ pairT (bytesT [0,1])
+ (pairT (bytesT [0,0,0,10])
+ (bytesT [0,0,0,20]))
+ , pairT (bytesT [0,2])
+ (pairT (bytesT [0,0,0,30])
+ (bytesT [0,0,0,40]))
+ ])
+ (bytesT [9])
+
+ , testCase "readSectionDirectory: truncated record returns EOF" $ do
+ let input = "readSectionDirectory 2 [(0) (1) (0) (0) (0) (10) (0) (0) (0) (20) (0) (2) (0) (0)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= errT eofT (bytesT [0,0])
+
+ -- ------------------------------------------------------------------------
+ -- Arborix section lookup and raw byte slicing
+ -- ------------------------------------------------------------------------
+
+ , testCase "lookupSectionRecord: finds record by raw section id" $ do
+ let input = "lookupSectionRecord [(0) (2)] [(pair [(0) (1)] (pair [(0) (0) (0) (10)] [(0) (0) (0) (20)])) (pair [(0) (2)] (pair [(0) (0) (0) (30)] [(0) (0) (0) (40)]))]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= justT
+ (pairT (bytesT [0,2])
+ (pairT (bytesT [0,0,0,30])
+ (bytesT [0,0,0,40])))
+
+ , testCase "lookupSectionRecord: missing section id returns nothing" $ do
+ let input = "lookupSectionRecord [(0) (3)] [(pair [(0) (1)] (pair [(0) (0) (0) (10)] [(0) (0) (0) (20)])) (pair [(0) (2)] (pair [(0) (0) (0) (30)] [(0) (0) (0) (40)]))]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= nothingT
+
+ , testCase "byteSlice: extracts requested byte range" $ do
+ let input = "byteSlice 2 3 [(10) (11) (12) (13) (14) (15)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= bytesT [12,13,14]
+
+ , testCase "byteSlice: overlong length returns remaining bytes" $ do
+ let input = "byteSlice 4 9 [(10) (11) (12) (13) (14) (15)]"
+ library <- evaluateFile "./lib/arborix.tri"
+ let env = evalTricu library (parseTricu input)
+ result env @?= bytesT [14,15]
]