aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Egge <Tor.Egge@broadpark.no>2016-12-09 17:19:01 +0100
committerGitHub <noreply@github.com>2016-12-09 17:19:01 +0100
commitf0b529841383c7fc24fbb6ed84ab0c1b57a88a9d (patch)
treef97918540f0d226e6f17e3fa777805fe76d4148e
parent1f95148b7bf920579c6903afc4e1bbfe03306743 (diff)
parenta7ed2e0fadf6f437dc57220843824a37d5200082 (diff)
Merge pull request #1292 from yahoo/havardpe/stash-array-create
added create_array and copy_array to stash
-rw-r--r--vespalib/src/tests/stash/stash.cpp116
-rw-r--r--vespalib/src/vespa/vespalib/util/stash.h61
2 files changed, 158 insertions, 19 deletions
diff --git a/vespalib/src/tests/stash/stash.cpp b/vespalib/src/tests/stash/stash.cpp
index b4ee201a50a..63a77cf3f21 100644
--- a/vespalib/src/tests/stash/stash.cpp
+++ b/vespalib/src/tests/stash/stash.cpp
@@ -13,9 +13,9 @@ struct Object {
int check1;
int check2;
int check3;
- bool &destructed;
+ size_t &destructed;
char bloat[fill_size];
- explicit Object(bool &dref)
+ explicit Object(size_t &dref)
: alive(true), check1(0x1111), check2(0x2222), check3(0x5555),
destructed(dref), bloat()
{
@@ -32,7 +32,7 @@ struct Object {
check1 = 0;
check2 = 0;
check3 = 0;
- destructed = true;
+ ++destructed;
}
};
@@ -40,19 +40,19 @@ typedef Object<8> SmallObject;
typedef Object<10000> LargeObject;
struct Small : SmallObject {
- Small(bool &dref) : SmallObject(dref) {}
+ Small(size_t &dref) : SmallObject(dref) {}
};
struct Large : LargeObject {
- Large(bool &dref) : LargeObject(dref) {}
+ Large(size_t &dref) : LargeObject(dref) {}
};
struct Small_NoDelete : SmallObject {
- Small_NoDelete(bool &dref) : SmallObject(dref) {}
+ Small_NoDelete(size_t &dref) : SmallObject(dref) {}
};
struct Large_NoDelete : LargeObject {
- Large_NoDelete(bool &dref) : LargeObject(dref) {}
+ Large_NoDelete(size_t &dref) : LargeObject(dref) {}
};
VESPA_CAN_SKIP_DESTRUCTION(Small_NoDelete);
@@ -91,6 +91,7 @@ size_t char_ptr_size() { return sizeof(char*); }
size_t chunk_header_size() { return sizeof(stash::Chunk); }
size_t dtor_hook_size() { return sizeof(stash::DestructObject<Small>); }
size_t free_hook_size() { return sizeof(stash::DeleteMemory); }
+size_t array_dtor_hook_size() { return sizeof(stash::DestructArray<Small>); }
//-----------------------------------------------------------------------------
@@ -99,6 +100,7 @@ TEST("require that base types have expected size") {
EXPECT_EQUAL(16u, chunk_header_size());
EXPECT_EQUAL(16u, dtor_hook_size());
EXPECT_EQUAL(16u, free_hook_size());
+ EXPECT_EQUAL(24u, array_dtor_hook_size());
}
TEST("require that raw memory can be allocated inside the stash") {
@@ -153,7 +155,7 @@ TEST("require that valid empty memory may be allocated") {
}
TEST("require that small object creation and destruction works") {
- bool destructed = false;
+ size_t destructed = 0;
{
Stash stash;
stash.create<Small>(destructed);
@@ -164,7 +166,7 @@ TEST("require that small object creation and destruction works") {
}
TEST("require that large object creation and destruction works") {
- bool destructed = false;
+ size_t destructed = 0;
{
Stash stash;
stash.create<Large>(destructed);
@@ -176,7 +178,7 @@ TEST("require that large object creation and destruction works") {
}
TEST("require that small objects can skip destruction") {
- bool destructed = false;
+ size_t destructed = 0;
{
Stash stash;
stash.create<Small_NoDelete>(destructed);
@@ -186,7 +188,7 @@ TEST("require that small objects can skip destruction") {
}
TEST("require that large objects can skip destruction") {
- bool destructed = false;
+ size_t destructed = 0;
{
Stash stash;
stash.create<Large_NoDelete>(destructed);
@@ -257,7 +259,7 @@ TEST("require that minimal chunk size is 4096") {
}
TEST("require that a stash can be moved by construction") {
- bool destructed = false;
+ size_t destructed = 0;
{
Stash outer_stash;
outer_stash.create<Small>(destructed);
@@ -273,7 +275,7 @@ TEST("require that a stash can be moved by construction") {
}
TEST("require that a stash can be moved by assignment") {
- bool destructed = false;
+ size_t destructed = 0;
{
Stash outer_stash;
outer_stash.create<Small>(destructed);
@@ -298,7 +300,7 @@ TEST("require that an empty stash can be cleared") {
}
TEST("require that a stash retains memory when cleared") {
- bool destructed = false;
+ size_t destructed = 0;
{
Stash stash;
stash.create<Small>(destructed);
@@ -327,4 +329,90 @@ TEST("require that a stash only retains a single chunk when cleared") {
EXPECT_EQUAL(sum({chunk_header_size()}), stash.count_used());
}
+TEST("require that array constructor parameters are passed correctly") {
+ Stash stash;
+ {
+ ArrayRef<Pair> pair_array_nodelete = stash.create_array<Pair>(3);
+ ArrayRef<PairD> pair_array = stash.create_array<PairD>(3);
+ ASSERT_EQUAL(pair_array_nodelete.size(), 3u);
+ ASSERT_EQUAL(pair_array.size(), 3u);
+ for (size_t i = 0; i < 3; ++i) {
+ ASSERT_EQUAL(pair_array_nodelete[i].a, 42);
+ ASSERT_EQUAL(pair_array_nodelete[i].b, 4.2);
+ ASSERT_EQUAL(pair_array[i].a, 42);
+ ASSERT_EQUAL(pair_array[i].b, 4.2);
+ }
+ }
+ {
+ ArrayRef<Pair> pair_array_nodelete = stash.create_array<Pair>(3,50,100.5);
+ ArrayRef<PairD> pair_array = stash.create_array<PairD>(3,50,100.5);
+ ASSERT_EQUAL(pair_array_nodelete.size(), 3u);
+ ASSERT_EQUAL(pair_array.size(), 3u);
+ for (size_t i = 0; i < 3; ++i) {
+ ASSERT_EQUAL(pair_array_nodelete[i].a, 50);
+ ASSERT_EQUAL(pair_array_nodelete[i].b, 100.5);
+ ASSERT_EQUAL(pair_array[i].a, 50);
+ ASSERT_EQUAL(pair_array[i].b, 100.5);
+ }
+ }
+}
+
+TEST("require that arrays can be copied into the stash") {
+ Stash stash;
+ std::vector<Pair> pair_vector({Pair(1,1.5),Pair(2,2.5),Pair(3,3.5)});
+ std::vector<PairD> paird_vector({PairD(1,1.5),PairD(2,2.5),PairD(3,3.5)});
+ ArrayRef<Pair> pair_array_nodelete = stash.copy_array<Pair>(ConstArrayRef<Pair>(pair_vector));
+ ArrayRef<PairD> pair_array = stash.copy_array<PairD>(ConstArrayRef<PairD>(paird_vector));
+ ASSERT_EQUAL(pair_array_nodelete.size(), 3u);
+ ASSERT_EQUAL(pair_array.size(), 3u);
+ for (size_t i = 0; i < 3; ++i) {
+ ASSERT_EQUAL(pair_array_nodelete[i].a, i + 1);
+ ASSERT_EQUAL(pair_array_nodelete[i].b, i + 1.5);
+ ASSERT_EQUAL(pair_array[i].a, i + 1);
+ ASSERT_EQUAL(pair_array[i].b, i + 1.5);
+ }
+}
+
+TEST("require that created arrays are destructed (or not) correctly") {
+ size_t destruct = 0;
+ size_t destruct_nodelete = 0;
+ {
+ Stash stash;
+ stash.create_array<Small>(5,destruct);
+ EXPECT_EQUAL(sum({chunk_header_size(), array_dtor_hook_size(), 5 * sizeof(Small)}), stash.count_used());
+ stash.create_array<Small_NoDelete>(7,destruct_nodelete);
+ EXPECT_EQUAL(sum({chunk_header_size(), array_dtor_hook_size(), 5 * sizeof(Small), 7 * sizeof(Small_NoDelete)}), stash.count_used());
+ EXPECT_EQUAL(0, destruct);
+ EXPECT_EQUAL(0, destruct_nodelete);
+ }
+ EXPECT_EQUAL(5, destruct);
+ EXPECT_EQUAL(0, destruct_nodelete);
+}
+
+TEST("require that copied arrays are destructed (or not) correctly") {
+ size_t destruct = 0;
+ size_t destruct_nodelete = 0;
+ size_t collateral_destruct = 0;
+ size_t collateral_destruct_nodelete = 0;
+ {
+ std::vector<Small> small_vector(5, Small(destruct));
+ std::vector<Small_NoDelete> small_nodelete_vector(7, Small_NoDelete(destruct_nodelete));
+ collateral_destruct = destruct;
+ collateral_destruct_nodelete = destruct_nodelete;
+ {
+ Stash stash;
+ stash.copy_array<Small>(ConstArrayRef<Small>(small_vector));
+ EXPECT_EQUAL(sum({chunk_header_size(), array_dtor_hook_size(), 5 * sizeof(Small)}), stash.count_used());
+ stash.copy_array<Small_NoDelete>(ConstArrayRef<Small_NoDelete>(small_nodelete_vector));
+ EXPECT_EQUAL(sum({chunk_header_size(), array_dtor_hook_size(), 5 * sizeof(Small), 7 * sizeof(Small_NoDelete)}), stash.count_used());
+ EXPECT_EQUAL(collateral_destruct, destruct);
+ EXPECT_EQUAL(collateral_destruct_nodelete, destruct_nodelete);
+ }
+ EXPECT_EQUAL(collateral_destruct + 5, destruct);
+ EXPECT_EQUAL(collateral_destruct_nodelete, destruct_nodelete);
+ }
+ EXPECT_EQUAL(collateral_destruct + 5 + 5, destruct);
+ EXPECT_EQUAL(collateral_destruct_nodelete + 7, destruct_nodelete);
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/util/stash.h b/vespalib/src/vespa/vespalib/util/stash.h
index 479021cfdcf..1ec0f7b0056 100644
--- a/vespalib/src/vespa/vespalib/util/stash.h
+++ b/vespalib/src/vespa/vespalib/util/stash.h
@@ -4,6 +4,7 @@
#include <vespa/fastos/fastos.h>
#include "traits.h"
+#include "array.h"
namespace vespalib {
namespace stash {
@@ -28,6 +29,19 @@ template<typename T> struct DestructObject : public Cleanup {
virtual void cleanup() override { reinterpret_cast<T*>(this + 1)->~T(); }
};
+// used as prefix for arrays to be destructed
+template<typename T> struct DestructArray : public Cleanup {
+ size_t size;
+ explicit DestructArray(Cleanup *next_in, size_t size_in) noexcept
+ : Cleanup(next_in), size(size_in) {}
+ virtual void cleanup() override {
+ T *array = reinterpret_cast<T*>(this + 1);
+ for (size_t i = size; i-- > 0;) {
+ array[i].~T();
+ }
+ }
+};
+
struct Chunk {
Chunk *next;
size_t used;
@@ -71,8 +85,21 @@ private:
bool is_small(size_t size) const { return (size < (_chunk_size / 4)); }
template <typename T, typename ... Args>
- T &create_nodelete(Args && ... args) {
- return *(new (alloc(sizeof(T))) T(std::forward<Args>(args)...));
+ T *init_array(char *mem, size_t size, Args && ... args) {
+ T *array = reinterpret_cast<T*>(mem);
+ for (size_t i = 0; i < size; ++i) {
+ new (array + i) T(std::forward<Args>(args)...);
+ }
+ return array;
+ }
+
+ template <typename T>
+ T *copy_elements(char *mem, ConstArrayRef<T> src) {
+ T *array = reinterpret_cast<T*>(mem);
+ for (size_t i = 0; i < src.size(); ++i) {
+ new (array + i) T(src[i]);
+ }
+ return array;
}
public:
@@ -100,14 +127,38 @@ public:
template <typename T, typename ... Args>
T &create(Args && ... args) {
if (can_skip_destruction<T>::value) {
- return create_nodelete<T, Args...>(std::forward<Args>(args)...);
+ return *(new (alloc(sizeof(T))) T(std::forward<Args>(args)...));
}
- typedef stash::DestructObject<T> DeleteHook;
+ using DeleteHook = stash::DestructObject<T>;
char *mem = alloc(sizeof(DeleteHook) + sizeof(T));
T *ret = new (mem + sizeof(DeleteHook)) T(std::forward<Args>(args)...);
- _cleanup = new (mem) stash::DestructObject<T>(_cleanup);
+ _cleanup = new (mem) DeleteHook(_cleanup);
return *ret;
}
+
+ template <typename T, typename ... Args>
+ ArrayRef<T> create_array(size_t size, Args && ... args) {
+ if (can_skip_destruction<T>::value) {
+ return ArrayRef<T>(init_array<T, Args...>(alloc(size * sizeof(T)), size, std::forward<Args>(args)...), size);
+ }
+ using DeleteHook = stash::DestructArray<T>;
+ char *mem = alloc(sizeof(DeleteHook) + (size * sizeof(T)));
+ T *ret = init_array<T, Args...>(mem + sizeof(DeleteHook), size, std::forward<Args>(args)...);
+ _cleanup = new (mem) DeleteHook(_cleanup, size);
+ return ArrayRef<T>(ret, size);
+ }
+
+ template <typename T>
+ ArrayRef<T> copy_array(ConstArrayRef<T> src) {
+ if (can_skip_destruction<T>::value) {
+ return ArrayRef<T>(copy_elements<T>(alloc(src.size() * sizeof(T)), src), src.size());
+ }
+ using DeleteHook = stash::DestructArray<T>;
+ char *mem = alloc(sizeof(DeleteHook) + (src.size() * sizeof(T)));
+ T *ret = copy_elements<T>(mem + sizeof(DeleteHook), src);
+ _cleanup = new (mem) DeleteHook(_cleanup, src.size());
+ return ArrayRef<T>(ret, src.size());
+ }
};
} // namespace vespalib