swimtracker-firmware/lib/userdb/UserDB.cpp

192 lines
4.8 KiB
C++

#include "UserDB.h"
#include "FilesystemAbstraction.h"
const String userDir = "/u/";
template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T &val)
{
ForwardIterator it;
iterator_traits<ForwardIterator>::difference_type count, step;
count = distance(first, last);
while (count > 0)
{
it = first;
step = count / 2;
advance(it, step);
if (*it < val)
{
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}
static String userFileName(const String &userName)
{
return userDir + userName;
}
bool User::load(const String &name)
{
name_ = name;
assert(name_.length() > 0);
const auto fileName = userFileName(name_);
if (!portablefs::exists(fileName.c_str()))
return false;
auto file = portablefs::open(fileName.c_str(), "r");
size_t sessionsInFile;
file.read((uint8_t *)&sessionsInFile, sizeof(numSessions_));
init(name, sessionsInFile * 2);
size_t expectedSize = sizeof(SessionIdType) * numSessions_;
auto bytesRead = file.read((uint8_t *)sessionIds_, expectedSize);
numSessions_ = sessionsInFile;
assert(expectedSize == bytesRead);
}
void User::init(const String &name, size_t sessionAllocateSize)
{
if (sessionIds_ != nullptr)
{
free(sessionIds_);
sessionIds_ = nullptr;
}
name_ = name;
numSessionsAllocated_ = sessionAllocateSize;
sessionIds_ = (SessionIdType *)ps_malloc(sizeof(SessionIdType) * numSessionsAllocated_);
numSessions_ = 0;
}
void User::save()
{
if (!portablefs::exists(userDir.c_str()))
portablefs::mkdir(userDir.c_str());
auto file = portablefs::open(userFileName(name_).c_str(), "w");
file.write((uint8_t *)&numSessions_, sizeof(numSessions_));
file.write((uint8_t *)sessionIds_, sizeof(SessionIdType) * numSessions_);
}
void User::freeResources()
{
if (sessionIds_ != nullptr)
{
free(sessionIds_);
sessionIds_ = nullptr;
}
}
void User::remove()
{
portablefs::remove(userFileName(name_).c_str());
freeResources();
name_ = "";
numSessions_ = 0;
numSessionsAllocated_ = 0;
}
void User::growSessionArrayIfNecessary()
{
assert(numSessions_ <= numSessionsAllocated_);
if (numSessions_ < numSessionsAllocated_)
return;
numSessionsAllocated_ *= 2;
sessionIds_ = (SessionIdType *)ps_realloc(sessionIds_, numSessionsAllocated_);
assert(numSessions_ < numSessionsAllocated_);
}
void User::insertSession(SessionIdType newSessionId)
{
growSessionArrayIfNecessary();
assert(numSessionsAllocated_ > numSessions_);
SessionIdType *insertPos = lower_bound(sessionIds_, sessionIds_ + numSessions_, newSessionId);
const size_t moveStartIdx = sessionIds_ + numSessions_ - insertPos;
for (size_t i = numSessions_ - 1; i >= moveStartIdx; --i)
sessionIds_[i + 1] = sessionIds_[i];
sessionIds_[moveStartIdx] = newSessionId;
numSessions_ += 1;
}
bool User::removeSession(SessionIdType sessionIdToRemove)
{
SessionIdType *removePos = lower_bound(sessionIds_, sessionIds_ + numSessions_, sessionIdToRemove);
const size_t removeIdx = sessionIds_ + numSessions_ - removePos;
if (sessionIds_[removeIdx] != sessionIdToRemove)
return false;
for (size_t i = removeIdx; i < numSessions_ - 1; ++i)
sessionIds_[i] = sessionIds_[i + 1];
numSessions_ -= 1;
return true;
}
// --------------------------------------------------------------------------------------------------------------------
User *UserStorage::getUserInfo(const String &userName)
{
// index 0 is the unassigned user
for (size_t i = 1; i < numUsers_; ++i)
if (users_[i].name() == userName)
return &users_[i];
return nullptr;
}
User *UserStorage::getUnassignedUser()
{
return &users_[0];
}
User *UserStorage::addNewUser(const String &userName)
{
if (numUsers_ >= MAX_USERS)
return nullptr;
auto userIdx = numUsers_;
numUsers_++;
assert(numUsers_ < MAX_USERS);
users_[userIdx].init(userName);
}
bool UserStorage::deleteUser(const String &userName)
{
User *userPtr = getUserInfo(userName);
if (userPtr == nullptr)
return false;
size_t userIdx = userPtr - users_;
userPtr->remove();
assert(numUsers_ > 0);
if (userIdx != numUsers_ - 1)
users_[userIdx] = users_[numUsers_ - 1];
}
void UserStorage::fillFromFileSystem()
{
for (size_t i = 0; i < numUsers_; ++i)
users_[i].freeResources();
numUsers_ = 1;
users_[0].load("_unassigned");
portablefs::Dir d(userDir);
while (d.next())
{
if (d.isFile() && d.fileName()[0] != '_')
{
users_[numUsers_].load(d.fileName());
++numUsers_;
}
}
}