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
// Copyright (c) 2026 Mike Grier
// Moved from windows-ioring-sys/src/numa_buffer.rs at 6101ca65, which had in
// turn moved it from examples/ring_copy/buffer.rs at 834c7afa.
//! A `VirtualAllocExNuma`-backed buffer, so a buffer can be placed on a chosen
//! NUMA node rather than wherever the default allocator's own heuristics land
//! it.
//!
//! # Why this is its own crate
//!
//! It was in `windows-ioring-sys`, which recommends placing a registered pool
//! near the device and so had a reason to provide the allocator rather than
//! leave every caller to write it. But the buffer has nothing to do with a
//! ring: its only connection was one doc line saying it *can* be registered
//! into one. Meanwhile `windows-placement-probe` -- which does not depend on
//! the ring crate -- had written the same `VirtualAllocExNuma` call for itself,
//! making two independent allocators in a workspace that had already hoisted
//! this code once to stop exactly that.
//!
//! # What this does not decide
//!
//! **Which node.** A caller who knows their storage or thread layout passes
//! `Some(node)`; one who does not passes `None` and gets the system's own
//! choice, which is what the default allocator would have given anyway.
//! [`volume_numa_node`](crate::volume_numa_node) is available for finding a
//! candidate, and says plainly what its answer is and is not worth.
//!
//! A node argument is a *preference*, which the underlying parameter says in
//! its own name (`nndPreferred`). So a successful allocation is not by itself
//! evidence that the pages landed on the node that was asked for, and code
//! that needs to know must measure rather than assume -- see the crate root.
use io;
use ptr;
use ;
use GetCurrentProcess;
use crateNumaNode;
/// `VirtualAllocExNuma`'s documented sentinel for "no NUMA preference" --
/// windows-sys does not name this constant, so it is named here rather than
/// written as a bare literal at the call site.
const NUMA_NO_PREFERRED_NODE: u32 = u32MAX;
/// An owned buffer allocated with `VirtualAllocExNuma`, freed with
/// `VirtualFree` on drop.
///
/// The allocation is page-granular: `VirtualAllocExNuma` rounds a request up
/// to the system page size, so a caller asking for a small buffer gets at
/// least a page. [`NumaBuffer::len`] reports the length that was *requested*,
/// which is the length a kernel call should be told about.
///
/// # Buffer traits
///
/// This type implements `io-buffers`' `IoBuf` and `IoBufMut`, so a ring takes it
/// as a buffer. This crate defines no trait of its own: the traits belong to
/// `io-buffers`, which is portable and so cannot depend on this allocator, and
/// the orphan rule lets only the crate that owns the trait or the type
/// implement it. A crate that owns a buffer trait of its own implements it for
/// this type the same way, over [`NumaBuffer::as_ptr`],
/// [`NumaBuffer::as_mut_ptr`] and [`NumaBuffer::len`].
// SAFETY: the allocation is exclusively owned by this value; sending it
// across threads only moves that ownership, never aliases it.
unsafe