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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! 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.