Substreams: containment, relocation and caves
Some formats are made of a sequence of headers followed by the space, within which the headers create blobs, but the pointers within the headers are absolute addresses rather than addresses base the beginning of the area. It can (but not necessarily, it depends on the actual use case of the format and designer intention) be a mistake when designing file formats, since this approach has a bunch of issues:

* DRY principle is violated, when moving the area of objects, one has to change the offsets within each pointer.
* it is possible to create objects overlapping to headers and to other objects, allowing to exploit flaws in the software not addressing such cases properly. And the whole design of the format encourages writing such software. For example, imagine a hypothetical file format used as a disk image for a virtual machine. Imagine that using some bit flips within NAND Flash memory (which mimics the architecture of RAM to some extent and [was also affected to rowhammer-like attacks](https://www.usenix.org/system/files/conference/woot17/woot17-paper-kurmus.pdf)) it was achieved re-pointing an allocated chunk exposed as raw storage and then as a file to an unprivileged process within the VM onto the headers of the disk image format. It may be a way to attack the VM software, if it is not designed to resist such attacks. 

The problems become more severe when such pointers-base-something-else are buried deeper within struct hierarchy.

So, there are pretty a lot of misdesigned formats. When writing KS specs for them authors may want to write the specs the way that the specs operated in a secure way even in the case of manipulated files. I mean,

* the chunks, pointing outside of the allowed span, should be considered invalid.
* the chunks, overlapping other chunks, should be considered invalid.

Also, for serialization purposes, it may be useful to know the caves - space between chunks.

Currently I don't have a well-designed proposal on how to solve this problem, but here are some ideas.

It is proposed to do it by adding `base-io` attr to `pos`-instances. What is currently its `io` for the purpose of this proposal is called `target_io`.

When an instance with `base-io` is met, the transpiler and the code generated by it should do the following:

0. Initialization: create a range tree. The range tree is not a simple, but allows transactions and snapshots. Opening a transaction spawns a snapshot from the parent. Committing it applies the snapshot to the parent. The range tree is generated within the `io` object, that is used as a `target_io`. When there is an error, the snapshot is just destroyed using RAII idiom.
1. check if target_io is within base_io, in compile time.

2. check if the span `offset_relative_to_base_io:offset_relative_to_base_io + size` (in Python syntax) is within the `target_io`
3. create a transaction, operate on the current snapshot.
4. check the range within the range tree. If it is there, then error.
5. parse the blob according to the type. The blob can use `io`s, 
6. add range into the range tree
7. add the parsed object where needed
8. commit the transaction

The similar checks can be used as constraints when serializing.

The range tree should be accessible somehow, and allows to get a list of caves using some API. This list of caves can be useful for serialization for allocation of chunks and reordering them.

The main features of this should be configurable. It should be possible to disable certain checks all globally and in situ both by using hints and by using compiler flags.
