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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
//! Block-level borrow API.
//!
//! The byte API (`fs_read` / `fs_write`) copies: a read copies out
//! of the cached block into the caller's buffer, a write copies the
//! caller's buffer into the cached block. Callers that are really
//! block-storage consumers — they only ever read block N, modify
//! block N, and write block N back — pay for a buffer they do not
//! want, and typically end up keeping a shadow copy of every block
//! they intend to modify so that they can write it out later.
//!
//! This module lets such a caller *borrow* the cached block
//! instead. [`BlockRef`] hands out `&[u8]` for reading;
//! [`BlockMut`] hands out `&mut [u8]` for modification in the very
//! buffer the next flush will write, so no shadow copy and no
//! write-back step is needed.
//!
//! # Holes
//!
//! The byte API cannot distinguish a hole from a block of zeros:
//! reading either returns zeros. Block borrows can, because
//! acquiring a block consults the bmap directly. See
//! [`BlockState`], and note that [`HyperFile::block`] returns
//! `Ok(None)` for anything not backed by real data.
//!
//! [`HyperFile::block`]: super::file::HyperFile::block
//!
//! # Guards and flush
//!
//! A guard borrows the file, so the borrow checker alone prevents
//! any other operation — including flush and eviction — while a
//! guard is alive. No runtime pinning is involved. The flip side is
//! that a guard cannot be held across a flush or across an `await`
//! on the same file; acquire it, use it, drop it.
//!
//! # `i_size` is not touched
//!
//! Neither guard changes `i_size`. Callers of this API manage their
//! own address-space layout and do not want a block write to move
//! EOF. A consequence worth understanding: a block dirtied above
//! `i_size` *is* flushed durably (the dirty set drives segment
//! build), but `fs_read` will not return it, because `read` stops
//! at `i_size`. Such blocks are reachable only through this API.
//! `i_blocks` *is* updated, so `st_blocks` still accounts for the
//! storage.
use fmt;
use Result;
use crateBlockIndex;
use crateDataBlock;
/// How a block index is mapped, as recorded in the bmap.
///
/// Distinguishing these is the point of [`HyperFile::block_state`]:
/// through the byte API all three read back as zeros, so a caller
/// that keeps its own index cannot tell "this block was never
/// written" from "this block holds real data that happens to be
/// zeros" — which is the difference between a consistent index and
/// a lost block.
///
/// [`HyperFile::block_state`]: super::file::HyperFile::block_state
/// A read-only borrow of one data block.
///
/// Obtained from [`HyperFile::block`]. The slice from
/// [`Self::as_slice`] is the cache's own buffer, not a copy, and is
/// always exactly one `data_block_size` long.
///
/// When the data cache is disabled — which `O_DIRECT` without `wal`
/// forces — there is no cached buffer to borrow, so the guard owns
/// the block it loaded instead. That is invisible through this API
/// except that the bytes are not retained for a subsequent call.
///
/// [`HyperFile::block`]: super::file::HyperFile::block
/// Deliberately does not print the block contents: these are
/// `data_block_size` bytes, and a `{:?}` in a log line should not
/// dump a whole block.
/// A mutable borrow of one cached data block.
///
/// Obtained from [`HyperFile::block_mut`]. Writes through
/// [`Self::as_mut_slice`] land in the buffer the next flush will
/// write out; no write-back call is needed. The block is marked
/// dirty at acquisition, not at drop, so an early return or a panic
/// between acquire and drop cannot lose the modification.
///
/// Because the block is dirty from acquisition, acquiring a
/// `BlockMut` and *not* modifying it still produces a new version
/// at the next flush — the same as writing identical bytes through
/// `fs_write`. Use [`HyperFile::block`] when a read-only borrow
/// will do.
///
/// [`HyperFile::block_mut`]: super::file::HyperFile::block_mut
/// [`HyperFile::block`]: super::file::HyperFile::block
/// Deliberately does not print the block contents; see
/// [`BlockRef`]'s implementation.
/// Result of asking for a block that may not be backed by data.
///
/// Kept as a type alias rather than an enum so that the common
/// case reads as an `Option`: `None` means "no data here", which
/// for a caller checking its own index against hyperfile's is the
/// interesting signal. Call
/// [`HyperFile::block_state`](super::file::HyperFile::block_state)
/// when the `Unmapped` / `Zero` distinction matters.
pub type MaybeBlockRef<'a> = ;
/// Mutable counterpart of [`MaybeBlockRef`].
pub type MaybeBlockMut<'a> = ;