diff --git a/p2p-rm/include/ftl/p2p-rm.hpp b/p2p-rm/include/ftl/p2p-rm.hpp index 5e0e2ff85126ab2feb27ce10a4b9666e5de6957f..9c94e5db2be2d551a5b9fa5a2bbfa85c236e8683 100644 --- a/p2p-rm/include/ftl/p2p-rm.hpp +++ b/p2p-rm/include/ftl/p2p-rm.hpp @@ -1,70 +1,85 @@ #ifndef _FTL_P2P_RA_HPP_ #define _FTL_P2P_RA_HPP_ -#include "ftl/p2p-rm/remote_ptr.hpp" +#include "ftl/p2p-rm/mapped_ptr.hpp" +#include "ftl/p2p-rm/internal.hpp" + +#include <type_traits> namespace ftl { namespace rm { void reset(); - void destroy() { reset(); } - - ftl::rm::Blob *_lookupBlob(const char *uri); - ftl::rm::Blob *_createBlob(const char *uri, size_t size); + inline void destroy() { reset(); } + /** + * Obtain a remote pointer from a URI. A nullptr is returned if the URI is + * not valid. If the URI is actually local then a remote pointer is still + * returned and may be used normally, although it will possibly result in + * unwanted memory copies. + */ template <typename T> - ftl::remote_ptr<T> getPointer(const char *uri) { - auto b = _lookupBlob(uri); + ftl::mapped_ptr<T> get(const char *uri) { + auto b = _lookup(uri); // TODO Verify type and size - return ftl::remote_ptr<T>{b,0}; + return ftl::mapped_ptr<T>{b,0}; } + /** + * Get a read-only memory reference from a URI. + */ template <typename T> ftl::read_ref<T> getReadable(const char *uri) { - return getPointer<T>(uri).readable(); + return get<T>(uri).readable(); } + /** + * Get a read/writable memory reference from a URI. + */ template <typename T> ftl::write_ref<T> getWritable(const char *uri) { - return getPointer<T>(uri).writable(); + return get<T>(uri).writable(); } - //template <typename T> - //ftl::rw_ref<T> getReadWrite(const char *uri); - /** - * Get a remote write only reference filled with a provided empty value - * rather than first performing a remote read to populate. - */ - //template <typename T> - //ftl::write_ref<T> getWritable(const char *uri, T nullvalue); - - /** - * Make a new memory allocation locally mapped to a given URI. The URI - * must not already exist within the peer group, otherwise a nullptr is - * returned. + * Register a memory area locally mapped to a given URI. The URI + * must not already exist within the peer group. */ template <typename T> - ftl::remote_ptr<T> alloc(const char *uri, size_t size) { - auto b = _createBlob(uri, size*sizeof(T)); - return ftl::remote_ptr<T>{b,0}; + ftl::mapped_ptr<T> map(const char *uri, T *addr, size_t size=1) { + if (std::is_pointer<T>::value) return ftl::null_ptr<T>; + if (std::is_function<T>::value) return ftl::null_ptr<T>; + if (std::is_void<T>::value) return ftl::null_ptr<T>; + + if (addr == NULL) return ftl::null_ptr<T>; + + return ftl::mapped_ptr<T>{_create(uri, (char*)addr, sizeof(T), size, + static_cast<flags_t>(std::is_integral<T>::value * ftl::rm::FLAG_INTEGER | + std::is_signed<T>::value * ftl::rm::FLAG_SIGNED | + std::is_trivial<T>::value * ftl::rm::FLAG_TRIVIAL), + typeid(T).name()),0}; } - void free(const char *uri); - + void unmap(const char *uri); + template <typename T> - void free(ftl::remote_ptr<T> p) { - - } + void unmap(ftl::mapped_ptr<T> ptr) {} /** * Obtain a list or URI memory blocks in the current peer group that match * the provided base URI. */ - std::vector<std::string> list(const char *partial_uri); + std::vector<std::string> search(const char *partial_uri); - void addPeer(ftl::net::raw::Socket *s); + /** + * Connect to a new peer node using the specified socket. + */ + void addPeer(ftl::net::Socket *s); + /** + * Connect to a new peer using a URL string. + */ + void addPeer(const char *url); } } diff --git a/p2p-rm/include/ftl/p2p-rm/blob.hpp b/p2p-rm/include/ftl/p2p-rm/blob.hpp index 3b72fb1721dee6465206e9f57f47c4245c20f4e0..8dbe537f84769fc2da19d66cacb0da472418cd13 100644 --- a/p2p-rm/include/ftl/p2p-rm/blob.hpp +++ b/p2p-rm/include/ftl/p2p-rm/blob.hpp @@ -1,33 +1,30 @@ -#ifndef _FTL_P2P_RA_BLOB_HPP_ -#define _FTL_P2P_RA_BLOB_HPP_ +#ifndef _FTL_P2P_RM_BLOB_HPP_ +#define _FTL_P2P_RM_BLOB_HPP_ #include <mutex> #include <shared_mutex> #include <ftl/net.hpp> +#include <string> +#include <vector> namespace ftl { namespace rm { -struct Header { - char magic[4]; - uint32_t version; - size_t size; - uint32_t format; - uint32_t blobid; -}; - /* NOT TO BE USED DIRECTLY */ struct Blob { - Blob(); - ~Blob(); + //Blob(); + //~Blob(); - ftl::net::raw::Socket *socket_; + std::vector<ftl::net::Socket*> sockets_; char *data_; size_t size_; + std::string uri_; + uint32_t blobid_; void finished(); - void write(size_t offset, const char *data, size_t size); - void read(size_t offset, char *data, size_t size); + //void write(size_t offset, const char *data, size_t size); + //void read(size_t offset, char *data, size_t size); + void sync(size_t offset, size_t size); mutable std::shared_mutex mutex_; }; @@ -35,5 +32,5 @@ struct Blob { } } -#endif // _FTL_P2P_RA_CACHE_HPP_ +#endif // _FTL_P2P_RM_CACHE_HPP_ diff --git a/p2p-rm/include/ftl/p2p-rm/internal.hpp b/p2p-rm/include/ftl/p2p-rm/internal.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8519b942b1ea48bde33354a1d05553e2c0d95287 --- /dev/null +++ b/p2p-rm/include/ftl/p2p-rm/internal.hpp @@ -0,0 +1,22 @@ +#ifndef _FTL_P2P_RM_INTERNAL_HPP_ +#define _FTL_P2P_RM_INTERNAL_HPP_ + +namespace ftl { +namespace rm { + +class Blob; + +enum flags_t : uint32_t { + FLAG_INTEGER = 1, + FLAG_SIGNED = 2, + FLAG_TRIVIAL = 4 +}; + +ftl::rm::Blob *_lookup(const char *uri); +ftl::rm::Blob *_create(const char *uri, char *addr, size_t size, size_t count, flags_t flags, const std::string &tname); + +}; // namespace rm +}; // namespace ftl + +#endif // _FTL_P2P_RM_INTERNAL_HPP_ + diff --git a/p2p-rm/include/ftl/p2p-rm/remote_ptr.hpp b/p2p-rm/include/ftl/p2p-rm/mapped_ptr.hpp similarity index 61% rename from p2p-rm/include/ftl/p2p-rm/remote_ptr.hpp rename to p2p-rm/include/ftl/p2p-rm/mapped_ptr.hpp index f7821b5257947d5b31e392a1558a0a3909a507e4..c6cc0422443ea108d4081d2dee1a08634cfab80e 100644 --- a/p2p-rm/include/ftl/p2p-rm/remote_ptr.hpp +++ b/p2p-rm/include/ftl/p2p-rm/mapped_ptr.hpp @@ -1,5 +1,5 @@ -#ifndef _FTL_P2P_RA_REMOTE_PTR_HPP_ -#define _FTL_P2P_RA_REMOTE_PTR_HPP_ +#ifndef _FTL_P2P_RM_MAPPED_PTR_HPP_ +#define _FTL_P2P_RM_MAPPED_PTR_HPP_ #include "ftl/p2p-rm/blob.hpp" @@ -10,7 +10,7 @@ namespace ftl { template <typename T> struct read_ptr; template <typename T> - struct remote_ptr { + struct mapped_ptr { rm::Blob *blob; size_t offset; @@ -31,9 +31,9 @@ namespace ftl { read_ref<T> operator[](ptrdiff_t idx) const; read_ref<T> readable() { return ftl::read_ref<T>(*this); } - remote_ptr<T> operator+(std::ptrdiff_t diff) const { + mapped_ptr<T> operator+(std::ptrdiff_t diff) const { size_t new_offset = offset + sizeof(T)*diff; - return remote_ptr<T>{blob, new_offset}; + return mapped_ptr<T>{blob, new_offset}; } /** Allow pointer casting if a local blob */ @@ -43,24 +43,28 @@ namespace ftl { return NULL; } }; + + template <typename T> + static const mapped_ptr<T> null_ptr = mapped_ptr<T>{0,0}; template <typename T> struct read_ref { - remote_ptr<T> ptr_; + mapped_ptr<T> ptr_; // Constructor - read_ref(remote_ptr<T> ptr) : ptr_(ptr), rlock_(ptr.blob->mutex_) {} + read_ref(mapped_ptr<T> ptr) : ptr_(ptr), rlock_(ptr.blob->mutex_) {} bool is_valid() const { return !ptr_.is_null(); } - remote_ptr<T> pointer() const { return ptr_; } + mapped_ptr<T> pointer() const { return ptr_; } void reset() { rlock_.unlock(); } void finish() { reset(); } operator T() const { //return static_cast<T>(ptr_.blob->data_[ptr_.offset]); - T t; - ptr_.blob->read(ptr_.offset, (char*)&t, sizeof(T)); - return t; + return static_cast<T>(ptr_.blob->data_[ptr_.offset]); + //T t; + //ptr_.blob->read(ptr_.offset, (char*)&t, sizeof(T)); + //return t; } read_ref &operator=(const T &value) { @@ -73,27 +77,30 @@ namespace ftl { template <typename T> struct write_ref { - remote_ptr<T> ptr_; + mapped_ptr<T> ptr_; // Constructor - write_ref(remote_ptr<T> ptr) : ptr_(ptr), wlock_(ptr.blob->mutex_) {} + write_ref(mapped_ptr<T> ptr) : ptr_(ptr), wlock_(ptr.blob->mutex_) {} ~write_ref() { ptr_.blob->finished(); } bool is_valid() const { return !ptr_.is_null(); } - remote_ptr<T> pointer() const { return ptr_; } + mapped_ptr<T> pointer() const { return ptr_; } void reset() { ptr_.blob->finished(); wlock_.unlock(); } - void finish() { reset(); } + void end() { reset(); } + void begin() { wlock_.lock(); } /** Cast to type reads the value */ operator T() const { - //return static_cast<T>(ptr_.blob->data_[ptr_.offset]); - T t; - ptr_.blob->read(ptr_.offset, (char*)&t, sizeof(T)); - return t; + return static_cast<T>(ptr_.blob->data_[ptr_.offset]); + //T t; + //ptr_.blob->read(ptr_.offset, (char*)&t, sizeof(T)); + //return t; } write_ref &operator=(const T &value) { - ptr_.blob->write(ptr_.offset, (char*)(&value), sizeof(T)); + //ptr_.blob->write(ptr_.offset, (char*)(&value), sizeof(T)); + *((T*)&ptr_.blob->data_[ptr_.offset]) = value; + ptr_.blob->sync(ptr_.offset, sizeof(T)); return *this; } @@ -102,24 +109,24 @@ namespace ftl { } template <typename T> -ftl::read_ref<T> ftl::remote_ptr<T>::operator*() const { +ftl::read_ref<T> ftl::mapped_ptr<T>::operator*() const { return ftl::read_ref<T>(*this); } template <typename T> -ftl::read_ref<T> ftl::remote_ptr<T>::operator[](ptrdiff_t idx) const { +ftl::read_ref<T> ftl::mapped_ptr<T>::operator[](ptrdiff_t idx) const { return ftl::read_ref<T>(*this + idx); } template <typename T> -ftl::write_ref<T> ftl::remote_ptr<T>::operator*() { +ftl::write_ref<T> ftl::mapped_ptr<T>::operator*() { return ftl::write_ref<T>(*this); } template <typename T> -ftl::write_ref<T> ftl::remote_ptr<T>::operator[](ptrdiff_t idx) { +ftl::write_ref<T> ftl::mapped_ptr<T>::operator[](ptrdiff_t idx) { return ftl::write_ref<T>(*this + idx); } -#endif // _FTL_P2P_RA_REMOTE_PTR_HPP_ +#endif // _FTL_P2P_RM_MAPPED_PTR_HPP_ diff --git a/p2p-rm/src/blob.cpp b/p2p-rm/src/blob.cpp index 89321564a10dac96c7e38571d9d1877438867f7d..3b8b1beeffd6275b8e41c25cadbe6e9acbc0e55e 100644 --- a/p2p-rm/src/blob.cpp +++ b/p2p-rm/src/blob.cpp @@ -3,31 +3,59 @@ #include "ftl/p2p-rm/blob.hpp" -void ftl::rm::Blob::write(size_t offset, const char *data, size_t size) { +#define MEMORY_SYNC 0x1000 + +struct Header { + uint32_t blobid; + uint32_t offset; + uint32_t size; +}; + +void ftl::rm::Blob::sync(size_t offset, size_t size) { + // Sanity check + if (offset + size > size_) throw -1; + + // TODO Delay send to collate many write operations? + + if (sockets_.size() > 0) { + Header header{blobid_,static_cast<uint32_t>(offset),static_cast<uint32_t>(size)}; + + // If local, write direct to data_, otherwise send over network + for (auto s : sockets_) { + // Send over network + s->send2(MEMORY_SYNC, std::string((const char*)&header,sizeof(header)), + std::string(&data_[offset],size)); + } + } +} + +/*void ftl::rm::Blob::write(size_t offset, const char *data, size_t size) { // Sanity check if (offset + size > size_) throw -1; // If local, write direct to data_, otherwise send over network if (socket_ != NULL) { + Header header{blobid_,static_cast<uint32_t>(offset),static_cast<uint32_t>(size)}; + // Send over network - //socket_->send(ftl::rm::MEMORY_WRITE, std::string(data,size)); + socket_->send2(MEMORY_WRITE, std::string((const char*)&header,sizeof(header)), + std::string(data,size)); } else { // Copy locally memcpy(data_+offset, data, size); } -} +}*/ -void ftl::rm::Blob::read(size_t offset, char *data, size_t size) { +/*void ftl::rm::Blob::read(size_t offset, char *data, size_t size) { // Sanity check if (offset + size > size_) throw -1; // If local, write direct to data_, otherwise send over network if (socket_ != NULL) { - // Send over network - //socket_->send(ftl::rm::MEMORY_WRITE, std::string(data,size)); + } else { // Copy locally memcpy(data,data_+offset, size); } -} +}*/ diff --git a/p2p-rm/src/p2prm.cpp b/p2p-rm/src/p2prm.cpp index 59edc3a8a9e26f323f8978147fee67e2b2ad24e0..1281c51ea0f33b0cd226c1ae16dc83216c5ad35e 100644 --- a/p2p-rm/src/p2prm.cpp +++ b/p2p-rm/src/p2prm.cpp @@ -2,17 +2,18 @@ #include <ftl/uri.hpp> #include <map> +#include <string> static std::map<std::string, ftl::rm::Blob*> blobs; void ftl::rm::reset() { for (auto x : blobs) { - delete x; + delete x.second; } blobs.clear(); } -ftl::rm::Blob *ftl::rm::_lookupBlob(const char *uri) { +ftl::rm::Blob *ftl::rm::_lookup(const char *uri) { URI u(uri); if (!u.isValid()) return NULL; if (u.getScheme() != ftl::URI::SCHEME_FTL) return NULL; @@ -21,7 +22,8 @@ ftl::rm::Blob *ftl::rm::_lookupBlob(const char *uri) { return blobs[u.getBaseURI()]; } -ftl::rm::Blob *ftl::rm::_createBlob(const char *uri, size_t size) { +ftl::rm::Blob *ftl::rm::_create(const char *uri, char *addr, size_t size, size_t count, + ftl::rm::flags_t flags, const std::string &tname) { URI u(uri); if (!u.isValid()) return NULL; if (u.getScheme() != ftl::URI::SCHEME_FTL) return NULL; @@ -31,15 +33,12 @@ ftl::rm::Blob *ftl::rm::_createBlob(const char *uri, size_t size) { ftl::rm::Blob *b = new ftl::rm::Blob; - char *raw = new char[size+sizeof(ftl::rm::Header)]; - - b->raw_ = raw; - b->header_ = (ftl::rm::Header*)raw; - b->data_ = raw+sizeof(ftl::rm::Header); + b->data_ = addr; b->size_ = size; - b->rawsize = size++sizeof(ftl::rm::Header); - b->socket_ = NULL; + b->uri_ = std::string(uri); blobs[u.getBaseURI()] = b; + + // TODO : Perhaps broadcast this new allocation? return b; } diff --git a/p2p-rm/test/CMakeLists.txt b/p2p-rm/test/CMakeLists.txt index 25895fabfcdd3c705fac07bb10572e6b6cbab896..17dd869af271582c34c9319ccb25e4d8b6b08186 100644 --- a/p2p-rm/test/CMakeLists.txt +++ b/p2p-rm/test/CMakeLists.txt @@ -1,9 +1,9 @@ include(CTest) enable_testing() -add_executable(remote_ptr EXCLUDE_FROM_ALL +add_executable(mapped_ptr EXCLUDE_FROM_ALL ./tests.cpp - ./remote_ptr.cpp + ./mapped_ptr.cpp ) add_executable(p2p_rm EXCLUDE_FROM_ALL @@ -14,9 +14,9 @@ add_executable(p2p_rm EXCLUDE_FROM_ALL ) target_link_libraries(p2p_rm uriparser) -add_test(Remote_ptr remote_ptr) +add_test(Mapped_ptr mapped_ptr) add_test(RM_API p2p_rm) add_custom_target(tests) -add_dependencies(tests remote_ptr p2p_rm) +add_dependencies(tests mapped_ptr p2p_rm) diff --git a/p2p-rm/test/remote_ptr.cpp b/p2p-rm/test/mapped_ptr.cpp similarity index 73% rename from p2p-rm/test/remote_ptr.cpp rename to p2p-rm/test/mapped_ptr.cpp index 23e20f205f41790af718edcc42fc7ef44c34f08c..72aaef8a49a2a4e2b149537ced84824ce867e3f8 100644 --- a/p2p-rm/test/remote_ptr.cpp +++ b/p2p-rm/test/mapped_ptr.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include <ftl/p2p-rm/remote_ptr.hpp> +#include <ftl/p2p-rm/mapped_ptr.hpp> #include <memory.h> // Mock the BLOB @@ -8,16 +8,10 @@ void ftl::rm::Blob::finished() { is_finished = true; } -static bool blob_size; -static const int *blob_data; -void ftl::rm::Blob::write(size_t offset, const char *data, size_t size) { - blob_size = size; - blob_data = (const int*)data; - memcpy(data_+offset,data,size); -} +static bool blob_sync = false; -void ftl::rm::Blob::read(size_t offset, char *data, size_t size) { - memcpy(data,data_+offset,size); +void ftl::rm::Blob::sync(size_t offset, size_t size) { + blob_sync = true; } SCENARIO( "Reading from a remote pointer", "[remote_ptr]" ) { @@ -28,7 +22,7 @@ SCENARIO( "Reading from a remote pointer", "[remote_ptr]" ) { ((int*)(blob->data_))[1] = 66; GIVEN( "a valid POD const remote pointer" ) { - const ftl::remote_ptr<int> pa{blob,0}; + const ftl::mapped_ptr<int> pa{blob,0}; REQUIRE( *pa == 55 ); REQUIRE( pa[0] == 55 ); REQUIRE( pa[1] == 66 ); @@ -43,7 +37,7 @@ SCENARIO( "Writing to a remote pointer", "[remote_ptr]" ) { ((int*)(blob->data_))[1] = 66; GIVEN( "a valid POD remote pointer" ) { - ftl::remote_ptr<int> pa{blob,0}; + ftl::mapped_ptr<int> pa{blob,0}; is_finished = false; *pa = 23; REQUIRE( *pa == 23 ); @@ -54,7 +48,7 @@ SCENARIO( "Writing to a remote pointer", "[remote_ptr]" ) { } GIVEN( "a persistent write_ref" ) { - ftl::remote_ptr<int> pa{blob,0}; + ftl::mapped_ptr<int> pa{blob,0}; is_finished = false; auto ra = *pa; @@ -74,7 +68,7 @@ SCENARIO( "Writing to readonly pointer fails", "[remote_ptr]" ) { ((int*)(blob->data_))[1] = 66; GIVEN( "a valid POD const remote pointer" ) { - const ftl::remote_ptr<int> pa{blob,0}; + const ftl::mapped_ptr<int> pa{blob,0}; *pa = 23; REQUIRE( *pa == 55 ); } diff --git a/p2p-rm/test/p2p-rm.cpp b/p2p-rm/test/p2p-rm.cpp index 31a3c9a6c390ddecafd81728b41c58a001d129e5..d124341a30cca2714637cd7b233a20ef41bce0b0 100644 --- a/p2p-rm/test/p2p-rm.cpp +++ b/p2p-rm/test/p2p-rm.cpp @@ -1,59 +1,77 @@ #include "catch.hpp" #include <ftl/p2p-rm.hpp> -SCENARIO( "ftl::rm::alloc()", "[alloc]" ) { +// --- Mock Socket Send + +int ftl::net::raw::Socket::send2(uint32_t service, const std::string &data1, const std::string &data2) { + return 0; +} + +// --- End Mock Socket Send + +SCENARIO( "ftl::rm::map()", "[map]" ) { ftl::rm::reset(); - GIVEN( "a valid URI and size" ) { - auto r = ftl::rm::alloc<int>("ftl://uti.fi/memory/test0", 10); + GIVEN( "a valid URI and array datatype" ) { + int data[10]; + auto m = ftl::rm::map<int[10]>("ftl://uti.fi/memory/test0", &data); + REQUIRE( m.is_valid() ); + + auto r = ftl::rm::get<int[10]>("ftl://uti.fi/memory/test0"); REQUIRE( r.is_valid() ); REQUIRE( r.size() == 10*sizeof(int) ); REQUIRE( r.is_local() ); } - GIVEN( "a valid URI and invalid size" ) { - auto r = ftl::rm::alloc<int>("ftl://uti.fi/memory/test0", 0); - REQUIRE( !r.is_valid() ); + GIVEN( "a valid URI and invalid data" ) { + auto m = ftl::rm::map<int>("ftl://uti.fi/memory/test0", NULL); + REQUIRE( !m.is_valid() ); } GIVEN( "an empty URI" ) { - auto r = ftl::rm::alloc<int>("", 10); - REQUIRE( !r.is_valid() ); + int data; + auto m = ftl::rm::map<int>("", &data); + REQUIRE( !m.is_valid() ); } GIVEN( "an invalid URI" ) { - auto r = ftl::rm::alloc<int>("noschema/test", 10); - REQUIRE( !r.is_valid() ); + int data; + auto m = ftl::rm::map<int>("noschema/test", &data); + REQUIRE( !m.is_valid() ); } GIVEN( "an invalid URI schema" ) { - auto r = ftl::rm::alloc<int>("http://uti.fi/memory/test0", 10); - REQUIRE( !r.is_valid() ); + int data; + auto m = ftl::rm::map<int>("http://uti.fi/memory/test0", &data); + REQUIRE( !m.is_valid() ); } GIVEN( "an invalid URI path segment" ) { - auto r = ftl::rm::alloc<int>("ftl://uti.fi/wrong/test0", 10); - REQUIRE( !r.is_valid() ); + int data; + auto m = ftl::rm::map<int>("ftl://uti.fi/wrong/test0", &data); + REQUIRE( !m.is_valid() ); } GIVEN( "a duplicate URI" ) { - auto a = ftl::rm::alloc<int>("ftl://uti.fi/memory/test0", 10); - auto b = ftl::rm::alloc<int>("ftl://uti.fi/memory/test0", 10); - REQUIRE( a.is_valid() ); + int data; + auto a = ftl::rm::map<int>("ftl://uti.fi/memory/test0", &data); + auto b = ftl::rm::map<int>("ftl://uti.fi/memory/test0", &data); REQUIRE( !b.is_valid() ); + REQUIRE( a.is_valid() ); } } SCENARIO( "Getting a read_ref", "[get]" ) { ftl::rm::reset(); - auto a = ftl::rm::alloc<int>("ftl://uti.fi/memory/test1", 10); - REQUIRE( a.is_valid() ); + int data = 89; + auto m = ftl::rm::map<int>("ftl://uti.fi/memory/test1", &data); + REQUIRE( m.is_valid() ); GIVEN( "a valid URI to local memory" ) { const auto r = ftl::rm::getReadable<int>("ftl://uti.fi/memory/test1"); REQUIRE( r.is_valid() ); REQUIRE( r.pointer().is_local() ); - REQUIRE( r.pointer().blob == a.blob ); + REQUIRE( r == 89 ); } }