From 48540940b6c28bb4378abfeb500ec45a625b37b6 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 15 May 2012 10:38:20 +0000 Subject: initial commit git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Dbx_tree/BTree.h | 1358 ++++++++++ plugins/Dbx_tree/BlockManager.cpp | 965 +++++++ plugins/Dbx_tree/BlockManager.h | 401 +++ plugins/Dbx_tree/Compatibility.cpp | 866 +++++++ plugins/Dbx_tree/Compatibility.h | 65 + plugins/Dbx_tree/DataBase.cpp | 374 +++ plugins/Dbx_tree/DataBase.h | 157 ++ plugins/Dbx_tree/DatabaseLink.cpp | 178 ++ plugins/Dbx_tree/DatabaseLink.h | 29 + plugins/Dbx_tree/DirectAccess.cpp | 118 + plugins/Dbx_tree/DirectAccess.h | 43 + plugins/Dbx_tree/EncryptionManager.cpp | 241 ++ plugins/Dbx_tree/EncryptionManager.h | 107 + plugins/Dbx_tree/Entities.cpp | 1032 ++++++++ plugins/Dbx_tree/Entities.h | 299 +++ plugins/Dbx_tree/Events.cpp | 986 ++++++++ plugins/Dbx_tree/Events.h | 263 ++ plugins/Dbx_tree/FileAccess.cpp | 299 +++ plugins/Dbx_tree/FileAccess.h | 191 ++ plugins/Dbx_tree/FileBTree.h | 102 + plugins/Dbx_tree/Filestructure.txt | Bin 0 -> 25894 bytes plugins/Dbx_tree/Hash.cpp | 183 ++ plugins/Dbx_tree/Hash.h | 29 + plugins/Dbx_tree/Interface.h | 58 + plugins/Dbx_tree/IterationHeap.h | 196 ++ plugins/Dbx_tree/Logger.cpp | 124 + plugins/Dbx_tree/Logger.h | 76 + plugins/Dbx_tree/MREWSync.cpp | 330 +++ plugins/Dbx_tree/MREWSync.h | 55 + plugins/Dbx_tree/MappedMemory.cpp | 167 ++ plugins/Dbx_tree/MappedMemory.h | 47 + plugins/Dbx_tree/SHA256.cpp | 301 +++ plugins/Dbx_tree/SHA256.h | 49 + plugins/Dbx_tree/Services.cpp | 402 +++ plugins/Dbx_tree/Services.h | 69 + plugins/Dbx_tree/Settings.cpp | 1479 +++++++++++ plugins/Dbx_tree/Settings.h | 235 ++ plugins/Dbx_tree/TLS.h | 161 ++ plugins/Dbx_tree/Thread.cpp | 114 + plugins/Dbx_tree/Thread.h | 77 + plugins/Dbx_tree/dbConfig.rc | 113 + plugins/Dbx_tree/dbConfig_rc.h | 20 + plugins/Dbx_tree/dbVersion.rc | 105 + plugins/Dbx_tree/dbVersion_rc.h | 14 + plugins/Dbx_tree/dbx_tree.vcproj | 1169 +++++++++ plugins/Dbx_tree/dbx_tree_10.sln | 37 + plugins/Dbx_tree/dbx_tree_10.vcxproj | 699 ++++++ plugins/Dbx_tree/dbx_tree_10.vcxproj.filters | 204 ++ plugins/Dbx_tree/encryption/ARC4.cpp | 129 + plugins/Dbx_tree/encryption/ARC4.h | 65 + plugins/Dbx_tree/encryption/ARC4.vcproj | 376 +++ plugins/Dbx_tree/encryption/ARC4.vcxproj | 476 ++++ plugins/Dbx_tree/encryption/ARC4.vcxproj.filters | 30 + plugins/Dbx_tree/encryption/CAST128.cpp | 260 ++ plugins/Dbx_tree/encryption/CAST128.h | 61 + plugins/Dbx_tree/encryption/CAST128.inc | 302 +++ plugins/Dbx_tree/encryption/Cast128.vcproj | 380 +++ plugins/Dbx_tree/encryption/Cast128.vcxproj | 479 ++++ .../Dbx_tree/encryption/Cast128.vcxproj.filters | 35 + plugins/Dbx_tree/encryption/Cipher.h | 180 ++ plugins/Dbx_tree/encryption/HC256.cpp | 243 ++ plugins/Dbx_tree/encryption/HC256.h | 66 + plugins/Dbx_tree/encryption/HC256.vcproj | 376 +++ plugins/Dbx_tree/encryption/HC256.vcxproj | 476 ++++ plugins/Dbx_tree/encryption/HC256.vcxproj.filters | 30 + plugins/Dbx_tree/init.cpp | 83 + plugins/Dbx_tree/intrinsics.h | 407 +++ plugins/Dbx_tree/inttypes.h | 305 +++ plugins/Dbx_tree/lockfree_hashmap.h | 749 ++++++ plugins/Dbx_tree/lockfree_hashmultimap.h | 750 ++++++ plugins/Dbx_tree/savestrings_gcc.h | 127 + plugins/Dbx_tree/sigslot.h | 2639 ++++++++++++++++++++ plugins/Dbx_tree/stdint.h | 247 ++ 73 files changed, 23858 insertions(+) create mode 100644 plugins/Dbx_tree/BTree.h create mode 100644 plugins/Dbx_tree/BlockManager.cpp create mode 100644 plugins/Dbx_tree/BlockManager.h create mode 100644 plugins/Dbx_tree/Compatibility.cpp create mode 100644 plugins/Dbx_tree/Compatibility.h create mode 100644 plugins/Dbx_tree/DataBase.cpp create mode 100644 plugins/Dbx_tree/DataBase.h create mode 100644 plugins/Dbx_tree/DatabaseLink.cpp create mode 100644 plugins/Dbx_tree/DatabaseLink.h create mode 100644 plugins/Dbx_tree/DirectAccess.cpp create mode 100644 plugins/Dbx_tree/DirectAccess.h create mode 100644 plugins/Dbx_tree/EncryptionManager.cpp create mode 100644 plugins/Dbx_tree/EncryptionManager.h create mode 100644 plugins/Dbx_tree/Entities.cpp create mode 100644 plugins/Dbx_tree/Entities.h create mode 100644 plugins/Dbx_tree/Events.cpp create mode 100644 plugins/Dbx_tree/Events.h create mode 100644 plugins/Dbx_tree/FileAccess.cpp create mode 100644 plugins/Dbx_tree/FileAccess.h create mode 100644 plugins/Dbx_tree/FileBTree.h create mode 100644 plugins/Dbx_tree/Filestructure.txt create mode 100644 plugins/Dbx_tree/Hash.cpp create mode 100644 plugins/Dbx_tree/Hash.h create mode 100644 plugins/Dbx_tree/Interface.h create mode 100644 plugins/Dbx_tree/IterationHeap.h create mode 100644 plugins/Dbx_tree/Logger.cpp create mode 100644 plugins/Dbx_tree/Logger.h create mode 100644 plugins/Dbx_tree/MREWSync.cpp create mode 100644 plugins/Dbx_tree/MREWSync.h create mode 100644 plugins/Dbx_tree/MappedMemory.cpp create mode 100644 plugins/Dbx_tree/MappedMemory.h create mode 100644 plugins/Dbx_tree/SHA256.cpp create mode 100644 plugins/Dbx_tree/SHA256.h create mode 100644 plugins/Dbx_tree/Services.cpp create mode 100644 plugins/Dbx_tree/Services.h create mode 100644 plugins/Dbx_tree/Settings.cpp create mode 100644 plugins/Dbx_tree/Settings.h create mode 100644 plugins/Dbx_tree/TLS.h create mode 100644 plugins/Dbx_tree/Thread.cpp create mode 100644 plugins/Dbx_tree/Thread.h create mode 100644 plugins/Dbx_tree/dbConfig.rc create mode 100644 plugins/Dbx_tree/dbConfig_rc.h create mode 100644 plugins/Dbx_tree/dbVersion.rc create mode 100644 plugins/Dbx_tree/dbVersion_rc.h create mode 100644 plugins/Dbx_tree/dbx_tree.vcproj create mode 100644 plugins/Dbx_tree/dbx_tree_10.sln create mode 100644 plugins/Dbx_tree/dbx_tree_10.vcxproj create mode 100644 plugins/Dbx_tree/dbx_tree_10.vcxproj.filters create mode 100644 plugins/Dbx_tree/encryption/ARC4.cpp create mode 100644 plugins/Dbx_tree/encryption/ARC4.h create mode 100644 plugins/Dbx_tree/encryption/ARC4.vcproj create mode 100644 plugins/Dbx_tree/encryption/ARC4.vcxproj create mode 100644 plugins/Dbx_tree/encryption/ARC4.vcxproj.filters create mode 100644 plugins/Dbx_tree/encryption/CAST128.cpp create mode 100644 plugins/Dbx_tree/encryption/CAST128.h create mode 100644 plugins/Dbx_tree/encryption/CAST128.inc create mode 100644 plugins/Dbx_tree/encryption/Cast128.vcproj create mode 100644 plugins/Dbx_tree/encryption/Cast128.vcxproj create mode 100644 plugins/Dbx_tree/encryption/Cast128.vcxproj.filters create mode 100644 plugins/Dbx_tree/encryption/Cipher.h create mode 100644 plugins/Dbx_tree/encryption/HC256.cpp create mode 100644 plugins/Dbx_tree/encryption/HC256.h create mode 100644 plugins/Dbx_tree/encryption/HC256.vcproj create mode 100644 plugins/Dbx_tree/encryption/HC256.vcxproj create mode 100644 plugins/Dbx_tree/encryption/HC256.vcxproj.filters create mode 100644 plugins/Dbx_tree/init.cpp create mode 100644 plugins/Dbx_tree/intrinsics.h create mode 100644 plugins/Dbx_tree/inttypes.h create mode 100644 plugins/Dbx_tree/lockfree_hashmap.h create mode 100644 plugins/Dbx_tree/lockfree_hashmultimap.h create mode 100644 plugins/Dbx_tree/savestrings_gcc.h create mode 100644 plugins/Dbx_tree/sigslot.h create mode 100644 plugins/Dbx_tree/stdint.h (limited to 'plugins/Dbx_tree') diff --git a/plugins/Dbx_tree/BTree.h b/plugins/Dbx_tree/BTree.h new file mode 100644 index 0000000000..20c2abfd71 --- /dev/null +++ b/plugins/Dbx_tree/BTree.h @@ -0,0 +1,1358 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#pragma once + +#include +#include "lockfree_hashmultimap.h" +#include "sigslot.h" +#ifdef _MSC_VER +#include "stdint.h" +#else +#include +#endif + +#include "Logger.h" + +#ifndef _MSC_VER +#ifdef offsetof +#undef offsetof +#endif +#define offsetof(TYPE, MEMBER) \ + ( (reinterpret_cast \ + (&reinterpret_cast \ + (static_cast (0)->MEMBER)))) +#endif + +template +class CBTree +{ +public: + typedef uint32_t TNodeRef; /// 32bit indices (not storing pointers) + typedef sigslot::signal2< void *, TNodeRef > TOnRootChanged; + + #pragma pack(push, 1) // push current alignment to stack, set alignment to 1 byte boundary + + typedef struct TNode { + uint16_t Info; /// Node information (IsLeaf and stored KeyCount) + uint16_t Signature; /// signature + TNodeRef Parent; /// Handle to the parent node + TKey Key[SizeParam * 2 - 1]; /// array with Keys + TNodeRef Child[SizeParam * 2]; /// array with child node handles + } TNode; + + #pragma pack(pop) + + class iterator + { + public: + iterator(); + iterator(CBTree* Tree, TNodeRef Node, uint16_t Index); + iterator(const iterator& Other); + ~iterator(); + + CBTree * Tree(); + + /** + \brief Keeps track of changes in the tree and refresh the iterator + **/ + void setManaged(); + bool wasDeleted(); + + operator bool() const; + bool operator !() const; + + const TKey & operator *(); + const TKey * operator->(); + + + bool operator == (iterator & Other); + bool operator < (iterator & Other); + bool operator > (iterator & Other); + + iterator& operator =(const iterator& Other); + + iterator& operator ++(); //pre ++i + iterator& operator --(); //pre --i + iterator operator ++(int); //post i++ + iterator operator --(int); //post i-- + + + protected: + friend class CBTree; + + TNodeRef m_Node; + uint16_t m_Index; + CBTree* m_Tree; + + bool m_Managed; + bool m_LoadedKey; + TKey m_ManagedKey; + bool m_ManagedDeleted; + + void Backup(); + void Dec(); + void Inc(); + void RemoveManaged(TNodeRef FromNode); + void InsertManaged(); + }; + + + CBTree(TNodeRef RootNode = 0); + virtual ~CBTree(); + + iterator Insert(const TKey & Key); + iterator Find(const TKey & Key); + iterator LowerBound(const TKey & Key); + iterator UpperBound(const TKey & Key); + bool Delete(const TKey & Key); + + typedef sigslot::signal3 TDeleteCallback; + void DeleteTree(TDeleteCallback * CallBack, uint32_t Param); + + TNodeRef getRoot(); + void setRoot(TNodeRef NewRoot); + + TOnRootChanged & sigRootChanged() {return m_sigRootChanged;}; + + +protected: + static const uint16_t cIsLeafMask = 0x8000; + static const uint16_t cKeyCountMask = 0x7FFF; + static const uint16_t cFullNode = SizeParam * 2 - 1; + static const uint16_t cEmptyNode = SizeParam - 1; + + typedef lockfree::hash_multimap TManagedMap; + + TNodeRef m_Root; + TOnRootChanged m_sigRootChanged; + TManagedMap m_ManagedIterators; + + bool m_DestroyTree; + + uint32_t m_AllocCount; + uint32_t m_Count; + uint32_t m_FreeIndex; + TNode * m_Alloc; + + virtual void PrepareInsertOperation(); + virtual TNode * CreateNewNode(TNodeRef & NodeRef); + virtual void DeleteNode(TNodeRef Node); + virtual TNode * Read(TNodeRef Node); + virtual void Write(TNodeRef Node); + + void DestroyTree(); + + +private: + friend class iterator; + + bool InNodeFind(const TNode * Node, const TKey & Key, uint16_t & GreaterEqual); + void SplitNode(TNodeRef Node, TNode * NodeData, TNodeRef & Left, TNodeRef & Right, TKey & UpKey, TNodeRef ParentNode, uint16_t ParentIndex); + TNodeRef MergeNodes(TNodeRef Left, TNode * LeftData, TNodeRef Right, TNode * RightData, const TKey & DownKey, TNodeRef ParentNode, uint16_t ParentIndex); + void KeyInsert(TNodeRef Node, TNode * NodeData, uint16_t Where); + void KeyDelete(TNodeRef Node, TNode * NodeData, uint16_t Where); + void KeyMove(TNodeRef Source, uint16_t SourceIndex, const TNode * SourceData, TNodeRef Dest, uint16_t DestIndex, TNode * DestData); +}; + + + + + + +template +CBTree::CBTree(TNodeRef RootNode = NULL) +: m_sigRootChanged(), + m_ManagedIterators() +{ + m_Root = RootNode; + m_DestroyTree = true; + + m_AllocCount = 0; + m_Count = 0; + m_FreeIndex = 0; + m_Alloc = NULL; +} + +template +CBTree::~CBTree() +{ + typename TManagedMap::iterator i = m_ManagedIterators.begin(); + while (i != m_ManagedIterators.end()) + { + i->second->m_Tree = NULL; + i++; + } + + if (m_DestroyTree) + DestroyTree(); +} + +template +inline bool CBTree::InNodeFind(const TNode * Node, const TKey & Key, uint16_t & GreaterEqual) +{ + uint16_t l = 0; + uint16_t r = (Node->Info & cKeyCountMask); + bool res = false; + GreaterEqual = 0; + while ((l < r) && !res) + { + GreaterEqual = (l + r) >> 1; + if (Node->Key[GreaterEqual] < Key) + { + GreaterEqual++; + l = GreaterEqual; + } else if (Node->Key[GreaterEqual] == Key) + { + //r = -1; + res = true; + } else { + r = GreaterEqual; + } + } + + return res; +} + + +template +inline void CBTree::SplitNode(TNodeRef Node, TNode * NodeData, TNodeRef & Left, TNodeRef & Right, TKey & UpKey, TNodeRef ParentNode, uint16_t ParentIndex) +{ + const uint16_t upindex = SizeParam - 1; + TNode *ldata, *rdata; + Left = Node; + ldata = NodeData; + rdata = CreateNewNode(Right); + + typename TManagedMap::iterator it = m_ManagedIterators.find(Node); + while ((it != m_ManagedIterators.end()) && (it->first == Node)) + { + if (it->second->m_Index == upindex) + { + it->second->m_Index = ParentIndex; + it->second->m_Node = ParentNode; + m_ManagedIterators.insert(std::make_pair(ParentNode, it->second)); + it = m_ManagedIterators.erase(it); + } else if (it->second->m_Index > upindex) + { + it->second->m_Index = it->second->m_Index - upindex - 1; + it->second->m_Node = Right; + m_ManagedIterators.insert(std::make_pair(Right, it->second)); + it = m_ManagedIterators.erase(it); + } else { + ++it; + } + } + + UpKey = NodeData->Key[upindex]; + + memcpy(&(rdata->Key[0]), &(NodeData->Key[upindex+1]), sizeof(TKey) * (cFullNode - upindex)); + if ((NodeData->Info & cIsLeafMask) == 0) + { + memcpy(&(rdata->Child[0]), &(NodeData->Child[upindex+1]), sizeof(TNodeRef) * (cFullNode - upindex + 1)); + + for (int i = 0; i <= upindex; i++) + { + TNode * tmp = Read(rdata->Child[i]); + tmp->Parent = Right; + Write(rdata->Child[i]); + } + } + + rdata->Info = (NodeData->Info & cIsLeafMask) | upindex; + NodeData->Info = rdata->Info; + rdata->Parent = NodeData->Parent; + + Write(Left); + Write(Right); +} + +template +inline typename CBTree::TNodeRef CBTree::MergeNodes(TNodeRef Left, TNode * LeftData, TNodeRef Right, TNode * RightData, const TKey & DownKey, TNodeRef ParentNode, uint16_t ParentIndex) +{ + uint16_t downindex = LeftData->Info & cKeyCountMask; + LeftData->Key[downindex] = DownKey; + + typename TManagedMap::iterator it = m_ManagedIterators.find(Right); + while ((it != m_ManagedIterators.end()) && (it->first == Right)) + { + it->second->m_Index = it->second->m_Index + downindex + 1; + it->second->m_Node = Left; + m_ManagedIterators.insert(std::make_pair(Left, it->second)); + it = m_ManagedIterators.erase(it); + } + + it = m_ManagedIterators.find(ParentNode); + while ((it != m_ManagedIterators.end()) && (it->first == ParentNode)) + { + if (it->second->m_Index == ParentIndex) + { + it->second->m_Index = downindex; + it->second->m_Node = Left; + m_ManagedIterators.insert(std::make_pair(Left, it->second)); + it = m_ManagedIterators.erase(it); + } else { + ++it; + } + } + + memcpy(&(LeftData->Key[downindex+1]), &(RightData->Key[0]), sizeof(TKey) * (RightData->Info & cKeyCountMask)); + if ((LeftData->Info & cIsLeafMask) == 0) + { + memcpy(&(LeftData->Child[downindex+1]), &(RightData->Child[0]), sizeof(TNodeRef) * ((RightData->Info & cKeyCountMask) + 1)); + + for (int i = 0; i <= (RightData->Info & cKeyCountMask); i++) + { + TNode * tmp = Read(RightData->Child[i]); + tmp->Parent = Left; + Write(RightData->Child[i]); + } + } + + LeftData->Info = ((LeftData->Info & cIsLeafMask) | (downindex + 1 + (RightData->Info & cKeyCountMask))); + + Write(Left); + DeleteNode(Right); + + return Left; +} + + +template +inline void CBTree::KeyInsert(TNodeRef Node, TNode * NodeData, uint16_t Where) +{ + memcpy(&(NodeData->Key[Where+1]), &(NodeData->Key[Where]), sizeof(TKey) * ((NodeData->Info & cKeyCountMask) - Where)); + + if ((NodeData->Info & cIsLeafMask) == 0) + memcpy(&(NodeData->Child[Where+1]), &(NodeData->Child[Where]), sizeof(TNodeRef) * ((NodeData->Info & cKeyCountMask) - Where + 1)); + + NodeData->Info++; + + typename TManagedMap::iterator it = m_ManagedIterators.find(Node); + while ((it != m_ManagedIterators.end()) && (it->first == Node)) + { + if (it->second->m_Index >= Where) + it->second->m_Index++; + + ++it; + } +} + +template +inline void CBTree::KeyDelete(TNodeRef Node, TNode * NodeData, uint16_t Where) +{ + NodeData->Info--; + + typename TManagedMap::iterator it = m_ManagedIterators.find(Node); + while ((it != m_ManagedIterators.end()) && (it->first == Node)) + { + if (it->second->m_Index == Where) + { + it->second->Backup(); + } else if (it->second->m_Index > Where) + { + it->second->m_Index--; + } + + ++it; + } + + memcpy(&(NodeData->Key[Where]), &(NodeData->Key[Where+1]), sizeof(TKey) * ((NodeData->Info & cKeyCountMask) - Where)); + + if ((NodeData->Info & cIsLeafMask) == 0) + memcpy(&(NodeData->Child[Where]), &(NodeData->Child[Where+1]), sizeof(TNodeRef) * ((NodeData->Info & cKeyCountMask) - Where + 1)); +} + + +template +inline void CBTree::KeyMove(TNodeRef Source, uint16_t SourceIndex, const TNode * SourceData, TNodeRef Dest, uint16_t DestIndex, TNode * DestData) +{ + DestData->Key[DestIndex] = SourceData->Key[SourceIndex]; + + typename TManagedMap::iterator it = m_ManagedIterators.find(Source); + while ((it != m_ManagedIterators.end()) && (it->first == Source)) + { + if (it->second->m_Index == SourceIndex) + { + it->second->m_Index = DestIndex; + it->second->m_Node = Dest; + m_ManagedIterators.insert(std::make_pair(Dest, it->second)); + it = m_ManagedIterators.erase(it); + } else { + ++it; + } + } +} + +template +typename CBTree::iterator +CBTree::Insert(const TKey & Key) +{ + TNode *node, *node2; + TNodeRef actnode; + TNodeRef nextnode; + bool exists; + uint16_t ge; + + PrepareInsertOperation(); + + if (!m_Root) + { + node = CreateNewNode(m_Root); + node->Info = cIsLeafMask; + Write(m_Root); + m_sigRootChanged.emit(this, m_Root); + } + + actnode = m_Root; + node = Read(actnode); + if ((node->Info & cKeyCountMask) == cFullNode) // root split + { + // be a little tricky and let the main code handle the actual splitting. + // just assign a new root with keycount to zero and one child = old root + // the InNode test will fail with GreaterEqual = 0 + node2 = CreateNewNode(nextnode); + node2->Info = 0; + node2->Child[0] = actnode; + Write(nextnode); + + node->Parent = nextnode; + Write(actnode); + + node = node2; + actnode = nextnode; + + m_Root = nextnode; + m_sigRootChanged.emit(this, m_Root); + } + + while (actnode) + { + exists = InNodeFind(node, Key, ge); + if (exists) // already exists + { + return iterator(this, actnode, ge); + } else { + if (node->Info & cIsLeafMask) // direct insert to leaf node + { + KeyInsert(actnode, node, ge); + + node->Key[ge] = Key; + + Write(actnode); + + return iterator(this, actnode, ge); + + } else { // middle node + nextnode = node->Child[ge]; + node2 = Read(nextnode); + + if ((node2->Info & cKeyCountMask) == cFullNode) // split the childnode + { + KeyInsert(actnode, node, ge); + SplitNode(nextnode, node2, node->Child[ge], node->Child[ge+1], node->Key[ge], actnode, ge); + + Write(actnode); + if (node->Key[ge] == Key) + { + return iterator(this, actnode, ge); + } else { + if (node->Key[ge] < Key) + { + nextnode = node->Child[ge+1]; + } else { + nextnode = node->Child[ge]; + } + } + + } + actnode = nextnode; + node = Read(actnode); + + } // if (node.Info & cIsLeafMask) + } // if (exists) + } // while (actnode) + + // something went wrong + return iterator(this, 0, 0xFFFF); +} + + +template +typename CBTree::iterator +CBTree::Find(const TKey & Key) +{ + TNode * node; + TNodeRef actnode = m_Root; + uint16_t ge; + + if (!m_Root) return iterator(this, 0, 0xFFFF); + + node = Read(actnode); + + while (actnode) + { + if (InNodeFind(node, Key, ge)) + { + return iterator(this, actnode, ge); + } + + if (!(node->Info & cIsLeafMask)) + { + actnode = node->Child[ge]; + node = Read(actnode); + } else { + actnode = 0; + } + } + + return iterator(this, 0, 0xFFFF); +} + +template +typename CBTree::iterator +CBTree::LowerBound(const TKey & Key) +{ + TNode * node; + TNodeRef actnode = m_Root; + uint16_t ge; + + if (!m_Root) return iterator(this, 0, 0xFFFF); + + node = Read(actnode); + + while (actnode) + { + if (InNodeFind(node, Key, ge)) + { + return iterator(this, actnode, ge); + } + + if (node->Info & cIsLeafMask) + { + if (ge >= (node->Info & cKeyCountMask)) + { + iterator i(this, actnode, ge - 1); + ++i; + return i; + } else { + return iterator(this, actnode, ge); + } + } else { + actnode = node->Child[ge]; + node = Read(actnode); + } + } + + return iterator(this, 0, 0xFFFF); +} + +template +typename CBTree::iterator +CBTree::UpperBound(const TKey & Key) +{ + TNode * node; + TNodeRef actnode = m_Root; + uint16_t ge; + if (!m_Root) return iterator(this, 0, 0xFFFF); + + node = Read(actnode); + + while (actnode) + { + if (InNodeFind(node, Key, ge)) + { + return iterator(this, actnode, ge); + } + + if (node->Info & cIsLeafMask) + { + if (ge == 0) + { + iterator i(this, actnode, 0); + --i; + return i; + } else { + return iterator(this, actnode, ge - 1); + } + } else { + actnode = node->Child[ge]; + node = Read(actnode); + } + } + + return iterator(this, 0, 0xFFFF); +} + +template +bool CBTree::Delete(const TKey& Key) +{ + if (!m_Root) return false; + + TNode *node, *node2, *lnode, *rnode; + + TNodeRef actnode = m_Root; + TNodeRef nextnode, l, r; + bool exists, skipread; + uint16_t ge; + + bool foundininnernode = false; + bool wantleftmost = false; + TNodeRef innernode = 0; + TNode * innernodedata = NULL; + uint16_t innerindex = 0xFFFF; + + node = Read(actnode); + + while (actnode) + { + skipread = false; + + if (foundininnernode) + { + exists = false; + if (wantleftmost) + ge = 0; + else + ge = node->Info & cKeyCountMask; + + } else { + exists = InNodeFind(node, Key, ge); + } + + if (exists) + { + if (node->Info & cIsLeafMask) // delete in leaf + { + KeyDelete(actnode, node, ge); + Write(actnode); + + return true; + + } else { // delete in inner node + l = node->Child[ge]; + r = node->Child[ge+1]; + lnode = Read(l); + rnode = Read(r); + + + if (((rnode->Info & cKeyCountMask) == cEmptyNode) && ((lnode->Info & cKeyCountMask) == cEmptyNode)) + { // merge childnodes and keep going + nextnode = MergeNodes(l, lnode, r, rnode, node->Key[ge], actnode, ge); + + KeyDelete(actnode, node, ge); + node->Child[ge] = nextnode; + + if ((actnode == m_Root) && ((node->Info & cKeyCountMask) == 0)) + { // root node is empty. delete it + DeleteNode(actnode); + m_Root = nextnode; + m_sigRootChanged.emit(this, m_Root); + } else { + Write(actnode); + } + + } else { // need a key-data-pair from a leaf to replace deleted pair -> save position + foundininnernode = true; + innernode = actnode; + innerindex = ge; + innernodedata = node; + + if ((lnode->Info & cKeyCountMask) == cEmptyNode) + { + wantleftmost = true; + nextnode = r; + } else { + wantleftmost = false; + nextnode = l; + } + } + } + + } else if (node->Info & cIsLeafMask) { // we are at the bottom. finish it + if (foundininnernode) + { + if (wantleftmost) + { + KeyMove(actnode, 0, node, innernode, innerindex, innernodedata); + Write(innernode); + + KeyDelete(actnode, node, 0); + Write(actnode); + + } else { + KeyMove(actnode, (node->Info & cKeyCountMask) - 1, node, innernode, innerindex, innernodedata); + Write(innernode); + + //KeyDelete(actnode, node, node.Info & cKeyCountMask); + node->Info--; + Write(actnode); + } + } + return foundininnernode; + + } else { // inner node. go on and check if moving or merging is neccessary + nextnode = node->Child[ge]; + node2 = Read(nextnode); + + if ((node2->Info & cKeyCountMask) == cEmptyNode) // move or merge + { + // set l and r for easier access + if (ge > 0) + { + l = node->Child[ge - 1]; + lnode = Read(l); + } else + l = 0; + + if (ge < (node->Info & cKeyCountMask)) + { + r = node->Child[ge + 1]; + rnode = Read(r); + } else + r = 0; + + if ((r != 0) && ((rnode->Info & cKeyCountMask) > cEmptyNode)) // move a Key-Data-pair from the right + { + // move key-data-pair down from current to the next node + KeyMove(actnode, ge, node, nextnode, node2->Info & cKeyCountMask, node2); + + // move the child from right to next node + node2->Child[(node2->Info & cKeyCountMask) + 1] = rnode->Child[0]; + + // move key-data-pair up from right to current node + KeyMove(r, 0, rnode, actnode, ge, node); + Write(actnode); + + // decrement right node key count and remove the first key-data-pair + KeyDelete(r, rnode, 0); + + // increment KeyCount of the next node + node2->Info++; + + if ((node2->Info & cIsLeafMask) == 0) // update the parent property of moved child + { + TNode * tmp = Read(node2->Child[node2->Info & cKeyCountMask]); + tmp->Parent = nextnode; + Write(node2->Child[node2->Info & cKeyCountMask]); + } + + + Write(r); + Write(nextnode); + node = node2; + skipread = true; + + } else if ((l != 0) && ((lnode->Info & cKeyCountMask) > cEmptyNode)) // move a Key-Data-pair from the left + { + // increment next node key count and make new first key-data-pair + KeyInsert(nextnode, node2, 0); + + // move key-data-pair down from current to the next node + KeyMove(actnode, ge - 1, node, nextnode, 0, node2); + + // move the child from left to next node + node2->Child[0] = lnode->Child[lnode->Info & cKeyCountMask]; + + // move key-data-pair up from left to current node + KeyMove(l, (lnode->Info & cKeyCountMask) - 1, lnode, actnode, ge - 1, node); + Write(actnode); + + // decrement left node key count + lnode->Info--; + Write(l); + + if ((node2->Info & cIsLeafMask) == 0) // update the parent property of moved child + { + TNode * tmp = Read(node2->Child[0]); + tmp->Parent = nextnode; + Write(node2->Child[0]); + } + + Write(nextnode); + node = node2; + skipread = true; + + } else { + if (l != 0) // merge with the left node + { + nextnode = MergeNodes(l, lnode, nextnode, node2, node->Key[ge - 1], actnode, ge - 1); + KeyDelete(actnode, node, ge - 1); + node->Child[ge - 1] = nextnode; + + } else { // merge with the right node + nextnode = MergeNodes(nextnode, node2, r, rnode, node->Key[ge], actnode, ge); + KeyDelete(actnode, node, ge); + node->Child[ge] = nextnode; + } + + if ((actnode == m_Root) && ((node->Info & cKeyCountMask) == 0)) + { + DeleteNode(actnode); + m_Root = nextnode; + m_sigRootChanged(this, nextnode); + } else { + Write(actnode); + } + } + } + } // if (exists) else if (node.Info & cIsLeafMask) + + actnode = nextnode; + if (!skipread) + node = Read(actnode); + + } // while(actnode) + + return false; +} + +template +typename CBTree::TNodeRef CBTree::getRoot() +{ + return m_Root; +} + +template +void CBTree::setRoot(TNodeRef NewRoot) +{ + m_Root = NewRoot; + return; +} + +template +void CBTree::PrepareInsertOperation() +{ + if (m_Count + 64 > m_AllocCount) + { + m_AllocCount += 64; + m_Alloc = (TNode *)realloc(m_Alloc, sizeof(TNode) * m_AllocCount); + + for (TNodeRef i = m_AllocCount - 64; i < m_AllocCount; ++i) + m_Alloc[i].Parent = i + 1; + + m_Alloc[m_AllocCount - 1].Parent = 0; + + if (m_FreeIndex) + { + TNodeRef i = m_FreeIndex; + while (m_Alloc[i].Parent) + i = m_Alloc[i].Parent; + + m_Alloc[i].Parent = m_AllocCount - 64; + + } else { + m_FreeIndex = m_AllocCount - 63; + } + + } +} + +template +typename CBTree::TNode * CBTree::CreateNewNode(TNodeRef & NodeRef) +{ + NodeRef = m_FreeIndex; + m_FreeIndex = m_Alloc[m_FreeIndex].Parent; + m_Count++; + memset(m_Alloc + NodeRef, 0, sizeof(TNode)); + return m_Alloc + NodeRef; +} + +template +void CBTree::DeleteNode(TNodeRef Node) +{ + CHECK((Node > 0) && (Node < m_AllocCount), logERROR, _T("Invalid Node")); + m_Alloc[Node].Parent = m_FreeIndex; + m_FreeIndex = Node; + m_Count--; +} + +template +typename CBTree::TNode * CBTree::Read(TNodeRef Node) +{ + CHECK((Node > 0) && (Node < m_AllocCount), logERROR, _T("Invalid Node")); + return m_Alloc + Node; +} + +template +void CBTree::Write(TNodeRef Node) +{ + return; +} + +template +void CBTree::DestroyTree() +{ + std::stack s; + TNodeRef node; + TNode* nodedata; + uint16_t i; + + if (m_Root) + s.push(m_Root); + while (!s.empty()) + { + node = s.top(); + nodedata = Read(node); + s.pop(); + + if ((nodedata->Info & cIsLeafMask) == 0) + { + for (i = 0; i <= (nodedata->Info & cKeyCountMask); i++) + s.push(nodedata->Child[i]); + } + + DeleteNode(node); + } + + if (m_Alloc) + free(m_Alloc); + m_Alloc = NULL; + m_AllocCount = 0; + m_Count = 0; + m_FreeIndex = 0; +} + + +template +void CBTree::DeleteTree(TDeleteCallback * CallBack, uint32_t Param) +{ + std::stack s; + TNodeRef actnode; + TNode * node; + uint16_t i; + + typename TManagedMap::iterator it = m_ManagedIterators.begin(); + while (it != m_ManagedIterators.end()) + { + it->second->m_Node = 0; + it->second->m_Index = 0xffff; + ++it; + } + + if (m_Root) + s.push(m_Root); + + m_Root = 0; + m_sigRootChanged.emit(this, m_Root); + + while (!s.empty()) + { + actnode = s.top(); + s.pop(); + + node = Read(actnode); + + if ((node->Info & cIsLeafMask) == 0) + { + for (i = 0; i <= (node->Info & cKeyCountMask); i++) + s.push(node->Child[i]); + + } + if (CallBack) + { + for (i = 0; i < (node->Info & cKeyCountMask); i++) + CallBack->emit(this, node->Key[i], Param); + } + + DeleteNode(actnode); + } +} + + + + + + + +template +CBTree::iterator::iterator() +{ + m_Tree = NULL; + m_Node = 0; + m_Index = 0xFFFF; + m_Managed = false; + m_ManagedDeleted = false; + m_LoadedKey = false; +} +template +CBTree::iterator::iterator(CBTree* Tree, TNodeRef Node, uint16_t Index) +{ + m_Tree = Tree; + m_Node = Node; + m_Index = Index; + m_Managed = false; + m_ManagedDeleted = false; + m_LoadedKey = false; +} +template +CBTree::iterator::iterator(const iterator& Other) +{ + m_Tree = Other.m_Tree; + m_Node = Other.m_Node; + m_Index = Other.m_Index; + m_ManagedDeleted = Other.m_ManagedDeleted; + m_Managed = Other.m_Managed; + m_LoadedKey = Other.m_LoadedKey; + m_ManagedKey = Other.m_ManagedKey; + + if (m_Managed) + InsertManaged(); +} + +template +CBTree::iterator::~iterator() +{ + RemoveManaged(m_Node); +} + + +template +void CBTree::iterator::setManaged() +{ + if (!m_Managed) + InsertManaged(); + + m_Managed = true; +} + +template +inline void CBTree::iterator::RemoveManaged(TNodeRef FromNode) +{ + if (m_Managed && m_Tree) + { + typename TManagedMap::iterator i = m_Tree->m_ManagedIterators.find(FromNode); + + while ((i != m_Tree->m_ManagedIterators.end()) && (i->second != this) && (i->first == FromNode)) + ++i; + + if ((i != m_Tree->m_ManagedIterators.end()) && (i->second == this)) + m_Tree->m_ManagedIterators.erase(i); + } +} +template +inline void CBTree::iterator::InsertManaged() +{ + if (m_Tree) + m_Tree->m_ManagedIterators.insert(std::make_pair(m_Node, this)); +} + +template +bool CBTree::iterator::wasDeleted() +{ + return m_ManagedDeleted; +} + +template +void CBTree::iterator::Backup() +{ + if ((!m_ManagedDeleted) && (*this)) + { + TNode * tmp; + if (!m_LoadedKey) + { + tmp = m_Tree->Read(m_Node); + m_ManagedKey = tmp->Key[m_Index]; + } + m_LoadedKey = true; + } + + m_ManagedDeleted = true; +} + +template +CBTree * CBTree::iterator::Tree() +{ + return m_Tree; +} + +template +const TKey& CBTree::iterator::operator *() +{ + if (!m_LoadedKey) + { + TNode * node; + node = m_Tree->Read(m_Node); + m_ManagedKey = node->Key[m_Index]; + m_LoadedKey = true; + } + return m_ManagedKey; +} + +template +const TKey* CBTree::iterator::operator ->() +{ + if (!m_LoadedKey) + { + TNode * node; + node = m_Tree->Read(m_Node); + m_ManagedKey = node->Key[m_Index]; + m_LoadedKey = true; + } + return &m_ManagedKey; +} + +template +inline CBTree::iterator::operator bool() const +{ + if (m_Tree && m_Node) + { + TNode * node; + node = m_Tree->Read(m_Node); + return (m_Index < (node->Info & cKeyCountMask)); + } else + return false; +} + +template +inline bool CBTree::iterator::operator !() const +{ + if (m_Tree && m_Node) + { + TNode * node; + node = m_Tree->Read(m_Node); + return (m_Index > (node->Info & cKeyCountMask)); + } else + return true; +} + +template +inline bool CBTree::iterator::operator ==(iterator & Other) +{ + //return (m_Tree == Other.m_Tree) && (m_Node == Other.m_Node) && (m_Index == Other.m_Index) && (!m_ManagedDeleted) && (!Other.m_ManagedDeleted); + return Key() == Other.Key(); +} + +template +inline bool CBTree::iterator::operator < (iterator & Other) +{ + return Key() < Other.Key(); +} +template +inline bool CBTree::iterator::operator > (iterator & Other) +{ + return Key() > Other.Key(); +} + + +template +typename CBTree::iterator& +CBTree::iterator::operator =(const iterator& Other) +{ + RemoveManaged(m_Node); + + m_Tree = Other.m_Tree; + m_Node = Other.m_Node; + m_Index = Other.m_Index; + m_ManagedDeleted = Other.m_ManagedDeleted; + m_Managed = Other.m_Managed; + m_LoadedKey = Other.m_LoadedKey; + m_ManagedKey = Other.m_ManagedKey; + + if (m_Managed) + InsertManaged(); + + return *this; +} + +template +typename CBTree::iterator& +CBTree::iterator::operator ++() //pre ++i +{ + TNodeRef oldnode = m_Node; + if (m_Managed && m_ManagedDeleted) + { + TKey oldkey = m_ManagedKey; + m_LoadedKey = false; + m_ManagedDeleted = false; + iterator other = m_Tree->LowerBound(m_ManagedKey); + m_Node = other.m_Node; + m_Index = other.m_Index; + while (((**this) == oldkey) && (*this)) + Inc(); + + } else + Inc(); + + if (m_Managed && (oldnode != m_Node)) + { + RemoveManaged(oldnode); + InsertManaged(); + } + return *this; +} + +template +typename CBTree::iterator& +CBTree::iterator::operator --() //pre --i +{ + TNodeRef oldnode = m_Node; + if (m_Managed && m_ManagedDeleted) + { + TKey oldkey = m_ManagedKey; + m_LoadedKey = false; + + m_ManagedDeleted = false; + iterator other = m_Tree->UpperBound(m_ManagedKey); + m_Node = other.m_Node; + m_Index = other.m_Index; + while (((**this) == oldkey) && (*this)) + Dec(); + } else + Dec(); + + if (m_Managed && (oldnode != m_Node)) + { + RemoveManaged(oldnode); + InsertManaged(); + } + return *this; +} + +template +typename CBTree::iterator +CBTree::iterator::operator ++(int) //post i++ +{ + iterator tmp(*this); + ++(*this); + return tmp; +} +template +typename CBTree::iterator +CBTree::iterator::operator --(int) //post i-- +{ + iterator tmp(*this); + --(*this); + return tmp; +} + +template +void CBTree::iterator::Inc() +{ + TNode * node; + TNodeRef nextnode; + node = m_Tree->Read(m_Node); + + m_LoadedKey = false; + + if ((node->Info & cIsLeafMask) && ((node->Info & cKeyCountMask) > m_Index + 1)) // leaf + { + m_Index++; + return; + } + + if ((node->Info & cIsLeafMask) == 0) // inner node. go down + { + m_Node = node->Child[m_Index + 1]; + node = m_Tree->Read(m_Node); + + m_Index = 0; + + while ((node->Info & cIsLeafMask) == 0) // go down to a leaf + { + m_Node = node->Child[0]; + node = m_Tree->Read(m_Node); + } + + return; + } + + while (m_Index >= (node->Info & cKeyCountMask) - 1) // go up + { + if (m_Node == m_Tree->m_Root) // the root is the top, we cannot go further + { + m_Index = 0xFFFF; + m_Node = 0; + return; + } + + nextnode = node->Parent; + node = m_Tree->Read(nextnode); + m_Index = 0; + + while ((m_Index <= (node->Info & cKeyCountMask)) && (node->Child[m_Index] != m_Node)) + m_Index++; + + m_Node = nextnode; + + if (m_Index < (node->Info & cKeyCountMask)) + return; + } + +} + +template +void CBTree::iterator::Dec() +{ + TNode * node; + TNodeRef nextnode; + node = m_Tree->Read(m_Node); + + m_LoadedKey = false; + + if ((node->Info & cIsLeafMask) && (m_Index > 0)) // leaf + { + m_Index--; + return; + } + + if ((node->Info & cIsLeafMask) == 0) // inner node. go down + { + m_Node = node->Child[m_Index]; + node = m_Tree->Read(m_Node); + m_Index = (node->Info & cKeyCountMask) - 1; + + while ((node->Info & cIsLeafMask) == 0) // go down to a leaf + { + m_Node = node->Child[node->Info & cKeyCountMask]; + node = m_Tree->Read(m_Node); + m_Index = (node->Info & cKeyCountMask) - 1; + } + + return; + } + + while (m_Index == 0) // go up + { + if (m_Node == m_Tree->m_Root) // the root is the top, we cannot go further + { + m_Index = 0xFFFF; + m_Node = 0; + return; + } + + nextnode = node->Parent; + node = m_Tree->Read(nextnode); + m_Index = 0; + + while ((m_Index <= (node->Info & cKeyCountMask)) && (node->Child[m_Index] != m_Node)) + m_Index++; + + m_Node = nextnode; + + if (m_Index > 0) + { + m_Index--; + return; + } + } +} diff --git a/plugins/Dbx_tree/BlockManager.cpp b/plugins/Dbx_tree/BlockManager.cpp new file mode 100644 index 0000000000..3be5d80506 --- /dev/null +++ b/plugins/Dbx_tree/BlockManager.cpp @@ -0,0 +1,965 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "BlockManager.h" +#include "Logger.h" + +CBlockManager::CBlockManager( + CFileAccess & FileAccess, + CEncryptionManager & EncryptionManager + ) +: m_BlockSync(), + m_FileAccess(FileAccess), + m_EncryptionManager(EncryptionManager), + m_BlockTable(1024), + m_FreeBlocks() +{ + m_Optimize.Thread = NULL; + m_Optimize.Source = 0; + m_Optimize.Dest = 0; + + m_CacheInfo.Growth = 0; + m_CacheInfo.Size = 0; + m_CacheInfo.LastPurge = time(NULL); + + m_PendingHead = NULL; + m_PendingTail = NULL; + m_PendingLast = NULL; + m_LastFlush = time(NULL); + m_BytesPending = 20; + m_FirstFreeIndex = 0; + + m_SaveMode = true; + m_ReadOnly = m_FileAccess.ReadOnly(); + + memset(m_Cache, 0, sizeof(m_Cache)); +} + +CBlockManager::~CBlockManager() +{ + m_BlockSync.BeginWrite(); + if (m_Optimize.Thread) + { + m_Optimize.Thread->FreeOnTerminate(false); + m_Optimize.Thread->Terminate(); + + m_BlockSync.EndWrite(); + m_Optimize.Thread->WaitFor(); + + delete m_Optimize.Thread; + } else { + m_BlockSync.EndWrite(); + } + + _PendingFlush(true); + + for (uint32_t buddy = 0; buddy < cCacheBuddyCount; buddy++) + { + TCacheEntry * i = m_Cache[buddy]; + while (i) + { + free(i->Cache); + + TCacheEntry * tmp = i; + i = i->Next; + free(tmp); + } + } +} + +// Optimize File Size +void CBlockManager::ExecuteOptimize() +{ /* + TBlockHeadFree h = {0,0}; + uint8_t * buf = (uint8_t*)malloc(1 << 18); // 256kb + uint32_t bufuse = 0; + uint32_t bufsize = 1 << 18; + uint32_t lastflush = 0; + + { + int i = 0; + while (!m_Optimize.Thread->Terminated() && (i < 600)) + { + ++i; + Sleep(100); // wait for Miranda to start + } + } + + TransactionBeginWrite(); + + while (!m_Optimize.Thread->Terminated() && (m_Optimize.Source < m_FileAccess.Size()) && !m_ReadOnly) + { + m_FileAccess.Read(&h, m_Optimize.Source, sizeof(h)); + if (h.ID == cFreeBlockID) + { + _RemoveFreeBlock(m_Optimize.Source, h.Size); + + m_Optimize.Source += h.Size; + } else { + + if (bufsize < bufuse + h.Size) + { + buf = (uint8_t*)realloc(buf, bufuse + h.Size); + bufsize = bufuse + h.Size; + } + m_FileAccess.Read(buf + bufuse, m_Optimize.Source, h.Size); + + m_BlockTable[h.ID >> 2].Addr = (m_Optimize.Dest + bufuse) >> 2; + + m_Optimize.Source += h.Size; + bufuse += h.Size; + } + + if ((m_BlockSync.Waiting() > 0) + || (bufuse + 1024 >= bufsize) + || (m_Optimize.Source >= m_FileAccess.Size())) // buffer is nearly full or EOF + { + if (m_Optimize.Dest != m_Optimize.Source) // move blocks + { + TBlockHeadFree h = {cFreeBlockID, m_Optimize.Source - m_Optimize.Dest}; + TBlockTailFree t = {m_Optimize.Source - m_Optimize.Dest, cFreeBlockID}; + + m_FileAccess.Write(buf, m_Optimize.Dest, bufuse); + + m_FileAccess.Write(&h, m_Optimize.Dest + bufuse, sizeof(h)); + m_FileAccess.Invalidate(m_Optimize.Dest + bufuse + sizeof(h), m_Optimize.Source - m_Optimize.Dest - bufuse - sizeof(h) - sizeof(t)); + m_FileAccess.Write(&t, m_Optimize.Dest + bufuse - sizeof(t), sizeof(t)); + + if (m_SaveMode) + { + m_FileAccess.CloseTransaction(); + m_FileAccess.Flush(); + m_FileAccess.UseJournal(false); + + m_FileAccess.Write(buf, m_Optimize.Dest, bufuse); + + m_FileAccess.Write(&h, m_Optimize.Dest + bufuse, sizeof(h)); + m_FileAccess.Invalidate(m_Optimize.Dest + bufuse + sizeof(h), m_Optimize.Source - m_Optimize.Dest - bufuse - sizeof(h) - sizeof(t)); + m_FileAccess.Write(&t, m_Optimize.Dest + bufuse - sizeof(t), sizeof(t)); + + m_FileAccess.Flush(); + m_FileAccess.CleanJournal(); + m_FileAccess.UseJournal(true); + } + + m_Optimize.Dest += bufuse; + bufuse = 0; + } + + if (m_BlockSync.Waiting() > 0) + { + unsigned int w = m_BlockSync.Waiting(); + m_BlockSync.EndWrite(); + Sleep(w * 64 + 1); + m_BlockSync.BeginWrite(); + m_FileAccess.UseJournal(m_SaveMode); + } + } + } + + if (m_Optimize.Source >= m_FileAccess.Size()) + m_FileAccess.Size(m_Optimize.Dest); + + m_Optimize.Thread = NULL; + m_Optimize.Source = 0; + m_Optimize.Dest = 0; + if (m_SaveMode) + TransactionEndWrite(); + else + m_BlockSync.EndWrite(); + + free(buf); */ + + m_Optimize.Thread = NULL; +} + +inline void CBlockManager::_PendingAdd(uint32_t BlockID, uint32_t Addr, uint32_t Size, TCacheEntry * Cache) +{ + TPendingOperation * p = NULL; + if (BlockID == cFreeBlockID) + { + p = (TPendingOperation*)malloc(sizeof(TPendingOperation)); + + p->BlockID = cFreeBlockID; + p->Addr = Addr; + p->Size = Size; + p->CacheEntry = NULL; + p->EncryptionBuffer = NULL; + + m_BytesPending += 24 + sizeof(TBlockHeadFree) + sizeof(TBlockTailFree); + if (Addr & cPendingInvalidate) + m_BytesPending += 12; + + } else { + if (Cache->Pending) + { + p = Cache->Pending; + _PendingRemove(Cache->Pending, false); + } else { + p = (TPendingOperation*)malloc(sizeof(TPendingOperation)); + } + + p->BlockID = BlockID; + p->Addr = Addr; + p->Size = Size; + p->CacheEntry = Cache; + p->EncryptionBuffer = NULL; + + m_BytesPending += 12 + Size; + + Cache->Pending = p; + } + + p->Next = NULL; + p->Prev = m_PendingTail; + if (m_PendingTail) + m_PendingTail->Next = p; + + m_PendingTail = p; + if (!m_PendingHead) + m_PendingHead = p; +} + +inline void CBlockManager::_PendingRemove(TPendingOperation * Pending, bool Free) +{ + if (Pending->Prev) + Pending->Prev->Next = Pending->Next; + else + m_PendingHead = Pending->Next; + + if (Pending->Next) + Pending->Next->Prev = Pending->Prev; + else + m_PendingTail = Pending->Prev; + + if (Pending->EncryptionBuffer) + free(Pending->EncryptionBuffer); + + if (m_PendingLast == Pending) + m_PendingLast = Pending->Prev; + + Pending->CacheEntry->Pending = NULL; + if (Free) + free(Pending); +} + +inline void CBlockManager::_PendingFlush(bool FullFlush) +{ + TPendingOperation * i = NULL; + + if (m_ReadOnly) + return; + + if (FullFlush) + { + if (m_SaveMode) + { + _PendingFlush(false); // write to journal + m_FileAccess.Flush(); + m_FileAccess.UseJournal(false); + m_FileAccess.Size(m_FileAccess.Size()); // resize real file + } else { + m_FileAccess.UseJournal(false); + } + + i = m_PendingHead; + } else if (m_PendingLast) + { + i = m_PendingLast->Next; + m_FileAccess.UseJournal(m_SaveMode); + } else { + i = m_PendingHead; + m_FileAccess.UseJournal(m_SaveMode); + } + + while (i) + { + if (i->BlockID == cFreeBlockID) + { + uint32_t addr = i->Addr & ~cPendingInvalidate; + if (addr + i->Size <= m_FileAccess.Size()) + { + TBlockHeadFree h = {cFreeBlockID, i->Size}; + TBlockTailFree t = {i->Size, cFreeBlockID}; + + m_FileAccess.Write(&h, addr, sizeof(h)); + if (i->Addr & cPendingInvalidate) + m_FileAccess.Invalidate(addr + sizeof(h), i->Size - sizeof(h) - sizeof(t)); + m_FileAccess.Write(&t, addr + i->Size - sizeof(t), sizeof(t)); + } + + } else { + + if (i->BlockID && !i->EncryptionBuffer && m_EncryptionManager.IsEncrypted(i->BlockID)) + { + i->EncryptionBuffer = (TBlockHeadOcc*) malloc(i->Size); + memcpy(i->EncryptionBuffer, i->CacheEntry->Cache, i->Size); + m_EncryptionManager.Encrypt(i->EncryptionBuffer + 1, i->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc), i->BlockID, 0); + } + + if (i->EncryptionBuffer) + { + m_FileAccess.Write(i->EncryptionBuffer, i->Addr, i->Size); + } else { + m_FileAccess.Write(i->CacheEntry->Cache, i->Addr, i->Size); + } + } + + i = i->Next; + } // while + + if (FullFlush) + { + m_FileAccess.Flush(); + if (m_SaveMode) + m_FileAccess.CleanJournal(); + + m_BytesPending = 20; + m_LastFlush = time(NULL); + + i = m_PendingHead; + while (i) + { + if (i->EncryptionBuffer) + free(i->EncryptionBuffer); + + if (i->CacheEntry) + i->CacheEntry->Pending = NULL; + + TPendingOperation * tmp = i; + i = i->Next; + free(tmp); + } + m_PendingHead = NULL; + m_PendingTail = NULL; + m_PendingLast = NULL; + } else { + m_PendingLast = m_PendingTail; + m_FileAccess.CloseTransaction(); + } +} + +inline CBlockManager::TCacheEntry * CBlockManager::_CacheInsert(uint32_t Idx, TBlockHeadOcc * Cache, bool Virtual) +{ + TCacheEntry * res; + uint32_t myidx = ROR_32(Idx, cCacheBuddyBits); + + res = (TCacheEntry *)malloc(sizeof(TCacheEntry)); + res->Cache = Cache; + res->Pending = NULL; + res->Idx = myidx; + res->Forced = Virtual; + + TCacheEntry * volatile * last = &m_Cache[Idx % cCacheBuddyCount]; + TCacheEntry * i; + do { + i = *last; + + while (i && (i->Idx < myidx)) + { + last = &i->Next; + i = i->Next; + } + + if (i && (i->Idx == myidx)) + { + free(res); + free(Cache); + + i->LastUse = time(NULL) >> 2; + return i; + } + + res->Next = i; + + } while (i != CMPXCHG_Ptr(*last, res, i) ); + + res->LastUse = time(NULL) >> 2; + + m_BlockTable[Idx].InCache = true; + if (!Virtual) + XADD_32(m_CacheInfo.Growth, res->Cache->Size); + + return res; +} + +inline CBlockManager::TCacheEntry * CBlockManager::_CacheFind(uint32_t Idx) +{ + TCacheEntry * i = m_Cache[Idx % cCacheBuddyCount]; + uint32_t myidx = ROR_32(Idx, cCacheBuddyBits); + while (i && (i->Idx < myidx)) + i = i->Next; + + if (i && (i->Idx == myidx)) + return i; + else + return NULL; +} + +inline void CBlockManager::_CacheErase(uint32_t Idx) +{ + TCacheEntry * i = m_Cache[Idx % cCacheBuddyCount]; + TCacheEntry * volatile * l = &m_Cache[Idx % cCacheBuddyCount]; + + uint32_t myidx = ROR_32(Idx, cCacheBuddyBits); + + while (i->Idx < myidx) + { + l = &i->Next; + i = i->Next; + } + *l = i->Next; + + if (i->Cache) + free(i->Cache); + free(i); +} + +inline void CBlockManager::_CachePurge() +{ + _PendingFlush(true); + + uint32_t ts = time(NULL); + if (m_CacheInfo.Size + m_CacheInfo.Growth > cCachePurgeSize) { + ts = ts - (ts - m_CacheInfo.LastPurge) * cCachePurgeSize / (m_CacheInfo.Size + 2 * m_CacheInfo.Growth); + } else if (m_CacheInfo.Growth > m_CacheInfo.Size) + { + ts = ts - (ts - m_CacheInfo.LastPurge) * m_CacheInfo.Size / m_CacheInfo.Growth; + } else if (m_CacheInfo.Size > m_CacheInfo.Growth) + { + ts = ts - (ts - m_CacheInfo.LastPurge) * m_CacheInfo.Growth / m_CacheInfo.Size; + } else { + ts = m_CacheInfo.LastPurge; + } + + m_CacheInfo.Size += m_CacheInfo.Growth; + m_CacheInfo.Growth = 0; + m_CacheInfo.LastPurge = time(NULL); + + for (uint32_t buddy = 0; buddy < cCacheBuddyCount; buddy++) + { + TCacheEntry * i = m_Cache[buddy]; + TCacheEntry * volatile * l = &m_Cache[buddy]; + + while (i) + { + if (!i->Forced && !i->KeepInCache && i->Idx && ((i->LastUse << 2) < ts)) + { + uint32_t idx = ROL_32(i->Idx, cCacheBuddyBits); + m_CacheInfo.Size -= i->Cache->Size; + m_BlockTable[idx].InCache = false; + free(i->Cache); + + *l = i->Next; + TCacheEntry * tmp = i; + i = i->Next; + free(tmp); + + } else { + l = &i->Next; + i = i->Next; + } + } + } +} + +inline uint32_t CBlockManager::_GetAvailableIndex() +{ + uint32_t id; + if (m_FirstFreeIndex) + { + id = m_FirstFreeIndex; + m_FirstFreeIndex = m_BlockTable[id].Addr; + TBlockTableEntry b = {false, false, 0}; + m_BlockTable[id] = b; + } else { + id = static_cast(m_BlockTable.size()); + if (id > (1 << 12)) + m_BlockTable.resize(id + (1 << 12)); + else + m_BlockTable.resize(id * 2); + + for (uint32_t i = static_cast(m_BlockTable.size() - 1); i > id; --i) + { + TBlockTableEntry b = {true, true, m_FirstFreeIndex}; + m_BlockTable[i] = b; + m_FirstFreeIndex = i; + } + } + return id; +} + +inline void CBlockManager::_InsertFreeBlock(uint32_t Addr, uint32_t Size, bool InvalidateData, bool Reuse) +{ + if (Addr + Size == m_FileAccess.Size()) + { + if (Reuse) // in FindFreePosition we would want to use that block + m_FileAccess.Size(Addr); + } else { + + if (Reuse) + m_FreeBlocks.insert(std::make_pair(Size, Addr)); + + if (!m_ReadOnly) + _PendingAdd(cFreeBlockID, InvalidateData ? Addr | cPendingInvalidate : Addr, Size, NULL); + + } +} + +inline void CBlockManager::_RemoveFreeBlock(uint32_t Addr, uint32_t Size) +{ + TFreeBlockMap::iterator i = m_FreeBlocks.find(Size); + while ((i != m_FreeBlocks.end()) && (i->first == Size)) + { + if (i->second == Addr) + { + m_FreeBlocks.erase(i); + i = m_FreeBlocks.end(); + } else { + ++i; + } + } +} + +inline uint32_t CBlockManager::_FindFreePosition(uint32_t Size) +{ + // try to find free block + TFreeBlockMap::iterator f; + TFreeBlockMap::iterator e = m_FreeBlocks.end(); + + if (m_FreeBlocks.size()) + { + f = m_FreeBlocks.find(Size); + if (f == e) + { + if (m_FreeBlocks.rbegin()->first > Size * 2) + { + f = m_FreeBlocks.end(); + --f; + } + } + } else { + f = e; + } + + uint32_t addr = 0; + + if (f == e) // no block found - expand file + { + addr = m_FileAccess.Size(); + m_FileAccess.Size(addr + Size); + + } else { + addr = f->second; + + if (f->first != Size) + { + _InsertFreeBlock(addr + Size, f->first - Size, false, true); + } + _InsertFreeBlock(addr, Size, false, false); + + m_FreeBlocks.erase(f); + } + + return addr; +} + +inline bool CBlockManager::_InitOperation(uint32_t BlockID, uint32_t & Addr, TCacheEntry * & Cache) +{ + if (!BlockID || (BlockID & 3) || ((BlockID >> 2) >= m_BlockTable.size())) + return false; + + uint32_t idx = BlockID >> 2; + TBlockTableEntry dat = m_BlockTable[idx]; + + if (dat.Deleted) // deleted or FreeIDList item + return false; + + Addr = dat.Addr << 2; + if (dat.InCache) + { + Cache = _CacheFind(idx); + } else if (Addr) + { + TBlockHeadOcc h; + + m_FileAccess.Read(&h, Addr, sizeof(h)); + + TBlockHeadOcc * block = (TBlockHeadOcc *) malloc(h.Size); + m_FileAccess.Read(block, Addr, h.Size); + + m_EncryptionManager.Decrypt(block + 1, h.Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc), BlockID, 0); + + Cache = _CacheInsert(idx, block, false); + } else { + return false; + } + + return Cache != NULL; +} + +inline void CBlockManager::_UpdateBlock(uint32_t BlockID, TCacheEntry * CacheEntry, uint32_t Addr) +{ + CacheEntry->KeepInCache = m_ReadOnly; + + if (!CacheEntry->Forced) + { + if (!m_ReadOnly) + _PendingAdd(BlockID, Addr, CacheEntry->Cache->Size, CacheEntry); + } +} + + +uint32_t CBlockManager::ScanFile(uint32_t FirstBlockStart, uint32_t HeaderSignature, uint32_t FileSize) +{ + TBlockHeadOcc h, lasth = {0, 0, 0}; + uint32_t p; + uint32_t res = 0; + bool invalidateblock = false; + + p = FirstBlockStart; + m_FirstBlockStart = FirstBlockStart; + m_Optimize.Source = 0; + m_Optimize.Dest = 0; + + { // insert header cache element + void * header = malloc(FirstBlockStart); + m_FileAccess.Read(header, 0, FirstBlockStart); + _CacheInsert(0, (TBlockHeadOcc*)header, false); + } + + TransactionBeginWrite(); + + while (p < FileSize) + { + m_FileAccess.Read(&h, p, sizeof(h)); + if (CLogger::Instance().Level() >= CLogger::logERROR || !h.Size) + { + LOG(logCRITICAL, _T("Block-structure of file is corrupt!")); + return 0; + } + + if (h.ID == cFreeBlockID) + { + if (m_Optimize.Dest == 0) + m_Optimize.Dest = p; + + if (lasth.ID == cFreeBlockID) + { + lasth.Size += h.Size; + invalidateblock = true; + } else { + lasth = h; + } + + } else { + + if (lasth.ID == cFreeBlockID) + { + if (m_Optimize.Source == 0) + m_Optimize.Source = p; + + _InsertFreeBlock(p - lasth.Size, lasth.Size, invalidateblock, true); + } + lasth = h; + invalidateblock = false; + + while ((h.ID >> 2) >= m_BlockTable.size()) + m_BlockTable.resize(m_BlockTable.size() << 1); + + m_BlockTable[h.ID >> 2].Addr = p >> 2; + + if (h.Signature == HeaderSignature) + res = h.ID; + } + + p = p + h.Size; + } + + m_FirstFreeIndex = 0; + for (uint32_t i = static_cast(m_BlockTable.size() - 1); i > 0; --i) + { + if (m_BlockTable[i].Addr == 0) + { + TBlockTableEntry b = {true, true, m_FirstFreeIndex}; + m_BlockTable[i] = b; + m_FirstFreeIndex = i; + } + } + + TransactionEndWrite(); + + if (m_Optimize.Source && !m_FileAccess.ReadOnly()) + { + m_Optimize.Thread = new COptimizeThread(*this); + m_Optimize.Thread->Priority(CThread::tpLowest); + m_Optimize.Thread->FreeOnTerminate(true); + m_Optimize.Thread->Resume(); + } + + return res; +} + +void * CBlockManager::_CreateBlock(uint32_t & BlockID, const uint32_t Signature, uint32_t Size) +{ + uint32_t idx = _GetAvailableIndex(); + BlockID = idx << 2; + + Size = m_EncryptionManager.AlignSize(BlockID, (Size + 3) & 0xfffffffc); // align on cipher after we aligned on 4 bytes + + TBlockHeadOcc h = {BlockID, Size + sizeof(TBlockHeadOcc) + sizeof(TBlockTailOcc), Signature}; + TBlockTailOcc t = {BlockID}; + + TBlockHeadOcc * block = (TBlockHeadOcc*) malloc(Size + sizeof(h) + sizeof(t)); + *block = h; + memset(block + 1, 0, Size); + *(TBlockTailOcc*)(((uint8_t*)(block + 1)) + Size) = t; + + TCacheEntry * ce = _CacheInsert(idx, block, false); + + if (m_ReadOnly) + { + TBlockTableEntry b = {false, true, 0}; + m_BlockTable[idx] = b; + } else { + uint32_t addr = _FindFreePosition(Size + sizeof(h) + sizeof(t)); + TBlockTableEntry b = {false, true, addr >> 2 }; + m_BlockTable[idx] = b; + + _UpdateBlock(BlockID, ce, addr); + } + + return ce->Cache + 1; +} + +void * CBlockManager::_CreateBlockVirtual(uint32_t & BlockID, const uint32_t Signature, uint32_t Size) +{ + uint32_t idx = _GetAvailableIndex(); + BlockID = idx << 2; + + Size = m_EncryptionManager.AlignSize(BlockID, (Size + 3) & 0xfffffffc); // align on cipher after we aligned on 4 bytes + + TBlockHeadOcc h = {BlockID, Size + sizeof(TBlockHeadOcc) + sizeof(TBlockTailOcc), Signature}; + TBlockTailOcc t = {BlockID}; + + TBlockHeadOcc * block = (TBlockHeadOcc*) malloc(Size + sizeof(h) + sizeof(t)); + *block = h; + memset(block + 1, 0, Size); + *(TBlockTailOcc*)(((uint8_t*)(block + 1)) + Size) = t; + + return _CacheInsert(idx, block, true)->Cache + 1; +} + +bool CBlockManager::DeleteBlock(uint32_t BlockID) +{ + uint32_t idx = BlockID >> 2; + uint32_t addr; + uint32_t size; + TCacheEntry * ce; + if (!_InitOperation(BlockID, addr, ce)) + return false; + + if (!ce->Forced) + XADD_32(m_CacheInfo.Size, 0 - ce->Cache->Size); + + if (ce->Pending) + _PendingRemove(ce->Pending, true); + + size = ce->Cache->Size; + _CacheErase(idx); + + if (addr == 0) // Block in memory only + { + TBlockTableEntry b = {false, false, 0}; + m_BlockTable[idx] = b; + + } else if (m_ReadOnly) + { + m_BlockTable[idx].Deleted = true; + m_BlockTable[idx].InCache = false; + } else { + _InsertFreeBlock(addr, size, true, true); + + TBlockTableEntry b = {false, false, 0}; + m_BlockTable[idx] = b; + } + + return true; +} + +uint32_t CBlockManager::_ResizeBlock(uint32_t BlockID, void * & Buffer, uint32_t Size) +{ + uint32_t idx = BlockID >> 2; + uint32_t addr; + TCacheEntry * ce; + + if (Size == 0) + return 0; + + if (!_InitOperation(BlockID, addr, ce)) + return 0; + + Size = m_EncryptionManager.AlignSize(BlockID, (Size + 3) & 0xfffffffc); // align on cipher after we aligned on 4 bytes + + uint32_t os = ce->Cache->Size; + uint32_t ns = Size + sizeof(TBlockHeadOcc) + sizeof(TBlockTailOcc); + if (ns == ce->Cache->Size) + { + Buffer = ce->Cache + 1; + return Size; + } + + ce->Cache = (TBlockHeadOcc*) realloc(ce->Cache, ns); + ce->Cache->Size = ns; + TBlockTailOcc t = {BlockID}; + *(TBlockTailOcc*)(((uint8_t*)(ce->Cache + 1)) + Size) = t; + XADD_32(m_CacheInfo.Size, ns - os); + + Buffer = ce->Cache + 1; + ce->KeepInCache = m_ReadOnly; + + if (!m_ReadOnly && addr) + { + _InsertFreeBlock(addr, os, true, true); + addr = _FindFreePosition(ns); + m_BlockTable[idx].Addr = addr >> 2; + + _UpdateBlock(BlockID, ce, addr); // write down + } + + return Size; +} + +bool CBlockManager::IsForcedVirtual(uint32_t BlockID) +{ + TCacheEntry * ce = _CacheFind(BlockID >> 2); + return ce && ce->Forced; +} + +bool CBlockManager::WriteBlockToDisk(uint32_t BlockID) +{ + uint32_t addr; + TCacheEntry * ce; + + if (!_InitOperation(BlockID, addr, ce)) + return false; + + if (!ce->Forced) + return true; + + ce->Forced = false; + XADD_32(m_CacheInfo.Size, ce->Cache->Size); + + if (!m_ReadOnly) + { + addr = _FindFreePosition(ce->Cache->Size); + m_BlockTable[BlockID >> 2].Addr = addr >> 2; + _UpdateBlock(BlockID, ce, addr); + } + return true; +} + +bool CBlockManager::MakeBlockVirtual(uint32_t BlockID) +{ + uint32_t addr; + TCacheEntry * ce; + + if (!_InitOperation(BlockID, addr, ce)) + return false; + + if (ce->Forced) + return true; + + if (ce->Pending) // don't write down, we kill it anyway + _PendingRemove(ce->Pending, true); + + ce->Forced = true; + XADD_32(m_CacheInfo.Size, 0 - ce->Cache->Size); + + if (!m_ReadOnly) + { + _InsertFreeBlock(addr, ce->Cache->Size, true, true); + m_BlockTable[BlockID >> 2].Addr = 0; + } + return true; +} + +void * CBlockManager::_ReadBlock(uint32_t BlockID, uint32_t & Size, uint32_t & Signature) +{ + uint32_t addr; + TCacheEntry * ce; + + if ((BlockID == 0) && (Signature == -1)) + { + Size = m_FirstBlockStart; + return m_Cache[0]->Cache; + } + + if (!_InitOperation(BlockID, addr, ce)) + return NULL; + + if ((Signature != 0) && (Signature != ce->Cache->Signature)) + { + Signature = ce->Cache->Signature; + return NULL; + } + Signature = ce->Cache->Signature; + + if ((Size != 0) && (Size != ce->Cache->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc))) + { + Size = ce->Cache->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc); + return NULL; + } + Size = ce->Cache->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc); + + return ce->Cache + 1; +} + +bool CBlockManager::UpdateBlock(uint32_t BlockID, uint32_t Signature) +{ + uint32_t addr; + TCacheEntry * ce; + + if ((BlockID == 0) && (Signature == -1)) + { + if (!m_ReadOnly) + _PendingAdd(0, 0, m_FirstBlockStart, m_Cache[0]); + + return true; + } + + if (!_InitOperation(BlockID, addr, ce)) + return false; + + if (Signature) + ce->Cache->Signature = Signature; + + _UpdateBlock(BlockID, ce, addr); + + return true; +} + +// make file writeable: +// write all cached blocks to file and check for the old addresses +// remove virtual only from file +// update m_FreeBlocks along the way + diff --git a/plugins/Dbx_tree/BlockManager.h b/plugins/Dbx_tree/BlockManager.h new file mode 100644 index 0000000000..dcccd4f0b9 --- /dev/null +++ b/plugins/Dbx_tree/BlockManager.h @@ -0,0 +1,401 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#pragma once + +#include +#include +#include + +#include "stdint.h" +#include "FileAccess.h" +#include "EncryptionManager.h" +#include "MREWSync.h" +#include "Thread.h" +#include "intrinsics.h" + +class CBlockManager +{ +protected: + static const uint32_t cFreeBlockID = 0xFFFFFFFF; + + static const uint32_t cJournalFlushBytes = (1 << 20) - 2048; // flush before reserved journal-space is exhausted + static const uint32_t cJournalFlushTimeout = 300; // journal flush every 5 minutes + + static const uint32_t cCacheBuddyBits = 10; + static const uint32_t cCacheBuddyCount = 1 << cCacheBuddyBits; // count of static allocated buddy nodes + static const uint32_t cCacheBuddyCheck = 0xffffffff << cCacheBuddyBits; + + static const uint32_t cCacheMinimumTimeout = 2; // purge less than every n seconds (high priority) + static const uint32_t cCacheMaximumTimeout = 600; // purge every 10 minutes (high priority) + static const uint32_t cCachePurgeSize = 1 << 21; // cache up to 2MB + static const uint32_t cCacheMinimumGrowthForPurge = 1 << 19; // cache only when 512kb were added + + #pragma pack(push, 1) // push current alignment to stack, set alignment to 1 byte boundary + + typedef struct TBlockHeadFree { + uint32_t ID; + uint32_t Size; + } TBlockHeadFree; + typedef struct TBlockHeadOcc { + uint32_t ID; + uint32_t Size; + uint32_t Signature; /// if occupied block + } TBlockHeadOcc; + + typedef struct TBlockTailOcc { + uint32_t ID; + } TBlockTailOcc; + + typedef struct TBlockTailFree { + uint32_t Size; /// if free block + uint32_t ID; + } TBlockTailFree; + + #pragma pack(pop) + + //////////////////////////////////////////////////////////////////////////////////////////////////// + /// Block table entry. + /// + /// Addr Deleted InCache Meaning + /// 0 0 0 successfully deleted block + /// 0 0 1 virtual only block (either forced virtual or created on a read-only file) + /// 0 1 0 invalid + /// 0 1 1 FreeID list (last entry) + /// set 0 0 Normal in-file block + /// set 0 1 in file and cache (normal cache which could differ on a read-only file or forced virtual out of a read-only file - check TCacheEntry) + /// set 1 0 deleted block or a read-only file + /// set 1 1 FreeID list entry + /// + /// Michael "Protogenes" Kunz, 07.09.2010. + //////////////////////////////////////////////////////////////////////////////////////////////////// + typedef struct TBlockTableEntry { + uint32_t Deleted : 1; ///< Flag is set if the block was deleted but can't be removed, because the file is read-only + uint32_t InCache : 1; ///< Flag is set if block is in the cache (either forced virtual or from the file) + uint32_t Addr : 30; ///< The Offset in the file div 4, so we can address files up to 4GB + } TBlockTableEntry; + std::vector m_BlockTable; + + struct TPendingOperation; + typedef struct TCacheEntry { + TCacheEntry * volatile Next; + TBlockHeadOcc * volatile Cache; + TPendingOperation * Pending; + + uint32_t Idx; + uint32_t Forced : 1; + uint32_t KeepInCache : 1; + uint32_t LastUse : 30; + } TCacheEntry; + + TCacheEntry * m_Cache[cCacheBuddyCount]; + + struct { + uint32_t volatile Size; + uint32_t volatile Growth; + uint32_t volatile LastPurge; + } m_CacheInfo; + + CFileAccess & m_FileAccess; + CEncryptionManager & m_EncryptionManager; + CMultiReadExclusiveWriteSynchronizer m_BlockSync; + + uint32_t m_FirstBlockStart; + bool m_SaveMode; + bool m_ReadOnly; + + typedef std::multimap TFreeBlockMap; + TFreeBlockMap m_FreeBlocks; + uint32_t m_FirstFreeIndex; + + static const uint32_t cPendingInvalidate = 0x00000001; + typedef struct TPendingOperation { + TPendingOperation * Next; ///< The next + TPendingOperation * Prev; ///< The previous + uint32_t BlockID; ///< Identifier for the block + uint32_t Addr; ///< The address in the file + uint32_t Size; ///< The size of the block + TCacheEntry * CacheEntry; ///< The cache entry + TBlockHeadOcc * EncryptionBuffer; ///< Buffer for encrypted block + } TPendingOperation; + + TPendingOperation * m_PendingHead; ///< The double linked list head + TPendingOperation * m_PendingTail; ///< The double linked list tail + TPendingOperation * m_PendingLast; ///< The last processed item + + uint32_t m_LastFlush; ///< The last flush timestamp + uint32_t m_BytesPending; ///< The bytes pending for write + + class COptimizeThread : public CThread + { + protected: + CBlockManager & m_Owner; + void Execute() { m_Owner.ExecuteOptimize(); }; + public: + COptimizeThread(CBlockManager & Owner) : CThread(true), m_Owner(Owner) {}; + ~COptimizeThread() {}; + }; + + struct { + uint32_t Source; + uint32_t Dest; + COptimizeThread * Thread; + } m_Optimize; + void ExecuteOptimize(); + + uint32_t _GetAvailableIndex(); + void _InsertFreeBlock(uint32_t Addr, uint32_t Size, bool InvalidateData, bool Reuse); + void _RemoveFreeBlock(uint32_t Addr, uint32_t Size); + uint32_t _FindFreePosition(uint32_t Size); + + bool _InitOperation(uint32_t BlockID, uint32_t & Addr, TCacheEntry * & Cache); + void _UpdateBlock(uint32_t BlockID, TCacheEntry * CacheEntry, uint32_t Addr); + + void * _ReadBlock(uint32_t BlockID, uint32_t & Size, uint32_t & Signature); + void * _CreateBlock(uint32_t & BlockID, const uint32_t Signature, uint32_t Size); + void * _CreateBlockVirtual(uint32_t & BlockID, const uint32_t Signature, uint32_t Size); + uint32_t _ResizeBlock(uint32_t BlockID, void * & Buffer, uint32_t Size); + + TCacheEntry * _CacheInsert(uint32_t Idx, TBlockHeadOcc * Cache, bool Virtual); + TCacheEntry * _CacheFind(uint32_t Idx); + void _CacheErase(uint32_t Idx); + + void _CachePurge(); + void _PendingAdd(uint32_t BlockID, uint32_t Addr, uint32_t Size, TCacheEntry * Cache); + void _PendingRemove(TPendingOperation * Pending, bool Free); + void _PendingFlush(bool FullFlush); + + + + void TransactionBeginRead() + { + m_BlockSync.BeginRead(); + }; + + void TransactionEndRead() + { + m_BlockSync.EndRead(); + }; + + void TransactionBeginWrite() + { + m_BlockSync.BeginWrite(); + m_FileAccess.UseJournal(m_SaveMode); + }; + + void TransactionEndWrite() + { + if (m_BlockSync.WriteRecursionCount() == 1) + { + m_FileAccess.CompleteTransaction(); + m_BytesPending += 12; + + if ((m_CacheInfo.LastPurge + cCacheMaximumTimeout < time(NULL)) + || ((m_CacheInfo.Size + m_CacheInfo.Growth > cCachePurgeSize) + && (m_CacheInfo.Growth > cCacheMinimumGrowthForPurge) + && (m_CacheInfo.LastPurge + cCacheMinimumTimeout < time(NULL)))) + { + _CachePurge(); + } else if ((m_BytesPending >= cJournalFlushBytes) || (time(NULL) > m_LastFlush + cJournalFlushTimeout)) + { + _PendingFlush(true); + } else { + _PendingFlush(false); + } + } + + m_BlockSync.EndWrite(); + }; +public: + CBlockManager(CFileAccess & FileAccess, CEncryptionManager & EncryptionManager); + ~CBlockManager(); + + + class ReadTransaction + { + private: + CBlockManager * m_Owner; + uint32_t volatile * m_RefCount; + bool m_Closed; + public: + ReadTransaction() + : m_Owner(NULL), + m_RefCount(NULL), + m_Closed(true) + { + + }; + ReadTransaction(CBlockManager & BlockManager) + : m_Owner(&BlockManager), + m_RefCount(new uint32_t(1)), + m_Closed(false) + { + m_Owner->TransactionBeginRead(); + }; + ReadTransaction(const ReadTransaction & Other) + : m_Owner(Other.m_Owner), + m_RefCount(Other.m_RefCount), + m_Closed(Other.m_Closed) + { + if (!m_Closed) + INC_32(*m_RefCount); + }; + ~ReadTransaction() + { + if (!m_Closed && (DEC_32(*m_RefCount) == 0)) + { + delete m_RefCount; + m_Owner->TransactionEndRead(); + } + }; + + ReadTransaction & operator =(const ReadTransaction & Other) + { + if (!m_Closed && (DEC_32(*m_RefCount) == 0)) + { + delete m_RefCount; + m_Owner->TransactionEndRead(); + } + + m_Owner = Other.m_Owner; + m_RefCount = Other.m_RefCount; + m_Closed = Other.m_Closed; + if (!m_Closed) + INC_32(*m_RefCount); + + return *this; + } + + void Close() + { + if (!m_Closed && (DEC_32(*m_RefCount) == 0)) + { + delete m_RefCount; + m_Owner->TransactionEndRead(); + } + m_Closed = true; + } + + }; + class WriteTransaction + { + private: + CBlockManager * m_Owner; + uint32_t volatile * m_RefCount; + bool m_Closed; + public: + WriteTransaction() + : m_Owner(NULL), + m_RefCount(NULL), + m_Closed(true) + { + + }; + WriteTransaction(CBlockManager & BlockManager) + : m_Owner(&BlockManager), + m_RefCount(new uint32_t(1)), + m_Closed(false) + { + m_Owner->TransactionBeginWrite(); + }; + WriteTransaction(const WriteTransaction & Other) + : m_Owner(Other.m_Owner), + m_RefCount(Other.m_RefCount), + m_Closed(Other.m_Closed) + { + if (!m_Closed) + INC_32(*m_RefCount); + }; + ~WriteTransaction() + { + if (!m_Closed && (DEC_32(*m_RefCount) == 0)) + { + delete m_RefCount; + m_Owner->TransactionEndWrite(); + } + }; + + WriteTransaction & operator =(const WriteTransaction & Other) + { + if (!m_Closed && (DEC_32(*m_RefCount) == 0)) + { + delete m_RefCount; + m_Owner->TransactionEndWrite(); + } + + m_Owner = Other.m_Owner; + m_RefCount = Other.m_RefCount; + m_Closed = Other.m_Closed; + if (!m_Closed) + INC_32(*m_RefCount); + + return *this; + } + + void Close() + { + if (!m_Closed && (DEC_32(*m_RefCount) == 0)) + { + delete m_RefCount; + m_Owner->TransactionEndWrite(); + } + m_Closed = true; + } + + }; + + uint32_t ScanFile(uint32_t FirstBlockStart, uint32_t HeaderSignature, uint32_t FileSize); + + template + BlockType * ReadBlock(uint32_t BlockID, uint32_t & Size, uint32_t & Signature) + { + return reinterpret_cast(_ReadBlock(BlockID, Size, Signature)); + }; + + template + BlockType * CreateBlock(uint32_t & BlockID, const uint32_t Signature, uint32_t Size = sizeof(BlockType)) + { + return reinterpret_cast(_CreateBlock(BlockID, Signature, Size)); + }; + + template + BlockType * CreateBlockVirtual(uint32_t & BlockID, const uint32_t Signature, uint32_t Size = sizeof(BlockType)) + { + return reinterpret_cast(_CreateBlockVirtual(BlockID, Signature, Size)); + }; + + template + uint32_t ResizeBlock(uint32_t BlockID, BlockType * & Buffer, uint32_t Size) + { + void * tmp = Buffer; + uint32_t res = _ResizeBlock(BlockID, tmp, Size); + Buffer = reinterpret_cast(tmp); + return res; + }; + + bool UpdateBlock(uint32_t BlockID, uint32_t Signature = 0); + bool DeleteBlock(uint32_t BlockID); + + bool IsForcedVirtual(uint32_t BlockID); + bool WriteBlockToDisk(uint32_t BlockID); + bool MakeBlockVirtual(uint32_t BlockID); +}; diff --git a/plugins/Dbx_tree/Compatibility.cpp b/plugins/Dbx_tree/Compatibility.cpp new file mode 100644 index 0000000000..38b8f92383 --- /dev/null +++ b/plugins/Dbx_tree/Compatibility.cpp @@ -0,0 +1,866 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "Compatibility.h" +#include "Logger.h" +#define DB_NOHELPERFUNCTIONS + #include "m_database.h" +#undef DB_NOHELPERFUNCTIONS +#ifndef _MSC_VER +#include "savestrings_gcc.h" +#endif + +HANDLE gCompServices[31] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +HANDLE gEvents[6] = {0,0,0,0,0,0}; + +HANDLE hEventDeletedEvent, + hEventAddedEvent, + hEventFilterAddedEvent, + hSettingChangeEvent, + hContactDeletedEvent, + hContactAddedEvent; + +INT_PTR CompAddContact(WPARAM wParam, LPARAM lParam) +{ + TDBTEntity entity = {0,0,0,0}; + entity.hParentEntity = DBEntityGetRoot(0, 0); + entity.hAccountEntity = entity.hParentEntity; + + TDBTEntityHandle res = gDataBase->getEntities().CreateEntity(entity); + if (res == DBT_INVALIDPARAM) + return 1; + + NotifyEventHooks(hContactAddedEvent, res, 0); + return res; +} +INT_PTR CompDeleteContact(WPARAM hContact, LPARAM lParam) +{ + NotifyEventHooks(hContactDeletedEvent, hContact, 0); + + int res = DBEntityDelete(hContact, 0); + if (res == DBT_INVALIDPARAM) + return 1; + + return res; +} +INT_PTR CompIsDbContact(WPARAM hContact, LPARAM lParam) +{ + int flags = DBEntityGetFlags(hContact, 0); + return (flags != DBT_INVALIDPARAM) && + ((flags & DBT_NFM_SpecialEntity) == 0); +} +INT_PTR CompGetContactCount(WPARAM wParam, LPARAM lParam) +{ + TDBTEntityIterFilter f = {0,0,0,0}; + f.cbSize = sizeof(f); + f.fDontHasFlags = DBT_NF_IsGroup | DBT_NF_IsVirtual | DBT_NF_IsAccount | DBT_NF_IsRoot; + f.Options = DBT_NIFO_OSC_AC | DBT_NIFO_OC_AC; + + TDBTEntityIterationHandle hiter = DBEntityIterInit((WPARAM)&f, gDataBase->getEntities().getRootEntity()); + int c = 0; + if ((hiter != 0) && (hiter != DBT_INVALIDPARAM)) + { + TDBTEntityHandle con = DBEntityIterNext(hiter, 0); + + while ((con != DBT_INVALIDPARAM) && (con != 0)) + { + if ((con != 0) && (con != DBT_INVALIDPARAM)) + c++; + + con = DBEntityIterNext(hiter, 0); + } + DBEntityIterClose(hiter, 0); + } + return c; +} +INT_PTR CompFindFirstContact(WPARAM wParam, LPARAM lParam) +{ + return gDataBase->getEntities().compFirstContact(); +} +INT_PTR CompFindNextContact(WPARAM hContact, LPARAM lParam) +{ + return gDataBase->getEntities().compNextContact(hContact); +} + +INT_PTR CompGetContactSetting(WPARAM hContact, LPARAM pSetting) +{ + DBCONTACTGETSETTING * dbcgs = reinterpret_cast(pSetting); + dbcgs->pValue->type = 0; + + char namebuf[512]; + namebuf[0] = 0; + + if (!(dbcgs->szModule || dbcgs->szSetting)) + return -1; + + if (dbcgs->szModule) + strcpy_s(namebuf, dbcgs->szModule); + strcat_s(namebuf, "/"); + if (dbcgs->szSetting) + strcat_s(namebuf, dbcgs->szSetting); + + TDBTSettingDescriptor desc = {0,0,0,0,0,0,0,0}; + TDBTSetting set = {0,0,0,0}; + desc.cbSize = sizeof(desc); + desc.Entity = hContact; + desc.pszSettingName = namebuf; + + set.cbSize = sizeof(set); + set.Descriptor = &desc; + + if (DBSettingRead(reinterpret_cast(&set), 0) == DBT_INVALIDPARAM) + return -1; + + switch (set.Type) + { + case DBT_ST_ANSI: + { + dbcgs->pValue->type = DBVT_ASCIIZ; + dbcgs->pValue->pszVal = set.Value.pAnsi; + dbcgs->pValue->cchVal = set.Value.Length - 1; + } break; + case DBT_ST_UTF8: + { + dbcgs->pValue->type = DBVT_WCHAR; + dbcgs->pValue->pwszVal = mir_utf8decodeW(set.Value.pUTF8); + if (dbcgs->pValue->pwszVal) + dbcgs->pValue->cchVal = static_cast(wcslen(dbcgs->pValue->pwszVal)); + else + dbcgs->pValue->cchVal = 0; + mir_free(set.Value.pUTF8); + } break; + case DBT_ST_WCHAR: + { + dbcgs->pValue->type = DBVT_WCHAR; + dbcgs->pValue->pwszVal = set.Value.pWide; + dbcgs->pValue->cchVal = set.Value.Length - 1; + } break; + case DBT_ST_BLOB: + { + dbcgs->pValue->type = DBVT_BLOB; + dbcgs->pValue->pbVal = set.Value.pBlob; + dbcgs->pValue->cpbVal = set.Value.Length; + } break; + case DBT_ST_BOOL: + { + dbcgs->pValue->type = DBVT_BYTE; + dbcgs->pValue->bVal = (uint8_t)set.Value.Bool; + } break; + case DBT_ST_BYTE: case DBT_ST_CHAR: + { + dbcgs->pValue->type = DBVT_BYTE; + dbcgs->pValue->bVal = set.Value.Byte; + } break; + case DBT_ST_SHORT: case DBT_ST_WORD: + { + dbcgs->pValue->type = DBVT_WORD; + dbcgs->pValue->wVal = set.Value.Word; + } break; + case DBT_ST_INT: case DBT_ST_DWORD: + { + dbcgs->pValue->type = DBVT_DWORD; + dbcgs->pValue->dVal = set.Value.DWord; + } break; + case DBT_ST_INT64: case DBT_ST_QWORD: + case DBT_ST_DOUBLE: case DBT_ST_FLOAT: + { + dbcgs->pValue->type = DBVT_BLOB; + dbcgs->pValue->cpbVal = sizeof(set.Value); + dbcgs->pValue->pbVal = reinterpret_cast(mir_alloc(sizeof(set.Value))); + memcpy(dbcgs->pValue->pbVal, &set.Value, sizeof(set.Value)); + } break; + default: + { + return -1; + } + } + + return 0; +} +INT_PTR CompGetContactSettingStr(WPARAM hContact, LPARAM pSetting) +{ + DBCONTACTGETSETTING * dbcgs = reinterpret_cast(pSetting); + + if ((dbcgs->pValue->type & DBVTF_VARIABLELENGTH) == 0) + { + CompFreeVariant(0, reinterpret_cast(dbcgs->pValue)); + dbcgs->pValue->type = 0; + } + + char namebuf[512]; + namebuf[0] = 0; + if (dbcgs->szModule) + strcpy_s(namebuf, dbcgs->szModule); + strcat_s(namebuf, "/"); + if (dbcgs->szSetting) + strcat_s(namebuf, dbcgs->szSetting); + + TDBTSettingDescriptor desc = {0,0,0,0,0,0,0,0}; + TDBTSetting set = {0,0,0,0}; + desc.cbSize = sizeof(desc); + desc.Entity = hContact; + desc.pszSettingName = namebuf; + + set.cbSize = sizeof(set); + set.Descriptor = &desc; + + + switch (dbcgs->pValue->type) + { + case DBVT_ASCIIZ: set.Type = DBT_ST_ANSI; break; + case DBVT_BLOB: set.Type = DBT_ST_BLOB; break; + case DBVT_UTF8: set.Type = DBT_ST_UTF8; break; + case DBVT_WCHAR: set.Type = DBT_ST_WCHAR; break; + } + + if (DBSettingRead(reinterpret_cast(&set), 0) == DBT_INVALIDPARAM) + return -1; + + switch (set.Type) + { + case DBT_ST_ANSI: + { + dbcgs->pValue->type = DBVT_ASCIIZ; + dbcgs->pValue->pszVal = set.Value.pAnsi; + dbcgs->pValue->cchVal = set.Value.Length - 1; + } break; + case DBT_ST_UTF8: + { + dbcgs->pValue->type = DBVT_UTF8; + dbcgs->pValue->pszVal = set.Value.pUTF8; + dbcgs->pValue->cchVal = set.Value.Length - 1; + } break; + case DBT_ST_WCHAR: + { + if (dbcgs->pValue->type == DBVT_WCHAR) + { + dbcgs->pValue->pwszVal = set.Value.pWide; + dbcgs->pValue->cchVal = set.Value.Length - 1; + } else { + dbcgs->pValue->type = DBVT_UTF8; + dbcgs->pValue->pszVal = mir_utf8encodeW(set.Value.pWide); + dbcgs->pValue->cchVal = static_cast(strlen(dbcgs->pValue->pszVal)); + mir_free(set.Value.pWide); + } + } break; + case DBT_ST_BLOB: + { + dbcgs->pValue->type = DBVT_BLOB; + dbcgs->pValue->pbVal = set.Value.pBlob; + dbcgs->pValue->cpbVal = set.Value.Length; + } break; + case DBT_ST_BOOL: + { + dbcgs->pValue->type = DBVT_BYTE; + dbcgs->pValue->bVal = (uint8_t)set.Value.Bool; + } break; + case DBT_ST_BYTE: case DBT_ST_CHAR: + { + dbcgs->pValue->type = DBVT_BYTE; + dbcgs->pValue->bVal = set.Value.Byte; + } break; + case DBT_ST_SHORT: case DBT_ST_WORD: + { + dbcgs->pValue->type = DBVT_WORD; + dbcgs->pValue->wVal = set.Value.Word; + } break; + case DBT_ST_INT: case DBT_ST_DWORD: + { + dbcgs->pValue->type = DBVT_DWORD; + dbcgs->pValue->dVal = set.Value.DWord; + } break; + case DBT_ST_INT64: case DBT_ST_QWORD: + case DBT_ST_DOUBLE: case DBT_ST_FLOAT: + { + dbcgs->pValue->type = DBVT_BLOB; + dbcgs->pValue->cpbVal = sizeof(set.Value); + dbcgs->pValue->pbVal = reinterpret_cast(mir_alloc(sizeof(set.Value))); + memcpy(dbcgs->pValue->pbVal, &set.Value, sizeof(set.Value)); + } break; + default: + { + return -1; + } + } + + return 0; +} +INT_PTR CompGetContactSettingStatic(WPARAM hContact, LPARAM pSetting) +{ + DBCONTACTGETSETTING * dbcgs = reinterpret_cast(pSetting); + + char namebuf[512]; + namebuf[0] = 0; + if (dbcgs->szModule) + strcpy_s(namebuf, dbcgs->szModule); + strcat_s(namebuf, "/"); + if (dbcgs->szSetting) + strcat_s(namebuf, dbcgs->szSetting); + + TDBTSettingDescriptor desc = {0,0,0,0,0,0,0,0}; + TDBTSetting set = {0,0,0,0}; + desc.cbSize = sizeof(desc); + desc.Entity = hContact; + desc.pszSettingName = namebuf; + + set.cbSize = sizeof(set); + set.Descriptor = &desc; + + if (DBSettingRead(reinterpret_cast(&set), 0) == DBT_INVALIDPARAM) + return -1; + + if ((set.Type & DBT_STF_VariableLength) ^ (dbcgs->pValue->type & DBVTF_VARIABLELENGTH)) + { + if (set.Type & DBT_STF_VariableLength) + mir_free(set.Value.pBlob); + return -1; + } + + switch (set.Type) + { + case DBT_ST_ANSI: + { + if (dbcgs->pValue->cchVal < set.Value.Length) + { + memcpy(dbcgs->pValue->pszVal, set.Value.pAnsi, dbcgs->pValue->cchVal); + dbcgs->pValue->pszVal[dbcgs->pValue->cchVal - 1] = 0; + } else { + memcpy(dbcgs->pValue->pszVal, set.Value.pAnsi, set.Value.Length); + } + dbcgs->pValue->type = DBVT_ASCIIZ; + dbcgs->pValue->cchVal = set.Value.Length - 1; + + mir_free(set.Value.pAnsi); + } break; + case DBT_ST_UTF8: + { + set.Value.pUTF8 = mir_utf8decode(set.Value.pUTF8, NULL); + set.Value.Length = static_cast(strlen(set.Value.pUTF8)); + + if (dbcgs->pValue->cchVal < set.Value.Length) + { + memcpy(dbcgs->pValue->pszVal, set.Value.pUTF8, dbcgs->pValue->cchVal); + dbcgs->pValue->pszVal[dbcgs->pValue->cchVal - 1] = 0; + } else { + memcpy(dbcgs->pValue->pszVal, set.Value.pUTF8, set.Value.Length); + } + dbcgs->pValue->type = DBVT_ASCIIZ; + dbcgs->pValue->cchVal = set.Value.Length - 1; + + mir_free(set.Value.pUTF8); + } break; + case DBT_ST_WCHAR: + { + char * tmp = mir_u2a(set.Value.pWide); + WORD l = static_cast(strlen(tmp)); + mir_free(set.Value.pWide); + + if (dbcgs->pValue->cchVal < l + 1) + { + memcpy(dbcgs->pValue->pszVal, tmp, dbcgs->pValue->cchVal); + dbcgs->pValue->pszVal[l] = 0; + } else { + memcpy(dbcgs->pValue->pszVal, tmp, l + 1); + } + dbcgs->pValue->type = DBVT_ASCIIZ; + dbcgs->pValue->cchVal = l; + + mir_free(tmp); + } break; + case DBT_ST_BLOB: + { + if (dbcgs->pValue->cchVal < set.Value.Length) + { + memcpy(dbcgs->pValue->pbVal, set.Value.pBlob, dbcgs->pValue->cchVal); + } else { + memcpy(dbcgs->pValue->pbVal, set.Value.pBlob, set.Value.Length); + } + dbcgs->pValue->type = DBVT_BLOB; + dbcgs->pValue->cchVal = set.Value.Length; + + mir_free(set.Value.pBlob); + } break; + case DBT_ST_BOOL: + { + dbcgs->pValue->type = DBVT_BYTE; + dbcgs->pValue->bVal = set.Value.Bool ? TRUE : FALSE; + } break; + case DBT_ST_BYTE: case DBT_ST_CHAR: + { + dbcgs->pValue->type = DBVT_BYTE; + dbcgs->pValue->bVal = set.Value.Byte; + } break; + case DBT_ST_SHORT: case DBT_ST_WORD: + { + dbcgs->pValue->type = DBVT_WORD; + dbcgs->pValue->wVal = set.Value.Word; + } break; + case DBT_ST_INT: case DBT_ST_DWORD: + { + dbcgs->pValue->type = DBVT_DWORD; + dbcgs->pValue->dVal = set.Value.DWord; + } break; + default: + { + return -1; + } + } + + return 0; +} +INT_PTR CompFreeVariant(WPARAM wParam, LPARAM pSetting) +{ + DBVARIANT * dbv = reinterpret_cast(pSetting); + + if ((dbv->type == DBVT_BLOB) && (dbv->pbVal)) + { + mir_free(dbv->pbVal); + dbv->pbVal = 0; + } else if ((dbv->type & DBVTF_VARIABLELENGTH) && (dbv->pszVal)) + { + mir_free(dbv->pszVal); + dbv->pszVal = NULL; + } + dbv->type = 0; + return 0; +} +INT_PTR CompWriteContactSetting(WPARAM hContact, LPARAM pSetting) +{ + DBCONTACTWRITESETTING * dbcws = reinterpret_cast(pSetting); + + char namebuf[512]; + namebuf[0] = 0; + if (dbcws->szModule) + strcpy_s(namebuf, dbcws->szModule); + strcat_s(namebuf, "/"); + if (dbcws->szSetting) + strcat_s(namebuf, dbcws->szSetting); + + TDBTSettingDescriptor desc = {0,0,0,0,0,0,0,0}; + TDBTSetting set = {0,0,0,0}; + desc.cbSize = sizeof(desc); + desc.Entity = hContact; + desc.pszSettingName = namebuf; + + set.cbSize = sizeof(set); + set.Descriptor = &desc; + + switch (dbcws->value.type) + { + case DBVT_ASCIIZ: + { + set.Type = DBT_ST_ANSI; + set.Value.pAnsi = dbcws->value.pszVal; + } break; + case DBVT_UTF8: + { + wchar_t * tmp = mir_utf8decodeW(dbcws->value.pszVal); + if (tmp == 0) + { + if (IsDebuggerPresent()) + { + DebugBreak(); +#ifdef _DEBUG + } else { + LOG(logWARNING, _T("Trying to write malformed UTF8 setting \"%hs\" in module \"%hs\""), dbcws->szSetting, dbcws->szModule); + CLogger::Instance().ShowMessage(); +#endif + } + return -1; + } else { + mir_free(tmp); + } + + set.Type = DBT_ST_UTF8; + set.Value.pUTF8 = dbcws->value.pszVal; + } break; + case DBVT_WCHAR: + { + set.Type = DBT_ST_WCHAR; + set.Value.pWide = dbcws->value.pwszVal; + } break; + case DBVT_BLOB: + { + set.Type = DBT_ST_BLOB; + set.Value.pBlob = dbcws->value.pbVal; + set.Value.Length = dbcws->value.cpbVal; + } break; + case DBVT_BYTE: + { + set.Type = DBT_ST_BYTE; + set.Value.Byte = dbcws->value.bVal; + } break; + case DBVT_WORD: + { + set.Type = DBT_ST_WORD; + set.Value.Word = dbcws->value.wVal; + } break; + case DBVT_DWORD: + { + set.Type = DBT_ST_DWORD; + set.Value.DWord = dbcws->value.dVal; + } break; + default: + { + return -1; + } + } + + if (DBSettingWrite(reinterpret_cast(&set), 0) == DBT_INVALIDPARAM) + return -1; + + if (dbcws->value.type == DBVT_WCHAR) + { + dbcws->value.type = DBVT_UTF8; + wchar_t * tmp = dbcws->value.pwszVal; + dbcws->value.pszVal = mir_utf8encodeW(dbcws->value.pwszVal); + NotifyEventHooks(hSettingChangeEvent, hContact, pSetting); + mir_free(dbcws->value.pszVal); + dbcws->value.type = DBVT_WCHAR; + dbcws->value.pwszVal = tmp; + } else { + NotifyEventHooks(hSettingChangeEvent, hContact, pSetting); + } + + return 0; +} + +INT_PTR CompDeleteContactSetting(WPARAM hContact, LPARAM pSetting) +{ + DBCONTACTGETSETTING * dbcgs = reinterpret_cast(pSetting); + + char namebuf[512]; + namebuf[0] = 0; + if (dbcgs->szModule) + strcpy_s(namebuf, dbcgs->szModule); + strcat_s(namebuf, "/"); + if (dbcgs->szSetting) + strcat_s(namebuf, dbcgs->szSetting); + + TDBTSettingDescriptor desc = {0,0,0,0,0,0,0,0}; + desc.cbSize = sizeof(desc); + desc.Entity = hContact; + desc.pszSettingName = namebuf; + + if (DBSettingDelete(reinterpret_cast(&desc), 0) == DBT_INVALIDPARAM) + return -1; + + { + DBCONTACTWRITESETTING tmp = {0,0,0,0}; + tmp.szModule = dbcgs->szModule; + tmp.szSetting = dbcgs->szSetting; + tmp.value.type = 0; + NotifyEventHooks(hSettingChangeEvent, hContact, reinterpret_cast(&tmp)); + } + + return 0; +} +INT_PTR CompEnumContactSettings(WPARAM hContact, LPARAM pEnum) +{ + DBCONTACTENUMSETTINGS * pces = reinterpret_cast(pEnum); + + TDBTSettingDescriptor desc = {0,0,0,0,0,0,0,0}; + desc.cbSize = sizeof(desc); + desc.Entity = hContact; + + char namebuf[512]; + namebuf[0] = 0; + if (pces->szModule) + strcpy_s(namebuf, pces->szModule); + strcat_s(namebuf, "/"); + + TDBTSettingIterFilter filter = {0,0,0,0,0,0,0,0}; + filter.cbSize = sizeof(filter); + filter.Descriptor = &desc; + filter.hEntity = hContact; + filter.NameStart = namebuf; + + TDBTSettingIterationHandle hiter = DBSettingIterInit(reinterpret_cast(&filter), 0); + if ((hiter == 0) || (hiter == DBT_INVALIDPARAM)) + return -1; + + int res = 0; + TDBTSettingHandle hset = DBSettingIterNext(hiter, 0); + while (hset != 0) + { + char * p = strchr(desc.pszSettingName, '/'); + if (p) { + ++p; + } else { + p = desc.pszSettingName; + } + + res = pces->pfnEnumProc(p, pces->lParam); + if (res == 0) + { + hset = DBSettingIterNext(hiter, 0); + } else { + hset = 0; + } + } + + DBSettingIterClose(hiter, 0); + + if (desc.pszSettingName) + mir_free(desc.pszSettingName); + + return res; +} + + +INT_PTR CompGetEventCount(WPARAM hContact, LPARAM lParam) +{ + if (hContact == 0) + hContact = gDataBase->getEntities().getRootEntity(); + + return DBEventGetCount(hContact, 0); +} +INT_PTR CompAddEvent(WPARAM hContact, LPARAM pEventInfo) +{ + DBEVENTINFO * dbei = reinterpret_cast(pEventInfo); + if (dbei->cbSize < sizeof(DBEVENTINFO)) + return -1; + + int tmp = NotifyEventHooks(hEventFilterAddedEvent, hContact, pEventInfo); + if (tmp != 0) + return tmp; + + if (hContact == 0) + hContact = gDataBase->getEntities().getRootEntity(); + + + TDBTEvent ev = {0,0,0,0,0,0,0}; + ev.cbSize = sizeof(ev); + ev.ModuleName = dbei->szModule; + ev.Timestamp = dbei->timestamp; + ev.Flags = dbei->flags; + if (ev.Flags & DBEF_SENT) + ev.Flags = ev.Flags | DBEF_READ; + ev.EventType = dbei->eventType; + ev.cbBlob = dbei->cbBlob; + ev.pBlob = dbei->pBlob; + + int res = DBEventAdd(hContact, reinterpret_cast(&ev)); + if (res != DBT_INVALIDPARAM) + { + NotifyEventHooks(hEventAddedEvent, hContact, res); + return res; + } + return 0; +} +INT_PTR CompDeleteEvent(WPARAM hContact, LPARAM hEvent) +{ + int res = NotifyEventHooks(hEventDeletedEvent, hContact, hEvent); + + if (hContact == 0) + hContact = gDataBase->getEntities().getRootEntity(); + + if (res == 0) + return DBEventDelete(hEvent, 0); + + return res; +} +INT_PTR CompGetBlobSize(WPARAM hEvent, LPARAM lParam) +{ + int res = DBEventGetBlobSize(hEvent, 0); + if (res == DBT_INVALIDPARAM) + return -1; + + return res; +} +INT_PTR CompGetEvent(WPARAM hEvent, LPARAM pEventInfo) +{ + DBEVENTINFO * dbei = reinterpret_cast(pEventInfo); + if (dbei->cbSize < sizeof(DBEVENTINFO)) + return -1; + + TDBTEvent ev = {0,0,0,0,0,0,0}; + ev.cbSize = sizeof(ev); + ev.cbBlob = 0; + ev.pBlob = NULL; + + int res = DBEventGet(hEvent, reinterpret_cast(&ev)); + + dbei->szModule = ev.ModuleName; + dbei->timestamp = ev.Timestamp; + dbei->flags = ev.Flags; + if (dbei->flags & DBEF_SENT) + dbei->flags = dbei->flags & ~DBEF_READ; + dbei->eventType = ev.EventType; + + if (dbei->cbBlob && dbei->pBlob) + { + if (dbei->cbBlob >= ev.cbBlob) + memcpy(dbei->pBlob, ev.pBlob, ev.cbBlob); + else + memcpy(dbei->pBlob, ev.pBlob, dbei->cbBlob); + } + mir_free(ev.pBlob); + dbei->cbBlob = ev.cbBlob; + + if (res == DBT_INVALIDPARAM) + return 1; + + return res; +} +INT_PTR CompMarkEventRead(WPARAM hContact, LPARAM hEvent) +{ + int res = DBEventMarkRead(hEvent, 0); + if ((res != DBT_INVALIDPARAM) && (res & DBEF_SENT)) + res = res & ~DBEF_READ; + return res; +} +INT_PTR CompGetEventContact(WPARAM hEvent, LPARAM lParam) +{ + TDBTEntityHandle res = DBEventGetEntity(hEvent, 0); + if (res == gDataBase->getEntities().getRootEntity()) + res = 0; + + return res; +} +INT_PTR CompFindFirstEvent(WPARAM hContact, LPARAM lParam) +{ + if (hContact == 0) + hContact = gDataBase->getEntities().getRootEntity(); + + return gDataBase->getEvents().compFirstEvent(hContact); +} +INT_PTR CompFindFirstUnreadEvent(WPARAM hContact, LPARAM lParam) +{ + if (hContact == 0) + hContact = gDataBase->getEntities().getRootEntity(); + return gDataBase->getEvents().compFirstUnreadEvent(hContact); +} +INT_PTR CompFindLastEvent(WPARAM hContact, LPARAM lParam) +{ + if (hContact == 0) + hContact = gDataBase->getEntities().getRootEntity(); + return gDataBase->getEvents().compLastEvent(hContact); +} +INT_PTR CompFindNextEvent(WPARAM hEvent, LPARAM lParam) +{ + return gDataBase->getEvents().compNextEvent(hEvent); +} +INT_PTR CompFindPrevEvent(WPARAM hEvent, LPARAM lParam) +{ + return gDataBase->getEvents().compPrevEvent(hEvent); +} + +INT_PTR CompEnumModules(WPARAM wParam, LPARAM pCallback) +{ + if (!pCallback) + return -1; + + return gDataBase->getSettings().CompEnumModules(reinterpret_cast(pCallback), wParam); +} + +void Encrypt(char* msg, BOOL up) +{ + int i; + const int jump = up ? 5 : -5; + + for (i=0; msg[i]; i++) + { + msg[i] = msg[i] + jump; + } + +} + +INT_PTR CompEncodeString(WPARAM wParam, LPARAM lParam) +{ + Encrypt(reinterpret_cast(lParam),TRUE); + return 0; +} + +INT_PTR CompDecodeString(WPARAM wParam, LPARAM lParam) +{ + Encrypt(reinterpret_cast(lParam),FALSE); + return 0; +} + +INT_PTR CompGetProfileName(WPARAM cbBytes, LPARAM pszName) +{ + return gDataBase->getProfileName(cbBytes, reinterpret_cast(pszName)); +} + +INT_PTR CompGetProfilePath(WPARAM cbBytes, LPARAM pszName) +{ + return gDataBase->getProfilePath(cbBytes, reinterpret_cast(pszName)); +} + +bool CompatibilityRegister() +{ + gCompServices[ 0] = CreateServiceFunction(MS_DB_CONTACT_GETCOUNT, CompGetContactCount); + gCompServices[ 1] = CreateServiceFunction(MS_DB_CONTACT_FINDFIRST, CompFindFirstContact); + gCompServices[ 2] = CreateServiceFunction(MS_DB_CONTACT_FINDNEXT, CompFindNextContact); + gCompServices[ 3] = CreateServiceFunction(MS_DB_CONTACT_DELETE, CompDeleteContact); + gCompServices[ 4] = CreateServiceFunction(MS_DB_CONTACT_ADD, CompAddContact); + gCompServices[ 5] = CreateServiceFunction(MS_DB_CONTACT_IS, CompIsDbContact); + + gCompServices[ 6] = CreateServiceFunction(MS_DB_CONTACT_GETSETTING, CompGetContactSetting); + gCompServices[ 7] = CreateServiceFunction(MS_DB_CONTACT_GETSETTING_STR, CompGetContactSettingStr); + gCompServices[ 8] = CreateServiceFunction(MS_DB_CONTACT_GETSETTINGSTATIC, CompGetContactSettingStatic); + gCompServices[ 9] = CreateServiceFunction(MS_DB_CONTACT_FREEVARIANT, CompFreeVariant); + gCompServices[10] = CreateServiceFunction(MS_DB_CONTACT_WRITESETTING, CompWriteContactSetting); + gCompServices[11] = CreateServiceFunction(MS_DB_CONTACT_DELETESETTING, CompDeleteContactSetting); + gCompServices[12] = CreateServiceFunction(MS_DB_CONTACT_ENUMSETTINGS, CompEnumContactSettings); + //gCompServices[13] = CreateServiceFunction(MS_DB_SETSETTINGRESIDENT, CompSetSettingResident); + + gCompServices[14] = CreateServiceFunction(MS_DB_EVENT_GETCOUNT, CompGetEventCount); + gCompServices[15] = CreateServiceFunction(MS_DB_EVENT_ADD, CompAddEvent); + gCompServices[16] = CreateServiceFunction(MS_DB_EVENT_DELETE, CompDeleteEvent); + gCompServices[17] = CreateServiceFunction(MS_DB_EVENT_GETBLOBSIZE, CompGetBlobSize); + gCompServices[18] = CreateServiceFunction(MS_DB_EVENT_GET, CompGetEvent); + gCompServices[19] = CreateServiceFunction(MS_DB_EVENT_MARKREAD, CompMarkEventRead); + gCompServices[20] = CreateServiceFunction(MS_DB_EVENT_GETCONTACT, CompGetEventContact); + gCompServices[21] = CreateServiceFunction(MS_DB_EVENT_FINDFIRST, CompFindFirstEvent); + gCompServices[22] = CreateServiceFunction(MS_DB_EVENT_FINDFIRSTUNREAD, CompFindFirstUnreadEvent); + gCompServices[23] = CreateServiceFunction(MS_DB_EVENT_FINDLAST, CompFindLastEvent); + gCompServices[24] = CreateServiceFunction(MS_DB_EVENT_FINDNEXT, CompFindNextEvent); + gCompServices[25] = CreateServiceFunction(MS_DB_EVENT_FINDPREV, CompFindPrevEvent); + + gCompServices[26] = CreateServiceFunction(MS_DB_MODULES_ENUM, CompEnumModules); + + gCompServices[27] = CreateServiceFunction(MS_DB_CRYPT_ENCODESTRING, CompEncodeString); + gCompServices[28] = CreateServiceFunction(MS_DB_CRYPT_DECODESTRING, CompDecodeString); + + gCompServices[29] = CreateServiceFunction(MS_DB_GETPROFILENAME, CompGetProfileName); + gCompServices[30] = CreateServiceFunction(MS_DB_GETPROFILEPATH, CompGetProfilePath); + + + hEventDeletedEvent = CreateHookableEvent(ME_DB_EVENT_DELETED); + hEventAddedEvent = CreateHookableEvent(ME_DB_EVENT_ADDED); + hEventFilterAddedEvent = CreateHookableEvent(ME_DB_EVENT_FILTER_ADD); + hSettingChangeEvent = CreateHookableEvent(ME_DB_CONTACT_SETTINGCHANGED); + hContactDeletedEvent = CreateHookableEvent(ME_DB_CONTACT_DELETED); + hContactAddedEvent = CreateHookableEvent(ME_DB_CONTACT_ADDED); + return true; +} +bool CompatibilityUnRegister() +{ + int i; + for (i = 0; i < sizeof(gCompServices) / sizeof(gCompServices[0]); ++i) + { + DestroyServiceFunction(gCompServices[i]); + } + return true; +} diff --git a/plugins/Dbx_tree/Compatibility.h b/plugins/Dbx_tree/Compatibility.h new file mode 100644 index 0000000000..b6b6c16f89 --- /dev/null +++ b/plugins/Dbx_tree/Compatibility.h @@ -0,0 +1,65 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#pragma once + +#include "Interface.h" +#include "DataBase.h" +#include "Services.h" + +bool CompatibilityRegister(); +bool CompatibilityUnRegister(); + +INT_PTR CompGetContactCount(WPARAM wParam,LPARAM lParam); +INT_PTR CompFindFirstContact(WPARAM wParam,LPARAM lParam); +INT_PTR CompFindNextContact(WPARAM hContact,LPARAM lParam); +INT_PTR CompDeleteContact(WPARAM hContact,LPARAM lParam); +INT_PTR CompAddContact(WPARAM wParam,LPARAM lParam); +INT_PTR CompIsDbContact(WPARAM hContact,LPARAM lParam); + +INT_PTR CompGetContactSetting(WPARAM hContact, LPARAM pSetting); +INT_PTR CompGetContactSettingStr(WPARAM hContact, LPARAM pSetting); +INT_PTR CompGetContactSettingStatic(WPARAM hContact, LPARAM pSetting); +INT_PTR CompFreeVariant(WPARAM wParam, LPARAM pSetting); +INT_PTR CompWriteContactSetting(WPARAM hContact, LPARAM pSetting); +INT_PTR CompDeleteContactSetting(WPARAM hContact, LPARAM pSetting); +INT_PTR CompEnumContactSettings(WPARAM hContact, LPARAM pEnum); + +INT_PTR CompGetEventCount(WPARAM wParam, LPARAM lParam); +INT_PTR CompAddEvent(WPARAM hContact, LPARAM pEventInfo); +INT_PTR CompDeleteEvent(WPARAM hContact, LPARAM hEvent); +INT_PTR CompGetBlobSize(WPARAM hEvent, LPARAM lParam); +INT_PTR CompGetEvent(WPARAM hEvent, LPARAM pEventInfo); +INT_PTR CompMarkEventRead(WPARAM hContact, LPARAM hEvent); +INT_PTR CompGetEventContact(WPARAM hEvent, LPARAM lParam); +INT_PTR CompFindFirstEvent(WPARAM hContact, LPARAM lParam); +INT_PTR CompFindFirstUnreadEvent(WPARAM hContact, LPARAM lParam); +INT_PTR CompFindLastEvent(WPARAM hContact, LPARAM lParam); +INT_PTR CompFindNextEvent(WPARAM hEvent, LPARAM lParam); +INT_PTR CompFindPrevEvent(WPARAM hEvent, LPARAM lParam); + + +INT_PTR CompEncodeString(WPARAM wParam, LPARAM lParam); +INT_PTR CompDecodeString(WPARAM wParam, LPARAM lParam); + +INT_PTR CompGetProfileName(WPARAM cbBytes, LPARAM pszName); +INT_PTR CompGetProfilePath(WPARAM cbBytes, LPARAM pszName); diff --git a/plugins/Dbx_tree/DataBase.cpp b/plugins/Dbx_tree/DataBase.cpp new file mode 100644 index 0000000000..85a9d67ccb --- /dev/null +++ b/plugins/Dbx_tree/DataBase.cpp @@ -0,0 +1,374 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "DataBase.h" +#include "newpluginapi.h" +#ifndef _MSC_VER +#include "savestrings_gcc.h" +#endif +#include "Logger.h" + +CDataBase *gDataBase = NULL; + +CDataBase::CDataBase(const char* FileName) +{ + int len; +#ifdef UNICODE + len = MultiByteToWideChar(CP_ACP, 0, FileName, -1, NULL, 0); + m_FileName[0] = new TCHAR[len + 1]; + MultiByteToWideChar(CP_ACP, 0, FileName, -1, m_FileName[0], len + 1); + m_FileName[0][len] = 0; +#else + len = strlen(FileName); + m_FileName[0] = new TCHAR[len + 1]; + strcpy_s(m_FileName[0], len + 1, FileName); +#endif + + TCHAR * tmp = _tcsrchr(m_FileName[0], '.'); + if (tmp) + { + m_FileName[1] = new TCHAR[len + 1]; + _tcsncpy_s(m_FileName[1], len + 1, m_FileName[0], tmp - m_FileName[0]); + _tcscat_s(m_FileName[1], len + 1, _T(".pri")); + } else { + m_FileName[1] = new TCHAR[len + 5]; + _tcscpy_s(m_FileName[1], len + 5, m_FileName[0]); + _tcscat_s(m_FileName[1], len + 5, _T(".pri")); + } + + m_Opened = false; + + for (int i = 0; i < DBFileMax; ++i) + { + m_BlockManager[i] = NULL; + m_FileAccess[i] = NULL; + m_EncryptionManager[i] = NULL; + m_HeaderBlock[i] = 0; + } + + m_Entities = NULL; + m_Settings = NULL; + m_Events = NULL; +} +CDataBase::~CDataBase() +{ + if (m_Events) delete m_Events; + if (m_Settings) delete m_Settings; + if (m_Entities) delete m_Entities; + + m_Entities = NULL; + m_Settings = NULL; + m_Events = NULL; + + for (int i = DBFileMax - 1; i >= 0; --i) + { + if (m_BlockManager[i]) delete m_BlockManager[i]; + if (m_FileAccess[i]) delete m_FileAccess[i]; + if (m_EncryptionManager[i]) delete m_EncryptionManager[i]; + + m_BlockManager[i] = NULL; + m_FileAccess[i] = NULL; + m_EncryptionManager[i] = NULL; + + delete [] (m_FileName[i]); + } + +} +int CDataBase::CreateDB() +{ + /// TODO: create and show wizard + if (!CreateNewFile(DBFileSetting) || + !CreateNewFile(DBFilePrivate)) + return EMKPRF_CREATEFAILED; + + return 0; +} + + +int CDataBase::CheckFile(TDBFileType Index) +{ + TGenericFileHeader h; + memset(&h, 0, sizeof(h)); + DWORD r = 0; + HANDLE htmp = CreateFile(m_FileName[Index], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); + if (htmp != INVALID_HANDLE_VALUE) + { + SetFilePointer(htmp, 0, NULL, FILE_BEGIN); + if (ReadFile(htmp, &h, sizeof(h), &r, NULL)) + { + if (0 != memcmp(h.Gen.Signature, cFileSignature[Index], sizeof(cFileSignature[Index]))) + { + CloseHandle(htmp); + return EGROKPRF_UNKHEADER; + } + + if (cDBVersion < h.Gen.Version) + { + CloseHandle(htmp); + return EGROKPRF_VERNEWER; + } + + CloseHandle(htmp); + return EGROKPRF_NOERROR; + } + CloseHandle(htmp); + } + + return EGROKPRF_CANTREAD; +} + +int CDataBase::CheckDB() +{ + int res = CheckFile(DBFileSetting); + + if (res != EGROKPRF_NOERROR) + return res; + + if (PrivateFileExists()) + res = CheckFile(DBFilePrivate); + + return res; +} + +int CDataBase::LoadFile(TDBFileType Index) +{ + TGenericFileHeader h; + m_EncryptionManager[Index] = new CEncryptionManager; + + if (CMappedMemory::InitMMAP()) + m_FileAccess[Index] = new CMappedMemory(m_FileName[Index]); + else + m_FileAccess[Index] = new CDirectAccess(m_FileName[Index]); + + m_FileAccess[Index]->Read(&h, 0, sizeof(h)); + m_EncryptionManager[Index]->InitEncryption(h.Gen.FileEncryption); + + m_FileAccess[Index]->Size(h.Gen.FileSize); + m_FileAccess[Index]->sigFileSizeChanged().connect(this, &CDataBase::onFileSizeChanged); + + m_BlockManager[Index] = new CBlockManager(*m_FileAccess[Index], *m_EncryptionManager[Index]); + + CBlockManager::WriteTransaction trans(*m_BlockManager[Index]); // don't fire size event until header is loaded + + m_HeaderBlock[Index] = m_BlockManager[Index]->ScanFile(sizeof(h), cHeaderBlockSignature, h.Gen.FileSize); + + if (m_HeaderBlock[Index] == 0) + { + LOG(logCRITICAL, _T("Header Block not found! File damaged: \"%s\""), m_FileName[Index]); + return -1; + } + + uint32_t size = sizeof(h); + uint32_t sig = -1; + m_Header[Index] = m_BlockManager[Index]->ReadBlock(0, size, sig); + + sig = cHeaderBlockSignature; + TGenericFileHeader * buf = m_BlockManager[Index]->ReadBlock(m_HeaderBlock[Index], size, sig); + if (!buf) + { + LOG(logCRITICAL, _T("Header Block cannot be read! File damaged: \"%s\""), m_FileName[Index]); + return -1; + } + + buf->Gen.Obscure = 0; + + if (memcmp(m_Header[Index], buf, size)) + { + LOG(logCRITICAL, _T("Header Block in \"%s\" damaged!"), m_FileName[Index]); + return -1; + } + + return 0; +} + +int CDataBase::OpenDB() +{ + if (!PrivateFileExists()) + { + // TODO WIZARD + if (!CreateNewFile(DBFilePrivate)) + return -1; + } + + int res = LoadFile(DBFileSetting); + if ((res != 0) && (CLogger::logERROR <= CLogger::Instance().ShowMessage())) + { + return res; + } + + res = LoadFile(DBFilePrivate); + + if ((res != 0) && (CLogger::logERROR <= CLogger::Instance().ShowMessage())) + { + return res; + } + if (CLogger::logERROR <= CLogger::Instance().ShowMessage()) + return -1; + + m_Entities = new CEntities(*m_BlockManager[DBFilePrivate], + m_Header[DBFilePrivate]->Pri.RootEntity, + m_Header[DBFilePrivate]->Pri.Entities, + m_Header[DBFilePrivate]->Pri.Virtuals); + + m_Entities->sigRootChanged().connect(this, &CDataBase::onEntitiesRootChanged); + m_Entities->sigVirtualRootChanged().connect(this, &CDataBase::onVirtualsRootChanged); + + if (m_Entities->getRootEntity() != m_Header[DBFilePrivate]->Pri.RootEntity) + { + m_Header[DBFilePrivate]->Pri.RootEntity = m_Entities->getRootEntity(); + ReWriteHeader(DBFilePrivate); + } + + m_Settings = new CSettings(*m_BlockManager[DBFileSetting], + *m_BlockManager[DBFilePrivate], + m_Header[DBFileSetting]->Set.Settings, + *m_Entities); + + m_Settings->sigRootChanged().connect(this, &CDataBase::onSettingsRootChanged); + + m_Events = new CEvents(*m_BlockManager[DBFilePrivate], + *m_EncryptionManager[DBFilePrivate], + *m_Entities, + *m_Settings); + + return 0; +} + +bool CDataBase::PrivateFileExists() +{ + HANDLE htmp = CreateFile(m_FileName[DBFilePrivate], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); + if (htmp != INVALID_HANDLE_VALUE) + { + CloseHandle(htmp); + return true; + } + + return false; +} + + +bool CDataBase::CreateNewFile(TDBFileType File) +{ + CEncryptionManager enc; + CDirectAccess fa(m_FileName[File]); + fa.Size(sizeof(TGenericFileHeader)); + CBlockManager bm(fa, enc); + bm.ScanFile(sizeof(TGenericFileHeader), 0, sizeof(TGenericFileHeader)); + + CBlockManager::WriteTransaction trans(bm); + + uint32_t block; + TGenericFileHeader * buf = bm.CreateBlock(block, cHeaderBlockSignature); + uint32_t size = 0; + uint32_t sig = -1; + TGenericFileHeader * h = bm.ReadBlock(0, size, sig); + + memset(h, 0, sizeof(TGenericFileHeader)); + memcpy(&h->Gen.Signature, &cFileSignature[File], sizeof(h->Gen.Signature)); + h->Gen.Version = cDBVersion; + h->Gen.FileSize = fa.Size(); + + memcpy(buf, h, sizeof(TGenericFileHeader)); + bm.UpdateBlock(block, 0); + bm.UpdateBlock(0, -1); + + return true; +} + +inline void CDataBase::ReWriteHeader(TDBFileType Index) +{ + m_BlockManager[Index]->UpdateBlock(0, -1); + uint32_t size = 0, sig = 0; + TGenericFileHeader * h = m_BlockManager[Index]->ReadBlock(m_HeaderBlock[Index], size, sig); + + *h = *m_Header[Index]; + h->Gen.Obscure = GetTickCount(); + m_BlockManager[Index]->UpdateBlock(m_HeaderBlock[Index], 0); +} + + +void CDataBase::onSettingsRootChanged(CSettings* Settings, CSettingsTree::TNodeRef NewRoot) +{ + m_Header[DBFileSetting]->Set.Settings = NewRoot; + ReWriteHeader(DBFileSetting); +} +void CDataBase::onVirtualsRootChanged(void* Virtuals, CVirtuals::TNodeRef NewRoot) +{ + m_Header[DBFilePrivate]->Pri.Virtuals = NewRoot; + ReWriteHeader(DBFilePrivate); +} +void CDataBase::onEntitiesRootChanged(void* Entities, CEntities::TNodeRef NewRoot) +{ + m_Header[DBFilePrivate]->Pri.Entities = NewRoot; + ReWriteHeader(DBFilePrivate); +} +void CDataBase::onFileSizeChanged(CFileAccess * File, uint32_t Size) +{ + if (File == m_FileAccess[DBFileSetting]) + { + m_Header[DBFileSetting]->Gen.FileSize = Size; + ReWriteHeader(DBFileSetting); + } else { + m_Header[DBFilePrivate]->Gen.FileSize = Size; + ReWriteHeader(DBFilePrivate); + } +} + +int CDataBase::getProfileName(int BufferSize, char * Buffer) +{ + TCHAR * slash = _tcsrchr(m_FileName[DBFileSetting], '\\'); + if (slash) + slash++; + else + slash = m_FileName[DBFileSetting]; + + int l = static_cast(_tcslen(slash)); + if (BufferSize < l + 1) + return -1; + + char * tmp = mir_t2a(slash); + strcpy_s(Buffer, BufferSize, tmp); + mir_free(tmp); + + return 0; +} +int CDataBase::getProfilePath(int BufferSize, char * Buffer) +{ + TCHAR * slash = _tcsrchr(m_FileName[DBFileSetting], '\\'); + if (!slash) + return -1; + + int l = slash - m_FileName[DBFileSetting]; + + if (BufferSize < l + 1) + { + return -1; + } + + *slash = 0; + char * tmp = mir_t2a(m_FileName[DBFileSetting]); + strcpy_s(Buffer, BufferSize, tmp); + mir_free(tmp); + *slash = '\\'; + + return 0; +} diff --git a/plugins/Dbx_tree/DataBase.h b/plugins/Dbx_tree/DataBase.h new file mode 100644 index 0000000000..2577249572 --- /dev/null +++ b/plugins/Dbx_tree/DataBase.h @@ -0,0 +1,157 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#pragma once + +#ifndef _MSC_VER +#include +#else +#include "stdint.h" +#endif +#include "MREWSync.h" + +#include "Events.h" +#include "Settings.h" +#include "Entities.h" + +#include "FileAccess.h" +#include "MappedMemory.h" +#include "DirectAccess.h" +#include "Blockmanager.h" + +#include "sigslot.h" + +#include "EncryptionManager.h" + +typedef enum TDBFileType { + DBFileSetting = 0, + DBFilePrivate = 1, + DBFileMax = 2 +} TDBFileType; + +static const uint8_t cFileSignature[DBFileMax][20] = {"Miranda IM Settings", "Miranda IM DataTree"}; +static const uint32_t cDBVersion = 0x00000001; + +static const uint32_t cHeaderBlockSignature = 0x7265491E; + +#pragma pack(push, 1) // push current alignment to stack, set alignment to 1 byte boundary + +typedef struct TSettingsHeader { + uint8_t Signature[20]; /// signature must be cSettingsHeader + uint32_t Version; /// internal DB version cDataBaseVersion + uint32_t Obscure; + TFileEncryption FileEncryption; /// Encryption Method + uint32_t FileStructureBlock; /// Offset of CBlockManager master block + uint32_t FileSize; /// Offset to the last used byte + 1 + uint32_t Settings; /// Offset to the SettingsBTree RootNode + uint8_t Reserved[256 - sizeof(TFileEncryption) - 20 - 5*sizeof(uint32_t)]; /// reserved storage +} TSettingsHeader; + +typedef struct TPrivateHeader { + uint8_t Signature[20]; /// signature must be CDataHeader + uint32_t Version; /// internal DB version cDataBaseVersion + uint32_t Obscure; + TFileEncryption FileEncryption; /// Encryption Method + uint32_t FileStructureBlock; /// Offset of CBlockManager master block + uint32_t FileSize; /// Offset to the last used byte + 1 + uint32_t RootEntity; /// Offset to the Root CList Entity + uint32_t Entities; /// Offset to the EntityBTree RootNode + uint32_t Virtuals; /// Offset to the VirtualsBTree RootNode + uint8_t Reserved[256 - sizeof(TFileEncryption) - 20 - 7*sizeof(uint32_t)]; /// reserved storage +} TPrivateHeader; + + +typedef union TGenericFileHeader { + struct { + uint8_t Signature[20]; /// signature must be cSettingsHeader + uint32_t Version; /// internal DB version cDataBaseVersion + uint32_t Obscure; + TFileEncryption FileEncryption; /// Encryption Method + uint32_t FileStructureBlock; /// Offset of CBlockManager master block + uint32_t FileSize; /// Offset to the last used byte + 1 + uint8_t Reserved[256 - sizeof(TFileEncryption) - 20 - 4*sizeof(uint32_t)]; /// reserved storage + } Gen; + TSettingsHeader Set; + TPrivateHeader Pri; +} TGenericFileHeader; + +#pragma pack(pop) + + +class CDataBase : public sigslot::has_slots<> +{ +private: + TCHAR* m_FileName[DBFileMax]; + bool m_Opened; + + CBlockManager *m_BlockManager[DBFileMax]; + CFileAccess *m_FileAccess[DBFileMax]; + TGenericFileHeader * m_Header[DBFileMax]; + CEncryptionManager *m_EncryptionManager[DBFileMax]; + + uint32_t m_HeaderBlock[DBFileMax]; + + void onSettingsRootChanged(CSettings* Settings, CSettingsTree::TNodeRef NewRoot); + void onVirtualsRootChanged(void* Virtuals, CVirtuals::TNodeRef NewRoot); + void onEntitiesRootChanged(void* Entities, CEntities::TNodeRef NewRoot); + void onFileSizeChanged(CFileAccess * File, uint32_t Size); + + bool PrivateFileExists(); + bool CreateNewFile(TDBFileType File); + + int CheckFile(TDBFileType Index); + int LoadFile(TDBFileType Index); +protected: + CEntities *m_Entities; + CSettings *m_Settings; + CEvents *m_Events; + + void ReWriteHeader(TDBFileType Index); + +public: + CDataBase(const char* FileName); + virtual ~CDataBase(); + + int CreateDB(); + int CheckDB(); + int OpenDB(); + + CEntities & getEntities() + { + return *m_Entities; + } + CSettings & getSettings() + { + return *m_Settings; + } + CEvents & getEvents() + { + return *m_Events; + } + + int getProfileName(int BufferSize, char * Buffer); + int getProfilePath(int BufferSize, char * Buffer); + +}; + + +extern CDataBase *gDataBase; diff --git a/plugins/Dbx_tree/DatabaseLink.cpp b/plugins/Dbx_tree/DatabaseLink.cpp new file mode 100644 index 0000000000..b6cd91eda4 --- /dev/null +++ b/plugins/Dbx_tree/DatabaseLink.cpp @@ -0,0 +1,178 @@ +/* + +dbx_tree: tree database driver for Miranda IM + +Copyright 2007-2010 Michael "Protogenes" Kunz, + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "DatabaseLink.h" +#ifndef _MSC_VER +#include "savestrings_gcc.h" +#endif + +static int getCapability(int); +static int getFriendlyName(char*, size_t, int); +static int makeDatabase(char*, int*); +static int grokHeader(char*, int*); +static int Load(char*, void*); +static int Unload(int); + +DATABASELINK gDBLink = { + sizeof(DATABASELINK), + getCapability, + getFriendlyName, + makeDatabase, + grokHeader, + Load, + Unload, +}; + +PLUGINLINK *pluginLink = NULL; +MM_INTERFACE mmi = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +UTF8_INTERFACE utfi = {0,0,0,0,0,0,0}; +HANDLE hSystemModulesLoaded = 0; + + +static int SystemModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + Update upd = {0,0,0,0,0,0,0,0,0,0,0,0,0}; + + upd.cbSize = sizeof(upd); + upd.szComponentName = gInternalName; + upd.szBetaVersionURL = "http://www-user.tu-chemnitz.de/~kunmi/?dbx_tree"; +#ifdef _M_X64 + upd.pbBetaVersionPrefix = (BYTE *)"