25 static_assert(std::is_trivially_copyable_v<Value>,
"disk id values are fixed records");
27 std::uint64_t left = 0, right = 0, parent = 0, next = 0;
28 std::uint64_t key_size = 0, height = 1, reserved = 0;
30 static_assert(
sizeof(Links) == 56,
"scratch id node layout");
40 file_ = std::move(other.file_);
41 root_ = std::exchange(other.root_, 0);
42 first_ = std::exchange(other.first_, 0);
43 last_ = std::exchange(other.last_, 0);
44 end_ = std::exchange(other.end_, 1);
45 size_ = std::exchange(other.size_, 0);
50 bool arena_backed() const noexcept {
return file_.arena_backed(); }
54 void set_directory(
const std::string& directory) { file_.set_directory(directory); }
58 "scratch id backend changed after use");
61 "scratch id table requires a tree region");
66 root_ = first_ = last_ = size_ = 0;
69 std::uint64_t
size() const noexcept {
return size_; }
70 std::uint64_t
file_bytes() const noexcept {
return size_ == 0 ? 0 : end_; }
73 std::string
key(
Offset at)
const {
return read_key(at, links(at)); }
77 if (row.right != 0)
return leftmost(row.right);
82 if (row.left == child)
break;
89 std::optional<Offset>
find(
const std::string&
key)
const {
92 const auto row = links(at);
93 const int order =
key.compare(read_key(at, row));
94 if (order == 0)
return at;
95 at = order < 0 ? row.left : row.right;
103 if (!file_.scratch_owner() ||
find(
key))
return;
104 require_new_key(
key.size());
110 if (!file_.scratch_owner())
return;
111 std::uint64_t ordinal = 0, extra_rows = 0, extra_bytes = 0;
112 enumerate([&](
const std::string&
key) {
113 require_id_size(
key.size());
114 const auto current = ordinal++;
116 bool repeated =
false;
117 std::uint64_t prior = 0;
118 enumerate([&](
const std::string& other) {
119 if (prior++ < current && other ==
key) repeated =
true;
121 if (repeated)
return;
122 const auto maximum = std::numeric_limits<std::uint64_t>::max();
123 if (extra_rows == maximum ||
key.size() > maximum - extra_bytes) {
124 const auto resource = file_.scratch_resource();
125 const auto& manifest = file_.scratch_owner()->manifest();
133 extra_bytes +=
key.size();
134 require_growth(extra_rows, extra_bytes);
140 file_.read(at +
sizeof(Links), &
value,
sizeof value);
145 file_.write(at +
sizeof(Links), &
value,
sizeof value);
152 const auto row = links(at);
153 const int order =
key.compare(read_key(at, row));
154 if (order == 0)
return {at,
false};
157 at = left ? row.left : row.right;
159 require_new_key(
key.size());
160 constexpr std::size_t header =
sizeof(Links) +
sizeof(Value);
164 if (size_ == std::numeric_limits<std::uint64_t>::max())
166 file_.open(
"pineforge-ids");
170 row.key_size =
key.size();
171 file_.write(at, &row,
sizeof row);
172 file_.write(at +
sizeof(Links), &
value,
sizeof value);
173 file_.write(at + header,
key.data(),
key.size());
177 end_ += header +
key.size();
179 auto old = links(parent);
180 (left ? old.left : old.right) = at;
181 put_links(parent, old);
184 auto previous = links(last_);
186 put_links(last_, previous);
198 for (
Offset at = first_; at != 0;) {
199 const auto row = links(at);
200 visit(read_key(at, row),
get(at));
206 void require_id_size(std::size_t key_size)
const {
211 void require_new_key(std::size_t key_size)
const {
212 require_id_size(key_size);
213 require_growth(1, key_size);
215 void require_growth(std::uint64_t extra_rows, std::uint64_t extra_bytes)
const {
219 const auto& manifest = slab->manifest();
222 constexpr std::uint64_t header =
sizeof(Links) +
sizeof(Value);
225 const auto key_bytes = end_ - 1 - header * size_;
226 if (size_ > rows || extra_rows > rows - size_)
228 if (key_bytes > bytes || extra_bytes > bytes - key_bytes)
230 const auto capacity = slab->region(
resource).capacity;
231 if (end_ > capacity || extra_rows > (capacity - end_) / header)
233 const auto fixed_bytes = header * extra_rows;
234 if (extra_bytes > capacity - end_ - fixed_bytes)
236 slab->require_extend(
resource, end_ + fixed_bytes + extra_bytes);
238 [[noreturn]]
void io_error(
int error,
const char* operation)
const {
239 throw AuxiliaryScratchIoError(error, operation);
243 const auto row = links(at);
244 if (row.left == 0)
break;
249 void validate(
Offset at)
const {
250 if (at == 0 || at >= end_ || end_ - at <
sizeof(Links) +
sizeof(Value))
251 io_error(EIO,
"scratch id invalid offset");
253 Links links(
Offset at)
const {
256 file_.read(at, &row,
sizeof row);
257 if (row.height == 0 || row.height > 128
258 || row.key_size > end_ - at -
sizeof(Links) -
sizeof(Value))
259 io_error(EIO,
"scratch id invalid node");
262 void put_links(
Offset at,
const Links& row) { file_.write(at, &row,
sizeof row); }
263 std::string read_key(
Offset at,
const Links& row)
const {
264 if (row.key_size > std::string{}.max_size())
265 io_error(EFBIG,
"scratch id key too long");
266 std::string
key(
static_cast<std::size_t
>(row.key_size),
'\0');
267 file_.read(at +
sizeof(Links) +
sizeof(Value),
key.data(),
key.size());
270 std::uint64_t height(
Offset at)
const {
return at == 0 ? 0 : links(at).height; }
271 void refresh(Links& row)
const { row.height = 1 + std::max(height(row.left), height(row.right)); }
272 int balance(
const Links& row)
const {
273 return static_cast<int>(height(row.left)) -
static_cast<int>(height(row.right));
276 auto old = links(at);
277 const Offset top_at = left ? old.right : old.left;
278 auto top = links(top_at);
279 const Offset middle_at = left ? top.left : top.right;
280 const Offset parent_at = old.parent;
281 if (parent_at != 0) {
282 auto parent = links(parent_at);
283 (parent.left == at ? parent.left : parent.right) = top_at;
284 put_links(parent_at, parent);
285 }
else root_ = top_at;
286 top.parent = parent_at;
288 (left ? old.right : old.left) = middle_at;
289 if (middle_at != 0) {
290 auto middle = links(middle_at);
292 put_links(middle_at, middle);
296 (left ? top.left : top.right) = at;
298 put_links(top_at, top);
301 void rebalance(
Offset at) {
303 auto row = links(at);
306 const int lean = balance(row);
308 if (balance(links(row.left)) < 0) (void)rotate(row.left,
true);
309 at = rotate(at,
false);
310 }
else if (lean < -1) {
311 if (balance(links(row.right)) > 0) (void)rotate(row.right,
false);
312 at = rotate(at,
true);
314 at = links(at).parent;
319 Offset root_ = 0, first_ = 0, last_ = 0, end_ = 1;
320 std::uint64_t size_ = 0;