using System; using System.Collections; using System.Collections.Generic; using System.Linq; using ln.collections; namespace ln.bson.storage.index { public delegate int BsonCompareDelegate(byte[] a, byte[] b); public class BsonIndex : IBsonIndex { private BTreeValueSet _lookup; private BTreeValueList _reverse = new BTreeValueList(); public BsonIndex() : this(CompareBytesLittleEndian) { } public BsonIndex(Comparison compareDelegate) { _lookup = new BTreeValueSet(compareDelegate); } public void AddValue(long key, BsonValue bsonValue) => AddValue(key, bsonValue.GetBytes()); public void AddValue(long key, byte[] value) { if (_reverse.TryAdd(key, value)) { if (_lookup.TryAdd(value, key)) return; _reverse.TryRemove(key, value); } throw new ArgumentOutOfRangeException(); } public void RemoveValue(long key) { if (_reverse.TryGet(key, out IEnumerable values)) { foreach (var value in values) { if (!_lookup.TryRemove(value, key)) throw new InvalidOperationException(); } _reverse.TryRemove(key); return; } throw new ArgumentOutOfRangeException(); } public void ReplaceValue(long oldKey, long newKey, BsonValue newValue) { AddValue(newKey, newValue); RemoveValue(oldKey); } public void Clear() { _lookup.Clear(); _reverse.Clear(); } public void Query(QueryOperator queryOperator, byte[] value, ISet resultSet) { IEnumerable> enumeration; switch (queryOperator) { case QueryOperator.EQUAL: enumeration = _lookup.GetInterval(value, value); break; case QueryOperator.EQUAL | QueryOperator.LESS: enumeration = _lookup.GetInterval(null, value); break; case QueryOperator.EQUAL | QueryOperator.GREATER: enumeration = _lookup.GetInterval(value, null); break; case QueryOperator.LESS: enumeration = _lookup.GetInterval(null, value, (k,v)=>_lookup.Comparison(k,value) < 0); break; case QueryOperator.GREATER: enumeration = _lookup.GetInterval(value, null, (k,v)=>_lookup.Comparison(k,value) > 0); break; case QueryOperator.IN: // ToDo: Implementation throw new NotSupportedException(); default: throw new NotSupportedException(); } resultSet.UnionWith(enumeration.Select(x => x.Value)); } public static int CompareBytesBigEndian(byte[] a, byte[] b) { int cntBytes = a.Length > b.Length ? a.Length : b.Length; int pa = a.Length - cntBytes; int pb = b.Length - cntBytes; while ((pa < a.Length) || (pb < b.Length)) { pa++; pb++; int va = (pa < 0) ? 0 : a[pa]; int vb = (pb < 0) ? 0 : b[pb]; int d = va - vb; if (d != 0) return d; } return 0; } public static int CompareBytesLittleEndian(byte[] a, byte[] b) { int cntBytes = a.Length > b.Length ? a.Length : b.Length; int pa = a.Length; int pb = b.Length; while ((pa > 0) || (pb > 0)) { pa--; pb--; int va = (pa >= a.Length) ? 0 : a[pa]; int vb = (pb >= b.Length) ? 0 : b[pb]; int d = va - vb; if (d != 0) return d; } return 0; } public static int CompareBytesLinear(byte[] a, byte[] b) { int cntBytes = a.Length > b.Length ? a.Length : b.Length; int p = 0; while (p < cntBytes) { int va = (p >= a.Length) ? -1 : a[p]; int vb = (p >= b.Length) ? -1 : b[p]; int d = va - vb; if (d != 0) return d; p++; } return 0; } public IEnumerator> GetEnumerator() => _reverse.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } }