2015-12-26 22:44:43 +00:00
|
|
|
|
using DSInternals.Common;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace DSInternals.DataStore
|
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
public class AttributeMetadataCollection
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
|
|
|
|
private const int guidSize = 16;
|
|
|
|
|
private const int entrySize = 3 * sizeof(long) + 2 * sizeof(int) + guidSize;
|
2019-12-12 12:29:22 +00:00
|
|
|
|
private const int HeaderSize = 2 * sizeof(long); // Structure: | Unknown | Number of Entries | Entries |
|
|
|
|
|
private const long DefaultUnknownValue = 1;
|
|
|
|
|
|
2015-12-26 22:44:43 +00:00
|
|
|
|
public long Unknown
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
private set;
|
|
|
|
|
}
|
2019-12-12 12:29:22 +00:00
|
|
|
|
|
2015-12-26 22:44:43 +00:00
|
|
|
|
public int Count
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
return this.InnerList?.Count ?? 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IList<int> Attributes
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.InnerList?.Keys;
|
2015-12-26 22:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-12 12:29:22 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Holds a list of attribute metadata sorted by attribute ID.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected SortedList<int,AttributeMetadata> InnerList
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
private set;
|
|
|
|
|
}
|
2019-12-12 12:29:22 +00:00
|
|
|
|
|
2015-12-26 22:44:43 +00:00
|
|
|
|
public AttributeMetadataCollection() : this(null) { }
|
2019-12-12 12:29:22 +00:00
|
|
|
|
|
2015-12-26 22:44:43 +00:00
|
|
|
|
public AttributeMetadataCollection(byte[] buffer)
|
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
if (buffer == null)
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
// Initialize an empty collection
|
|
|
|
|
this.Unknown = DefaultUnknownValue;
|
|
|
|
|
this.InnerList = new SortedList<int, AttributeMetadata>();
|
2015-12-26 22:44:43 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-12 12:29:22 +00:00
|
|
|
|
Validator.AssertMinLength(buffer, HeaderSize, nameof(buffer));
|
|
|
|
|
|
|
|
|
|
using (Stream stream = new MemoryStream(buffer))
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
using (BinaryReader reader = new BinaryReader(stream))
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
// Read structure and validate header
|
2015-12-26 22:44:43 +00:00
|
|
|
|
this.Unknown = reader.ReadInt64();
|
|
|
|
|
long numEntries = reader.ReadInt64();
|
|
|
|
|
long expectedBufferSize = CalculateBinarySize(numEntries);
|
2019-12-12 12:29:22 +00:00
|
|
|
|
Validator.AssertLength(buffer, expectedBufferSize, nameof(buffer));
|
|
|
|
|
|
|
|
|
|
// Read all entries
|
|
|
|
|
this.InnerList = new SortedList<int, AttributeMetadata>((int)numEntries);
|
|
|
|
|
for (int i = 1; i <= numEntries; i++)
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
|
|
|
|
int attributeId = reader.ReadInt32();
|
|
|
|
|
int version = reader.ReadInt32();
|
|
|
|
|
long timestamp = reader.ReadInt64();
|
|
|
|
|
Guid originatingDSA = new Guid(reader.ReadBytes(16));
|
|
|
|
|
long originatingUSN = reader.ReadInt64();
|
|
|
|
|
long localUSN = reader.ReadInt64();
|
2019-12-12 12:29:22 +00:00
|
|
|
|
var entry = new AttributeMetadata(version, timestamp, originatingDSA, originatingUSN, localUSN);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
this.InnerList.Add(attributeId, entry);
|
|
|
|
|
}
|
|
|
|
|
catch(ArgumentException)
|
|
|
|
|
{
|
|
|
|
|
// An element with the same key already exists.
|
|
|
|
|
// We will simply ignore duplicate values and thus remove them on save.
|
|
|
|
|
}
|
2015-12-26 22:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Update(int attributeId, Guid invocationId, DateTime time, long usn)
|
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
this.InnerList.TryGetValue(attributeId, out AttributeMetadata entry);
|
|
|
|
|
|
|
|
|
|
if (entry != null)
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
|
|
|
|
// This attribute is already contained in the list, so we just update it
|
2019-12-12 12:29:22 +00:00
|
|
|
|
entry.Update(invocationId, time, usn);
|
2015-12-26 22:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// This is a newly added attribute
|
2019-12-12 12:29:22 +00:00
|
|
|
|
entry = new AttributeMetadata(invocationId, time, usn);
|
|
|
|
|
this.InnerList.Add(attributeId, entry);
|
2015-12-26 22:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ToByteArray()
|
|
|
|
|
{
|
|
|
|
|
byte[] buffer = new byte[CalculateBinarySize(this.Count)];
|
|
|
|
|
using (MemoryStream stream = new MemoryStream(buffer))
|
|
|
|
|
{
|
|
|
|
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
|
|
|
|
{
|
|
|
|
|
writer.Write(this.Unknown);
|
2019-12-12 12:29:22 +00:00
|
|
|
|
|
2015-12-26 22:44:43 +00:00
|
|
|
|
// Important: Write Count as 64-bit and not 32-bit:
|
2019-12-12 12:29:22 +00:00
|
|
|
|
writer.Write((long)this.Count);
|
|
|
|
|
|
|
|
|
|
foreach (var entry in this.InnerList)
|
2015-12-26 22:44:43 +00:00
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
writer.Write(entry.Key);
|
|
|
|
|
writer.Write(entry.Value.Version);
|
|
|
|
|
writer.Write(entry.Value.LastOriginatingChangeTimestamp);
|
|
|
|
|
writer.Write(entry.Value.LastOriginatingInvocationId.ToByteArray());
|
|
|
|
|
writer.Write(entry.Value.OriginatingChangeUsn);
|
|
|
|
|
writer.Write(entry.Value.LocalChangeUsn);
|
2015-12-26 22:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
var text = new StringBuilder();
|
|
|
|
|
foreach (var entry in InnerList)
|
|
|
|
|
{
|
2019-12-12 12:29:22 +00:00
|
|
|
|
text.AppendFormat("AttId: {0}, ", entry.Key);
|
|
|
|
|
text.AppendLine(entry.Value.ToString());
|
2015-12-26 22:44:43 +00:00
|
|
|
|
}
|
|
|
|
|
return text.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static long CalculateBinarySize(long numEntries)
|
|
|
|
|
{
|
|
|
|
|
// Unknown Value + Entry Count + Entries
|
|
|
|
|
return 2 * sizeof(long) + numEntries * entrySize;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|