mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-23 01:21:42 +00:00
Add SaveEntries() and LoadEntries(). (#233)
These allow saving and loading non-persistent entries in the persistent file format.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user