Add SaveEntries() and LoadEntries(). (#233)

These allow saving and loading non-persistent entries in the persistent
file format.
This commit is contained in:
Peter Johnson
2017-10-01 09:13:43 -07:00
committed by GitHub
parent e68a71022c
commit 1f18cc5416
17 changed files with 352 additions and 26 deletions

View File

@@ -948,6 +948,31 @@ bool Storage::GetPersistentEntries(
return true;
}
bool Storage::GetEntries(
StringRef prefix,
std::vector<std::pair<std::string, std::shared_ptr<Value>>>* entries)
const {
// copy values out of storage as quickly as possible so lock isn't held
{
std::lock_guard<std::mutex> lock(m_mutex);
entries->reserve(m_entries.size());
for (auto& i : m_entries) {
Entry* entry = i.getValue();
// only write values with given prefix
if (!i.getKey().startswith(prefix)) continue;
entries->emplace_back(i.getKey(), entry->value);
}
}
// sort in name order
std::sort(entries->begin(), entries->end(),
[](const std::pair<std::string, std::shared_ptr<Value>>& a,
const std::pair<std::string, std::shared_ptr<Value>>& b) {
return a.first < b.first;
});
return true;
}
void Storage::CreateRpc(unsigned int local_id, StringRef def,
unsigned int rpc_uid) {
std::unique_lock<std::mutex> lock(m_mutex);

View File

@@ -129,12 +129,18 @@ class Storage : public IStorage {
StringRef filename,
std::function<void(std::size_t line, const char* msg)> warn) override;
const char* SaveEntries(StringRef filename, StringRef prefix) const;
const char* LoadEntries(
StringRef filename, StringRef prefix,
std::function<void(std::size_t line, const char* msg)> warn);
// Stream-based save/load functions (exposed for testing purposes). These
// implement the guts of the filename-based functions.
void SavePersistent(llvm::raw_ostream& os, bool periodic) const;
bool LoadPersistent(
wpi::raw_istream& is,
std::function<void(std::size_t line, const char* msg)> warn);
bool LoadEntries(wpi::raw_istream& is, StringRef prefix, bool persistent,
std::function<void(std::size_t line, const char* msg)> warn);
void SaveEntries(llvm::raw_ostream& os, StringRef prefix) const;
// RPC configuration needs to come through here as RPC definitions are
// actually special Storage value types.
@@ -231,6 +237,9 @@ class Storage : public IStorage {
bool periodic,
std::vector<std::pair<std::string, std::shared_ptr<Value>>>* entries)
const;
bool GetEntries(StringRef prefix,
std::vector<std::pair<std::string, std::shared_ptr<Value>>>*
entries) const;
void SetEntryValueImpl(Entry* entry, std::shared_ptr<Value> value,
std::unique_lock<std::mutex>& lock, bool local);
void SetEntryFlagsImpl(Entry* entry, unsigned int flags,

View File

@@ -30,7 +30,7 @@ class LoadPersistentImpl {
LoadPersistentImpl(wpi::raw_istream& is, WarnFunc warn)
: m_is(is), m_warn(warn) {}
bool Load(std::vector<Entry>* entries);
bool Load(StringRef prefix, std::vector<Entry>* entries);
private:
bool ReadLine();
@@ -141,7 +141,7 @@ static llvm::StringRef UnescapeString(llvm::StringRef source,
return llvm::StringRef{buf.data(), buf.size()};
}
bool LoadPersistentImpl::Load(std::vector<Entry>* entries) {
bool LoadPersistentImpl::Load(StringRef prefix, std::vector<Entry>* entries) {
if (!ReadHeader()) return false; // header
while (ReadLine()) {
@@ -155,7 +155,7 @@ bool LoadPersistentImpl::Load(std::vector<Entry>* entries) {
// name
llvm::SmallString<128> buf;
llvm::StringRef name = ReadName(buf);
if (name.empty()) continue;
if (name.empty() || !name.startswith(prefix)) continue;
// =
m_line = m_line.ltrim(" \t");
@@ -361,14 +361,14 @@ std::shared_ptr<Value> LoadPersistentImpl::ReadStringArrayValue() {
return Value::MakeStringArray(std::move(m_buf_string_array));
}
bool Storage::LoadPersistent(
wpi::raw_istream& is,
bool Storage::LoadEntries(
wpi::raw_istream& is, StringRef prefix, bool persistent,
std::function<void(std::size_t line, const char* msg)> warn) {
// entries to add
std::vector<LoadPersistentImpl::Entry> entries;
// load file
if (!LoadPersistentImpl(is, warn).Load(&entries)) return false;
if (!LoadPersistentImpl(is, warn).Load(prefix, &entries)) return false;
// copy values into storage as quickly as possible so lock isn't held
std::vector<std::shared_ptr<Message>> msgs;
@@ -378,7 +378,7 @@ bool Storage::LoadPersistent(
auto old_value = entry->value;
entry->value = i.second;
bool was_persist = entry->IsPersistent();
if (!was_persist) entry->flags |= NT_PERSISTENT;
if (!was_persist && persistent) entry->flags |= NT_PERSISTENT;
// if we're the server, assign an id if it doesn't have one
if (m_server && entry->id == 0xffff) {
@@ -397,7 +397,7 @@ bool Storage::LoadPersistent(
if (!was_persist) notify_flags |= NT_NOTIFY_FLAGS;
m_notifier.NotifyEntry(entry->local_id, i.first, i.second,
notify_flags);
} else if (!was_persist) {
} else if (!was_persist && persistent) {
m_notifier.NotifyEntry(entry->local_id, i.first, i.second,
NT_NOTIFY_FLAGS | NT_NOTIFY_LOCAL);
}
@@ -436,6 +436,16 @@ const char* Storage::LoadPersistent(
std::error_code ec;
wpi::raw_fd_istream is(filename, ec);
if (ec.value() != 0) return "could not open file";
if (!LoadPersistent(is, warn)) return "error reading file";
if (!LoadEntries(is, "", true, warn)) return "error reading file";
return nullptr;
}
const char* Storage::LoadEntries(
StringRef filename, StringRef prefix,
std::function<void(std::size_t line, const char* msg)> warn) {
std::error_code ec;
wpi::raw_fd_istream is(filename, ec);
if (ec.value() != 0) return "could not open file";
if (!LoadEntries(is, prefix, false, warn)) return "error reading file";
return nullptr;
}

View File

@@ -224,3 +224,45 @@ done:
if (err && periodic) m_persistent_dirty = true;
return err;
}
void Storage::SaveEntries(llvm::raw_ostream& os, StringRef prefix) const {
std::vector<SavePersistentImpl::Entry> entries;
if (!GetEntries(prefix, &entries)) return;
SavePersistentImpl(os).Save(entries);
}
const char* Storage::SaveEntries(StringRef filename, StringRef prefix) const {
llvm::SmallString<128> fn = filename;
llvm::SmallString<128> tmp = filename;
tmp += ".tmp";
llvm::SmallString<128> bak = filename;
bak += ".bak";
// Get entries before creating file
std::vector<SavePersistentImpl::Entry> entries;
if (!GetEntries(prefix, &entries)) return nullptr;
// start by writing to temporary file
std::error_code ec;
llvm::raw_fd_ostream os(tmp, ec, llvm::sys::fs::F_Text);
if (ec.value() != 0) {
return "could not open file";
}
DEBUG("saving file '" << filename << "'");
SavePersistentImpl(os).Save(entries);
os.close();
if (os.has_error()) {
std::remove(tmp.c_str());
return "error saving file";
}
// Safely move to real file. We ignore any failures related to the backup.
std::remove(bak.c_str());
std::rename(fn.c_str(), bak.c_str());
if (std::rename(tmp.c_str(), fn.c_str()) != 0) {
std::rename(bak.c_str(), fn.c_str()); // attempt to restore backup
return "could not rename temp file to real file";
}
return nullptr;
}

View File

@@ -1604,6 +1604,59 @@ JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_networktables_NetworkTablesJNI
return MakeJStringArray(env, warns);
}
/*
* Class: edu_wpi_first_networktables_NetworkTablesJNI
* Method: saveEntries
* Signature: (ILjava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_networktables_NetworkTablesJNI_saveEntries
(JNIEnv *env, jclass, jint inst, jstring filename, jstring prefix)
{
if (!filename) {
nullPointerEx.Throw(env, "filename cannot be null");
return;
}
if (!prefix) {
nullPointerEx.Throw(env, "prefix cannot be null");
return;
}
const char* err =
nt::SaveEntries(inst, JStringRef{env, filename}, JStringRef{env, prefix});
if (err) persistentEx.Throw(env, err);
}
/*
* Class: edu_wpi_first_networktables_NetworkTablesJNI
* Method: loadEntries
* Signature: (ILjava/lang/String;Ljava/lang/String;)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_networktables_NetworkTablesJNI_loadEntries
(JNIEnv *env, jclass, jint inst, jstring filename, jstring prefix)
{
if (!filename) {
nullPointerEx.Throw(env, "filename cannot be null");
return nullptr;
}
if (!prefix) {
nullPointerEx.Throw(env, "prefix cannot be null");
return nullptr;
}
std::vector<std::string> warns;
const char* err =
nt::LoadEntries(inst, JStringRef{env, filename}, JStringRef{env, prefix},
[&](size_t line, const char* msg) {
llvm::SmallString<128> warn;
llvm::raw_svector_ostream oss(warn);
oss << line << ": " << msg;
warns.emplace_back(oss.str());
});
if (err) {
persistentEx.Throw(env, err);
return nullptr;
}
return MakeJStringArray(env, warns);
}
/*
* Class: edu_wpi_first_networktables_NetworkTablesJNI
* Method: now

View File

@@ -460,3 +460,17 @@ std::shared_ptr<Value> NetworkTable::GetValue(StringRef key) const {
}
StringRef NetworkTable::GetPath() const { return m_path; }
const char* NetworkTable::SaveEntries(StringRef filename) const {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
return nt::SaveEntries(m_inst, filename, path);
}
const char* NetworkTable::LoadEntries(
StringRef filename,
std::function<void(size_t line, const char* msg)> warn) {
llvm::SmallString<128> path(m_path);
path += PATH_SEPARATOR_CHAR;
return nt::LoadEntries(m_inst, filename, path, warn);
}

View File

@@ -633,7 +633,7 @@ struct NT_ConnectionInfo* NT_GetConnections(NT_Inst inst, size_t* count) {
}
/*
* Persistent Functions
* File Save/Load Functions
*/
const char* NT_SavePersistent(NT_Inst inst, const char* filename) {
@@ -645,6 +645,17 @@ const char* NT_LoadPersistent(NT_Inst inst, const char* filename,
return nt::LoadPersistent(inst, filename, warn);
}
const char* NT_SaveEntries(NT_Inst inst, const char* filename,
const char* prefix, size_t prefix_len) {
return nt::SaveEntries(inst, filename, StringRef(prefix, prefix_len));
}
const char* NT_LoadEntries(NT_Inst inst, const char* filename,
const char* prefix, size_t prefix_len,
void (*warn)(size_t line, const char* msg)) {
return nt::LoadEntries(inst, filename, StringRef(prefix, prefix_len), warn);
}
/*
* Utility Functions
*/

View File

@@ -936,6 +936,22 @@ const char* LoadPersistent(
return ii->storage.LoadPersistent(filename, warn);
}
const char* SaveEntries(NT_Inst inst, StringRef filename, StringRef prefix) {
auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance));
if (!ii) return "invalid instance handle";
return ii->storage.SaveEntries(filename, prefix);
}
const char* LoadEntries(
NT_Inst inst, StringRef filename, StringRef prefix,
std::function<void(size_t line, const char* msg)> warn) {
auto ii = InstanceImpl::Get(Handle{inst}.GetTypedInst(Handle::kInstance));
if (!ii) return "invalid instance handle";
return ii->storage.LoadEntries(filename, prefix, warn);
}
void SetLogger(LogFunc func, unsigned int min_level) {
auto ii = InstanceImpl::GetDefault();
static std::mutex mutex;