tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

io_indexed.py (5744B)


      1 #!/usr/bin/env python3
      2 """Independent framed/authenticated indexed-read and bounded-transfer controls."""
      3 import argparse
      4 import hashlib
      5 import json
      6 from pathlib import Path
      7 import struct
      8 from source_order import Runner
      9 from process_rss import run_guarded
     10 from native_indexed_store import indexed_bytes, digest
     11 from executable_object import INDEXED, indexed_manifest
     12 from http_headers import number, result, tree_list
     13 
     14 ROOT = Path(__file__).resolve().parent.parent
     15 
     16 def text(s):
     17     return tree_list([number(ord(c)) for c in s])
     18 
     19 def problem(category, detail, progress):
     20     return result(False, tree_list([text(category), text('indexed-tree/read'), text(detail), text(progress)]))
     21 
     22 def padded(size):
     23     # Valid noncritical opaque section grows TRANSPORT, not the decoded DAG.
     24     # This is not a large-DAG memory or compiler-capacity benchmark.
     25     manifest = indexed_manifest(0)
     26     nodes = struct.pack('>QIB', 1, 1, 0)
     27     def section(kind, flags, offset, count):
     28         return struct.pack('>IHHIQQI', kind, 1, flags, 0, offset, count, 0)
     29     offset = 128 + len(manifest) + len(nodes)
     30     return (b'ARBORICX' + struct.pack('>HHIQQ', 1, 0, 3, 0, 32)
     31             + section(1, 1, 128, len(manifest))
     32             + section(2, 1, 128 + len(manifest), len(nodes))
     33             + section(99, 0, offset, size) + manifest + nodes + b'x' * size)
     34 
     35 def main():
     36     parser = argparse.ArgumentParser(description=__doc__)
     37     parser.add_argument('tricu', type=Path)
     38     parser.add_argument('--directory', required=True, type=Path)
     39     args = parser.parse_args()
     40     directory = args.directory.resolve()
     41     directory.mkdir(parents=True, exist_ok=False)
     42     scratch = directory/'spool-λ'
     43     scratch.mkdir()
     44     runner = Runner(args.tricu, directory)
     45     source = ROOT/'demos/io/indexed-read.tri'
     46     bundle = directory/'indexed-read.arboricx'
     47     runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle])
     48     pin = runner.imported(bundle, 'import')
     49     checks = []
     50     def check(label, payload, size, limit, expected_digest, expected_reply, position):
     51         path = directory/(label+'.bin')
     52         path.write_bytes(payload)
     53         values = [text(str(path)), text(str(scratch)), number(limit), number(size), text(expected_digest)]
     54         argv = ['--io']
     55         for i, value in enumerate(values):
     56             argument = directory/f'{label}-{i}.tree'
     57             argument.write_bytes(value)
     58             argv += ['--arg-tree-file', argument]
     59         expected = tree_list([expected_reply, number(position)])
     60         assert runner.invoke(label, pin, 'probe', argv) == expected, label
     61         assert path.read_bytes() == payload, label
     62         assert list(scratch.iterdir()) == [], label
     63         checks.append(dict(label=label, input_bytes=len(payload), frame_bytes=size))
     64     for i, tree in enumerate([b'\0', b'\1\0', b'\2\1\0\0', number(42)]):
     65         payload = indexed_bytes(tree)
     66         check(f'tree-{i}', payload+b'next frame', len(payload), len(payload), digest(INDEXED, payload), result(True, tree), len(payload))
     67     payload = indexed_bytes(number(7))
     68     check('wrong-digest', payload, len(payload), len(payload), '0'*64,
     69           problem('integrity-failed', 'CAS digest mismatch', 'unknown'), len(payload))
     70     check('truncated', payload[:-1], len(payload), len(payload), digest(INDEXED, payload),
     71           problem('invalid-data', 'incomplete indexed frame', 'unknown'), len(payload)-1)
     72     check('limit', payload, len(payload), len(payload)-1, digest(INDEXED, payload),
     73           problem('invalid-bound', 'invalid snapshot size limit', 'none'), 0)
     74     check('bad-digest', payload, len(payload), len(payload), 'A'*64,
     75           problem('invalid-argument', '', 'none'), 0)
     76     check('bad-bound-wire', payload, 2**63, 2**63, digest(INDEXED, payload),
     77           problem('invalid-argument', '', 'none'), 0)
     78     malformed = b'not a bundle'
     79     check('malformed-authenticated', malformed, len(malformed), len(malformed), digest(INDEXED, malformed),
     80           problem('invalid-data', 'section extends beyond bundle end', 'unknown'), len(malformed))
     81     check('malformed-unauthenticated', malformed, len(malformed), len(malformed), '0'*64,
     82           problem('integrity-failed', 'CAS digest mismatch', 'unknown'), len(malformed))
     83     for size in [4096, 1048576, 16777216, 67108864]:
     84         payload = padded(size)
     85         check(f'transport-{size}', payload, len(payload), len(payload), digest(INDEXED, payload), result(True, b'\0'), len(payload))
     86     observations = [s for s in runner.records if s['label'].startswith('transport-')]
     87     for sample in observations:
     88         assert sample['peak_kib'] <= 48*1024, sample
     89         assert sample['rts_process']['maximum_live_bytes'] <= 16*1024*1024, sample
     90     files = ['lib/io.tri', 'demos/io/indexed-read.tri', 'test/io_indexed.py',
     91              'test/source_order.py', 'test/rts_metrics.py', 'test/native_indexed_store.py',
     92              'test/executable_object.py', 'test/stable_bundle.py', 'src/ContentStore/Snapshot.hs',
     93              'src/ContentStore/Indexed.hs', 'src/Wire.hs', 'src/IO/Codec.hs',
     94              'src/IO/Resources.hs', 'src/IO/Driver.hs', 'src/IO/Protocol.hs',
     95              'bootstrap/build-stable.arboricx']
     96     (directory/'report.json').write_text(json.dumps(dict(checks=checks, observations=observations,
     97 
     98         measurement_notes='Opaque extension grows transport, not DAG. RSS is sampled: tiny runs can exit before representative samples; use the 64-MiB control for the transfer RSS guard. No concurrency or large-DAG capacity claim.',
     99         source_hashes={p:hashlib.sha256((ROOT/p).read_bytes()).hexdigest() for p in files}), indent=2)+'\n')
    100     print(f'{len(checks)} indexed-read controls passed')
    101 
    102 if __name__ == '__main__':
    103     main()