common.tri (8707B)
1 !import prelude * 2 !import binary * 3 !import native_math as N 4 !export * 5 6 7 arboricxMagic = [(65) (82) (66) (79) (82) (73) (67) (88)] 8 arboricxMajorVersion = [(0) (1)] 9 arboricxMinorVersion = [(0) (0)] 10 arboricxManifestSectionId = [(0) (0) (0) (1)] 11 arboricxNodesSectionId = [(0) (0) (0) (2)] 12 13 -- Manifest magic and version constants 14 arboricxManifestMagic = [(65) (82) (66) (77) (78) (70) (83) (84)] 15 arboricxManifestMajorVersion = [(0) (1)] 16 arboricxManifestMinorVersion = [(0) (1)] 17 18 errMissingSection = 4 19 errUnsupportedVersion = 5 20 errDuplicateSection = 6 21 errDuplicateNode = 7 22 errInvalidNodePayload = 8 23 errMissingNode = 9 24 errInvalidManifestMagic = 10 25 errUnsupportedManifestVersion = 11 26 errTrailingManifestBytes = 12 27 errManifestValidationFailed = 13 28 29 nodePayloadLeafTag = 0 30 nodePayloadStemTag = 1 31 nodePayloadForkTag = 2 32 33 readArboricxMagic = (bs : expectBytes arboricxMagic bs) 34 35 readArboricxHeader = (do bindParser 36 readArboricxMagic 37 majorVersion <- readBytes 2 38 minorVersion <- readBytes 2 39 sectionCount <- readBytes 4 40 flags <- readBytes 8 41 dirOffset <- readBytes 8 42 pureParser (pair majorVersion (pair minorVersion (pair sectionCount (pair flags dirOffset))))) 43 44 readSectionRecord = (do bindParser 45 sectionId <- readBytes 4 46 sectionVersion <- readBytes 2 47 sectionFlags <- readBytes 2 48 compression <- readBytes 2 49 reserved1 <- readBytes 2 50 offset <- readBytes 8 51 length <- readBytes 8 52 reserved2 <- readBytes 4 53 pureParser (pair sectionId (pair sectionVersion (pair sectionFlags (pair compression (pair reserved1 (pair offset (pair length reserved2)))))))) 54 55 readSectionDirectory count = repeatParser count readSectionRecord 56 57 sectionRecordId record = case record of 58 (value, _) = value 59 _ = t 60 61 sectionRecordVersion record = case record of 62 (_, (value, _)) = value 63 _ = t 64 65 sectionRecordFlags record = case record of 66 (_, (_, (value, _))) = value 67 _ = t 68 69 sectionRecordCompression record = case record of 70 (_, (_, (_, (value, _)))) = value 71 _ = t 72 73 sectionRecordReserved1 record = case record of 74 (_, (_, (_, (_, (value, _))))) = value 75 _ = t 76 77 sectionRecordOffset record = case record of 78 (_, (_, (_, (_, (_, (value, _)))))) = value 79 _ = t 80 81 sectionRecordLength record = case record of 82 (_, (_, (_, (_, (_, (_, (value, _))))))) = value 83 _ = t 84 85 sectionRecordReserved2 record = case record of 86 (_, (_, (_, (_, (_, (_, (_, value))))))) = value 87 _ = t 88 89 lookupSectionRecord sectionId directory = find (record : bytesEq? sectionId (sectionRecordId record)) directory 90 sectionDirectoryHasId? sectionId directory = any? (record : bytesEq? sectionId (sectionRecordId record)) directory 91 sectionDirectoryHasDuplicateIds? directory = case directory of 92 [] = false 93 [record | rest] = or? (sectionDirectoryHasId? (sectionRecordId record) rest) (sectionDirectoryHasDuplicateIds? rest) 94 _ = true 95 96 errUnsupportedSection = 17 97 errInvalidSectionRange = 18 98 sectionRecordSupported? record = (and? 99 (and? (equal? (sectionRecordVersion record) [0 1]) 100 (or? (equal? (sectionRecordFlags record) [0 0]) (equal? (sectionRecordFlags record) [0 1]))) 101 (and? (equal? (sectionRecordCompression record) [0 0]) 102 (and? (equal? (sectionRecordReserved1 record) [0 0]) 103 (and? (equal? (sectionRecordReserved2 record) [0 0 0 0]) 104 (or? (equal? (sectionRecordFlags record) [0 0]) 105 (or? (equal? (sectionRecordId record) arboricxManifestSectionId) 106 (equal? (sectionRecordId record) arboricxNodesSectionId))))))) 107 108 validateSectionDirectory directory rest = (lazyBool 109 (_ : err errDuplicateSection rest) 110 (_ : lazyBool (_ : ok directory rest) (_ : err errUnsupportedSection rest) 111 (all? sectionRecordSupported? directory)) 112 (sectionDirectoryHasDuplicateIds? directory)) 113 114 -- Section ranges must be wholly after the directory, in bounds and disjoint. 115 -- Sections are few; pairwise overlap checking is deliberately simple. 116 validateSectionRanges directory total rest = (let 117 start = N.add 32 (N.multiply 32 (length directory)) 118 end record = N.add (sectionRecordOffsetNat record) (sectionRecordLengthNat record) 119 overlaps a b = (and? (not? (N.zero (sectionRecordLengthNat a))) 120 (and? (not? (N.zero (sectionRecordLengthNat b))) 121 (and? (N.lt (sectionRecordOffsetNat a) (end b)) (N.lt (sectionRecordOffsetNat b) (end a))))) 122 in let rec 123 valid records = (case records of 124 [] = true 125 [record | tail] = (and? (N.gte (sectionRecordOffsetNat record) start) 126 (and? (N.lte (end record) total) 127 (and? (not? (any? (overlaps record) tail)) (valid tail)))) 128 _ = false) 129 in lazyBool (_ : ok directory rest) (_ : err errInvalidSectionRange rest) (valid directory)) 130 131 byteSlice = (offset length bytes : bytesTake length (bytesDrop offset bytes)) 132 133 -- Unchecked canonical-byte fold; readers validate octets before this conversion. 134 -- Remove the independent broken adder rather than preserving its representation bug. 135 beBytesToNat bytes = foldl (acc byte : N.add (N.multiply acc 256) byte) 0 bytes 136 u32BEBytesToNat = beBytesToNat 137 u64BEBytesToNat = beBytesToNat 138 139 arboricxHeaderMajorVersion record = case record of 140 (value, _) = value 141 _ = t 142 143 arboricxHeaderMinorVersion record = case record of 144 (_, (value, _)) = value 145 _ = t 146 147 arboricxHeaderSectionCount record = case record of 148 (_, (_, (value, _))) = value 149 _ = t 150 151 arboricxHeaderFlags record = case record of 152 (_, (_, (_, (value, _)))) = value 153 _ = t 154 155 arboricxHeaderDirOffset record = case record of 156 (_, (_, (_, (_, value)))) = value 157 _ = t 158 159 validateArboricxHeader = (header rest : 160 matchBool 161 (ok header rest) 162 (err errUnsupportedVersion rest) 163 (and? 164 (bytesEq? arboricxMajorVersion (arboricxHeaderMajorVersion header)) 165 (and? (bytesEq? arboricxMinorVersion (arboricxHeaderMinorVersion header)) 166 (and? (equal? (arboricxHeaderFlags header) [0 0 0 0 0 0 0 0]) 167 (equal? (arboricxHeaderDirOffset header) [0 0 0 0 0 0 0 32]))))) 168 169 readArboricxContainerChecked = (bs : 170 bindResult (readArboricxHeader bs) 171 (header afterHeader : 172 bindResult (validateArboricxHeader header afterHeader) 173 (validHeader afterValidHeader : 174 bindResult (readSectionDirectory 175 (u32BEBytesToNat (arboricxHeaderSectionCount validHeader)) 176 (bytesDrop (u64BEBytesToNat (arboricxHeaderDirOffset validHeader)) bs)) 177 (directory afterDirectory : 178 bindResult (validateSectionDirectory directory afterDirectory) 179 (validDirectory afterValidDirectory : 180 bindResult (validateSectionRanges validDirectory (length bs) afterValidDirectory) 181 (checked rest : ok (pair validHeader checked) rest)))))) 182 183 -- This is an in-memory boundary, not an incremental stream parser. 184 readArboricxContainer bs = (lazyBool 185 (_ : readArboricxContainerChecked bs) 186 (_ : err errInvalidBinaryInput bs) 187 (binaryBytes? bs)) 188 189 sectionRecordOffsetNat = (sectionRecord : 190 u64BEBytesToNat (sectionRecordOffset sectionRecord)) 191 192 sectionRecordLengthNat = (sectionRecord : 193 u64BEBytesToNat (sectionRecordLength sectionRecord)) 194 195 extractSectionBytes = (sectionRecord containerBytes : 196 byteSlice 197 (sectionRecordOffsetNat sectionRecord) 198 (sectionRecordLengthNat sectionRecord) 199 containerBytes) 200 201 extractSectionBytesResult = (sectionRecord containerBytes rest : 202 (sectionBytes : 203 matchBool 204 (ok sectionBytes rest) 205 (err errUnexpectedEof rest) 206 (equal? (bytesLength sectionBytes) (sectionRecordLengthNat sectionRecord))) 207 (extractSectionBytes sectionRecord containerBytes)) 208 209 lookupSectionBytes = (sectionId directory containerBytes : 210 triage 211 nothing 212 (sectionRecord : just (extractSectionBytes sectionRecord containerBytes)) 213 (_ _ : nothing) 214 (lookupSectionRecord sectionId directory)) 215 216 sectionBytesOrErr = (sectionId directory containerBytes rest : 217 triage 218 (err errMissingSection rest) 219 (sectionRecord : extractSectionBytesResult sectionRecord containerBytes rest) 220 (_ _ : err errMissingSection rest) 221 (lookupSectionRecord sectionId directory)) 222 223 readArboricxSectionBytes = (sectionId bs : 224 bindResult (readArboricxContainer bs) 225 (container afterContainer : 226 matchPair 227 (_ directory : sectionBytesOrErr sectionId directory bs afterContainer) 228 container)) 229 230 readArboricxRequiredSections = (bs : 231 bindResult (readArboricxContainer bs) 232 (container afterContainer : 233 matchPair 234 (_ directory : 235 bindResult (sectionBytesOrErr arboricxManifestSectionId directory bs afterContainer) 236 (manifestBytes _ : 237 bindResult (sectionBytesOrErr arboricxNodesSectionId directory bs afterContainer) 238 (nodesBytes _ : 239 ok (pair manifestBytes nodesBytes) afterContainer))) 240 container))