mirror of
https://github.com/moparisthebest/keepass2android
synced 2025-01-10 21:18:18 -05:00
Commit of initial kdbx load perf testing and optimisations
This commit is contained in:
parent
9d8e10b236
commit
f157329cab
@ -40,8 +40,14 @@ namespace KeePassLib.Collections
|
|||||||
IDeepCloneable<ProtectedBinaryDictionary>,
|
IDeepCloneable<ProtectedBinaryDictionary>,
|
||||||
IEnumerable<KeyValuePair<string, ProtectedBinary>>
|
IEnumerable<KeyValuePair<string, ProtectedBinary>>
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
private SortedDictionary<string, ProtectedBinary> m_vBinaries =
|
private SortedDictionary<string, ProtectedBinary> m_vBinaries =
|
||||||
new SortedDictionary<string, ProtectedBinary>();
|
new SortedDictionary<string, ProtectedBinary>();
|
||||||
|
*/
|
||||||
|
|
||||||
|
private Dictionary<string, ProtectedBinary> m_vBinaries =
|
||||||
|
new Dictionary<string, ProtectedBinary>();
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the number of binaries in this entry.
|
/// Get the number of binaries in this entry.
|
||||||
|
@ -40,8 +40,9 @@ namespace KeePassLib.Collections
|
|||||||
IDeepCloneable<ProtectedStringDictionary>,
|
IDeepCloneable<ProtectedStringDictionary>,
|
||||||
IEnumerable<KeyValuePair<string, ProtectedString>>
|
IEnumerable<KeyValuePair<string, ProtectedString>>
|
||||||
{
|
{
|
||||||
private SortedDictionary<string, ProtectedString> m_vStrings =
|
/*private SortedDictionary<string, ProtectedString> m_vStrings =
|
||||||
new SortedDictionary<string, ProtectedString>();
|
new SortedDictionary<string, ProtectedString>();*/
|
||||||
|
private Dictionary<string, ProtectedString> m_vStrings = new Dictionary<string, ProtectedString>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the number of strings in this entry.
|
/// Get the number of strings in this entry.
|
||||||
|
@ -101,5 +101,19 @@ namespace KeePassLib.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bModified">Update last modification time.</param>
|
/// <param name="bModified">Update last modification time.</param>
|
||||||
void Touch(bool bModified);
|
void Touch(bool bModified);
|
||||||
|
|
||||||
|
#region Set times lazily
|
||||||
|
// Passing xml datetime string to be parsed only on demand
|
||||||
|
|
||||||
|
void SetLazyLastModificationTime(string xmlDateTime);
|
||||||
|
|
||||||
|
void SetLazyCreationTime(string xmlDateTime);
|
||||||
|
|
||||||
|
void SetLazyLastAccessTime(string xmlDateTime);
|
||||||
|
|
||||||
|
void SetLazyExpiryTime(string xmlDateTime);
|
||||||
|
|
||||||
|
void SetLazyLocationChanged(string xmlDateTime);
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>False</Optimize>
|
<Optimize>False</Optimize>
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
<DefineConstants>DEBUG;</DefineConstants>
|
<DefineConstants>TRACE;DEBUG;</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<ConsolePause>False</ConsolePause>
|
<ConsolePause>False</ConsolePause>
|
||||||
@ -113,6 +113,7 @@
|
|||||||
<Compile Include="Security\XorredBuffer.cs" />
|
<Compile Include="Security\XorredBuffer.cs" />
|
||||||
<Compile Include="Security\ProtectedBinary.cs" />
|
<Compile Include="Security\ProtectedBinary.cs" />
|
||||||
<Compile Include="Security\ProtectedString.cs" />
|
<Compile Include="Security\ProtectedString.cs" />
|
||||||
|
<Compile Include="Serialization\AsynchronousBufferedXmlReader.cs" />
|
||||||
<Compile Include="Serialization\BinaryReaderEx.cs" />
|
<Compile Include="Serialization\BinaryReaderEx.cs" />
|
||||||
<Compile Include="Serialization\FileLock.cs" />
|
<Compile Include="Serialization\FileLock.cs" />
|
||||||
<Compile Include="Serialization\FileTransactionEx.cs" />
|
<Compile Include="Serialization\FileTransactionEx.cs" />
|
||||||
|
@ -40,6 +40,7 @@ namespace KeePassLib
|
|||||||
private PwUuid m_uuid = PwUuid.Zero;
|
private PwUuid m_uuid = PwUuid.Zero;
|
||||||
private PwGroup m_pParentGroup = null;
|
private PwGroup m_pParentGroup = null;
|
||||||
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
||||||
|
private string m_tParentGroupLastModLazy;
|
||||||
|
|
||||||
private ProtectedStringDictionary m_listStrings = new ProtectedStringDictionary();
|
private ProtectedStringDictionary m_listStrings = new ProtectedStringDictionary();
|
||||||
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
|
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
|
||||||
@ -56,6 +57,12 @@ namespace KeePassLib
|
|||||||
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
|
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
|
||||||
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
|
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
|
||||||
private DateTime m_tExpire = PwDefs.DtDefaultNow;
|
private DateTime m_tExpire = PwDefs.DtDefaultNow;
|
||||||
|
|
||||||
|
private string m_tCreationLazy;
|
||||||
|
private string m_tLastModLazy;
|
||||||
|
private string m_tLastAccessLazy;
|
||||||
|
private string m_tExpireLazy;
|
||||||
|
|
||||||
private bool m_bExpires = false;
|
private bool m_bExpires = false;
|
||||||
private ulong m_uUsageCount = 0;
|
private ulong m_uUsageCount = 0;
|
||||||
|
|
||||||
@ -92,8 +99,13 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime LocationChanged
|
public DateTime LocationChanged
|
||||||
{
|
{
|
||||||
get { return m_tParentGroupLastMod; }
|
get { return GetLazyTime(ref m_tParentGroupLastModLazy, ref m_tParentGroupLastMod); }
|
||||||
set { m_tParentGroupLastMod = value; }
|
set { m_tParentGroupLastMod = value; m_tParentGroupLastModLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyLocationChanged(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tParentGroupLastModLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -195,8 +207,13 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime CreationTime
|
public DateTime CreationTime
|
||||||
{
|
{
|
||||||
get { return m_tCreation; }
|
get { return GetLazyTime(ref m_tCreationLazy, ref m_tCreation); }
|
||||||
set { m_tCreation = value; }
|
set { m_tCreation = value; m_tCreationLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyCreationTime(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tCreationLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -204,8 +221,13 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime LastAccessTime
|
public DateTime LastAccessTime
|
||||||
{
|
{
|
||||||
get { return m_tLastAccess; }
|
get { return GetLazyTime(ref m_tLastAccessLazy, ref m_tLastAccess); }
|
||||||
set { m_tLastAccess = value; }
|
set { m_tLastAccess = value; m_tLastAccessLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyLastAccessTime(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tLastAccessLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -213,8 +235,13 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime LastModificationTime
|
public DateTime LastModificationTime
|
||||||
{
|
{
|
||||||
get { return m_tLastMod; }
|
get { return GetLazyTime(ref m_tLastModLazy, ref m_tLastMod); }
|
||||||
set { m_tLastMod = value; }
|
set { m_tLastMod = value; m_tLastModLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyLastModificationTime(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tLastModLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -223,8 +250,13 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime ExpiryTime
|
public DateTime ExpiryTime
|
||||||
{
|
{
|
||||||
get { return m_tExpire; }
|
get { return GetLazyTime(ref m_tExpireLazy, ref m_tExpire); }
|
||||||
set { m_tExpire = value; }
|
set { m_tExpire = value; m_tExpireLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyExpiryTime(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tExpireLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -844,6 +876,16 @@ namespace KeePassLib
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DateTime GetLazyTime(ref string lazyTime, ref DateTime dateTime)
|
||||||
|
{
|
||||||
|
if (lazyTime != null)
|
||||||
|
{
|
||||||
|
dateTime = TimeUtil.DeserializeUtcOrDefault(lazyTime, dateTime);
|
||||||
|
lazyTime = null;
|
||||||
|
}
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class PwEntryComparer : IComparer<PwEntry>
|
public sealed class PwEntryComparer : IComparer<PwEntry>
|
||||||
|
@ -42,6 +42,7 @@ namespace KeePassLib
|
|||||||
private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>();
|
private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>();
|
||||||
private PwGroup m_pParentGroup = null;
|
private PwGroup m_pParentGroup = null;
|
||||||
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
||||||
|
private string m_tParentGroupLastModLazy;
|
||||||
|
|
||||||
private PwUuid m_uuid = PwUuid.Zero;
|
private PwUuid m_uuid = PwUuid.Zero;
|
||||||
private string m_strName = string.Empty;
|
private string m_strName = string.Empty;
|
||||||
@ -54,6 +55,12 @@ namespace KeePassLib
|
|||||||
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
|
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
|
||||||
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
|
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
|
||||||
private DateTime m_tExpire = PwDefs.DtDefaultNow;
|
private DateTime m_tExpire = PwDefs.DtDefaultNow;
|
||||||
|
|
||||||
|
private string m_tCreationLazy;
|
||||||
|
private string m_tLastModLazy;
|
||||||
|
private string m_tLastAccessLazy;
|
||||||
|
private string m_tExpireLazy;
|
||||||
|
|
||||||
private bool m_bExpires = false;
|
private bool m_bExpires = false;
|
||||||
private ulong m_uUsageCount = 0;
|
private ulong m_uUsageCount = 0;
|
||||||
|
|
||||||
@ -146,8 +153,13 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime LocationChanged
|
public DateTime LocationChanged
|
||||||
{
|
{
|
||||||
get { return m_tParentGroupLastMod; }
|
get { return GetLazyTime(ref m_tParentGroupLastModLazy, ref m_tParentGroupLastMod); }
|
||||||
set { m_tParentGroupLastMod = value; }
|
set { m_tParentGroupLastMod = value; m_tParentGroupLastModLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyLocationChanged(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tParentGroupLastModLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -165,17 +177,13 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime CreationTime
|
public DateTime CreationTime
|
||||||
{
|
{
|
||||||
get { return m_tCreation; }
|
get { return GetLazyTime(ref m_tCreationLazy, ref m_tCreation); }
|
||||||
set { m_tCreation = value; }
|
set { m_tCreation = value; m_tCreationLazy = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void SetLazyCreationTime(string xmlDateTime)
|
||||||
/// The date/time when this group was last modified.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime LastModificationTime
|
|
||||||
{
|
{
|
||||||
get { return m_tLastMod; }
|
m_tCreationLazy = xmlDateTime;
|
||||||
set { m_tLastMod = value; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -183,17 +191,42 @@ namespace KeePassLib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime LastAccessTime
|
public DateTime LastAccessTime
|
||||||
{
|
{
|
||||||
get { return m_tLastAccess; }
|
get { return GetLazyTime(ref m_tLastAccessLazy, ref m_tLastAccess); }
|
||||||
set { m_tLastAccess = value; }
|
set { m_tLastAccess = value; m_tLastAccessLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyLastAccessTime(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tLastAccessLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The date/time when this group expires.
|
/// The date/time when this group was last modified.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime LastModificationTime
|
||||||
|
{
|
||||||
|
get { return GetLazyTime(ref m_tLastModLazy, ref m_tLastMod); }
|
||||||
|
set { m_tLastMod = value; m_tLastModLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyLastModificationTime(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tLastModLazy = xmlDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The date/time when this group expires. Use the <c>Expires</c> property
|
||||||
|
/// to specify if the group does actually expire or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime ExpiryTime
|
public DateTime ExpiryTime
|
||||||
{
|
{
|
||||||
get { return m_tExpire; }
|
get { return GetLazyTime(ref m_tExpireLazy, ref m_tExpire); }
|
||||||
set { m_tExpire = value; }
|
set { m_tExpire = value; m_tExpireLazy = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLazyExpiryTime(string xmlDateTime)
|
||||||
|
{
|
||||||
|
m_tExpireLazy = xmlDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1422,6 +1455,16 @@ namespace KeePassLib
|
|||||||
}
|
}
|
||||||
m_listGroups.Clear();
|
m_listGroups.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DateTime GetLazyTime(ref string lazyTime, ref DateTime dateTime)
|
||||||
|
{
|
||||||
|
if (lazyTime != null)
|
||||||
|
{
|
||||||
|
dateTime = TimeUtil.DeserializeUtcOrDefault(lazyTime, m_tLastMod);
|
||||||
|
lazyTime = null;
|
||||||
|
}
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class PwGroupComparer : IComparer<PwGroup>
|
public sealed class PwGroupComparer : IComparer<PwGroup>
|
||||||
|
@ -0,0 +1,367 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace KeePassLib.Serialization
|
||||||
|
{
|
||||||
|
public class AsynchronousBufferedXmlReader : XmlReader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An element which indicates the end of the XML document has been reached.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Element EndMarker = new Element();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The next buffered element available for reading.
|
||||||
|
/// Volatility: only read/written to by non-buffering thread. Passed to the buffer thread as an initial parameter.
|
||||||
|
/// </summary>
|
||||||
|
Element mBufferQueueHead = new Element(); // Start off with the pre-document element. No content, yet.
|
||||||
|
|
||||||
|
private readonly Thread mWorkerThread;
|
||||||
|
private readonly AutoResetEvent mWaitForBuffer = new AutoResetEvent(false);
|
||||||
|
/// <summary>
|
||||||
|
/// True while the reader thread is stalled waiting for buffering.
|
||||||
|
/// Volaitlity: Only written by read thread. Only read by buffer thread
|
||||||
|
/// </summary>
|
||||||
|
private volatile bool mWaitingForBuffer;
|
||||||
|
|
||||||
|
#if TRACE
|
||||||
|
private Stopwatch mReadWaitTimer = new Stopwatch();
|
||||||
|
private Stopwatch mBufferCompletedTimer = new Stopwatch();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Testing helper method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static XmlReader FullyBuffer(Stream input)
|
||||||
|
{
|
||||||
|
var reader = new AsynchronousBufferedXmlReader();
|
||||||
|
reader.ReadStreamWorker(input);
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AsynchronousBufferedXmlReader()
|
||||||
|
{
|
||||||
|
// Once the end is reached, it stays there.
|
||||||
|
EndMarker.NextElement = EndMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsynchronousBufferedXmlReader(Stream input) : this()
|
||||||
|
{
|
||||||
|
mWorkerThread = new Thread(ReadStreamWorker) { Name = GetType().Name };
|
||||||
|
mWorkerThread.Start(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Buffering
|
||||||
|
private void ReadStreamWorker(object state)
|
||||||
|
{
|
||||||
|
var input = (Stream)state;
|
||||||
|
|
||||||
|
var xr = XmlReader.Create(input, KdbxFile.CreateStdXmlReaderSettings());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The last buffered element available for reading.
|
||||||
|
/// </summary>
|
||||||
|
Element bufferQueueTail = mBufferQueueHead;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The element currently being buffered. Not yet available for reading.
|
||||||
|
/// </summary>
|
||||||
|
Element currentElement = null;
|
||||||
|
|
||||||
|
while (xr.Read())
|
||||||
|
{
|
||||||
|
switch (xr.NodeType)
|
||||||
|
{
|
||||||
|
case XmlNodeType.Element:
|
||||||
|
// Start a new element
|
||||||
|
if (currentElement != null)
|
||||||
|
{
|
||||||
|
// Add the previous current element to the tail of the buffer
|
||||||
|
bufferQueueTail.NextElement = currentElement;
|
||||||
|
bufferQueueTail = currentElement;
|
||||||
|
if (mWaitingForBuffer) mWaitForBuffer.Set(); // Signal that a new element is available in the buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
currentElement = new Element { Name = xr.Name };
|
||||||
|
|
||||||
|
// Process attributes - current optimisation, all elements have 0 or 1 attribute
|
||||||
|
if (xr.MoveToNextAttribute())
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Debug.Assert(xr.AttributeCount == 1);
|
||||||
|
currentElement.AttributeName = xr.Name;
|
||||||
|
#endif
|
||||||
|
currentElement.AttributeValue = xr.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentElement.IsEmpty = xr.IsEmptyElement;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XmlNodeType.Text:
|
||||||
|
currentElement.Value = xr.Value;
|
||||||
|
currentElement.IsEmpty = true; // Mark as empty because it will have no end element written for it
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XmlNodeType.EndElement:
|
||||||
|
Debug.Assert(currentElement != null, "Ending an element that was never started");
|
||||||
|
|
||||||
|
// If this is an element with children (not one with a value) add an end element marker to the queue
|
||||||
|
if (currentElement.Value == null || currentElement.Name != xr.Name)
|
||||||
|
{
|
||||||
|
bufferQueueTail.NextElement = currentElement;
|
||||||
|
bufferQueueTail = currentElement;
|
||||||
|
if (mWaitingForBuffer) mWaitForBuffer.Set(); // Signal that a new element is available in the buffer
|
||||||
|
|
||||||
|
currentElement = new Element { Name = xr.Name, IsEndElement = true };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conclude the document, add the final element to the buffer and mark the ending
|
||||||
|
currentElement.NextElement = EndMarker;
|
||||||
|
bufferQueueTail.NextElement = currentElement;
|
||||||
|
bufferQueueTail = currentElement;
|
||||||
|
mWaitForBuffer.Set(); // Signal that final element is available in the buffer (regardless of wait flag, to avoid race condition)
|
||||||
|
#if TRACE
|
||||||
|
mBufferCompletedTimer.Start();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private class Element
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Link to the next buffered element.
|
||||||
|
/// Volatility: Written to by buffer thread only. Read by both threads
|
||||||
|
/// </summary>
|
||||||
|
public volatile Element NextElement;
|
||||||
|
|
||||||
|
public string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If this element marks the end of an xml element with child nodes, the IsEndElement will be true, and Value must be null.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEndElement;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set true if this represents an empty element
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEmpty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If Value is non-null, then there will be no corresponding Element with IsEndElement created.
|
||||||
|
/// </summary>
|
||||||
|
public string Value;
|
||||||
|
|
||||||
|
// Currently KDBX has a maximum of one attribute per element, so no need for a dictionary here, and the name is only used for debug asserts
|
||||||
|
#if DEBUG
|
||||||
|
public string AttributeName;
|
||||||
|
#endif
|
||||||
|
public string AttributeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Custom XmlReader implementation for usage by KdbxFile only
|
||||||
|
public override bool Read()
|
||||||
|
{
|
||||||
|
Element nextElement;
|
||||||
|
while ((nextElement = mBufferQueueHead.NextElement) == null)
|
||||||
|
{
|
||||||
|
#if TRACE
|
||||||
|
mReadWaitTimer.Start();
|
||||||
|
#endif
|
||||||
|
mWaitingForBuffer = true;
|
||||||
|
mWaitForBuffer.WaitOne();
|
||||||
|
mWaitingForBuffer = false;
|
||||||
|
|
||||||
|
#if TRACE
|
||||||
|
mReadWaitTimer.Stop();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
mBufferQueueHead = mBufferQueueHead.NextElement;
|
||||||
|
|
||||||
|
|
||||||
|
#if TRACE
|
||||||
|
if (mBufferQueueHead == EndMarker)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(String.Format("Asynchronous Buffered XmlReader waited for a total of: {0}ms, buffer completed {1}ms ahead of read", mReadWaitTimer.ElapsedMilliseconds, mBufferCompletedTimer.ElapsedMilliseconds));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return mBufferQueueHead != EndMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ReadElementString()
|
||||||
|
{
|
||||||
|
var result = mBufferQueueHead.Value ?? String.Empty; // ReadElementString returns empty strings for null content
|
||||||
|
Read(); // Read element string always skips to the start of the next element
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XmlNodeType NodeType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return mBufferQueueHead.IsEndElement ? XmlNodeType.EndElement : XmlNodeType.Element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsEmptyElement
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return mBufferQueueHead.IsEmpty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return mBufferQueueHead.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HasAttributes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return mBufferQueueHead.AttributeValue != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool MoveToAttribute(string name)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Debug.Assert(mBufferQueueHead.AttributeName == name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Value
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return mBufferQueueHead.AttributeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool MoveToElement()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unimplemented XmlReader overrides
|
||||||
|
|
||||||
|
public override int AttributeCount
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string BaseURI
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Depth
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool EOF
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetAttribute(int i)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetAttribute(string name, string namespaceURI)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetAttribute(string name)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HasValue
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string LocalName
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string LookupNamespace(string prefix)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool MoveToAttribute(string name, string ns)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool MoveToFirstAttribute()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool MoveToNextAttribute()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override XmlNameTable NameTable
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string NamespaceURI
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Prefix
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ReadAttributeValue()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ReadState ReadState
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ResolveEntity()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -497,19 +497,19 @@ namespace KeePassLib.Serialization
|
|||||||
Debug.Assert(tl != null);
|
Debug.Assert(tl != null);
|
||||||
|
|
||||||
if(xr.Name == ElemLastModTime)
|
if(xr.Name == ElemLastModTime)
|
||||||
tl.LastModificationTime = ReadTime(xr);
|
tl.SetLazyLastModificationTime(ReadString(xr));
|
||||||
else if(xr.Name == ElemCreationTime)
|
else if(xr.Name == ElemCreationTime)
|
||||||
tl.CreationTime = ReadTime(xr);
|
tl.SetLazyCreationTime(ReadString(xr));
|
||||||
else if(xr.Name == ElemLastAccessTime)
|
else if(xr.Name == ElemLastAccessTime)
|
||||||
tl.LastAccessTime = ReadTime(xr);
|
tl.SetLazyLastAccessTime(ReadString(xr));
|
||||||
else if(xr.Name == ElemExpiryTime)
|
else if(xr.Name == ElemExpiryTime)
|
||||||
tl.ExpiryTime = ReadTime(xr);
|
tl.SetLazyExpiryTime(ReadString(xr));
|
||||||
else if(xr.Name == ElemExpires)
|
else if(xr.Name == ElemExpires)
|
||||||
tl.Expires = ReadBool(xr, false);
|
tl.Expires = ReadBool(xr, false);
|
||||||
else if(xr.Name == ElemUsageCount)
|
else if(xr.Name == ElemUsageCount)
|
||||||
tl.UsageCount = ReadULong(xr, 0);
|
tl.UsageCount = ReadULong(xr, 0);
|
||||||
else if(xr.Name == ElemLocationChanged)
|
else if(xr.Name == ElemLocationChanged)
|
||||||
tl.LocationChanged = ReadTime(xr);
|
tl.SetLazyLocationChanged(ReadString(xr));
|
||||||
else ReadUnknown(xr);
|
else ReadUnknown(xr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -70,6 +70,8 @@ namespace KeePassLib.Serialization
|
|||||||
Debug.Assert(sSource != null);
|
Debug.Assert(sSource != null);
|
||||||
if(sSource == null) throw new ArgumentNullException("sSource");
|
if(sSource == null) throw new ArgumentNullException("sSource");
|
||||||
|
|
||||||
|
var stopWatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
m_format = kdbFormat;
|
m_format = kdbFormat;
|
||||||
m_slLogger = slLogger;
|
m_slLogger = slLogger;
|
||||||
|
|
||||||
@ -127,7 +129,33 @@ namespace KeePassLib.Serialization
|
|||||||
}
|
}
|
||||||
else m_randomStream = null; // No random stream for plain-text files
|
else m_randomStream = null; // No random stream for plain-text files
|
||||||
|
|
||||||
ReadXmlStreamed(readerStream, hashedStream);
|
Debug.WriteLine(String.Format("Crypto setup: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||||
|
stopWatch.Restart();
|
||||||
|
|
||||||
|
/*
|
||||||
|
var memStream = new MemoryStream((int)hashedStream.Length);
|
||||||
|
CopyStream(readerStream, memStream);
|
||||||
|
readerStream = memStream;
|
||||||
|
Debug.WriteLine(String.Format("CopyStream: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||||
|
|
||||||
|
|
||||||
|
stopWatch.Restart();
|
||||||
|
*/
|
||||||
|
|
||||||
|
//var bufferedXmlReader = AsynchronousBufferedXmlReader.FullyBuffer(readerStream);
|
||||||
|
//Debug.WriteLine(String.Format("ReadToBuffer: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||||
|
|
||||||
|
if (Java.Lang.Runtime.GetRuntime().AvailableProcessors() > 1)
|
||||||
|
{
|
||||||
|
ReadDocumentStreamed(new AsynchronousBufferedXmlReader(readerStream), hashedStream);
|
||||||
|
Debug.WriteLine(String.Format("ReadDocumentStreamed: {0}ms multi-threaded", stopWatch.ElapsedMilliseconds));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReadXmlStreamed(readerStream, hashedStream);
|
||||||
|
Debug.WriteLine(String.Format("ReadXmlStreamed: {0}ms single-threaded", stopWatch.ElapsedMilliseconds));
|
||||||
|
}
|
||||||
|
stopWatch.Restart();
|
||||||
// ReadXmlDom(readerStream);
|
// ReadXmlDom(readerStream);
|
||||||
|
|
||||||
readerStream.Close();
|
readerStream.Close();
|
||||||
@ -138,9 +166,26 @@ namespace KeePassLib.Serialization
|
|||||||
{
|
{
|
||||||
throw new CryptographicException(KLRes.FileCorrupted);
|
throw new CryptographicException(KLRes.FileCorrupted);
|
||||||
}
|
}
|
||||||
finally { CommonCleanUpRead(sSource, hashedStream); }
|
finally
|
||||||
|
{
|
||||||
|
|
||||||
|
CommonCleanUpRead(sSource, hashedStream);
|
||||||
|
Debug.WriteLine(String.Format("Close and Clean Up: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static void CopyStream(Stream input, Stream output)
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[32768];
|
||||||
|
int read;
|
||||||
|
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
|
||||||
|
{
|
||||||
|
output.Write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
output.Seek(0, SeekOrigin.Begin);
|
||||||
|
}*/
|
||||||
|
|
||||||
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
|
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
|
||||||
{
|
{
|
||||||
hashedStream.Close();
|
hashedStream.Close();
|
||||||
|
@ -167,6 +167,22 @@ namespace KeePassLib.Utility
|
|||||||
return bResult;
|
return bResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserializes a UTC XML DateTime to a local time, or returns defaultValue if it could not be parsed
|
||||||
|
/// </summary>
|
||||||
|
public static DateTime DeserializeUtcOrDefault(string xmlDateTimeString, DateTime defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return System.Xml.XmlConvert.ToDateTime(xmlDateTimeString, System.Xml.XmlDateTimeSerializationMode.Local);
|
||||||
|
}
|
||||||
|
catch(FormatException)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static DateTime? m_dtUnixRoot = null;
|
private static DateTime? m_dtUnixRoot = null;
|
||||||
public static DateTime ConvertUnixTime(double dtUnix)
|
public static DateTime ConvertUnixTime(double dtUnix)
|
||||||
{
|
{
|
||||||
|
@ -120,6 +120,8 @@ namespace keepass2android
|
|||||||
|
|
||||||
public void LoadData(Context ctx, IOConnectionInfo iocInfo, String password, String keyfile, UpdateStatus status)
|
public void LoadData(Context ctx, IOConnectionInfo iocInfo, String password, String keyfile, UpdateStatus status)
|
||||||
{
|
{
|
||||||
|
var stopWatch = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
|
||||||
mIoc = iocInfo;
|
mIoc = iocInfo;
|
||||||
|
|
||||||
KeePassLib.PwDatabase pwDatabase = new KeePassLib.PwDatabase();
|
KeePassLib.PwDatabase pwDatabase = new KeePassLib.PwDatabase();
|
||||||
@ -137,9 +139,15 @@ namespace keepass2android
|
|||||||
throw new KeyFileException();
|
throw new KeyFileException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine(String.Format("LoadData Pre-open: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||||
|
stopWatch.Restart();
|
||||||
|
|
||||||
pwDatabase.Open(iocInfo, key, status);
|
pwDatabase.Open(iocInfo, key, status);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine(String.Format("LoadData Open: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||||
|
stopWatch.Restart();
|
||||||
|
|
||||||
if (iocInfo.IsLocalFile())
|
if (iocInfo.IsLocalFile())
|
||||||
{
|
{
|
||||||
mLastChangeDate = System.IO.File.GetLastWriteTimeUtc(iocInfo.Path);
|
mLastChangeDate = System.IO.File.GetLastWriteTimeUtc(iocInfo.Path);
|
||||||
@ -155,6 +163,8 @@ namespace keepass2android
|
|||||||
Loaded = true;
|
Loaded = true;
|
||||||
pm = pwDatabase;
|
pm = pwDatabase;
|
||||||
searchHelper = new SearchDbHelper(ctx);
|
searchHelper = new SearchDbHelper(ctx);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine(String.Format("LoadData Post-open: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool quickUnlockEnabled = false;
|
bool quickUnlockEnabled = false;
|
||||||
|
@ -204,16 +204,12 @@ namespace keepass2android
|
|||||||
extraGroup.RemoveAllViews();
|
extraGroup.RemoveAllViews();
|
||||||
}
|
}
|
||||||
bool hasExtraFields = false;
|
bool hasExtraFields = false;
|
||||||
foreach (KeyValuePair<string, ProtectedString> pair in mEntry.Strings)
|
foreach (var view in from pair in mEntry.Strings where !PwDefs.IsStandardField(pair.Key) orderby pair.Key
|
||||||
|
select CreateEditSection(pair.Key, pair.Value.ReadString()))
|
||||||
{
|
{
|
||||||
String key = pair.Key;
|
//View view = new EntrySection(this, null, key, pair.Value.ReadString());
|
||||||
if (!PwDefs.IsStandardField(key))
|
extraGroup.AddView(view);
|
||||||
{
|
hasExtraFields = true;
|
||||||
//View view = new EntrySection(this, null, key, pair.Value.ReadString());
|
|
||||||
View view = CreateEditSection(key, pair.Value.ReadString());
|
|
||||||
extraGroup.AddView(view);
|
|
||||||
hasExtraFields = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FindViewById(Resource.Id.entry_extra_strings_label).Visibility = hasExtraFields ? ViewStates.Visible : ViewStates.Gone;
|
FindViewById(Resource.Id.entry_extra_strings_label).Visibility = hasExtraFields ? ViewStates.Visible : ViewStates.Gone;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user