#include "UserDB.h" #include "FilesystemAbstraction.h" const String userDir = "/u/"; template ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T &val) { ForwardIterator it; int count, step; count = last - first; while (count > 0) { it = first; step = count / 2; 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 &stringId) { stringId_ = stringId; assert(stringId_.length() > 0); const auto fileName = userFileName(stringId_); 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(stringId, 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 &stringId, size_t sessionAllocateSize) { if (sessionIds_ != nullptr) { free(sessionIds_); sessionIds_ = nullptr; } stringId_ = stringId; 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(stringId_).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(stringId_).c_str()); freeResources(); stringId_ = ""; 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 &stringId) { // index 0 is the unassigned user for (size_t i = 1; i < numUsers_; ++i) if (users_[i].stringId() == stringId) return &users_[i]; return nullptr; } User *UserStorage::getUnassignedUser() { return &users_[0]; } User *UserStorage::addNewUser(const String &stringId) { if (numUsers_ >= MAX_USERS) return nullptr; if(stringId.length() >= USER_STRING_ID_MAX_LEN) return nullptr; auto userIdx = numUsers_; numUsers_++; assert(numUsers_ < MAX_USERS); users_[userIdx].init(stringId); } bool UserStorage::deleteUser(const String &stringId) { User *userPtr = getUserInfo(stringId); 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_; } } }