Commit 62beedb5 authored by Kenton Varda's avatar Kenton Varda

Apply @harrishancock review comments.

parent 2d0d0d8c
...@@ -40,7 +40,7 @@ struct HashCoder { ...@@ -40,7 +40,7 @@ struct HashCoder {
// a function like `hashCode(T)` and then rely on argument-dependent lookup. However, this has // a function like `hashCode(T)` and then rely on argument-dependent lookup. However, this has
// the problem that it pollutes other people's namespaces and even the global namespace. For // the problem that it pollutes other people's namespaces and even the global namespace. For
// example, some other project may already have functions called `hashCode` which do something // example, some other project may already have functions called `hashCode` which do something
// different. Declaring `operator*` with `Stringifier` as the left operand cannot conflict with // different. Declaring `operator*` with `HashCoder` as the left operand cannot conflict with
// anything. // anything.
uint operator*(ArrayPtr<const byte> s) const; uint operator*(ArrayPtr<const byte> s) const;
...@@ -114,15 +114,16 @@ static KJ_CONSTEXPR(const) HashCoder HASHCODER = HashCoder(); ...@@ -114,15 +114,16 @@ static KJ_CONSTEXPR(const) HashCoder HASHCODER = HashCoder();
} // namespace _ (private) } // namespace _ (private)
#define KJ_HASHCODE(...) operator*(::kj::_::HashCoder, __VA_ARGS__) #define KJ_HASHCODE(...) operator*(::kj::_::HashCoder, __VA_ARGS__)
// Defines a stringifier for a custom type. Example: // Defines a hash function for a custom type. Example:
// //
// class Foo {...}; // class Foo {...};
// inline StringPtr KJ_STRINGIFY(const Foo& foo) { return foo.name(); } // inline uint KJ_HASHCODE(const Foo& foo) { return kj::hashCode(foo.x, foo.y); }
// //
// This allows Foo to be passed to str(). // This allows Foo to be passed to hashCode().
// //
// The function should be declared either in the same namespace as the target type or in the global // The function should be declared either in the same namespace as the target type or in the global
// namespace. It can return any type which is an iterable container of chars. // namespace. It can return any type which itself is hashable -- that value will be hashed in turn
// until a `uint` comes out.
inline uint hashCode(uint value) { return value; } inline uint hashCode(uint value) { return value; }
template <typename T> template <typename T>
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
namespace kj { namespace kj {
namespace _ { namespace _ {
namespace {
KJ_TEST("HashMap") { KJ_TEST("HashMap") {
HashMap<String, int> map; HashMap<String, int> map;
...@@ -147,5 +148,6 @@ KJ_TEST("TreeMap range") { ...@@ -147,5 +148,6 @@ KJ_TEST("TreeMap range") {
} }
} }
} // namespace kj } // namespace
} // namespace _ } // namespace _
} // namespace kj
...@@ -36,7 +36,7 @@ class HashMap { ...@@ -36,7 +36,7 @@ class HashMap {
// //
// `Key` must be hashable (via a `.hashCode()` method or `KJ_HASHCODE()`; see `hash.h`) and must // `Key` must be hashable (via a `.hashCode()` method or `KJ_HASHCODE()`; see `hash.h`) and must
// implement `operator==()`. Additionally, when performing lookups, you can use key types other // implement `operator==()`. Additionally, when performing lookups, you can use key types other
// that `Key` as long as the other type is also hashable (producing the same hash codes) and // than `Key` as long as the other type is also hashable (producing the same hash codes) and
// there is an `operator==` implementation with `Key` on the left and that other type on the // there is an `operator==` implementation with `Key` on the left and that other type on the
// right. For example, if the key type is `String`, you can pass `StringPtr` to `find()`. // right. For example, if the key type is `String`, you can pass `StringPtr` to `find()`.
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
namespace kj { namespace kj {
namespace _ { namespace _ {
namespace {
#if defined(KJ_DEBUG) && !__OPTIMIZE__ #if defined(KJ_DEBUG) && !__OPTIMIZE__
static constexpr uint MEDIUM_PRIME = 619; static constexpr uint MEDIUM_PRIME = 619;
...@@ -1100,5 +1101,6 @@ KJ_TEST("insertion order index") { ...@@ -1100,5 +1101,6 @@ KJ_TEST("insertion order index") {
} }
} }
} // namespace kj } // namespace
} // namespace _ } // namespace _
} // namespace kj
...@@ -149,8 +149,6 @@ size_t chooseHashTableSize(uint size) { ...@@ -149,8 +149,6 @@ size_t chooseHashTableSize(uint size) {
kj::Array<HashBucket> rehash(kj::ArrayPtr<const HashBucket> oldBuckets, size_t targetSize) { kj::Array<HashBucket> rehash(kj::ArrayPtr<const HashBucket> oldBuckets, size_t targetSize) {
// Rehash the whole table. // Rehash the whole table.
// The element at `invalidPos` will be ignored.
// The element at `replacePos` will be recorded as if it were located at `invalidPos`.
KJ_REQUIRE(targetSize < (1 << 30), "hash table has reached maximum size"); KJ_REQUIRE(targetSize < (1 << 30), "hash table has reached maximum size");
...@@ -321,7 +319,8 @@ void BTreeImpl::growTree(uint minCapacity) { ...@@ -321,7 +319,8 @@ void BTreeImpl::growTree(uint minCapacity) {
} }
BTreeImpl::Iterator BTreeImpl::search(const SearchKey& searchKey) const { BTreeImpl::Iterator BTreeImpl::search(const SearchKey& searchKey) const {
// Find the "first" row number (in sorted order) for which predicate(rowNumber) returns true. // Find the "first" row number (in sorted order) for which searchKey.isAfter(rowNumber) returns
// false.
uint pos = 0; uint pos = 0;
...@@ -330,10 +329,8 @@ BTreeImpl::Iterator BTreeImpl::search(const SearchKey& searchKey) const { ...@@ -330,10 +329,8 @@ BTreeImpl::Iterator BTreeImpl::search(const SearchKey& searchKey) const {
pos = parent.children[searchKey.search(parent)]; pos = parent.children[searchKey.search(parent)];
} }
{ auto& leaf = tree[pos].leaf;
auto& leaf = tree[pos].leaf; return { tree, &leaf, searchKey.search(leaf) };
return { tree, &leaf, searchKey.search(leaf) };
}
} }
template <typename T> template <typename T>
...@@ -412,16 +409,14 @@ BTreeImpl::Iterator BTreeImpl::insert(const SearchKey& searchKey) { ...@@ -412,16 +409,14 @@ BTreeImpl::Iterator BTreeImpl::insert(const SearchKey& searchKey) {
pos = node.children[indexInParent]; pos = node.children[indexInParent];
} }
{ Leaf& leaf = insertHelper(searchKey, tree[pos].leaf, parent, indexInParent, pos);
Leaf& leaf = insertHelper(searchKey, tree[pos].leaf, parent, indexInParent, pos);
// Fun fact: Unlike erase(), there's no need to climb back up the tree modifying keys, because // Fun fact: Unlike erase(), there's no need to climb back up the tree modifying keys, because
// either the newly-inserted node will not be the last in the leaf (and thus parent keys aren't // either the newly-inserted node will not be the last in the leaf (and thus parent keys aren't
// modified), or the leaf is the last leaf in the tree (and thus there's no parent key to // modified), or the leaf is the last leaf in the tree (and thus there's no parent key to
// modify). // modify).
return { tree, &leaf, searchKey.search(leaf) }; return { tree, &leaf, searchKey.search(leaf) };
}
} }
template <typename Node> template <typename Node>
...@@ -510,24 +505,22 @@ void BTreeImpl::erase(uint row, const SearchKey& searchKey) { ...@@ -510,24 +505,22 @@ void BTreeImpl::erase(uint row, const SearchKey& searchKey) {
} }
} }
{ Leaf& leaf = eraseHelper(tree[pos].leaf, parent, indexInParent, pos, fixup);
Leaf& leaf = eraseHelper(tree[pos].leaf, parent, indexInParent, pos, fixup);
uint r = searchKey.search(leaf); uint r = searchKey.search(leaf);
if (leaf.rows[r] == row) { if (leaf.rows[r] == row) {
leaf.erase(r); leaf.erase(r);
if (fixup != nullptr) { if (fixup != nullptr) {
// There's a key in a parent node that needs fixup. This is only possible if the removed // There's a key in a parent node that needs fixup. This is only possible if the removed
// node is the last in its leaf. // node is the last in its leaf.
KJ_DASSERT(leaf.rows[r] == nullptr); KJ_DASSERT(leaf.rows[r] == nullptr);
KJ_DASSERT(r > 0); // non-root nodes must be at least half full so this can't be item 0 KJ_DASSERT(r > 0); // non-root nodes must be at least half full so this can't be item 0
KJ_DASSERT(*fixup == row); KJ_DASSERT(*fixup == row);
*fixup = leaf.rows[r - 1]; *fixup = leaf.rows[r - 1];
}
} else {
logInconsistency();
} }
} else {
logInconsistency();
} }
} }
...@@ -618,14 +611,12 @@ void BTreeImpl::renumber(uint oldRow, uint newRow, const SearchKey& searchKey) { ...@@ -618,14 +611,12 @@ void BTreeImpl::renumber(uint oldRow, uint newRow, const SearchKey& searchKey) {
KJ_DASSERT(pos != 0); KJ_DASSERT(pos != 0);
} }
{ auto& leaf = tree[pos].leaf;
auto& leaf = tree[pos].leaf; uint r = searchKey.search(leaf);
uint r = searchKey.search(leaf); if (leaf.rows[r] == oldRow) {
if (leaf.rows[r] == oldRow) { leaf.rows[r] = newRow;
leaf.rows[r] = newRow; } else {
} else { logInconsistency();
logInconsistency();
}
} }
} }
...@@ -711,7 +702,7 @@ void BTreeImpl::rotateLeft( ...@@ -711,7 +702,7 @@ void BTreeImpl::rotateLeft(
Parent& left, Parent& right, Parent& parent, uint indexInParent, MaybeUint*& fixup) { Parent& left, Parent& right, Parent& parent, uint indexInParent, MaybeUint*& fixup) {
// Steal one item from the right node and move it to the left node. // Steal one item from the right node and move it to the left node.
// Like mergeFrom(), this is only called on an exactly-half-empty node. // Like merge(), this is only called on an exactly-half-empty node.
KJ_DASSERT(left.isHalfFull()); KJ_DASSERT(left.isHalfFull());
KJ_DASSERT(right.isMostlyFull()); KJ_DASSERT(right.isMostlyFull());
......
...@@ -160,6 +160,9 @@ public: ...@@ -160,6 +160,9 @@ public:
void insertAll(Collection& collection); void insertAll(Collection& collection);
// Given an iterable collection of Rows, inserts all of them into this table. If the input is // Given an iterable collection of Rows, inserts all of them into this table. If the input is
// an rvalue, the rows will be moved rather than copied. // an rvalue, the rows will be moved rather than copied.
//
// If an insertion throws (e.g. because it violates a uniqueness constraint of some index),
// subsequent insertions do not occur, but previous insertions remain inserted.
template <typename UpdateFunc> template <typename UpdateFunc>
Row& upsert(Row&& row, UpdateFunc&& update); Row& upsert(Row&& row, UpdateFunc&& update);
...@@ -180,7 +183,7 @@ public: ...@@ -180,7 +183,7 @@ public:
// Like find(), but if the row doesn't exist, call a function to create it. createFunc() must // Like find(), but if the row doesn't exist, call a function to create it. createFunc() must
// return `Row` or something that implicitly converts to `Row`. // return `Row` or something that implicitly converts to `Row`.
// //
// NOTE: C++ doesn't actually properly suppoprt inferring types of a parameter pack at the // NOTE: C++ doesn't actually properly support inferring types of a parameter pack at the
// beginning of an argument list, but we define a hack to support it below. Don't worry about // beginning of an argument list, but we define a hack to support it below. Don't worry about
// it. // it.
...@@ -308,7 +311,7 @@ class HashIndex; ...@@ -308,7 +311,7 @@ class HashIndex;
// // methods to match this row. // // methods to match this row.
// //
// bool matches(const Row&, SearchParams&&...) const; // bool matches(const Row&, SearchParams&&...) const;
// // Returns true if the the row on the left matches thes search params on the right. // // Returns true if the row on the left matches thes search params on the right.
// //
// uint hashCode(SearchParams&&...) const; // uint hashCode(SearchParams&&...) const;
// // Computes the hash code of the given search params. Matching rows (as determined by // // Computes the hash code of the given search params. Matching rows (as determined by
...@@ -318,7 +321,7 @@ class HashIndex; ...@@ -318,7 +321,7 @@ class HashIndex;
// }; // };
// //
// If your `Callbacks` type has dynamic state, you may pass its constructor parameters as the // If your `Callbacks` type has dynamic state, you may pass its constructor parameters as the
// consturctor parameters to `HashIndex`. // constructor parameters to `HashIndex`.
template <typename Callbacks> template <typename Callbacks>
class TreeIndex; class TreeIndex;
...@@ -401,7 +404,7 @@ private: ...@@ -401,7 +404,7 @@ private:
template <typename Inner, typename Mapping> template <typename Inner, typename Mapping>
class MappedIterable: private Mapping { class MappedIterable: private Mapping {
// An iterator that wraps some other iterator and maps the values through a mapping function. // An iterable that wraps some other iterable and maps the values through a mapping function.
// The type `Mapping` must define a method `map()` which performs this mapping. // The type `Mapping` must define a method `map()` which performs this mapping.
// //
// TODO(cleanup): This seems generally useful. Should we put it somewhere resuable? // TODO(cleanup): This seems generally useful. Should we put it somewhere resuable?
...@@ -1074,18 +1077,18 @@ public: ...@@ -1074,18 +1077,18 @@ public:
Iterator end() const; Iterator end() const;
Iterator search(const SearchKey& searchKey) const; Iterator search(const SearchKey& searchKey) const;
// Find the "first" row number (in sorted order) for which predicate(rowNumber) returns false. // Find the "first" row (in sorted order) for which searchKey.isAfter(rowNumber) returns true.
Iterator insert(const SearchKey& searchKey); Iterator insert(const SearchKey& searchKey);
// Like search() but ensures that there is room in the leaf node to insert a new row. // Like search() but ensures that there is room in the leaf node to insert a new row.
void erase(uint row, const SearchKey& searchKey); void erase(uint row, const SearchKey& searchKey);
// Erase the given row number from the tree. predicate() returns false for the given row and all // Erase the given row number from the tree. searchKey.isAfter() returns true for the given row
// rows after it. // and all rows after it.
void renumber(uint oldRow, uint newRow, const SearchKey& searchKey); void renumber(uint oldRow, uint newRow, const SearchKey& searchKey);
// Renumber the given row from oldRow to newRow. predicate() returns false for oldRow and all // Renumber the given row from oldRow to newRow. searchKey.isAfter() returns true for oldRow and
// rows after it. (It will not be called on newRow.) // all rows after it. (It will not be called on newRow.)
void verify(size_t size, FunctionParam<bool(uint, uint)>); void verify(size_t size, FunctionParam<bool(uint, uint)>);
...@@ -1436,7 +1439,7 @@ public: ...@@ -1436,7 +1439,7 @@ public:
TreeIndex(Params&&... params): cb(kj::fwd<Params>(params)...) {} TreeIndex(Params&&... params): cb(kj::fwd<Params>(params)...) {}
template <typename Row> template <typename Row>
void verify(kj::ArrayPtr<Row> table) { // KJ_DBG void verify(kj::ArrayPtr<Row> table) {
impl.verify(table.size(), [&](uint i, uint j) { impl.verify(table.size(), [&](uint i, uint j) {
return cb.isBefore(table[i], table[j]); return cb.isBefore(table[i], table[j]);
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment