From 6778e3b87cc9a6f5d195a2c80e5b499e3d94558b Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 27 Jan 2019 02:09:50 +0100 Subject: Add binds to emoji parsing, refactor --- src/Cache.cpp | 94 +--------------------------- src/IncomingMessage.cpp | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ src/MessageComposer.cpp | 160 ------------------------------------------------ src/OutgoingMessage.cpp | 136 ++++++++++++++++++++++++++++++++++++++++ src/Room.cpp | 4 +- src/Storage.cpp | 105 +++++++++++++++++++++++++++++++ 6 files changed, 404 insertions(+), 255 deletions(-) create mode 100644 src/IncomingMessage.cpp delete mode 100644 src/MessageComposer.cpp create mode 100644 src/OutgoingMessage.cpp create mode 100644 src/Storage.cpp (limited to 'src') diff --git a/src/Cache.cpp b/src/Cache.cpp index 84bee97..0d5dd05 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2,6 +2,7 @@ #include "../include/env.hpp" #include "../include/dchat/FileUtil.hpp" #include "../include/dchat/Gif.hpp" +#include "../include/dchat/Storage.hpp" #include #include #include @@ -11,7 +12,6 @@ #include #if OS_FAMILY == OS_FAMILY_POSIX -#include #define toNativeString(str) str #else #include @@ -32,98 +32,6 @@ using namespace TinyProcessLib; namespace dchat { const i64 CONTENT_NOT_VISIBLE_AGE_MS = 30000; // Delete content from cache after a specified amount of time if the content is not visible on the screen - - static boost::filesystem::path getHomeDir() - { - #if OS_FAMILY == OS_FAMILY_POSIX - const char *homeDir = getenv("HOME"); - if(!homeDir) - { - passwd *pw = getpwuid(getuid()); - homeDir = pw->pw_dir; - } - return boost::filesystem::path(homeDir); - #elif OS_FAMILY == OS_FAMILY_WINDOWS - BOOL ret; - HANDLE hToken; - std::wstring homeDir; - DWORD homeDirLen = MAX_PATH; - homeDir.resize(homeDirLen); - - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) - throw std::runtime_error("Failed to open process token"); - - if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen)) - { - CloseHandle(hToken); - throw std::runtime_error("Failed to get home directory"); - } - - CloseHandle(hToken); - homeDir.resize(wcslen(homeDir.c_str())); - return boost::filesystem::path(homeDir); - #endif - } - - boost::filesystem::path Cache::getDchatDir() - { - boost::filesystem::path dchatHomeDir = getHomeDir() / ".local" / "share" / "dchat"; - boost::filesystem::create_directories(dchatHomeDir); - return dchatHomeDir; - } - - boost::filesystem::path Cache::getImagesDir() - { - boost::filesystem::path imagesDir = getDchatDir() / "images"; - boost::filesystem::create_directories(imagesDir); - return imagesDir; - } - - void Cache::loadBindsFromFile(LoadBindsCallbackFunc callbackFunc) - { - assert(callbackFunc); - StringView fileContent; - try - { - fileContent = getFileContent(getDchatDir() / "binds"); - sibs::SafeDeserializer deserializer((const u8*)fileContent.data, fileContent.size); - - while(!deserializer.empty()) - { - u8 keySize = deserializer.extract(); - string key; - key.resize(keySize); - deserializer.extract((u8*)&key[0], keySize); - - u8 valueSize = deserializer.extract(); - string value; - value.resize(valueSize); - deserializer.extract((u8*)&value[0], valueSize); - - callbackFunc(key, value); - } - } - catch(FileException &e) - { - fprintf(stderr, "Failed to read binds from file, reason: %s\n", e.what()); - } - - delete[] fileContent.data; - } - - void Cache::replaceBindsInFile(const unordered_map &binds) - { - sibs::SafeSerializer serializer; - for(auto &it : binds) - { - serializer.add((u8)it.first.size()); - serializer.add((const u8*)it.first.data(), it.first.size()); - - serializer.add((u8)it.second.size()); - serializer.add((const u8*)it.second.data(), it.second.size()); - } - fileReplace(getDchatDir() / "binds", StringView((const char*)serializer.getBuffer().data(), serializer.getBuffer().size())); - } static bool downscaleImage(const boost::filesystem::path &filepath, void *data, const int size, const int newWidth, const int newHeight) { diff --git a/src/IncomingMessage.cpp b/src/IncomingMessage.cpp new file mode 100644 index 0000000..e003a22 --- /dev/null +++ b/src/IncomingMessage.cpp @@ -0,0 +1,160 @@ +#include "../include/dchat/IncomingMessage.hpp" +#include +#include + +namespace dchat +{ + enum class Token + { + NONE, + END_OF_FILE, + + TEXT, + TYPE + }; + + struct Tokenizer + { + Tokenizer(const char *_text, usize _size) + { + assert(_text); + text = _text; + size = _size; + index = 0; + identifierRange = { 0, 0 }; + typeRange = { 0, 0 }; + typeDataRange = { 0, 0 }; + } + + enum class EnclosedType + { + TEXT, + DATA + }; + + EnclosedType parseEnclosedData(char endSymbol) + { + bool foundEndOfType = false; + + while(index < size) + { + char c = getChar(); + ++index; + if(c == endSymbol) + { + foundEndOfType = true; + break; + } + } + + if(!foundEndOfType) + return EnclosedType::TEXT; + + return EnclosedType::DATA; + } + + Token next() + { + if(index >= size) + return Token::END_OF_FILE; + + char c = getChar(); + if(c == '[') + { + usize start = index; + ++index; + if(parseEnclosedData(']') == EnclosedType::TEXT) + { + identifierRange.start = start; + identifierRange.end = index; + return Token::TEXT; + } + + if(index == size || getChar() != '(') + { + identifierRange.start = start; + identifierRange.end = index; + return Token::TEXT; + } + + typeRange.start = start + 1; + typeRange.end = index - 1; + typeDataRange.start = index + 1; + + ++index; + switch(parseEnclosedData(')')) + { + case EnclosedType::TEXT: + { + identifierRange.start = start; + identifierRange.end = index; + return Token::TEXT; + } + case EnclosedType::DATA: + { + typeDataRange.end = index - 1; + return Token::TYPE; + } + } + } + else + { + identifierRange.start = index; + ++index; + while(index < size) + { + c = getChar(); + if(c == '[') + break; + ++index; + } + identifierRange.end = index; + return Token::TEXT; + } + + assert(false); + return Token::NONE; + } + + char getChar() const + { + assert(index < size); + return text[index]; + } + + const char *text; + usize size; + usize index; + + Range identifierRange; + Range typeRange; + Range typeDataRange; + }; + + void parseIncomingMessage(const char *text, usize size, std::function callbackFunc) + { + Tokenizer tokenizer(text, size); + Token token = tokenizer.next(); + while(token != Token::END_OF_FILE) + { + if(token == Token::TEXT) + { + callbackFunc(IncomingMessagePart { IncomingMessagePart::Type::TEXT, tokenizer.identifierRange }); + token = tokenizer.next(); + } + else if(token == Token::TYPE) + { + if(tokenizer.typeRange.length() == 5 && memcmp(text + tokenizer.typeRange.start, "emoji", 5) == 0) + { + callbackFunc(IncomingMessagePart { IncomingMessagePart::Type::EMOJI, tokenizer.typeDataRange }); + } + else + { + Range typeToTextRange = { tokenizer.typeRange.start - 1, tokenizer.typeDataRange.end + 1 }; + callbackFunc(IncomingMessagePart{ IncomingMessagePart::Type::TEXT, typeToTextRange }); + } + token = tokenizer.next(); + } + } + } +} \ No newline at end of file diff --git a/src/MessageComposer.cpp b/src/MessageComposer.cpp deleted file mode 100644 index 71fa62e..0000000 --- a/src/MessageComposer.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "../include/dchat/MessageComposer.hpp" -#include -#include - -namespace dchat -{ - enum class Token - { - NONE, - END_OF_FILE, - - TEXT, - TYPE - }; - - struct Tokenizer - { - Tokenizer(const char *_text, usize _size) - { - assert(_text); - text = _text; - size = _size; - index = 0; - identifierRange = { 0, 0 }; - typeRange = { 0, 0 }; - typeDataRange = { 0, 0 }; - } - - enum class EnclosedType - { - TEXT, - DATA - }; - - EnclosedType parseEnclosedData(char endSymbol) - { - bool foundEndOfType = false; - - while(index < size) - { - char c = getChar(); - ++index; - if(c == endSymbol) - { - foundEndOfType = true; - break; - } - } - - if(!foundEndOfType) - return EnclosedType::TEXT; - - return EnclosedType::DATA; - } - - Token next() - { - if(index >= size) - return Token::END_OF_FILE; - - char c = getChar(); - if(c == '[') - { - usize start = index; - ++index; - if(parseEnclosedData(']') == EnclosedType::TEXT) - { - identifierRange.start = start; - identifierRange.end = index; - return Token::TEXT; - } - - if(index == size || getChar() != '(') - { - identifierRange.start = start; - identifierRange.end = index; - return Token::TEXT; - } - - typeRange.start = start + 1; - typeRange.end = index - 1; - typeDataRange.start = index + 1; - - ++index; - switch(parseEnclosedData(')')) - { - case EnclosedType::TEXT: - { - identifierRange.start = start; - identifierRange.end = index; - return Token::TEXT; - } - case EnclosedType::DATA: - { - typeDataRange.end = index - 1; - return Token::TYPE; - } - } - } - else - { - identifierRange.start = index; - ++index; - while(index < size) - { - c = getChar(); - if(c == '[') - break; - ++index; - } - identifierRange.end = index; - return Token::TEXT; - } - - assert(false); - return Token::NONE; - } - - char getChar() const - { - assert(index < size); - return text[index]; - } - - const char *text; - usize size; - usize index; - - Range identifierRange; - Range typeRange; - Range typeDataRange; - }; - - void compose(const char *text, usize size, std::function callbackFunc) - { - Tokenizer tokenizer(text, size); - Token token = tokenizer.next(); - while(token != Token::END_OF_FILE) - { - if(token == Token::TEXT) - { - callbackFunc(MessagePart { MessagePart::Type::TEXT, tokenizer.identifierRange }); - token = tokenizer.next(); - } - else if(token == Token::TYPE) - { - if(tokenizer.typeRange.length() == 5 && memcmp(text + tokenizer.typeRange.start, "emoji", 5) == 0) - { - callbackFunc(MessagePart { MessagePart::Type::EMOJI, tokenizer.typeDataRange }); - } - else - { - Range typeToTextRange = { tokenizer.typeRange.start - 1, tokenizer.typeDataRange.end + 1 }; - callbackFunc(MessagePart{ MessagePart::Type::TEXT, typeToTextRange }); - } - token = tokenizer.next(); - } - } - } -} \ No newline at end of file diff --git a/src/OutgoingMessage.cpp b/src/OutgoingMessage.cpp new file mode 100644 index 0000000..e774940 --- /dev/null +++ b/src/OutgoingMessage.cpp @@ -0,0 +1,136 @@ +#include "../include/dchat/OutgoingMessage.hpp" +#include + +namespace dchat +{ + namespace OutgoingMessage + { + enum class Token + { + NONE, + END_OF_FILE, + + TEXT, + BIND + }; + + struct Tokenizer + { + Tokenizer(const char *_text, usize _size, const EmojiBindMap *_emojiBindMap) + { + assert(_text); + assert(_emojiBindMap); + text = _text; + size = _size; + index = 0; + emoji = nullptr; + emojiBindMap = _emojiBindMap; + textRange = { 0, 0 }; + } + + // returns -1 if character can't be found in the string + usize find(const char *str, usize size, char c) const + { + for(usize i = 0; i < size; ++i) + { + if(str[i] == c) + return i; + } + return -1; + } + + // Return nullptr if bind is not found + const std::string* getBindEmoji(const char *text, usize size) const + { + // TODO: unecessary conversion to string, convert emojibindmap to const char* and uses custom hasher + auto it = emojiBindMap->find(std::string(text, size)); + if(it != emojiBindMap->end()) + return &it->second; + return nullptr; + } + + Token next() + { + if(index >= size) + return Token::END_OF_FILE; + + char c = getChar(); + usize start = index; + ++index; + if(c == ':') + { + while(index < size) + { + c = getChar(); + ++index; + if(c == ':') + { + usize end = index - 1; + const std::string *_emoji = getBindEmoji(text + start + 1, end - (start + 1)); + if(_emoji) + { + emoji = _emoji; + return Token::BIND; + } + break; + } + } + + textRange.start = start; + textRange.end = index; + return Token::TEXT; + } + else + { + while(index < size) + { + c = getChar(); + if(c == ':') + break; + ++index; + } + + textRange.start = start; + textRange.end = index; + return Token::TEXT; + } + + assert(false); + return Token::NONE; + } + + char getChar() const + { + assert(index < size); + return text[index]; + } + + const char *text; + usize size; + usize index; + const std::string *emoji; + const EmojiBindMap *emojiBindMap; + + Range textRange; + }; + } + + void parseOutgoingMessage(const char *text, usize size, const EmojiBindMap &emojiBindMap, std::function callbackFunc) + { + OutgoingMessage::Tokenizer tokenizer(text, size, &emojiBindMap); + OutgoingMessage::Token token = tokenizer.next(); + while(token != OutgoingMessage::Token::END_OF_FILE) + { + if(token == OutgoingMessage::Token::TEXT) + { + callbackFunc(OutgoingMessagePart { OutgoingMessagePart::Type::TEXT, tokenizer.textRange }); + token = tokenizer.next(); + } + else if(token == OutgoingMessage::Token::BIND) + { + callbackFunc(OutgoingMessagePart { OutgoingMessagePart::Type::EMOJI, tokenizer.emoji }); + token = tokenizer.next(); + } + } + } +} \ No newline at end of file diff --git a/src/Room.cpp b/src/Room.cpp index c7135da..b526833 100644 --- a/src/Room.cpp +++ b/src/Room.cpp @@ -1,5 +1,5 @@ #include "../include/dchat/Room.hpp" -#include "../include/dchat/Cache.hpp" +#include "../include/dchat/Storage.hpp" #include "../include/dchat/FileUtil.hpp" #include "../include/dchat/RoomDataType.hpp" #include @@ -203,7 +203,7 @@ namespace dchat databaseCallbackFuncs.createNodeCallbackFunc = std::bind(&Rooms::createNodeCallbackFunc, this, std::placeholders::_1); databaseCallbackFuncs.addNodeCallbackFunc = std::bind(&Rooms::addNodeCallbackFunc, this, std::placeholders::_1); databaseCallbackFuncs.addUserCallbackFunc = std::bind(&Rooms::addUserCallbackFunc, this, std::placeholders::_1); - database = odhtdb::Database::connect(address, port, Cache::getDchatDir(), databaseCallbackFuncs).get(); + database = odhtdb::Database::connect(address, port, getDchatDir(), databaseCallbackFuncs).get(); } void Rooms::createNodeCallbackFunc(const odhtdb::DatabaseCreateNodeRequest &request) diff --git a/src/Storage.cpp b/src/Storage.cpp new file mode 100644 index 0000000..a0cc891 --- /dev/null +++ b/src/Storage.cpp @@ -0,0 +1,105 @@ +#include "../include/dchat/Storage.hpp" +#include "../include/dchat/env.hpp" +#include "../include/dchat/FileUtil.hpp" +#include +#include +#include + +#if OS_FAMILY == OS_FAMILY_POSIX +#include +#endif + +namespace dchat +{ + boost::filesystem::path getHomeDir() + { + #if OS_FAMILY == OS_FAMILY_POSIX + const char *homeDir = getenv("HOME"); + if(!homeDir) + { + passwd *pw = getpwuid(getuid()); + homeDir = pw->pw_dir; + } + return boost::filesystem::path(homeDir); + #elif OS_FAMILY == OS_FAMILY_WINDOWS + BOOL ret; + HANDLE hToken; + std::wstring homeDir; + DWORD homeDirLen = MAX_PATH; + homeDir.resize(homeDirLen); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) + throw std::runtime_error("Failed to open process token"); + + if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen)) + { + CloseHandle(hToken); + throw std::runtime_error("Failed to get home directory"); + } + + CloseHandle(hToken); + homeDir.resize(wcslen(homeDir.c_str())); + return boost::filesystem::path(homeDir); + #endif + } + + boost::filesystem::path getDchatDir() + { + boost::filesystem::path dchatHomeDir = getHomeDir() / ".local" / "share" / "dchat"; + boost::filesystem::create_directories(dchatHomeDir); + return dchatHomeDir; + } + + boost::filesystem::path getImagesDir() + { + boost::filesystem::path imagesDir = getDchatDir() / "images"; + boost::filesystem::create_directories(imagesDir); + return imagesDir; + } + + void loadBindsFromFile(LoadBindsCallbackFunc callbackFunc) + { + assert(callbackFunc); + StringView fileContent; + try + { + fileContent = getFileContent(getDchatDir() / "binds"); + sibs::SafeDeserializer deserializer((const u8*)fileContent.data, fileContent.size); + + while(!deserializer.empty()) + { + u8 keySize = deserializer.extract(); + std::string key; + key.resize(keySize); + deserializer.extract((u8*)&key[0], keySize); + + u8 valueSize = deserializer.extract(); + std::string value; + value.resize(valueSize); + deserializer.extract((u8*)&value[0], valueSize); + + callbackFunc(key, value); + } + } + catch(FileException &e) + { + fprintf(stderr, "Failed to read binds from file, reason: %s\n", e.what()); + } + + delete[] fileContent.data; + } + + void replaceBindsInFile(const std::unordered_map &binds) + { + sibs::SafeSerializer serializer; + for(auto &it : binds) + { + serializer.add((u8)it.first.size()); + serializer.add((const u8*)it.first.data(), it.first.size()); + + serializer.add((u8)it.second.size()); + serializer.add((const u8*)it.second.data(), it.second.size()); + } + fileReplace(getDchatDir() / "binds", StringView((const char*)serializer.getBuffer().data(), serializer.getBuffer().size())); + } +} \ No newline at end of file -- cgit v1.2.3