diff --git a/src/user/user_cache.cc b/src/user/user_cache.cc index 504610ee..64d1c9dd 100644 --- a/src/user/user_cache.cc +++ b/src/user/user_cache.cc @@ -26,35 +26,54 @@ #include #include -// adds a block of data for the asset and returns number of bytes stored -template -std::size_t mjCAsset::Add(const std::string& name, const std::vector& v) { +// copies a block of data into the asset and returns number of bytes stored +// loading data into an asset should happen in a single thread +template std::size_t mjCAsset::Add(const std::string& name, + const T* data, std::size_t n) { auto [it, inserted] = blocks_.insert({name, mjCAssetData()}); if (!inserted) { return 0; } - std::size_t n = v.size() * sizeof(T); - const uint8_t* ptr = reinterpret_cast(v.data()); + std::size_t nbytes = n * sizeof(T); + const uint8_t* ptr = reinterpret_cast(data); mjCAssetData& block = it->second; - block.bytes = std::make_shared(n); - std::copy(ptr, ptr + n, block.bytes.get()); - block.nbytes = n; - nbytes_ += n; - return n; + block.bytes = std::make_shared(nbytes); + std::copy(ptr, ptr + nbytes, block.bytes.get()); + block.nbytes = nbytes; + nbytes_ += nbytes; + return nbytes; } - template std::size_t mjCAsset::Add(const std::string& name, - const std::vector& v); + const uint8_t* data, std::size_t n); template std::size_t mjCAsset::Add(const std::string& name, - const std::vector& v); + const int* data, std::size_t n); template std::size_t mjCAsset::Add(const std::string& name, - const std::vector& v); + const float* data, std::size_t n); +template std::size_t mjCAsset::Add(const std::string& name, + const double* data, std::size_t n); -// fetches a block of data, sets n to size of data +// copies a vector into the asset and returns number of bytes stored +// loading data into an asset should happen in a single thread +template std::size_t mjCAsset::AddVector(const std::string& name, + const std::vector& v) { + return Add(name, v.data(), v.size()); +} +template std::size_t mjCAsset::AddVector(const std::string& name, + const std::vector& v); +template std::size_t mjCAsset::AddVector(const std::string& name, + const std::vector& v); +template std::size_t mjCAsset::AddVector(const std::string& name, + const std::vector& v); +template std::size_t mjCAsset::AddVector(const std::string& name, + const std::vector& v); + + + +// returns a pointer to a block of data, sets n to size of data template const T* mjCAsset::Get(const std::string& name, std::size_t* n) const { auto it = blocks_.find(name); @@ -76,6 +95,8 @@ const T* mjCAsset::Get(const std::string& name, std::size_t* n) const { return reinterpret_cast(data.bytes.get()); } +template +const uint8_t* mjCAsset::Get(const std::string& name, std::size_t* n) const; template const int* mjCAsset::Get(const std::string& name, std::size_t* n) const; template @@ -85,6 +106,28 @@ const double* mjCAsset::Get(const std::string& name, std::size_t* n) const; +// copies a block of data into a vector +template std::optional> +mjCAsset::GetVector(const std::string& name) const { + std::size_t n; + const T* ptr = Get(name, &n); + if (ptr == nullptr) { + return std::nullopt; + } + return std::vector(ptr, ptr + n); +} + +template std::optional> +mjCAsset::GetVector(const std::string& name) const; +template std::optional> +mjCAsset::GetVector(const std::string& name) const; +template std::optional> +mjCAsset::GetVector(const std::string& name) const; +template std::optional> +mjCAsset::GetVector(const std::string& name) const; + + + // replaces blocks data in asset void mjCAsset::ReplaceBlocks( const std::unordered_map& blocks, diff --git a/src/user/user_cache.h b/src/user/user_cache.h index a47cc779..ee0f1ef1 100644 --- a/src/user/user_cache.h +++ b/src/user/user_cache.h @@ -56,17 +56,30 @@ class mjCAsset { mjCAsset(const mjCAsset& other) = default; mjCAsset& operator=(const mjCAsset& other) = default; - // adds a block of data for the asset and returns number of bytes stored - // loading data into an asset should happen single thread + // copies a block of data into the asset and returns number of bytes stored + // loading data into an asset should happen in a single thread template std::size_t Add(const std::string& name, - const std::vector& v); + const T* data, std::size_t n); - // fetches a block of data, sets n to size of data - // TODO(kylebayes): The C++ span utility doesn't seem to be supported by - // Google C++ coding standards. For now, we fallback to an C style API. + + // copies a vector into the asset and returns number of bytes stored + // loading data into an asset should happen in a single thread + template std::size_t AddVector(const std::string& name, + const std::vector& v); + + // returns a pointer to a block of data, sets n to size of data template const T* Get(const std::string& name, std::size_t* n) const; + // copies a block of data into a vector + template + std::optional> GetVector(const std::string& name) const; + + // returns true if a block of data by the given name is stored in the asset + bool HasData(const std::string& name) const { + return blocks_.find(name) != blocks_.end(); + } + private: mjCAsset() = default; diff --git a/test/user/user_cache_test.cc b/test/user/user_cache_test.cc index 72e02cd8..ed8b3ff4 100644 --- a/test/user/user_cache_test.cc +++ b/test/user/user_cache_test.cc @@ -66,8 +66,8 @@ TEST(CacheTest, AddSuccessTest) { std::vector v2 = {1.0, 2.0, 3.0}; mjCAsset asset("file.xml", "foo.obj", "now"); - std::size_t nbytes1 = asset.Add("v1", v1); - std::size_t nbytes2 = asset.Add("v2", v2); + std::size_t nbytes1 = asset.AddVector("v1", v1); + std::size_t nbytes2 = asset.AddVector("v2", v2); cache.Insert(asset); ASSERT_EQ(nbytes1, 12); @@ -81,8 +81,8 @@ TEST(CacheTest, AddFailureTest) { std::vector v2 = {1.0, 2.0, 3.0}; mjCAsset asset("file.xml", "foo.obj", "now"); - asset.Add("v1", v1); - std::size_t nbytes = asset.Add("v1", v2); + asset.AddVector("v1", v1); + std::size_t nbytes = asset.AddVector("v1", v2); cache.Insert(asset); ASSERT_EQ(nbytes, 0); @@ -95,18 +95,16 @@ TEST(CacheTest, InsertReplaceTest) { std::vector v2 = {1.0, 2.0, 3.0}; mjCAsset asset("file.xml", "foo.obj", "now"); - asset.Add("v", v1); + asset.AddVector("v", v1); cache.Insert(asset); mjCAsset asset2("file.xml", "foo.obj", "nower"); - asset2.Add("v", v2); + asset2.AddVector("v", v2); bool inserted = cache.Insert(asset2); EXPECT_TRUE(inserted); mjCAsset asset3 = *(cache.Get("foo.obj")); - std::size_t n = 0; - const double* ptr = asset3.Get("v", &n); - std::vector v3 = std::vector(ptr, ptr + n); + std::vector v3 = asset3.GetVector("v").value(); EXPECT_THAT(v3, ElementsAreArray(v2)); ASSERT_EQ(cache.Size(), 24); @@ -118,8 +116,8 @@ TEST(CacheTest, MoveInsertNewTest) { std::vector v2 = {1.0, 2.0, 3.0}; mjCAsset asset("file.xml", "foo.obj", "now"); - std::size_t nbytes1 = asset.Add("v1", v1); - std::size_t nbytes2 = asset.Add("v2", v2); + std::size_t nbytes1 = asset.AddVector("v1", v1); + std::size_t nbytes2 = asset.AddVector("v2", v2); cache.Insert(std::move(asset)); ASSERT_EQ(nbytes1, 12); @@ -133,18 +131,16 @@ TEST(CacheTest, MoveInsertReplaceTest) { std::vector v2 = {1.0, 2.0, 3.0}; mjCAsset asset("file.xml", "foo.obj", "now"); - asset.Add("v", v1); + asset.AddVector("v", v1); cache.Insert(std::move(asset)); mjCAsset asset2("file.xml", "foo.obj", "nower"); - asset2.Add("v", v2); + asset2.AddVector("v", v2); bool inserted = cache.Insert(std::move(asset2)); EXPECT_TRUE(inserted); mjCAsset asset3 = *(cache.Get("foo.obj")); - std::size_t n = 0; - const double* ptr = asset3.Get("v", &n); - std::vector v3 = std::vector(ptr, ptr + n); + std::vector v3 = asset3.GetVector("v").value(); EXPECT_THAT(v3, ElementsAreArray(v2)); ASSERT_EQ(cache.Size(), 24); @@ -153,40 +149,33 @@ TEST(CacheTest, MoveInsertReplaceTest) { TEST(CacheTest, GetSuccessTest) { mjCCache cache(kMaxSize); std::vector v1 = {1, 2, 3}; - std::vector v2 = {1.0, 2.0, 3.0}; + std::vector v2 = {4.0, 5.0, 6.0}; mjCAsset asset("file.xml", "foo.obj", "now"); - asset.Add("v1", v1); - asset.Add("v2", v2); + asset.AddVector("v1", v1); + asset.AddVector("v2", v2); cache.Insert(asset); - std::size_t n = 0; - mjCAsset asset2 = *(cache.Get("foo.obj")); - const int* ptr1 = asset2.Get("v1", &n); - EXPECT_EQ(n, 3); - std::vector v3 = std::vector(ptr1, ptr1 + n); + std::vector v3 = asset2.GetVector("v1").value(); EXPECT_THAT(v3, ElementsAreArray(v1)); - const double* ptr2 = asset2.Get("v2", &n); - EXPECT_EQ(n, 3); - std::vector v4 = std::vector(ptr2, ptr2 + n); - EXPECT_THAT(v4, ElementsAreArray(v3)); + std::vector v4 = asset2.GetVector("v2").value(); + EXPECT_THAT(v4, ElementsAreArray(v2)); } TEST(CacheTest, GetFailueTest) { mjCCache cache(kMaxSize); std::vector v = {1, 2, 3}; mjCAsset asset("file.xml", "foo.obj", "now"); - asset.Add("v", v); + asset.AddVector("v", v); cache.Insert(asset); mjCAsset asset2 = *(cache.Get("foo.obj")); EXPECT_EQ(cache.Get("bar.obj").has_value(), false); - std::size_t n = 0; - const int* ptr = asset.Get("v2", &n); - EXPECT_THAT(ptr, IsNull()); + auto v2 = asset2.GetVector("v2"); + EXPECT_EQ(v2.has_value(), false); } // Trim cache based off of access count @@ -197,8 +186,8 @@ TEST(CacheTest, LimitTest1) { mjCAsset asset1 = mjCAsset("file.xml", "foo.obj", "now"); mjCAsset asset2 = mjCAsset("file.xml", "bar.obj", "now"); - asset1.Add("foo.obj", v); - asset2.Add("bar.obj", v); + asset1.AddVector("foo.obj", v); + asset2.AddVector("bar.obj", v); cache.Insert(asset1); cache.Insert(asset2); @@ -223,8 +212,8 @@ TEST(CacheTest, LimitTest2) { std::vector v = {1, 2, 3}; mjCAsset asset1("file.xml", "foo.obj", "now"); mjCAsset asset2("file.xml", "bar.obj", "now"); - asset1.Add("v", v); - asset2.Add("v", v); + asset1.AddVector("v", v); + asset2.AddVector("v", v); cache.Insert(asset1); cache.Insert(asset2);