1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! What a consumer's sink is handed, and what it says it needs.
//!
//! [`GameRef`] is entirely borrowed. Every field is a slice of bytes the walk
//! already holds — the record from the memory map, the names out of the
//! mapped namebases, the `moves2` and the keys in the walker's reused buffers —
//! so a sink that only counts games allocates nothing, and a sink that writes a
//! row copies into its own target and never back.
//!
//! Two of the design's fields are additions to the shape this file was given:
//! [`GameRef::site`] and [`GameRef::annotations`]. The first is a resolved name
//! the consumer's `Games` row needs (`SiteID`) and costs one reused buffer; the
//! second is what [`GameSink::wants_annotations`] promises to deliver, which the
//! design's field list had no slot for. Everything else is as specified.
use ;
use Error;
/// One game, as a conversion or a read-only view hands it over.
///
/// Every field borrows: nothing here owns memory, and a sink that keeps a
/// `GameRef` past the call has to copy what it wants. The `moves` slice is the
/// **main line** only — the format's variations are walked, and their keys
/// computed, but the line the target stores is the one handed over (see
/// `docs/bridge.md`).
///
/// `keys` is empty unless the sink asked for keys with
/// [`GameSink::wants_keys`], and then holds one Polyglot key per position of
/// the main line, **the start position first**: `keys.len() == moves.len() + 1`,
/// and the sequence equals what `gigachess::database::replay_moves2_hashes`
/// returns for the same `moves` and `start_fen`, which is the contract the
/// consumer's own sidecar is built on.
/// The consumer's side of the conversion: one call per game, in game-number
/// order, from one thread at a time.
///
/// A sink is asked once per run what it needs ([`GameSink::wants_keys`],
/// [`GameSink::wants_annotations`]) rather than once per ply, so the walk can
/// choose its make — `Board::play_fast` with neither, the hash-maintaining
/// `play_hashed` with keys — once for the whole conversion rather than
/// per move. That decision is the same contract ADR-003 made one level down,
/// moved up to where it belongs.