tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

object.tri (8330B)


      1 !import prelude *
      2 !import io *
      3 !import native_math as N
      4 !export sourceObjectTreeDomain
      5 !export sourceObjectFlatTreeDomain
      6 !export sourceObjectManifestDomain
      7 !export sourceObjectTreeAbi
      8 !export sourceObjectTreeKind
      9 !export sourceObjectEncodeOnto
     10 !export sourceObjectEncode
     11 !export sourceObjectDecode
     12 !export sourceObjectDecodeComplete
     13 !export sourceObjectShard
     14 !export sourceObjectPath
     15 !export sourceObjectDigest
     16 !export sourceObjectHashHex
     17 !export sourceObjectHashValid
     18 !export sourceObjectVerify
     19 !export sourceObjectGet
     20 !export sourceObjectStoreAt
     21 !export sourceObjectStore
     22 !export sourceObjectPutTree
     23 !export sourceObjectGetTree
     24 
     25 -- Native executable objects preserve sharing as name-neutral indexed bundles.
     26 -- The flat codec below remains an independent data codec with its own domain;
     27 -- it is not the native executable publication format.
     28 sourceObjectTreeDomain = "arboricx.indexed-tree.v1"
     29 sourceObjectFlatTreeDomain = "arboricx.tree-term.v1"
     30 sourceObjectManifestDomain = "arboricx.module-manifest.v1"
     31 sourceObjectTreeAbi = "arboricx.abi.tree.v1"
     32 sourceObjectTreeKind = "arboricx.indexed-tree.v1"
     33 
     34 -- Build directly onto a suffix. Appending each encoded left subtree copies
     35 -- its byte list once per enclosing fork and retains huge intermediate lists
     36 -- for compiler exports. This visits each expanded tree occurrence once.
     37 -- Keep triage as the small shape eliminator; canonical preorder is unchanged.
     38 sourceObjectEncodeOnto tree suffix =
     39   triage
     40     (pair 0 suffix)
     41     (child : pair 1 (sourceObjectEncodeOnto child suffix))
     42     (left right : pair 2 (sourceObjectEncodeOnto left (sourceObjectEncodeOnto right suffix)))
     43     tree
     44 sourceObjectEncode tree = sourceObjectEncodeOnto tree t
     45 
     46 sourceObjectDecode bytes =
     47   lazyList
     48     (_ : err "truncated tree-term" bytes)
     49     (tag rest :
     50       lazyBool
     51         (_ : ok t rest)
     52         (_ : lazyBool
     53           (_ : lazyResult
     54             (problem remaining : err problem remaining)
     55             (child remaining : ok (t child) remaining)
     56             (sourceObjectDecode rest))
     57           (_ : lazyBool
     58             (_ : lazyResult
     59               (problem remaining : err problem remaining)
     60               (left remaining : lazyResult
     61                 (problem tail : err problem tail)
     62                 (right tail : ok (t left right) tail)
     63                 (sourceObjectDecode remaining))
     64               (sourceObjectDecode rest))
     65             (_ : err "invalid tree-term tag" rest)
     66             (equal? tag 2))
     67           (equal? tag 1))
     68         (equal? tag 0))
     69     bytes
     70 
     71 sourceObjectDecodeComplete bytes =
     72   lazyResult
     73     (problem remaining : err problem remaining)
     74     (tree remaining : lazyList
     75       (_ : ok tree t)
     76       (_ _ : err "trailing tree-term bytes" t)
     77       remaining)
     78     (sourceObjectDecode bytes)
     79 
     80 -- The shard is taken by finite destructuring: it must not cause recursion
     81 -- over an abstract hash during fresh-store normalization.
     82 sourceObjectShard hash =
     83   matchList t (a r0 :
     84     matchList t (b r1 :
     85       matchList t (c _ : pair a (pair b (pair c t))) r1) r0) hash
     86 sourceObjectPath root hash =
     87   append root (append "/objects/" (append (sourceObjectShard hash)
     88     (append "/" hash)))
     89 sourceObjectDigest domain payload =
     90   sha256Hex (append domain (pair 0 payload))
     91 
     92 -- Native math assumes canonical naturals. Keep the existing behavior for
     93 -- arbitrary trees: only canonical bytes use the binary comparison fast path.
     94 -- Canonical-byte helpers stay private; malformed trees retain the reference path.
     95 sourceObjectByteLeaf? value = triage true (_ : false) (_ _ : false) value
     96 sourceObjectByteBit? value = triage true (child : sourceObjectByteLeaf? child) (_ _ : false) value
     97 sourceObjectByteOne? value = triage false (child : sourceObjectByteLeaf? child) (_ _ : false) value
     98 -- Eight statically bounded structural steps avoid numeric countdown work for
     99 -- every digest byte. Inspect the rest shape before the bit, as in the bounded
    100 -- recognizer; an invalid bit or exhausted bound must not force deeper fields.
    101 sourceObjectByteStep next value = (triage
    102   true
    103   (_ : false)
    104   (bit rest : lazyBool
    105     (_ : sourceObjectByteOne? bit)
    106     (_ : andLazy? (sourceObjectByteBit? bit) (_ : next rest))
    107     (sourceObjectByteLeaf? rest))
    108   value)
    109 sourceObjectCanonicalByte value = (sourceObjectByteStep
    110   (sourceObjectByteStep
    111     (sourceObjectByteStep
    112       (sourceObjectByteStep
    113         (sourceObjectByteStep
    114           (sourceObjectByteStep
    115             (sourceObjectByteStep
    116               (sourceObjectByteStep sourceObjectByteLeaf?)))))))
    117   value)
    118 -- This compatibility path must not call base's canonical-only arithmetic.
    119 -- The historical predecessor leaves Stem values unchanged and interprets any
    120 -- non-Leaf bit/tail as truthy. Comparison counts down its finite byte bound;
    121 -- structural equality (not numeric equality) controls the strict endpoint.
    122 sourceObjectReferencePred value = (triage
    123   0
    124   (child : t child)
    125   (bit rest : lazyBool
    126     (_ : matchBool (t t rest) 0 rest)
    127     (_ : t (t t) (sourceObjectReferencePred rest)) bit)
    128   value)
    129 sourceObjectReferenceLte value bound = (lazyBool
    130   (_ : true)
    131   (_ : lazyBool (_ : false)
    132     (_ : sourceObjectReferenceLte (sourceObjectReferencePred value)
    133       (sourceObjectReferencePred bound)) (isZero_? bound))
    134   (isZero_? value))
    135 sourceObjectReferenceLt value bound = (and?
    136   (sourceObjectReferenceLte value bound) (not? (equal? value bound)))
    137 sourceObjectHashByteReference? byte = (or?
    138   (and? (not? (sourceObjectReferenceLt byte 48)) (sourceObjectReferenceLte byte 57))
    139   (and? (not? (sourceObjectReferenceLt byte 97)) (sourceObjectReferenceLte byte 102)))
    140 sourceObjectHashByte? byte = (lazyBool
    141   (_ : N.lowerHex byte)
    142   (_ : sourceObjectHashByteReference? byte)
    143   (sourceObjectCanonicalByte byte))
    144 sourceObjectHashHex bytes = (lazyList
    145   (_ : true)
    146   (byte rest : andLazy? (sourceObjectHashByte? byte) (_ : sourceObjectHashHex rest))
    147   bytes)
    148 sourceObjectHashValid hash =
    149   andLazy? (equal? (length hash) 64)
    150     (_ : sourceObjectHashHex hash)
    151 
    152 sourceObjectVerify domain payload expected =
    153 
    154   onResult_ (sourceObjectDigest domain payload)
    155     (problem : pure (err problem t))
    156     (actual : lazyBool
    157       (_ : pure (ok payload t))
    158       (_ : pure (err "CAS digest mismatch" t))
    159       (equal? actual expected))
    160 
    161 sourceObjectGet root domain hash =
    162   lazyBool
    163     (_ : onResult_ (readFile (sourceObjectPath root hash))
    164       (problem : pure (err problem t))
    165       (payload : sourceObjectVerify domain payload hash))
    166     (_ : pure (err "invalid CAS digest" t))
    167     (sourceObjectHashValid hash)
    168 
    169 sourceObjectStore root domain payload =
    170   onOk_ (sourceObjectDigest domain payload) (hash :
    171     onOk_ (createDirectory root) (_ :
    172       onOk_ (createDirectory (append root "/objects")) (_ :
    173         onOk_ (createDirectory (append root (append "/objects/"
    174           (sourceObjectShard hash)))) (_ :
    175           sourceObjectStoreAt root domain payload hash))))
    176 
    177 -- Host publishes via a unique same-directory temp and an atomic no-replace
    178 -- link; tricu owns the object identity and verifies even a racing winner.
    179 sourceObjectStoreAt root domain payload hash =
    180   onOk_ (writeBytesIfAbsent (sourceObjectPath root hash) payload) (_ :
    181     onResult_ (sourceObjectGet root domain hash)
    182       (problem : pure (err problem t))
    183       (old : lazyBool
    184         (_ : pure (ok hash t))
    185         (_ : pure (err "CAS object exists with different bytes" t))
    186         (equal? old payload)))
    187 
    188 -- tricu owns sharding/directories; the host hashes and atomically transports
    189 -- the inert tree as an indexed DAG, with verified no-replace writes.
    190 sourceObjectPutTree root tree =
    191   onOk_ (hashIndexedTree tree) (hash :
    192     onOk_ (createDirectory root) (_ :
    193       onOk_ (createDirectory (append root "/objects")) (_ :
    194         onOk_ (createDirectory (append root (append "/objects/"
    195           (sourceObjectShard hash)))) (_ :
    196           onOk_ (writeIndexedTreeIfAbsent (sourceObjectPath root hash) hash tree)
    197             (_ : pure (ok hash t))))))
    198 -- Typed verified transport avoids expanding raw executable bytes into a
    199 -- tricu byte list for hashing and decoding. The portable codec stays available
    200 -- as an independent reference; this path does not silently fall back to it.
    201 sourceObjectGetTree root hash =
    202   lazyBool
    203     (_ : readIndexedTree (sourceObjectPath root hash) hash)
    204     (_ : pure (err "invalid CAS digest" t))
    205     (sourceObjectHashValid hash)
    206