#!/usr/bin/env dub
/+ dub.sdl:
name "autological_relocation_join"
targetPath "build"
dflags "-preview=in" "-preview=dip1000"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
/**
* "If relocations are a join, the loader is a query engine" — executed.
*
* The catalog's cluster-D claim is that `ld.so` computes, at every single
* process start, the answer to a query it has already answered identically
* thousands of times: for each undefined symbol, which object in the search
* scope defines it? This program implements that query over a small in-memory
* relational model — `objects`, `needs`, `defines`, `undefined` — and prints:
*
* 1. The **scope order**: a breadth-first walk of `DT_NEEDED` from the
* executable, deduplicated on first sight. This is the ordering rule
* `ld.so` uses, and it is why the *shape* of the dependency graph, not
* just its contents, decides which definition wins.
* 2. The **join**, resolved under first-wins interposition, with the number
* of scope probes each lookup cost.
* 3. The same computation with an `LD_PRELOAD` object spliced in at the front
* — SELF's "`LD_PRELOAD` becomes a row" claim, shown as exactly that: one
* inserted tuple, no other change, different answers.
* 4. A **cost summary** demonstrating the confounder the measurement page
* warns about: the work is proportional to the *object count* traversed,
* not to the byte size of anything.
* 5. The identical query written as SQL and as Datalog, so the tree's
* "SQL or Datalog?" open question can be read rather than argued: the
* transitive part is one line in Datalog and a recursive CTE in SQL.
*
* Nothing here is a simulation of performance — it is a statement of *what is
* being computed*. The interesting number is the probe count, because that is
* the quantity a materialized view (`prelink`, or a resolved-address table
* stored in the artifact) would drive to zero.
*
* Companions:
* docs/research/autological-artifacts/dynamic-linking.md
* docs/research/autological-artifacts/code-as-database.md
* docs/research/autological-artifacts/measurement.md
* docs/research/autological-artifacts/self-selfdb/index.md
*
* Run with: `dub run --single relocation-join.d`
*
* Portability: pure `std`, no I/O beyond stdout. Deterministic everywhere.
*/
module (module) autological_relocation_join"If relocations are a join, the loader is a query engine" — executed.
The catalog's cluster-D claim is that ld.so computes, at every single
process start, the answer to a query it has already answered identically
thousands of times: for each undefined symbol, which object in the search
scope defines it? This program implements that query over a small in-memory
relational model — objects, needs, defines, undefined — and prints:
The scope order: a breadth-first walk of DT_NEEDED from the
executable, deduplicated on first sight. This is the ordering rule
ld.so uses, and it is why the shape of the dependency graph, not
just its contents, decides which definition wins.
The join, resolved under first-wins interposition, with the number
of scope probes each lookup cost.
The same computation with an LD_PRELOAD object spliced in at the front
— SELF's "LD_PRELOAD becomes a row" claim, shown as exactly that: one
inserted tuple, no other change, different answers.
A cost summary demonstrating the confounder the measurement page
warns about: the work is proportional to the object count traversed,
not to the byte size of anything.
The identical query written as SQL and as Datalog, so the tree's
"SQL or Datalog?" open question can be read rather than argued: the
transitive part is one line in Datalog and a recursive CTE in SQL.
Nothing here is a simulation of performance — it is a statement of what is
being computed. The interesting number is the probe count, because that is
the quantity a materialized view (prelink, or a resolved-address table
stored in the artifact) would drive to zero.
Companions
docs/research/autological-artifacts/dynamic-linking.md
docs/research/autological-artifacts/code-as-database.md
docs/research/autological-artifacts/measurement.md
docs/research/autological-artifacts/self-selfdb/index.md
Run with: dub run --single relocation-join.d
Portability
pure std, no I/O beyond stdout. Deterministic everywhere.
autological_relocation_join;
import (package) stdstd.(module) std.algorithmThis package implements generic algorithms oriented towards the processing of
sequences. Sequences processed by these functions define range-based
interfaces. See also Reference on ranges and
tutorial on ranges.
Algorithms are categorized into the following submodules:
Submodule Functions
| Searching |
all
any
balancedParens
boyerMooreFinder
canFind
commonPrefix
count
countUntil
endsWith
find
findAdjacent
findAmong
findSkip
findSplit
findSplitAfter
findSplitBefore
minCount
maxCount
minElement
maxElement
minIndex
maxIndex
minPos
maxPos
skipOver
startsWith
until
|
| Comparison |
among
castSwitch
clamp
cmp
either
equal
isPermutation
isSameLength
levenshteinDistance
levenshteinDistanceAndPath
max
min
mismatch
predSwitch
|
| Iteration |
cache
cacheBidirectional
chunkBy
cumulativeFold
each
filter
filterBidirectional
fold
group
joiner
map
mean
permutations
reduce
splitWhen
splitter
substitute
sum
uniq
|
| Sorting |
completeSort
isPartitioned
isSorted
isStrictlyMonotonic
ordered
strictlyOrdered
makeIndex
merge
multiSort
nextEvenPermutation
nextPermutation
nthPermutation
partialSort
partition
partition3
schwartzSort
sort
topN
topNCopy
topNIndex
|
| Set operations (setops) |
cartesianProduct
largestPartialIntersection
largestPartialIntersectionWeighted
multiwayMerge
multiwayUnion
setDifference
setIntersection
setSymmetricDifference
|
| Mutation |
bringToFront
copy
fill
initializeAll
move
moveAll
moveSome
moveEmplace
moveEmplaceAll
moveEmplaceSome
remove
reverse
strip
stripLeft
stripRight
swap
swapRanges
uninitializedFill
|
Many functions in this package are parameterized with a predicate.
The predicate may be any suitable callable type
(a function, a delegate, a functor, or a lambda), or a
compile-time string. The string may consist of any legal D
expression that uses the symbol a (for unary functions) or the
symbols a and b (for binary functions). These names will NOT
interfere with other homonym symbols in user code because they are
evaluated in a different context. The default for all binary
comparison predicates is "a == b" for unordered operations and
"a < b" for ordered operations.
Example
int[] a = ...;
static bool greater(int a, int b)
{
return a > b;
}
sort!greater(a); // predicate as alias
sort!((a, b) => a > b)(a); // predicate as a lambda.
sort!"a > b"(a); // predicate as string
// (no ambiguity with array name)
sort(a); // no predicate, "a < b" is implicit
Source
std/algorithm/package.d
algorithm : (alias template) autological_relocation_join.canFind = std.algorithm.searching.canFind(alias pred = "a == b")Convenience function. Like find, but only returns whether or not the search
was successful.
For more information about pred see find.
canFind, (alias template) autological_relocation_join.filter = std.algorithm.iteration.filter(alias predicate) if (is(typeof(unaryFun!predicate)))``filter!(predicate)(range) returns a new range containing only elements x in range for
which predicate(x) returns true.
The predicate is passed to unaryFun, and can be either a string, or
any callable that can be executed via pred(element).
filter, (alias template) autological_relocation_join.map = std.algorithm.iteration.map(fun...) if (fun.length >= 1)Implements the homonym function (also known as transform) present
in many languages of functional flavor. The call ``map!(fun)(range)
returns a range of which elements are obtained by applying fun(a)
left to right for all elements a in range. The original ranges are
not changed. Evaluation is done lazily.
map, (alias template) autological_relocation_join.sum = std.algorithm.iteration.sum(R)(R r) if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front)))Sums elements of r, which must be a finite
input range. Although
conceptually sum`(r)` is equivalent to `fold`!((a, b) => a +
b)(r, 0), sum`` uses specialized algorithms to maximize accuracy,
as follows.
If ElementType!R is a floating-point
type and R is a
random-access range with
length and slicing, then sum uses the
pairwise summation
algorithm.
If ElementType!R is a floating-point type and R is a
finite input range (but not a random-access range with slicing), then
sum uses the Kahan summation algorithm.
In all other cases, a simple element by element addition is done.
For floating point inputs, calculations are made in
spec/type, Types, real
precision for real inputs and in double precision otherwise
(Note this is a special case that deviates from fold's behavior,
which would have kept float precision for a float range).
For all other types, the calculations are done in the same type obtained
from from adding two elements of the range, which may be a different
type from the elements themselves (for example, in case of
integral promotion).
A seed may be passed to sum. Not only will this seed be used as an initial
value, but its type will override all the above, and determine the algorithm
and precision used for summation. If a seed is not passed, one is created with
the value of typeof(r.front + r.front)(0), or typeof(r.front + r.front).zero
if no constructor exists that takes an int.
Note that these specialized summing algorithms execute more primitive operations
than vanilla summation. Therefore, if in certain cases maximum speed is required
at expense of precision, one can use fold!((a, b) => a + b)(r, 0), which
is not specialized for summation.
sum;
import (package) stdstd.(module) std.arrayFunctions and types that manipulate built-in arrays and associative arrays.
This module provides all kinds of functions to create, manipulate or convert arrays:
Function Name Description
| array |
Returns a copy of the input in a newly allocated dynamic array.
|
| appender |
Returns a new Appender or RefAppender initialized with a given array.
|
| assocArray |
Returns a newly allocated associative array from a range/ranges of keys and values.
|
| byPair |
Construct a range iterating over an associative array by key/value tuples.
|
| insertInPlace |
Inserts into an existing array at a given position.
|
| join |
Concatenates a range of ranges into one array.
|
| minimallyInitializedArray |
Returns a new array of type T.
|
| replace |
Returns a new array with all occurrences of a certain subrange replaced.
|
| replaceFirst |
Returns a new array with the first occurrence of a certain subrange replaced.
|
| replaceInPlace |
Replaces all occurrences of a certain subrange and puts the result into a given array.
|
| replaceInto |
Replaces all occurrences of a certain subrange and puts the result into an output range.
|
| replaceLast |
Returns a new array with the last occurrence of a certain subrange replaced.
|
| replaceSlice |
Returns a new array with a given slice replaced.
|
| replicate |
Creates a new array out of several copies of an input array or range.
|
| sameHead |
Checks if the initial segments of two arrays refer to the same
place in memory.
|
| sameTail |
Checks if the final segments of two arrays refer to the same place
in memory.
|
| split |
Eagerly split a range or string into an array.
|
| staticArray |
Creates a new static array from given data.
|
| uninitializedArray |
Returns a new array of type T without initializing its elements.
|
Source
std/array.d
array : (alias template) autological_relocation_join.array = std.array.array(Range)(Range r) if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range)Allocates an array and initializes it with copies of the elements
of range r.
Narrow strings are handled as follows:
If autodecoding is turned on (default), then they are handled as a separate overload.
If autodecoding is turned off, then this is equivalent to duplicating the array.
array, (alias template) autological_relocation_join.join = std.array.join(RoR, R)(RoR ror, R sep) if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR)) && isInputRange!R && (is(immutable(ElementType!(ElementType!RoR)) == immutable(ElementType!R)) || isSomeChar!(ElementType!(ElementType!RoR)) && isSomeChar!(ElementType!R)))Eagerly concatenates all of the ranges in ror together (with the GC)
into one array using sep as the separator if present.
join;
import (package) stdstd.(module) std.convA one-stop shop for converting values from one type to another.
Category Functions Generic asOriginalType castFrom parse to toChars bitCast Strings text wtext dtext writeText writeWText writeDText hexString Numeric octal roundTo signed unsigned Exceptions ConvException ConvOverflowException
Source
std/conv.d
conv : (alias template) autological_relocation_join.text = std.conv.text(T...)(T args) if (T.length > 0)Convenience functions for converting one or more arguments
of any type into text (the three character widths).
text;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) autological_relocation_join.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) autological_relocation_join.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
/// One shared object in the model: the `objects` table, with its columns.
struct (struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object
{
(alias) object.string = stringstring (field) string autological_relocation_join.Object.sonamesoname;
(alias) object.string = stringstring[] (field) string[] autological_relocation_join.Object.needsneeds; // the DT_NEEDED edges
(alias) object.string = stringstring[] (field) string[] autological_relocation_join.Object.definesdefines; // exported definitions
(alias) object.string = stringstring[] (field) string[] autological_relocation_join.Object.undefinedundefined; // symbols this object must have resolved
}
/// One resolved relocation: the join's output row.
struct (struct) autological_relocation_join.ResolutionOne resolved relocation: the join's output row.
Resolution
{
(alias) object.string = stringstring (field) string autological_relocation_join.Resolution.referrerreferrer;
(alias) object.string = stringstring (field) string autological_relocation_join.Resolution.symbolsymbol;
(alias) object.string = stringstring (field) string autological_relocation_join.Resolution.providerprovider; // null when unresolved
(alias) object.size_t = ulongsize_t (field) ulong autological_relocation_join.Resolution.probesprobes; // objects examined before the answer was found
}
/++
The scope order `ld.so` builds: breadth-first over `DT_NEEDED`, first sight wins.
Depth-first would produce a different order and therefore different
interposition winners; the breadth-first rule is the one glibc implements, and
stating it is half the point of this program — the answer to the query depends
on a traversal order that lives in the loader, not in the data.
+/
(alias) object.string = stringstring[] string[] autological_relocation_join.scopeOrder(in autological_relocation_join.Object[string] world, string root, string[] preload = null) @safeThe scope order ld.so builds: breadth-first over DT_NEEDED, first sight wins.
Depth-first would produce a different order and therefore different
interposition winners; the breadth-first rule is the one glibc implements, and
stating it is half the point of this program — the answer to the query depends
on a traversal order that lives in the loader, not in the data.
scopeOrder(in (struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object[(alias) object.string = stringstring] (parameter) const(autological_relocation_join.Object[string]) worldworld, (alias) object.string = stringstring (parameter) string rootroot, (alias) object.string = stringstring[] (parameter) string[] preloadpreload = null) @safe
{
(alias) object.string = stringstring[] (local variable) string[] orderorder;
bool[(alias) object.string = stringstring] (local variable) bool[string] seenseen;
void void autological_relocation_join.scopeOrder.admit(string name) pure nothrow @safeadmit((alias) object.string = stringstring (parameter) string namename)
{
if ((parameter) string namename in (local variable) bool[string] seenseen)
return;
bool* core.internal.newaa._d_aaGetY!(string, bool, bool[string], string, bool, string)(ref scope bool[string] aa, ref string key, out bool found) pure nothrow @safeLookup key in aa.
Called only from implementation of (aakey) expressions when value is mutable.
seen[bool* core.internal.newaa._d_aaGetY!(string, bool, bool[string], string, bool, string)(ref scope bool[string] aa, ref string key, out bool found) pure nothrow @safeLookup key in aa.
Called only from implementation of (aakey) expressions when value is mutable.
name] = true;
(local variable) string[] orderorder ~= (parameter) string namename;
}
void autological_relocation_join.scopeOrder.admit(string name) pure nothrow @safeadmit((parameter) string rootroot);
// `LD_PRELOAD` objects are admitted immediately after the executable and
// before anything the executable needs — which is the whole mechanism.
foreach ((parameter) string pp; (parameter) string[] preloadpreload)
void autological_relocation_join.scopeOrder.admit(string name) pure nothrow @safeadmit((local variable) string pp);
for ((alias) object.size_t = ulongsize_t (local variable) ulong ii = 0; i < order.length; i++)
{
if (auto (local variable) const(autological_relocation_join.Object)* oo = (local variable) string[] orderorder[(local variable) ulong ii] in (parameter) const(autological_relocation_join.Object[string]) worldworld)
foreach ((parameter) const(string) nn; (local variable) const(autological_relocation_join.Object)* oo.(field) string[] autological_relocation_join.Object.needsneeds)
void autological_relocation_join.scopeOrder.admit(string name) pure nothrow @safeadmit((local variable) const(string) nn);
}
return (local variable) string[] orderorder;
}
/++
The join itself: for every undefined symbol, the first definition in scope order.
`probes` counts how many objects were examined. In glibc this is a `.gnu.hash`
bloom-filter test per object followed by a bucket walk on a hit; the count below
is the number of objects the loader must at minimum touch, which is the quantity
that scales with object count rather than with image size.
+/
(struct) autological_relocation_join.ResolutionOne resolved relocation: the join's output row.
Resolution[] autological_relocation_join.Resolution[] autological_relocation_join.resolve(in autological_relocation_join.Object[string] world, in string[] order) @safeThe join itself: for every undefined symbol, the first definition in scope order.
probes counts how many objects were examined. In glibc this is a .gnu.hash
bloom-filter test per object followed by a bucket walk on a hit; the count below
is the number of objects the loader must at minimum touch, which is the quantity
that scales with object count rather than with image size.
resolve(in (struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object[(alias) object.string = stringstring] (parameter) const(autological_relocation_join.Object[string]) worldworld, in (alias) object.string = stringstring[] (parameter) const(string[]) orderorder) @safe
{
(struct) autological_relocation_join.ResolutionOne resolved relocation: the join's output row.
Resolution[] (local variable) autological_relocation_join.Resolution[] out_out_;
foreach ((parameter) const(string) referrerreferrer; (parameter) const(string[]) orderorder)
{
const (local variable) const(autological_relocation_join.Object*) oo = (local variable) const(string) referrerreferrer in (parameter) const(autological_relocation_join.Object[string]) worldworld;
if ((local variable) const(autological_relocation_join.Object*) oo is null)
continue;
foreach ((parameter) const(string) symsym; (local variable) const(autological_relocation_join.Object*) oo.(field) string[] autological_relocation_join.Object.undefinedundefined)
{
(alias) object.size_t = ulongsize_t (local variable) ulong probesprobes;
(alias) object.string = stringstring (local variable) string providerprovider;
foreach ((parameter) const(string) candidatecandidate; (parameter) const(string[]) orderorder)
{
(local variable) ulong probesprobes++;
if (auto (local variable) const(autological_relocation_join.Object)* cc = (local variable) const(string) candidatecandidate in (parameter) const(autological_relocation_join.Object[string]) worldworld)
if ((local variable) const(autological_relocation_join.Object)* cc.(field) string[] autological_relocation_join.Object.definesdefines.bool std.algorithm.searching.canFind!().canFind!(const(string)[], string)(const(string)[] haystack, scope string needle) pure nothrow @nogc @safeConvenience function. Like find, but only returns whether or not the search
was successful.
For more information about pred see find.
Examples
const arr = [0, 1, 2, 3];
assert(canFind(arr, 2));
assert(!canFind(arr, 4));
// find one of several needles
assert(arr.canFind(3, 2));
assert(arr.canFind(3, 2) == 2); // second needle found
assert(arr.canFind([1, 3], 2) == 2);
assert(canFind(arr, [1, 2], [2, 3]));
assert(canFind(arr, [1, 2], [2, 3]) == 1);
assert(canFind(arr, [1, 7], [2, 3]));
assert(canFind(arr, [1, 7], [2, 3]) == 2);
assert(!canFind(arr, [1, 3], [2, 4]));
assert(canFind(arr, [1, 3], [2, 4]) == 0);
Example using a custom predicate.
Note that the needle appears as the second argument of the predicate.
auto words = [
"apple",
"beeswax",
"cardboard"
];
assert(!canFind(words, "bees"));
assert( canFind!((string elem, string needle) => elem.startsWith(needle))(words, "bees"));
Search for multiple items in an array of items (search for needles in an array of haystacks)
string s1 = "aaa111aaa";
string s2 = "aaa222aaa";
string s3 = "aaa333aaa";
string s4 = "aaa444aaa";
const hay = [s1, s2, s3, s4];
assert(hay.canFind!(e => e.canFind("111", "222")));
canFind((local variable) const(string) symsym))
{
(local variable) string providerprovider = (local variable) const(string) candidatecandidate;
break;
}
}
(local variable) autological_relocation_join.Resolution[] out_out_ ~= (struct) autological_relocation_join.ResolutionOne resolved relocation: the join's output row.
Resolution((local variable) const(string) referrerreferrer, (local variable) const(string) symsym, (local variable) string providerprovider, (local variable) ulong probesprobes);
}
}
return (local variable) autological_relocation_join.Resolution[] out_out_;
}
/// Prints one resolution table.
void void autological_relocation_join.printResolutions(string title, in autological_relocation_join.Resolution[] rows) @safePrints one resolution table.
printResolutions((alias) object.string = stringstring (parameter) string titletitle, in (struct) autological_relocation_join.ResolutionOne resolved relocation: the join's output row.
Resolution[] (parameter) const(autological_relocation_join.Resolution[]) rowsrows) @safe
{
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln((parameter) string titletitle);
void std.stdio.writefln!(char, string, string, string, string)(in char[] fmt, string __param_1, string __param_2, string __param_3, string __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-12s %-16s %-16s %s", "referrer", "symbol", "resolved to", "probes");
void std.stdio.writefln!(char, string, string, string, string)(in char[] fmt, string __param_1, string __param_2, string __param_3, string __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-12s %-16s %-16s %s", "------------", "----------------", "----------------", "------");
foreach ((parameter) const(autological_relocation_join.Resolution) rr; (parameter) const(autological_relocation_join.Resolution[]) rowsrows)
void std.stdio.writefln!(char, string, string, string, const(ulong))(in char[] fmt, string __param_1, string __param_2, string __param_3, const(ulong) __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-12s %-16s %-16s %s", (local variable) const(autological_relocation_join.Resolution) rr.(field) string autological_relocation_join.Resolution.referrerreferrer, (local variable) const(autological_relocation_join.Resolution) rr.(field) string autological_relocation_join.Resolution.symbolsymbol,
(local variable) const(autological_relocation_join.Resolution) rr.(field) string autological_relocation_join.Resolution.providerprovider.(field) ulong const(string).lengthlength ? (local variable) const(autological_relocation_join.Resolution) rr.(field) string autological_relocation_join.Resolution.providerprovider : "** UNRESOLVED **", (local variable) const(autological_relocation_join.Resolution) rr.(field) ulong autological_relocation_join.Resolution.probesprobes);
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" total probes: %s across %s relocations", (parameter) const(autological_relocation_join.Resolution[]) rowsrows.autological_relocation_join.printResolutions.MapResult!(__lambda_L156_C68, const(Resolution)[]) autological_relocation_join.printResolutions.map!(const(autological_relocation_join.Resolution)[])(const(autological_relocation_join.Resolution)[] r) pure nothrow @nogc @safeImplements the homonym function (also known as transform) present
in many languages of functional flavor. The call ``map!(fun)(range)
returns a range of which elements are obtained by applying fun(a)
left to right for all elements a in range. The original ranges are
not changed. Evaluation is done lazily.
Examples
import std.algorithm.comparison : equal;
import std.range : chain, only;
auto squares =
chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a);
assert(equal(squares, only(1, 4, 9, 16, 25, 36)));
Multiple functions can be passed to map. In that case, the
element type of map is a tuple containing one element for each
function.
auto sums = [2, 4, 6, 8];
auto products = [1, 4, 9, 16];
size_t i = 0;
foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a"))
{
assert(result[0] == sums[i]);
assert(result[1] == products[i]);
++i;
}
You may alias map with some function(s) to a symbol and use
it separately:
import std.algorithm.comparison : equal;
import std.conv : to;
alias stringize = map!(to!string);
assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
map!(r => r.probes).ulong std.algorithm.iteration.sum!(autological_relocation_join.printResolutions.MapResult!(__lambda_L156_C68, const(Resolution)[]))(autological_relocation_join.printResolutions.MapResult!(__lambda_L156_C68, const(Resolution)[]) r) pure nothrow @nogc @safeSums elements of r, which must be a finite
input range. Although
conceptually sum`(`r`)` is equivalent to `fold`!((a, b) => a +
b)(`r`, 0), sum`` uses specialized algorithms to maximize accuracy,
as follows.
If ElementType!R is a floating-point
type and R is a
random-access range with
length and slicing, then sum uses the
pairwise summation
algorithm.
If ElementType!R is a floating-point type and R is a
finite input range (but not a random-access range with slicing), then
sum uses the Kahan summation algorithm.
In all other cases, a simple element by element addition is done.
For floating point inputs, calculations are made in
spec/type, Types, real
precision for real inputs and in double precision otherwise
(Note this is a special case that deviates from fold's behavior,
which would have kept float precision for a float range).
For all other types, the calculations are done in the same type obtained
from from adding two elements of the range, which may be a different
type from the elements themselves (for example, in case of
integral promotion).
A seed may be passed to sum. Not only will this seed be used as an initial
value, but its type will override all the above, and determine the algorithm
and precision used for summation. If a seed is not passed, one is created with
the value of typeof(r.front + r.front)(0), or typeof(r.front + r.front).zero
if no constructor exists that takes an int.
Note that these specialized summing algorithms execute more primitive operations
than vanilla summation. Therefore, if in certain cases maximum speed is required
at expense of precision, one can use fold!((a, b) => a + b)(r, 0), which
is not specialized for summation.
sum, (parameter) const(autological_relocation_join.Resolution[]) rowsrows.(field) ulong const(autological_relocation_join.Resolution[]).lengthlength);
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
}
int int D main()main()
{
// A small but realistic graph: an application over a TLS library and a
// logging library that both want `malloc`, plus a diamond on libc.
const (struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object[] (local variable) const(autological_relocation_join.Object[]) catalogcatalog = [
(struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object("app", ["libssl.so", "liblog.so"], ["main"], ["SSL_connect", "log_write", "malloc"]),
(struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object("libssl.so", ["libcrypto.so", "libc.so.6"], ["SSL_connect"], ["EVP_encrypt", "malloc"]),
(struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object("liblog.so", ["libc.so.6"], ["log_write"], ["malloc", "fprintf"]),
(struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object("libcrypto.so", ["libc.so.6"], ["EVP_encrypt"], ["malloc"]),
(struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object("libc.so.6", [], ["malloc", "free", "fprintf"], []),
// Present in the store but not reachable: it defines `malloc` too.
(struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object("libjemalloc.so", ["libc.so.6"], ["malloc", "free"], []),
];
(struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object[(alias) object.string = stringstring] (local variable) autological_relocation_join.Object[string] worldworld;
foreach ((parameter) const(autological_relocation_join.Object) oo; (local variable) const(autological_relocation_join.Object[]) catalogcatalog)
autological_relocation_join.Object* core.internal.newaa._d_aaGetY!(string, autological_relocation_join.Object, autological_relocation_join.Object[string], string, autological_relocation_join.Object, const(string))(ref scope autological_relocation_join.Object[string] aa, ref const(string) key, out bool found) pure nothrow @safeLookup key in aa.
Called only from implementation of (aakey) expressions when value is mutable.
world[autological_relocation_join.Object* core.internal.newaa._d_aaGetY!(string, autological_relocation_join.Object, autological_relocation_join.Object[string], string, autological_relocation_join.Object, const(string))(ref scope autological_relocation_join.Object[string] aa, ref const(string) key, out bool found) pure nothrow @safeLookup key in aa.
Called only from implementation of (aakey) expressions when value is mutable.
o.autological_relocation_join.Object* core.internal.newaa._d_aaGetY!(string, autological_relocation_join.Object, autological_relocation_join.Object[string], string, autological_relocation_join.Object, const(string))(ref scope autological_relocation_join.Object[string] aa, ref const(string) key, out bool found) pure nothrow @safeLookup key in aa.
Called only from implementation of (aakey) expressions when value is mutable.
soname] = (struct) autological_relocation_join.ObjectOne shared object in the model: the objects table, with its columns.
Object((local variable) const(autological_relocation_join.Object) oo.(field) string autological_relocation_join.Object.sonamesoname, (local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.needsneeds.string[] object.dup!string(const(string)[] a) pure nothrow @property @safedup, (local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.definesdefines.string[] object.dup!string(const(string)[] a) pure nothrow @property @safedup, (local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.undefinedundefined.string[] object.dup!string(const(string)[] a) pure nothrow @property @safedup);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("The `objects` table (soname, |needs|, |defines|, |undefined|):");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
foreach ((parameter) const(autological_relocation_join.Object) oo; (local variable) const(autological_relocation_join.Object[]) catalogcatalog)
void std.stdio.writefln!(char, string, ulong, ulong, ulong, string)(in char[] fmt, string __param_1, ulong __param_2, ulong __param_3, ulong __param_4, string __param_5) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-16s needs=%-2s defines=%-2s undefined=%-2s needs: %s",
(local variable) const(autological_relocation_join.Object) oo.(field) string autological_relocation_join.Object.sonamesoname, (local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.needsneeds.(field) ulong const(string[]).lengthlength, (local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.definesdefines.(field) ulong const(string[]).lengthlength, (local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.undefinedundefined.(field) ulong const(string[]).lengthlength,
(local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.needsneeds.(field) ulong const(string[]).lengthlength ? (local variable) const(autological_relocation_join.Object) oo.(field) string[] autological_relocation_join.Object.needsneeds.string std.array.join!(const(string)[], string)(const(string)[] ror, string sep) pure nothrow @safeEagerly concatenates all of the ranges in ror together (with the GC)
into one array using sep as the separator if present.
join(", ") : "-");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
const (local variable) const(string[]) plainplain = string[] autological_relocation_join.scopeOrder(in autological_relocation_join.Object[string] world, string root, string[] preload = null) @safeThe scope order ld.so builds: breadth-first over DT_NEEDED, first sight wins.
Depth-first would produce a different order and therefore different
interposition winners; the breadth-first rule is the one glibc implements, and
stating it is half the point of this program — the answer to the query depends
on a traversal order that lives in the loader, not in the data.
scopeOrder((local variable) autological_relocation_join.Object[string] worldworld, "app");
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("Scope order (breadth-first over DT_NEEDED from `app`): %s", (local variable) const(string[]) plainplain.string std.array.join!(const(string)[], string)(const(string)[] ror, string sep) pure nothrow @safeEagerly concatenates all of the ranges in ror together (with the GC)
into one array using sep as the separator if present.
join(" -> "));
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" `libjemalloc.so` is in the store but not in scope — unreachable objects");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" do not participate in the join, which is exactly a WHERE clause.");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
const (local variable) const(autological_relocation_join.Resolution[]) beforebefore = autological_relocation_join.Resolution[] autological_relocation_join.resolve(in autological_relocation_join.Object[string] world, in string[] order) @safeThe join itself: for every undefined symbol, the first definition in scope order.
probes counts how many objects were examined. In glibc this is a .gnu.hash
bloom-filter test per object followed by a bucket walk on a hit; the count below
is the number of objects the loader must at minimum touch, which is the quantity
that scales with object count rather than with image size.
resolve((local variable) autological_relocation_join.Object[string] worldworld, (local variable) const(string[]) plainplain);
void autological_relocation_join.printResolutions(string title, in autological_relocation_join.Resolution[] rows) @safePrints one resolution table.
printResolutions("Resolutions, no preload:", (local variable) const(autological_relocation_join.Resolution[]) beforebefore);
// One inserted row, at one position.
const (local variable) const(string[]) preloadedpreloaded = string[] autological_relocation_join.scopeOrder(in autological_relocation_join.Object[string] world, string root, string[] preload = null) @safeThe scope order ld.so builds: breadth-first over DT_NEEDED, first sight wins.
Depth-first would produce a different order and therefore different
interposition winners; the breadth-first rule is the one glibc implements, and
stating it is half the point of this program — the answer to the query depends
on a traversal order that lives in the loader, not in the data.
scopeOrder((local variable) autological_relocation_join.Object[string] worldworld, "app", ["libjemalloc.so"]);
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("Scope order with LD_PRELOAD=libjemalloc.so: %s", (local variable) const(string[]) preloadedpreloaded.string std.array.join!(const(string)[], string)(const(string)[] ror, string sep) pure nothrow @safeEagerly concatenates all of the ranges in ror together (with the GC)
into one array using sep as the separator if present.
join(" -> "));
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
const (local variable) const(autological_relocation_join.Resolution[]) afterafter = autological_relocation_join.Resolution[] autological_relocation_join.resolve(in autological_relocation_join.Object[string] world, in string[] order) @safeThe join itself: for every undefined symbol, the first definition in scope order.
probes counts how many objects were examined. In glibc this is a .gnu.hash
bloom-filter test per object followed by a bucket walk on a hit; the count below
is the number of objects the loader must at minimum touch, which is the quantity
that scales with object count rather than with image size.
resolve((local variable) autological_relocation_join.Object[string] worldworld, (local variable) const(string[]) preloadedpreloaded);
void autological_relocation_join.printResolutions(string title, in autological_relocation_join.Resolution[] rows) @safePrints one resolution table.
printResolutions("Resolutions, libjemalloc.so preloaded:", (local variable) const(autological_relocation_join.Resolution[]) afterafter);
// The diff is the argument: one tuple changed the answer to N queries.
(alias) object.size_t = ulongsize_t (local variable) ulong changedchanged;
foreach ((parameter) ulong ii, (parameter) const(autological_relocation_join.Resolution) rr; (local variable) const(autological_relocation_join.Resolution[]) beforebefore)
if ((local variable) ulong ii < (local variable) const(autological_relocation_join.Resolution[]) afterafter.(field) ulong const(autological_relocation_join.Resolution[]).lengthlength && (local variable) const(autological_relocation_join.Resolution[]) afterafter[(local variable) ulong ii].(field) string autological_relocation_join.Resolution.providerprovider != (local variable) const(autological_relocation_join.Resolution) rr.(field) string autological_relocation_join.Resolution.providerprovider)
(local variable) ulong changedchanged++;
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("LD_PRELOAD inserted ONE row into the scope relation and changed %s of %s",
(local variable) ulong changedchanged, (local variable) const(autological_relocation_join.Resolution[]) beforebefore.(field) ulong const(autological_relocation_join.Resolution[]).lengthlength);
void std.stdio.writefln!char(in char[] fmt) @safeEquivalent to writef(fmt, args, '\n').
writefln("resolutions. Nothing about any object's bytes changed. This is why SELF");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("can model `LD_PRELOAD` as a row rather than as an environment variable.");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
void std.stdio.writefln!(char, ulong, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2, ulong __param_3) @safeEquivalent to writef(fmt, args, '\n').
writefln("Cost: %s probes for %s relocations over %s objects in scope.",
(local variable) const(autological_relocation_join.Resolution[]) beforebefore.autological_relocation_join.main.MapResult!(__lambda_L214_C21, const(Resolution)[]) autological_relocation_join.main.map!(const(autological_relocation_join.Resolution)[])(const(autological_relocation_join.Resolution)[] r) pure nothrow @nogc @safeImplements the homonym function (also known as transform) present
in many languages of functional flavor. The call ``map!(fun)(range)
returns a range of which elements are obtained by applying fun(a)
left to right for all elements a in range. The original ranges are
not changed. Evaluation is done lazily.
Examples
import std.algorithm.comparison : equal;
import std.range : chain, only;
auto squares =
chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a);
assert(equal(squares, only(1, 4, 9, 16, 25, 36)));
Multiple functions can be passed to map. In that case, the
element type of map is a tuple containing one element for each
function.
auto sums = [2, 4, 6, 8];
auto products = [1, 4, 9, 16];
size_t i = 0;
foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a"))
{
assert(result[0] == sums[i]);
assert(result[1] == products[i]);
++i;
}
You may alias map with some function(s) to a symbol and use
it separately:
import std.algorithm.comparison : equal;
import std.conv : to;
alias stringize = map!(to!string);
assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
map!(r => r.probes).ulong std.algorithm.iteration.sum!(autological_relocation_join.main.MapResult!(__lambda_L214_C21, const(Resolution)[]))(autological_relocation_join.main.MapResult!(__lambda_L214_C21, const(Resolution)[]) r) pure nothrow @nogc @safeSums elements of r, which must be a finite
input range. Although
conceptually sum`(`r`)` is equivalent to `fold`!((a, b) => a +
b)(`r`, 0), sum`` uses specialized algorithms to maximize accuracy,
as follows.
If ElementType!R is a floating-point
type and R is a
random-access range with
length and slicing, then sum uses the
pairwise summation
algorithm.
If ElementType!R is a floating-point type and R is a
finite input range (but not a random-access range with slicing), then
sum uses the Kahan summation algorithm.
In all other cases, a simple element by element addition is done.
For floating point inputs, calculations are made in
spec/type, Types, real
precision for real inputs and in double precision otherwise
(Note this is a special case that deviates from fold's behavior,
which would have kept float precision for a float range).
For all other types, the calculations are done in the same type obtained
from from adding two elements of the range, which may be a different
type from the elements themselves (for example, in case of
integral promotion).
A seed may be passed to sum. Not only will this seed be used as an initial
value, but its type will override all the above, and determine the algorithm
and precision used for summation. If a seed is not passed, one is created with
the value of typeof(r.front + r.front)(0), or typeof(r.front + r.front).zero
if no constructor exists that takes an int.
Note that these specialized summing algorithms execute more primitive operations
than vanilla summation. Therefore, if in certain cases maximum speed is required
at expense of precision, one can use fold!((a, b) => a + b)(r, 0), which
is not specialized for summation.
sum, (local variable) const(autological_relocation_join.Resolution[]) beforebefore.(field) ulong const(autological_relocation_join.Resolution[]).lengthlength, (local variable) const(string[]) plainplain.(field) ulong const(string[]).lengthlength);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" Every process start recomputes this. The probe count grows with the");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" number of objects, not with how large they are — which is the confounder");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" that makes a naive ELF-vs-SELF startup comparison meaningless unless the");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" object count is held fixed.");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("The same query, two ways:");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" -- SQL: the transitive part needs a recursive CTE, and the");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" -- first-wins rule needs a window function over a traversal order");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" -- the query itself has to reconstruct.");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" WITH RECURSIVE scope(obj, depth) AS (");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" SELECT 'app', 0");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" UNION");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" SELECT n.needed, s.depth + 1 FROM needs n JOIN scope s ON n.obj = s.obj");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" )");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" SELECT u.obj, u.sym, MIN(s.depth), d.obj");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" FROM undefined u JOIN scope s JOIN defines d");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" ON d.obj = s.obj AND d.sym = u.sym");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" GROUP BY u.obj, u.sym;");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" % Datalog: the transitive closure is one rule, and it terminates by");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" % construction under semi-naive evaluation.");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" scope(\"app\").");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" scope(N) :- scope(O), needs(O, N).");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" resolves(U, S, D) :- undefined(U, S), scope(D), defines(D, S).");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" Both compute the reachable set. Only the Datalog version says nothing");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" about *how*, which is why every code-as-a-database system that must");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" express reachability picked it. See ../code-as-database.md.");
return 0;
}