# Data flow Miller data flow is records produced by a record-reader in `input/`, followed by one or more mappers in `mapping/`, written by a record-writer in `output/`, controlled by logic in `stream/`. Argument parsing for initial stream setup is in `cli/`. # Container names The user-visible concept of *stream record* (or *srec*) is implemented in the `lrec_t` (*linked-record type*) data structure. The user-visible concept of *out-of-stream variables* is implemented using the `mlhmmv_t` (multi-level hashmap of mlrvals) structure. Source-code comments and names within the code refer to `srec`/`lrec` and `oosvar`/`mlhmmv` depending on the context. While those two data structures contain user-visible data structures, others are used in Miller's implementation: `slls` and `sllv` are singly-linked lists of string and void-star respectively; `lhmss` is a linked hashmap from string to string; `lhmsi` is a linked hashmap from string to int; and so on. # Memory management Miller is streaming and as near stateless as possible. For most Miller functions, you can ingest a 20GB file with 4GB RAM, no problem. For example, `mlr cat` of a DKVP file retains no data in memory from one line to another; `mlr cat` of a CSV file retains only the field names from the header line. The `stats1` and `stats2` commands retain only aggregation state (e.g. count and sum over specified fields needed to compute mean of specified fields). The `mlr tac` and `mlr sort` commands, obviously, need to consume and retain all input records before emitting any output records. Miller classes are in general modular, following a constructor/destructor model with minimal dependencies between classes. As a general rule, void-star payloads (`sllv`, `lhmslv`) must be freed by the callee (which has access to the data type) whereas non-void-star payloads (`slls`, `hss`) are freed by the container class. One complication is for free-flags in `lrec` and `slls`: the idea is that an entire line is mallocked and presented by the record reader; then individual fields are split out and populated into linked list or records. To reduce the amount of strduping there, free-flags are used to track which fields should be freed by the destructor and which are freed elsewhere. The `header_keeper` object is an elaboration on this theme: suppose there is a CSV file with header line `a,b,c` and data lines `1,2,3`, then `4,5,6`, then `7,8,9`. Then the keys `a`, `b`, and `c` are shared between all three records; they are retained in a single `header_keeper` object. A bigger complication to the otherwise modular nature of Miller is its *baton-passing memory-management model*. Namely, one class may be responsible for freeing memory allocated by another class. For example, using `mlr cat`: The record-reader produces records and returns pointers to them. The record-mapper is just a pass-through; it returns the record-pointers it receives. The record-writer formats the records to stdout and does not return them, so it is responsible for freeing them. Similarly, `mlr cut -x` and any other mappers which modify record objects without creating new ones. By contrast,`stats1` et al. produce their own records; they free what they do not pass on. # Null-lrec conventions Record-readers return a null lrec-pointer to signify end of input stream. Each mapper takes an lrec-pointer as input and returns a linked list of lrec-pointer. Null-lrec is input to mappers to signify end of stream: e.g. `sort` or `tac` should use this as a signal to deliver the sorted/reversed list of rows. When a mapper has no output before end of stream (e.g. `sort` or `tac` while accumulating inputs) it returns a null lrec-pointer which is treated as synonymous with returning an empty list. At end of stream, a mapper returns a linked list of records ending in a null lrec-pointer. A null lrec-pointer at end of stream is passed to lrec writers so that they may produce final output (e.g. pretty-print which produces no output until end of stream). # Performance optimizations The initial implementation of Miller used `lhmss` (insertion-ordered string-to-string hash map) for record objects. Keys and values were strduped out of file-input lines. Each of the following produced from 5 to 30 percent performance gains: * The `lrec` object is a hashless map suited to low access-to-creation ratio. See detailed comments in https://github.com/johnkerl/miller/blob/master/c/containers/lrec.h. * Free-flags as discussed above removed additional occurrences of string copies. * Using `mmap` to read files gets rid of double passes on record parsing (one to find end of line, and another to separate fields) as well as most use of `malloc`. Note however that standard input cannot be mmapped, so both record-reader options are retained.