105 if (_schema.
key.empty())
107 throw std::invalid_argument(
"layered_table: the schema needs a key column");
126 const std::function<
void(
const tsv_row&)>& validate = {})
131 w.missing_required = check_columns(doc, info);
132 if (mode != layer_mode::replace)
137 w.removed.assign(w.rows.size(),
false);
138 w.touched.assign(w.rows.size(), untouched);
139 w.rows.reserve(w.rows.size() + doc.
size());
140 w.index.reserve(w.rows.size() + doc.
size());
142 bool any_removed =
false;
143 for (
const auto& row : doc.
rows())
145 any_removed |= apply_row(row, mode, has_disabled, validate, w, info);
150 std::vector<tsv_row> kept;
151 key_index kept_index;
154 kept.reserve(w.rows.size());
155 kept_index.reserve(w.rows.size());
156 for (std::size_t i = 0; i < w.rows.size(); ++i)
158 if (w.removed[i])
continue;
159 kept_index.insert(key_index::hash_of(key_of(w.rows[i])), kept.size());
160 kept.push_back(w.rows[i]);
165 kept = std::move(w.rows);
166 kept_index = std::move(w.index);
172 if (mode == layer_mode::replace)
177 _documents.push_back(std::move(doc));
178 _rows = std::move(kept);
179 _index = std::move(kept_index);
180 _layers.push_back(std::move(info));
181 return _layers.back();
195 [[nodiscard]]
const std::vector<tsv_row>&
rows() const noexcept {
return _rows; }
199 [[nodiscard]] std::size_t
size() const noexcept {
return _rows.size(); }
203 [[nodiscard]]
bool empty() const noexcept {
return _rows.empty(); }
210 [[nodiscard]] std::optional<tsv_row>
find(std::initializer_list<std::string_view> key)
const
212 if (key.size() != _schema.
key.size())
214 throw std::invalid_argument(
"layered_table::find: wrong number of key cells");
218 for (
const auto part : key)
220 if (i++ > 0) joined += key_separator;
223 const auto at = locate(_index, _rows, *
this, joined);
224 if (!at)
return std::nullopt;
231 [[nodiscard]]
const std::vector<layer_info>&
layers() const noexcept {
return _layers; }
243 for (
const auto& layer : _layers)
255 static constexpr char key_separator =
'\x1f';
264 [[nodiscard]]
static std::uint32_t hash_of(std::string_view key)
noexcept
266 return static_cast<std::uint32_t
>(std::hash<std::string_view>{}(key));
271 template <
typename Matches>
272 [[nodiscard]] std::optional<std::size_t> find(std::uint32_t hash, Matches&& matches)
const
274 if (_slots.empty())
return std::nullopt;
275 const std::size_t mask = _slots.size() - 1;
276 for (std::size_t i = hash & mask;; i = (i + 1) & mask)
278 const slot& s = _slots[i];
279 if (s.row == vacant)
return std::nullopt;
280 if (s.hash == hash && matches(
static_cast<std::size_t
>(s.row)))
return s.row;
285 void insert(std::uint32_t hash, std::size_t row)
287 if (row >= vacant)
throw std::length_error(
"layered_table: too many rows");
288 if ((_count + 1) * 2 > _slots.size()) grow(_slots.empty() ? 16 : _slots.
size() * 2);
289 place(hash,
static_cast<std::uint32_t
>(row));
293 void clear() noexcept
299 void reserve(std::size_t
rows)
301 std::size_t want = 16;
302 while (want <
rows * 2) want *= 2;
303 if (want > _slots.size()) grow(want);
307 static constexpr std::uint32_t vacant =
static_cast<std::uint32_t
>(-1);
310 std::uint32_t hash{0};
311 std::uint32_t row{vacant};
314 void place(std::uint32_t hash, std::uint32_t row)
316 const std::size_t mask = _slots.size() - 1;
317 std::size_t i = hash & mask;
318 while (_slots[i].row != vacant) i = (i + 1) & mask;
319 _slots[i] = {hash, row};
322 void grow(std::size_t slots)
324 std::vector<slot> old = std::move(_slots);
325 _slots.assign(slots, slot{});
326 for (
const slot& s : old)
328 if (s.row != vacant) place(s.hash, s.row);
332 std::vector<slot> _slots;
333 std::size_t _count{0};
338 [[nodiscard]]
bool key_matches(
const tsv_row& row, std::string_view joined)
const
340 for (std::size_t i = 0; i < _schema.
key.size(); ++i)
344 if (joined.empty() || joined.front() != key_separator)
return false;
345 joined.remove_prefix(1);
347 const auto cell = row[_schema.
key[i]];
348 if (!joined.starts_with(cell))
return false;
349 joined.remove_prefix(cell.size());
351 return joined.empty();
354 [[nodiscard]]
static std::optional<std::size_t> locate(
const key_index& index,
355 const std::vector<tsv_row>&
rows,
356 const layered_table& self,
357 std::string_view joined)
359 return index.find(key_index::hash_of(joined), [&](std::size_t r) {
360 return self.key_matches(
rows[r], joined);
364 static constexpr std::size_t untouched =
static_cast<std::size_t
>(-1);
369 std::vector<tsv_row> rows;
371 std::vector<bool> removed;
374 std::vector<std::size_t> touched;
375 std::unordered_map<std::string, std::size_t> disabled_unloaded;
377 std::string missing_required;
381 bool apply_row(
const tsv_row& row, layer_mode mode,
bool has_disabled,
382 const std::function<
void(
const tsv_row&)>& validate, working& w,
383 layer_info& info)
const
385 std::string key = key_of(row);
386 const auto existing = locate(w.index, w.rows, *
this, key);
387 const bool exists = existing.has_value();
390 std::size_t first_line = untouched;
393 first_line = w.touched[*existing];
395 else if (
const auto it = w.disabled_unloaded.find(key); it != w.disabled_unloaded.end())
397 first_line = it->second;
399 if (first_line != untouched)
401 throw row.error(
"",
"key repeated (first on line " + std::to_string(first_line) +
")");
404 if (has_disabled && is_disabled(row))
406 if (mode == layer_mode::add)
408 throw row.error(
disabled_column,
"a layer in add mode cannot disable rows");
412 w.removed[*existing] =
true;
413 w.touched[*existing] = row.line();
417 w.disabled_unloaded.emplace(std::move(key), row.line());
418 info.warnings.push_back(row.source() +
":" + std::to_string(row.line()) +
419 ": disabled key is not loaded");
424 if (!w.missing_required.empty())
426 throw row.error(w.missing_required,
"required column missing");
428 if (validate) validate(row);
431 if (mode == layer_mode::add)
433 throw row.error(
"",
"key already loaded (layer is in add mode)");
435 w.rows[*existing] = row;
436 w.touched[*existing] = row.line();
440 w.index.insert(key_index::hash_of(key), w.rows.size());
441 w.rows.push_back(row);
442 w.removed.push_back(
false);
443 w.touched.push_back(row.line());
450 std::string check_columns(
const tsv_document& doc, layer_info& info)
const
453 std::string missing_required;
454 for (
const auto& column : _schema.key)
456 known.insert(column);
457 if (!doc.has_column(column))
459 throw tsv_error{doc.source(), 0, column,
"key column missing"};
462 for (
const auto& column : _schema.required)
464 known.insert(column);
465 if (missing_required.empty() && !doc.has_column(column)) missing_required = column;
467 for (
const auto& column : _schema.optional) known.insert(column);
468 for (
const auto& column : doc.columns())
470 if (!known.contains(column))
472 info.warnings.push_back(doc.source() +
": column '" + column +
473 "' is not used and was ignored");
476 return missing_required;
479 [[nodiscard]] std::string key_of(
const tsv_row& row)
const
482 for (std::size_t i = 0; i < _schema.
key.size(); ++i)
484 const auto cell = row[_schema.
key[i]];
487 throw row.error(_schema.
key[i],
"key cell is empty");
489 if (i > 0) key += key_separator;
495 [[nodiscard]]
static bool is_disabled(
const tsv_row& row)
498 if (value.empty() || value ==
"0")
return false;
499 if (value ==
"1")
return true;
500 throw row.error(
disabled_column,
"expected 0, 1 or empty, got '" + std::string{value} +
"'");
503 table_schema _schema;
504 std::vector<tsv_document> _documents;
505 std::vector<tsv_row> _rows;
507 std::vector<layer_info> _layers;