mirror of
https://github.com/Kopano-dev/kopano-ol-extension.git
synced 2023-10-10 13:37:40 +02:00
[KOE-185] Fixed GAB caching
This commit is contained in:
parent
955e2557f0
commit
ec3fb5fb88
@ -262,6 +262,14 @@ namespace Acacia.Features.GAB
|
|||||||
}
|
}
|
||||||
private static readonly BoolOption OPTION_FILE_AS_DISPLAY_NAME = new BoolOption("FileAsDisplayName", false);
|
private static readonly BoolOption OPTION_FILE_AS_DISPLAY_NAME = new BoolOption("FileAsDisplayName", false);
|
||||||
|
|
||||||
|
[AcaciaOption("If enabled, an item cache will be used during GAB creation")]
|
||||||
|
public bool ItemCache
|
||||||
|
{
|
||||||
|
get { return GetOption(OPTION_ITEM_CACHE); }
|
||||||
|
set { SetOption(OPTION_ITEM_CACHE, value); }
|
||||||
|
}
|
||||||
|
private static readonly BoolOption OPTION_ITEM_CACHE = new BoolOption("ItemCache", false);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Modification suppression
|
#region Modification suppression
|
||||||
|
@ -117,6 +117,8 @@ namespace Acacia.Features.GAB
|
|||||||
this._feature = feature;
|
this._feature = feature;
|
||||||
this._contactsProvider = contactsProvider;
|
this._contactsProvider = contactsProvider;
|
||||||
this._contactsDisposer = contactsDisposer;
|
this._contactsDisposer = contactsDisposer;
|
||||||
|
_items = new ItemCache(this);
|
||||||
|
_items.Enabled = feature.ItemCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
@ -130,15 +132,6 @@ namespace Acacia.Features.GAB
|
|||||||
|
|
||||||
#region Processing
|
#region Processing
|
||||||
|
|
||||||
private string[] _chunkStateStringCache;
|
|
||||||
private int? _currentSequenceCache;
|
|
||||||
|
|
||||||
private void ClearCache()
|
|
||||||
{
|
|
||||||
_chunkStateStringCache = null;
|
|
||||||
_currentSequenceCache = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FullResync(CompletionTracker completion)
|
public void FullResync(CompletionTracker completion)
|
||||||
{
|
{
|
||||||
ClearCache();
|
ClearCache();
|
||||||
@ -181,7 +174,18 @@ namespace Acacia.Features.GAB
|
|||||||
/// <param name="item">If specified, this item has changed. If null, means a global check should be performed</param>
|
/// <param name="item">If specified, this item has changed. If null, means a global check should be performed</param>
|
||||||
public void Process(CompletionTracker completion, IZPushItem item)
|
public void Process(CompletionTracker completion, IZPushItem item)
|
||||||
{
|
{
|
||||||
using (CompletionTracker.Step step = completion?.Begin())
|
var watch = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
if (completion == null)
|
||||||
|
completion = new CompletionTracker(null);
|
||||||
|
|
||||||
|
completion.AddCompletion(() =>
|
||||||
|
{
|
||||||
|
// Log time
|
||||||
|
watch.Stop();
|
||||||
|
Logger.Instance.Info(this, "GAB.Process done in {0}ms", watch.ElapsedMilliseconds);
|
||||||
|
});
|
||||||
|
|
||||||
|
using (CompletionTracker.Step step = completion.Begin())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -238,7 +242,8 @@ namespace Acacia.Features.GAB
|
|||||||
ProcessMessage(completion, (IZPushItem)item2);
|
ProcessMessage(completion, (IZPushItem)item2);
|
||||||
}
|
}
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
Logger.Instance.Warning(this, "ProcessChunk: {0}ms", watch.ElapsedMilliseconds);
|
_items.Clear();
|
||||||
|
Logger.Instance.Warning(this, "ProcessChunk: {0} in {1}ms", entryId, watch.ElapsedMilliseconds);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -537,7 +542,7 @@ namespace Acacia.Features.GAB
|
|||||||
CreateObject(index, id, value);
|
CreateObject(index, id, value);
|
||||||
}
|
}
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
Logger.Instance.Warning(this, "ProcessChunkBody: {0}ms", watch.ElapsedMilliseconds);
|
Logger.Instance.Warning(this, "ProcessChunkBody: {0} in {1}ms", index, watch.ElapsedMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateObject(ChunkIndex index, string id, Dictionary<string, object> value)
|
private void CreateObject(ChunkIndex index, string id, Dictionary<string, object> value)
|
||||||
@ -546,6 +551,9 @@ namespace Acacia.Features.GAB
|
|||||||
{
|
{
|
||||||
_feature?.BeginProcessing();
|
_feature?.BeginProcessing();
|
||||||
|
|
||||||
|
// Remove any cached entry
|
||||||
|
_items.Remove(id);
|
||||||
|
|
||||||
string type = Get<string>(value, "type");
|
string type = Get<string>(value, "type");
|
||||||
if (type == "contact")
|
if (type == "contact")
|
||||||
CreateContact(id, value, index, 0);
|
CreateContact(id, value, index, 0);
|
||||||
@ -655,10 +663,17 @@ namespace Acacia.Features.GAB
|
|||||||
// Set the chunk data
|
// Set the chunk data
|
||||||
SetItemStandard(index, id, com, com.Add(contact.UserProperties));
|
SetItemStandard(index, id, com, com.Add(contact.UserProperties));
|
||||||
|
|
||||||
// TODO: groups
|
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
contact.Save();
|
contact.Save();
|
||||||
|
|
||||||
|
// Add to groups
|
||||||
|
if (_feature.GroupMembers && Get<ArrayList>(value, "memberOf") != null)
|
||||||
|
{
|
||||||
|
using (IContactItem wrapped = Mapping.Wrap<IContactItem>(contact, false))
|
||||||
|
{
|
||||||
|
AddItemToGroups(wrapped, id, value, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,12 +695,9 @@ namespace Acacia.Features.GAB
|
|||||||
if (!_feature.CreateGroups)
|
if (!_feature.CreateGroups)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string smtpAddress = Get<string> (value, "smtpAddress");
|
string smtpAddress = Get<string>(value, "smtpAddress");
|
||||||
if (!string.IsNullOrEmpty(smtpAddress) && _feature.SMTPGroupsAsContacts)
|
if (!string.IsNullOrEmpty(smtpAddress) && _feature.SMTPGroupsAsContacts)
|
||||||
{
|
{
|
||||||
// TODO:
|
|
||||||
throw new NotImplementedException();
|
|
||||||
/*
|
|
||||||
// Create a contact
|
// Create a contact
|
||||||
using (IContactItem contact = Contacts.Create<IContactItem>())
|
using (IContactItem contact = Contacts.Create<IContactItem>())
|
||||||
{
|
{
|
||||||
@ -705,7 +717,7 @@ namespace Acacia.Features.GAB
|
|||||||
string membersBody = null;
|
string membersBody = null;
|
||||||
foreach (string memberId in members)
|
foreach (string memberId in members)
|
||||||
{
|
{
|
||||||
using (IItem item = FindItemById(memberId))
|
using (IItem item = _items.Find(memberId))
|
||||||
{
|
{
|
||||||
Logger.Instance.Debug(this, "Finding member {0} of {1}: {2}", memberId, id, item?.EntryID);
|
Logger.Instance.Debug(this, "Finding member {0} of {1}: {2}", memberId, id, item?.EntryID);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
@ -741,21 +753,21 @@ namespace Acacia.Features.GAB
|
|||||||
contact.Save();
|
contact.Save();
|
||||||
|
|
||||||
AddItemToGroups(contact, id, value, index);
|
AddItemToGroups(contact, id, value, index);
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Create a proper group
|
// Create a proper group
|
||||||
Contacts.GABCreate<NSOutlook.DistListItem>(NSOutlook.OlItemType.olDistributionListItem, (com, group) =>
|
using (IDistributionList group = Contacts.Create<IDistributionList>())
|
||||||
{
|
{
|
||||||
Logger.Instance.Debug(this, "Creating group: {0}", id);
|
Logger.Instance.Debug(this, "Creating group: {0}", id);
|
||||||
group.DLName = Get<string>(value, "displayName");
|
group.DLName = Get<string>(value, "displayName");
|
||||||
if (smtpAddress != null)
|
if (smtpAddress != null)
|
||||||
{
|
{
|
||||||
// TODO? group.SMTPAddress = smtpAddress;
|
group.SMTPAddress = smtpAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetItemStandard(index, id, com, com.Add(group.UserProperties));
|
SetItemStandard(group, id, value, index);
|
||||||
group.Save();
|
group.Save();
|
||||||
|
|
||||||
if (_feature.GroupMembers)
|
if (_feature.GroupMembers)
|
||||||
@ -765,31 +777,20 @@ namespace Acacia.Features.GAB
|
|||||||
{
|
{
|
||||||
foreach (string memberId in members)
|
foreach (string memberId in members)
|
||||||
{
|
{
|
||||||
using (IItem item = FindItemById(memberId))
|
using (IItem item = _items.Find(memberId))
|
||||||
{
|
{
|
||||||
Logger.Instance.Debug(this, "Finding member {0} of {1}: {2}", memberId, id, item?.EntryID);
|
Logger.Instance.Debug(this, "Finding member {0} of {1}: {2}", memberId, id, item?.EntryID);
|
||||||
//if (item != null)
|
if (item != null)
|
||||||
// AddGroupMember(group, item);
|
AddGroupMember(group, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
group.Save();
|
group.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO AddItemToGroups(group, id, value, index);
|
AddItemToGroups(group, id, value, index);
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IItem FindItemById(string id)
|
|
||||||
{
|
|
||||||
using (ISearch<IItem> search = Contacts.Search<IItem>())
|
|
||||||
{
|
|
||||||
search.AddField(PROP_GAB_ID, true).SetOperation(SearchOperation.Equal, id);
|
|
||||||
return search.SearchOne();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetItemStandard(IItem item, string id, Dictionary<string, object> value, ChunkIndex index)
|
private void SetItemStandard(IItem item, string id, Dictionary<string, object> value, ChunkIndex index)
|
||||||
@ -827,7 +828,7 @@ namespace Acacia.Features.GAB
|
|||||||
string memberOf = memberOfObject as string;
|
string memberOf = memberOfObject as string;
|
||||||
if (memberOf != null)
|
if (memberOf != null)
|
||||||
{
|
{
|
||||||
using (IItem groupItem = FindItemById(memberOf))
|
using (IItem groupItem = _items.Find(memberOf))
|
||||||
{
|
{
|
||||||
Logger.Instance.Debug(this, "Finding group {0} for {1}: {2}", memberOf, id, groupItem?.EntryID);
|
Logger.Instance.Debug(this, "Finding group {0} for {1}: {2}", memberOf, id, groupItem?.EntryID);
|
||||||
if (groupItem is IDistributionList)
|
if (groupItem is IDistributionList)
|
||||||
@ -847,6 +848,133 @@ namespace Acacia.Features.GAB
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Caching
|
||||||
|
|
||||||
|
private string[] _chunkStateStringCache;
|
||||||
|
private int? _currentSequenceCache;
|
||||||
|
|
||||||
|
private void ClearCache()
|
||||||
|
{
|
||||||
|
_chunkStateStringCache = null;
|
||||||
|
_currentSequenceCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ItemCache
|
||||||
|
{
|
||||||
|
private readonly GABHandler _gab;
|
||||||
|
private Dictionary<string, IItem> _items;
|
||||||
|
|
||||||
|
public bool Enabled
|
||||||
|
{
|
||||||
|
get { return _items != null; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
if (_items == null)
|
||||||
|
_items = new Dictionary<string, IItem>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_items = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemCache(GABHandler gab)
|
||||||
|
{
|
||||||
|
this._gab = gab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IItem Find(string id)
|
||||||
|
{
|
||||||
|
// First try the item cache
|
||||||
|
if (Enabled)
|
||||||
|
{
|
||||||
|
IItem item;
|
||||||
|
if (_items.TryGetValue(id, out item))
|
||||||
|
{
|
||||||
|
bool ok = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
// Get the entry id to test if the underlying object is still valid
|
||||||
|
string s = item.EntryID;
|
||||||
|
if (string.IsNullOrEmpty(s))
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Runtime.InteropServices.InvalidComObjectException)
|
||||||
|
{
|
||||||
|
Logger.Instance.Trace(this, "Cache item detached");
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's ok, we're done
|
||||||
|
if (ok)
|
||||||
|
return item;
|
||||||
|
|
||||||
|
// Otherwise clear the cache, as usually all items are stale
|
||||||
|
Clear();
|
||||||
|
System.GC.Collect();
|
||||||
|
// And fall through to fetch it properly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do a lookup.
|
||||||
|
using (ISearch<IItem> search = _gab.Contacts.Search<IItem>())
|
||||||
|
{
|
||||||
|
search.AddField(PROP_GAB_ID, true).SetOperation(SearchOperation.Equal, id);
|
||||||
|
IItem item = search.SearchOne();
|
||||||
|
|
||||||
|
// Add to cache. Also for failed lookups, will be updated when created
|
||||||
|
if (Enabled)
|
||||||
|
{
|
||||||
|
_items.Add(id, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(string id)
|
||||||
|
{
|
||||||
|
_items?.Remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Dictionary<string, IItem> old = _items;
|
||||||
|
_items = null;
|
||||||
|
if (old != null)
|
||||||
|
{
|
||||||
|
_items = new Dictionary<string, IItem>();
|
||||||
|
|
||||||
|
Logger.Instance.Info(this, "GAB ItemCache: {0} entries", old.Count);
|
||||||
|
foreach (IItem item in old.Values)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
item?.Dispose();
|
||||||
|
}
|
||||||
|
catch(System.Runtime.InteropServices.InvalidComObjectException)
|
||||||
|
{
|
||||||
|
// Ignore silently, means it already got disposed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
old.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ItemCache _items;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Removal
|
#region Removal
|
||||||
|
|
||||||
public void Remove()
|
public void Remove()
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
|
/// Copyright 2019 Kopano b.v.
|
||||||
using Acacia.Native.MAPI;
|
|
||||||
/// Copyright 2016 Kopano b.v.
|
|
||||||
///
|
///
|
||||||
/// This program is free software: you can redistribute it and/or modify
|
/// This program is free software: you can redistribute it and/or modify
|
||||||
/// it under the terms of the GNU Affero General Public License, version 3,
|
/// it under the terms of the GNU Affero General Public License, version 3,
|
||||||
@ -20,6 +18,9 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Acacia.Native.MAPI;
|
||||||
|
using Acacia.Utils;
|
||||||
|
using Acacia.Stubs;
|
||||||
|
|
||||||
namespace Acacia
|
namespace Acacia
|
||||||
{
|
{
|
||||||
@ -290,5 +291,52 @@ namespace Acacia
|
|||||||
public const string MESSAGE_CLASS_NOTES = "IPM.StickyNote";
|
public const string MESSAGE_CLASS_NOTES = "IPM.StickyNote";
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Misc helpers
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly byte[] PREFIX_MEMBER_ID =
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xC0, 0x91, 0xAD, 0xD3, 0x51, 0x9D, 0xCF, 0x11, 0xA4, 0xA9, 0x00, 0xAA, 0x00, 0x47, 0xFA, 0xA4, 0xB4
|
||||||
|
};
|
||||||
|
|
||||||
|
public static byte[] CreateMemberId(IDistributionList member)
|
||||||
|
{
|
||||||
|
List<byte> id = new List<byte>();
|
||||||
|
id.AddRange(PREFIX_MEMBER_ID);
|
||||||
|
id.AddRange(StringUtil.HexToBytes(member.EntryID));
|
||||||
|
return id.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly byte[] PREFIX_ONEOFFMEMBER_ID =
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x81, 0x2B, 0x1F, 0xA4, 0xBE, 0xA3, 0x10, 0x19, 0x9D, 0x6E, 0x00, 0xDD, 0x01, 0x0F, 0x54, 0x02, 0x00, 0x00, 0x01, 0x80
|
||||||
|
};
|
||||||
|
|
||||||
|
public static byte[] CreateOneOffMemberId(IDistributionList member)
|
||||||
|
{
|
||||||
|
return CreateOneOffMemberId(member.DLName, "UNKNOWN", "UNKNOWN");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] CreateOneOffMemberId(string displayName, string addressType, string address)
|
||||||
|
{
|
||||||
|
byte[] zeroes = { 0, 0 };
|
||||||
|
List<byte> id = new List<byte>();
|
||||||
|
id.AddRange(PREFIX_ONEOFFMEMBER_ID);
|
||||||
|
|
||||||
|
id.AddRange(Encoding.Unicode.GetBytes(displayName));
|
||||||
|
id.AddRange(zeroes);
|
||||||
|
|
||||||
|
id.AddRange(Encoding.Unicode.GetBytes(addressType));
|
||||||
|
id.AddRange(zeroes);
|
||||||
|
|
||||||
|
id.AddRange(Encoding.Unicode.GetBytes(address));
|
||||||
|
id.AddRange(zeroes);
|
||||||
|
|
||||||
|
id.AddRange(zeroes);
|
||||||
|
return id.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
string displayName = DLName + " (" + value + ")";
|
string displayName = DLName + " (" + value + ")";
|
||||||
byte[] oneOffId = CreateOneOffMemberId(DLName, "SMTP", value);
|
byte[] oneOffId = OutlookConstants.CreateOneOffMemberId(DLName, "SMTP", value);
|
||||||
|
|
||||||
SetProperties(
|
SetProperties(
|
||||||
new string[]
|
new string[]
|
||||||
@ -111,8 +111,8 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
object[] oneOffMembers = (object[])GetProperty(OutlookConstants.PR_DISTLIST_ONEOFFMEMBERS);
|
object[] oneOffMembers = (object[])GetProperty(OutlookConstants.PR_DISTLIST_ONEOFFMEMBERS);
|
||||||
|
|
||||||
// Create the new member ids
|
// Create the new member ids
|
||||||
byte[] memberId = CreateMemberId(member);
|
byte[] memberId = OutlookConstants.CreateMemberId(member);
|
||||||
byte[] oneOffMemberId = CreateOneOffMemberId(member);
|
byte[] oneOffMemberId = OutlookConstants.CreateOneOffMemberId(member);
|
||||||
|
|
||||||
// See if it is already a member
|
// See if it is already a member
|
||||||
// Compare on one-off member id, as memberId changes
|
// Compare on one-off member id, as memberId changes
|
||||||
@ -151,47 +151,6 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly byte[] PREFIX_MEMBER_ID =
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0xC0, 0x91, 0xAD, 0xD3, 0x51, 0x9D, 0xCF, 0x11, 0xA4, 0xA9, 0x00, 0xAA, 0x00, 0x47, 0xFA, 0xA4, 0xB4
|
|
||||||
};
|
|
||||||
|
|
||||||
private byte[] CreateMemberId(IDistributionList member)
|
|
||||||
{
|
|
||||||
List<byte> id = new List<byte>();
|
|
||||||
id.AddRange(PREFIX_MEMBER_ID);
|
|
||||||
id.AddRange(StringUtil.HexToBytes(member.EntryID));
|
|
||||||
return id.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly byte[] PREFIX_ONEOFFMEMBER_ID =
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x81, 0x2B, 0x1F, 0xA4, 0xBE, 0xA3, 0x10, 0x19, 0x9D, 0x6E, 0x00, 0xDD, 0x01, 0x0F, 0x54, 0x02, 0x00, 0x00, 0x01, 0x80
|
|
||||||
};
|
|
||||||
|
|
||||||
private byte[] CreateOneOffMemberId(IDistributionList member)
|
|
||||||
{
|
|
||||||
return CreateOneOffMemberId(member.DLName, "UNKNOWN", "UNKNOWN");
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] CreateOneOffMemberId(string displayName, string addressType, string address)
|
|
||||||
{
|
|
||||||
byte[] zeroes = { 0, 0 };
|
|
||||||
List<byte> id = new List<byte>();
|
|
||||||
id.AddRange(PREFIX_ONEOFFMEMBER_ID);
|
|
||||||
|
|
||||||
id.AddRange(Encoding.Unicode.GetBytes(displayName));
|
|
||||||
id.AddRange(zeroes);
|
|
||||||
|
|
||||||
id.AddRange(Encoding.Unicode.GetBytes(addressType));
|
|
||||||
id.AddRange(zeroes);
|
|
||||||
|
|
||||||
id.AddRange(Encoding.Unicode.GetBytes(address));
|
|
||||||
id.AddRange(zeroes);
|
|
||||||
|
|
||||||
id.AddRange(zeroes);
|
|
||||||
return id.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -24,12 +24,13 @@ namespace Acacia.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Action _completion;
|
private readonly List<Action> _completions = new List<Action>();
|
||||||
private int steps = 0;
|
private int steps = 0;
|
||||||
|
|
||||||
public CompletionTracker(Action completion)
|
public CompletionTracker(Action completion)
|
||||||
{
|
{
|
||||||
this._completion = completion;
|
if (completion != null)
|
||||||
|
_completions.Add(completion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -47,8 +48,14 @@ namespace Acacia.Utils
|
|||||||
if (Interlocked.Decrement(ref steps) == 0)
|
if (Interlocked.Decrement(ref steps) == 0)
|
||||||
{
|
{
|
||||||
// Done
|
// Done
|
||||||
_completion();
|
foreach(Action completion in _completions)
|
||||||
|
completion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddCompletion(Action completion)
|
||||||
|
{
|
||||||
|
_completions.Add(completion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user