Basic implementation of ObjectDb and ObjectManager. Local files can be
put as a set of fully formatted ContentObjects to the ObjectDb, and
extracted from this Db.
diff --git a/include/ccnx-common.h b/include/ccnx-common.h
index 031d27d..41d7814 100644
--- a/include/ccnx-common.h
+++ b/include/ccnx-common.h
@@ -38,6 +38,7 @@
#include <sstream>
#include <map>
#include <utility>
+#include <string.h>
using namespace std;
namespace Ccnx {
@@ -45,13 +46,45 @@
typedef vector<string>Comps;
typedef boost::shared_ptr<Bytes> BytesPtr;
-
-// --- Bytes operations start ---
-void
-readRaw(Bytes &bytes, const unsigned char *src, size_t len);
+inline
const unsigned char *
-head(const Bytes &bytes);
+head(const Bytes &bytes)
+{
+ return &bytes[0];
+}
+
+inline
+unsigned char *
+head (Bytes &bytes)
+{
+ return &bytes[0];
+}
+
+// --- Bytes operations start ---
+inline void
+readRaw(Bytes &bytes, const unsigned char *src, size_t len)
+{
+ if (len > 0)
+ {
+ bytes.resize(len);
+ memcpy (head (bytes), src, len);
+ }
+}
+
+inline BytesPtr
+readRawPtr (const unsigned char *src, size_t len)
+{
+ if (len > 0)
+ {
+ BytesPtr ret (new Bytes (len));
+ memcpy (head (*ret), src, len);
+
+ return ret;
+ }
+ else
+ return BytesPtr ();
+}
// --- Bytes operations end ---
diff --git a/include/ccnx-pco.h b/include/ccnx-pco.h
index 7956dad..0c52c7a 100644
--- a/include/ccnx-pco.h
+++ b/include/ccnx-pco.h
@@ -22,6 +22,9 @@
Bytes
content() const;
+ BytesPtr
+ contentPtr() const;
+
Name
name() const;
diff --git a/src/ccnx-pco.cpp b/src/ccnx-pco.cpp
index af10c7a..8c2fd3f 100644
--- a/src/ccnx-pco.cpp
+++ b/src/ccnx-pco.cpp
@@ -54,6 +54,20 @@
return bytes;
}
+BytesPtr
+ParsedContentObject::contentPtr() const
+{
+ const unsigned char *content;
+ size_t len;
+ int res = ccn_content_get_value(head(m_bytes), m_pco.offset[CCN_PCO_E], &m_pco, &content, &len);
+ if (res < 0)
+ {
+ boost::throw_exception(MisformedContentObjectException());
+ }
+
+ return readRawPtr (content, len);
+}
+
Name
ParsedContentObject::name() const
{
diff --git a/src/ccnx-wrapper.cpp b/src/ccnx-wrapper.cpp
index b6902a8..3f82889 100644
--- a/src/ccnx-wrapper.cpp
+++ b/src/ccnx-wrapper.cpp
@@ -16,22 +16,6 @@
namespace Ccnx {
-void
-readRaw(Bytes &bytes, const unsigned char *src, size_t len)
-{
- if (len > 0)
- {
- bytes.resize(len);
- memcpy(&bytes[0], src, len);
- }
-}
-
-const unsigned char *
-head(const Bytes &bytes)
-{
- return &bytes[0];
-}
-
CcnxWrapper::CcnxWrapper()
: m_handle (0)
, m_running (true)
diff --git a/src/object-db.cc b/src/object-db.cc
index 8cadf7e..0d1a2a6 100644
--- a/src/object-db.cc
+++ b/src/object-db.cc
@@ -39,6 +39,7 @@
\
PRIMARY KEY (device_name, segment) \n\
); \n\
+CREATE INDEX device ON File(device_name); \n\
";
ObjectDb::ObjectDb (const fs::path &folder, const std::string &hash)
@@ -61,11 +62,50 @@
res = sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, &errmsg);
if (res != SQLITE_OK && errmsg != 0)
{
- std::cerr << "DEBUG: " << errmsg << std::endl;
+ // std::cerr << "DEBUG: " << errmsg << std::endl;
sqlite3_free (errmsg);
}
}
+bool
+ObjectDb::DoesExist (const boost::filesystem::path &folder, const Ccnx::Name &deviceName, const std::string &hash)
+{
+ fs::path actualFolder = folder / "objects" / hash.substr (0, 2);
+ bool retval = false;
+
+ sqlite3 *db;
+ int res = sqlite3_open((actualFolder / hash.substr (2, hash.size () - 2)).c_str (), &db);
+ if (res == SQLITE_OK)
+ {
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2 (db, "SELECT count(*), count(nullif(content_object,0)) FROM File WHERE device_name=?", -1, &stmt, 0);
+
+ CcnxCharbufPtr buf = deviceName.toCcnxCharbuf ();
+ sqlite3_bind_blob (stmt, 1, buf->buf (), buf->length (), SQLITE_TRANSIENT);
+
+ int res = sqlite3_step (stmt);
+ if (res == SQLITE_ROW)
+ {
+ int countAll = sqlite3_column_int (stmt, 0);
+ int countNonNull = sqlite3_column_int (stmt, 1);
+
+ cout << countAll << ", " << countNonNull << endl;
+
+ if (countAll > 0 && countAll==countNonNull)
+ {
+ cout << "2" << endl;
+ retval = true;
+ }
+ }
+
+ sqlite3_finalize (stmt);
+ }
+
+ sqlite3_close (db);
+ return retval;
+}
+
+
ObjectDb::~ObjectDb ()
{
int res = sqlite3_close (m_db);
@@ -83,6 +123,8 @@
"(device_name, segment, content_object) "
"VALUES (?, ?, ?)", -1, &stmt, 0);
+ cout << deviceName << endl;
+
CcnxCharbufPtr buf = deviceName.toCcnxCharbuf ();
sqlite3_bind_blob (stmt, 1, buf->buf (), buf->length (), SQLITE_TRANSIENT);
sqlite3_bind_int64 (stmt, 2, segment);
@@ -118,7 +160,20 @@
return ret;
}
+
// sqlite3_int64
// ObjectDb::getNumberOfSegments (const Ccnx::Name &deviceName)
// {
+// sqlite3_stmt *stmt;
+// sqlite3_prepare_v2 (m_db, "SELECT count(*) FROM File WHERE device_name=?", -1, &stmt, 0);
+
+// bool retval = false;
+// int res = sqlite3_step (stmt);
+// if (res == SQLITE_ROW)
+// {
+// retval = true;
+// }
+// sqlite3_finalize (stmt);
+
+// return retval;
// }
diff --git a/src/object-db.h b/src/object-db.h
index 9e824de..9ecd753 100644
--- a/src/object-db.h
+++ b/src/object-db.h
@@ -43,6 +43,9 @@
// sqlite3_int64
// getNumberOfSegments (const Ccnx::Name &deviceName);
+
+ static bool
+ DoesExist (const boost::filesystem::path &folder, const Ccnx::Name &deviceName, const std::string &hash);
private:
sqlite3 *m_db;
diff --git a/src/object-manager.cc b/src/object-manager.cc
index a2de9f1..7f66b2a 100644
--- a/src/object-manager.cc
+++ b/src/object-manager.cc
@@ -22,6 +22,7 @@
#include "object-manager.h"
#include "ccnx-name.h"
#include "ccnx-common.h"
+#include "ccnx-pco.h"
#include "object-db.h"
#include <sys/stat.h>
@@ -29,6 +30,7 @@
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/throw_exception.hpp>
+#include <boost/filesystem/fstream.hpp>
using namespace Ccnx;
using namespace boost;
@@ -37,9 +39,8 @@
const int MAX_FILE_SEGMENT_SIZE = 1024;
-ObjectManager::ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const Ccnx::Name &localDeviceName, const fs::path &folder)
+ObjectManager::ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const fs::path &folder)
: m_ccnx (ccnx)
- , m_localDeviceName (localDeviceName)
, m_folder (folder / ".chronoshare")
{
fs::create_directories (m_folder);
@@ -50,30 +51,64 @@
}
HashPtr
-ObjectManager::storeLocalFile (const fs::path &file)
+ObjectManager::localFileToObjects (const fs::path &file, const Ccnx::Name &deviceName)
{
HashPtr fileHash = Hash::FromFileContent (file);
ObjectDb fileDb (m_folder, lexical_cast<string> (*fileHash));
- ifstream iff (file.c_str (), std::ios::in | std::ios::binary);
- int segment = 0;
+ fs::ifstream iff (file, std::ios::in | std::ios::binary);
+ sqlite3_int64 segment = 0;
while (iff.good ())
{
char buf[MAX_FILE_SEGMENT_SIZE];
iff.read (buf, MAX_FILE_SEGMENT_SIZE);
-
- Name name (m_localDeviceName);
+ Name name (deviceName);
name
.appendComp ("file")
.appendComp (fileHash->GetHash (), fileHash->GetHashBytes ())
.appendComp (segment);
- cout << *fileHash << endl;
- cout << name << endl;
+ // cout << *fileHash << endl;
+ // cout << name << endl;
+
+ Bytes data = m_ccnx->createContentObject (name, buf, iff.gcount ());
+ fileDb.saveContentObject (deviceName, segment, data);
segment ++;
}
return fileHash;
}
+
+bool
+ObjectManager::objectsToLocalFile (/*in*/const Ccnx::Name &deviceName, /*in*/const Hash &fileHash, /*out*/ const fs::path &file)
+{
+ string hashStr = lexical_cast<string> (fileHash);
+ if (!ObjectDb::DoesExist (m_folder, deviceName, hashStr))
+ {
+ cout << "Brr" << endl;
+ // file does not exist or not all segments are available
+ return false;
+ }
+
+ fs::ofstream off (file, std::ios::out | std::ios::binary);
+ ObjectDb fileDb (m_folder, hashStr);
+
+ sqlite3_int64 segment = 0;
+ BytesPtr bytes = fileDb.fetchSegment (deviceName, 0);
+ while (bytes != BytesPtr())
+ {
+ ParsedContentObject obj (*bytes);
+ BytesPtr data = obj.contentPtr ();
+
+ off.write (reinterpret_cast<const char*> (head(*data)), data->size());
+
+ segment ++;
+ bytes = fileDb.fetchSegment (deviceName, segment);
+ }
+
+ // permission and timestamp should be assigned somewhere else (ObjectManager has no idea about that)
+
+ return true;
+}
diff --git a/src/object-manager.h b/src/object-manager.h
index 4ae719b..f0cd6e6 100644
--- a/src/object-manager.h
+++ b/src/object-manager.h
@@ -32,15 +32,17 @@
class ObjectManager
{
public:
- ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const Ccnx::Name &localDeviceName, const boost::filesystem::path &folder);
+ ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const boost::filesystem::path &folder);
virtual ~ObjectManager ();
HashPtr
- storeLocalFile (const boost::filesystem::path &file);
+ localFileToObjects (const boost::filesystem::path &file, const Ccnx::Name &deviceName);
+
+ bool
+ objectsToLocalFile (/*in*/const Ccnx::Name &deviceName, /*in*/const Hash &hash, /*out*/ const boost::filesystem::path &file);
private:
Ccnx::CcnxWrapperPtr m_ccnx;
- Ccnx::Name m_localDeviceName;
boost::filesystem::path m_folder;
};
diff --git a/test/database-test.cc b/test/database-test.cc
index 633c4cf..dcbcc2b 100644
--- a/test/database-test.cc
+++ b/test/database-test.cc
@@ -39,8 +39,6 @@
BOOST_AUTO_TEST_CASE (BasicDatabaseTest)
{
fs::path tmpdir = fs::unique_path (fs::temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
- fs::create_directories (tmpdir);
-
SyncLog db (tmpdir, "/alex");
HashPtr hash = db.RememberStateInStateLog ();
diff --git a/test/test-object-manager.cc b/test/test-object-manager.cc
index a27fd1b..78337b3 100644
--- a/test/test-object-manager.cc
+++ b/test/test-object-manager.cc
@@ -22,29 +22,46 @@
#include "object-manager.h"
#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+
#include <boost/test/unit_test.hpp>
#include <unistd.h>
#include <boost/make_shared.hpp>
#include <iostream>
+#include <iterator>
using namespace Ccnx;
using namespace std;
using namespace boost;
-using namespace boost::filesystem;
+namespace fs = boost::filesystem;
BOOST_AUTO_TEST_SUITE(ObjectManagerTests)
BOOST_AUTO_TEST_CASE (ObjectManagerTest)
{
- path tmpdir = unique_path (temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
- create_directories (tmpdir);
-
+ fs::path tmpdir = fs::unique_path (fs::temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
+ cout << tmpdir << endl;
Name deviceName ("/device");
CcnxWrapperPtr ccnx = make_shared<CcnxWrapper> ();
- ObjectManager manager (ccnx, deviceName, tmpdir);
+ ObjectManager manager (ccnx, tmpdir);
+ HashPtr hash = manager.localFileToObjects (fs::path("test") / "test-object-manager.cc", deviceName);
+ bool ok = manager.objectsToLocalFile (deviceName, *hash, tmpdir / "test.cc");
+ BOOST_CHECK_EQUAL (ok, true);
+
+ {
+ fs::ifstream origFile (fs::path("test") / "test-object-manager.cc");
+ fs::ifstream newFile (tmpdir / "test.cc");
+
+ istream_iterator<char> eof,
+ origFileI (origFile),
+ newFileI (newFile);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS (origFileI, eof, newFileI, eof);
+ }
+
remove_all (tmpdir);
}