public static Icon Parse(Stream stream) { if (stream is null) { throw new ArgumentNullException(nameof(stream)); } UnsafeEx.SkipInitIfPossible(out ICONDIR icondir); // 6 bytes stream.SafeRead(UnsafeEx.AsBytes(ref icondir)); if (icondir.idCount < 0) { ThrowHelper.ThrowFormatException(); } using var entries = new UnsafeRawArray <ICONDIRENTRY>(icondir.idCount, false); stream.SafeRead(entries.AsBytes()); return(Icon.Create(icondir.idCount, (stream, entries), static (images, state) => { var(stream, entries) = state; for (int i = 0; i < images.Length; i++) { images[i] = ParseImage(stream, entries[i]); } })); }
public void CeqCgtCltWork() { // int32 Assert.AreEqual(1, UnsafeEx.Ceq(1, 1)); Assert.AreEqual(0, UnsafeEx.Ceq(1, 2)); Assert.AreEqual(1, UnsafeEx.Cgt(2, 1)); Assert.AreEqual(0, UnsafeEx.Cgt(1, 1)); Assert.AreEqual(0, UnsafeEx.Cgt(0, 1)); Assert.AreEqual(1, UnsafeEx.Clt(1, 2)); Assert.AreEqual(0, UnsafeEx.Clt(1, 1)); Assert.AreEqual(0, UnsafeEx.Clt(1, 0)); // int64 Assert.AreEqual(1, UnsafeEx.Ceq(1L, 1L)); Assert.AreEqual(0, UnsafeEx.Ceq(1L, 2L)); Assert.AreEqual(1, UnsafeEx.Cgt(2L, 1L)); Assert.AreEqual(0, UnsafeEx.Cgt(1L, 1L)); Assert.AreEqual(0, UnsafeEx.Cgt(0L, 1L)); Assert.AreEqual(1, UnsafeEx.Clt(1L, 2L)); Assert.AreEqual(0, UnsafeEx.Clt(1L, 1L)); Assert.AreEqual(0, UnsafeEx.Clt(1L, 0L)); // explicit int32 -> int64 Assert.AreEqual(1, UnsafeEx.Ceq(1, 1L)); Assert.AreEqual(0, UnsafeEx.Ceq(1, 2L)); Assert.AreEqual(1, UnsafeEx.Cgt(2, 1L)); Assert.AreEqual(0, UnsafeEx.Cgt(1, 1L)); Assert.AreEqual(0, UnsafeEx.Cgt(0, 1L)); Assert.AreEqual(1, UnsafeEx.Clt(1, 2L)); Assert.AreEqual(0, UnsafeEx.Clt(1, 1L)); Assert.AreEqual(0, UnsafeEx.Clt(1, 0L)); // IntPtr Assert.AreEqual(1, UnsafeEx.Ceq((IntPtr)1, (IntPtr)1)); Assert.AreEqual(0, UnsafeEx.Ceq((IntPtr)1, (IntPtr)2)); Assert.AreEqual(1, UnsafeEx.Cgt((IntPtr)2, (IntPtr)1)); Assert.AreEqual(0, UnsafeEx.Cgt((IntPtr)1, (IntPtr)1)); Assert.AreEqual(0, UnsafeEx.Cgt((IntPtr)0, (IntPtr)1)); Assert.AreEqual(1, UnsafeEx.Clt((IntPtr)1, (IntPtr)2)); Assert.AreEqual(0, UnsafeEx.Clt((IntPtr)1, (IntPtr)1)); Assert.AreEqual(0, UnsafeEx.Clt((IntPtr)1, (IntPtr)0)); // Float Assert.AreEqual(1, UnsafeEx.Ceq(1.23, 1.23)); Assert.AreEqual(0, UnsafeEx.Ceq(1.23, 1.24)); Assert.AreEqual(1, UnsafeEx.Ceq(1.23f, 1.23f)); Assert.AreEqual(0, UnsafeEx.Ceq(1.23f, 1.24f)); Assert.AreEqual(1, UnsafeEx.BoolAsInt(true)); Assert.AreEqual(0, UnsafeEx.BoolAsInt(false)); }
public void CouldUseIDisposableMethods() { var disposable = new TestDisposable(); UnsafeEx.DisposeConstrained(ref disposable); Assert.True(disposable.Disposed); }
public void ComparerInterfaceAndCachedConstrainedComparer() { var c = Comparer <long> .Default; IComparer <long> ic = Comparer <long> .Default; var cc = KeyComparer <long> .Default; const int count = 100000000; for (int r = 0; r < 10; r++) { var sum = 0L; using (Benchmark.Run("Binary", count)) { for (int i = 0; i < count; i++) { sum += c.Compare(i, i - 1); } } Assert.True(sum > 0); sum = 0L; using (Benchmark.Run("Interface", count)) { for (int i = 0; i < count; i++) { sum += ic.Compare(i, i - 1); } } Assert.True(sum > 0); sum = 0L; using (Benchmark.Run("KeyComparer", count)) { for (int i = 0; i < count; i++) { sum += cc.Compare(i, i - 1); } } Assert.True(sum > 0); sum = 0L; using (Benchmark.Run("Unsafe", count)) { for (int i = 0; i < count; i++) { var other = i - 1; sum += UnsafeEx.CompareToConstrained(ref i, ref other); } } } Benchmark.Dump(); }
public void CouldUseGetHashCodeMethods() { var v = 1; var vh = UnsafeEx.GetHashCodeConstrained(ref v); Assert.AreEqual(v, vh); var d = new TestDisposable(); var dh = UnsafeEx.GetHashCodeConstrained(ref d); Assert.AreEqual(42, dh); }
public void CouldUseIDiffableMethods() { var first = new LongDiffable { Value = 123 }; var second = new LongDiffable { Value = 456 }; var diff = 456 - 123; Assert.AreEqual(diff, UnsafeEx.DiffLongConstrained(ref first, ref second)); Assert.AreEqual(second, UnsafeEx.AddLongConstrained(ref first, diff)); }
public void CouldUseCompareAndEqualsMethods() { var v1 = new CompEq(1); var v2 = new CompEq(2); var v3 = new CompEq(2); Assert.AreEqual(-1, UnsafeEx.CompareToConstrained(ref v1, ref v2)); Assert.AreEqual(1, UnsafeEx.CompareToConstrained(ref v2, ref v1)); Assert.AreEqual(0, UnsafeEx.CompareToConstrained(ref v2, ref v3)); Assert.AreEqual(1, UnsafeEx.CompareToConstrained(ref v3, ref v1)); Assert.True(UnsafeEx.EqualsConstrained(ref v2, ref v3)); Assert.False(UnsafeEx.EqualsConstrained(ref v1, ref v3)); }
public static int BinaryLookup <T>(ref T searchSpace, int offset, int length, ref T value, Lookup lookup, KeyComparer <T> comparer = default) { Debug.Assert(length >= 0); var i = BinarySearch(ref searchSpace, offset, length, value, comparer); var li = SearchToLookup2(offset, length, lookup, i); if (li != i & li >= offset) // not && { value = UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, li)); } return(li); }
public void CouldUseIDeltaMethods() { var first = new IntDelta { Value = 123 }; var second = new IntDelta { Value = 456 }; var delta = new IntDelta { Value = 456 - 123 }; Assert.AreEqual(delta, UnsafeEx.GetDeltaConstrained(ref first, ref second)); Assert.AreEqual(second, UnsafeEx.AddDeltaConstrained(ref first, ref delta)); }
internal static int BinarySearchClassic <T>(ref T searchSpace, int offset, int length, T value, KeyComparer <T> comparer = default) { unchecked { int lo = offset; int hi = offset + length - 1; // If length == 0, hi == -1, and loop will not be entered while (lo <= hi) { // PERF: `lo` or `hi` will never be negative inside the loop, // so computing median using uints is safe since we know // `length <= int.MaxValue`, and indices are >= 0 // and thus cannot overflow an uint. // Saves one subtraction per loop compared to // `int i = lo + ((hi - lo) >> 1);` int i = (int)(((uint)hi + (uint)lo) >> 1); int c = comparer.Compare(value, UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, i))); if (c == 0) { return(i); } if (c > 0) { lo = i + 1; } else { hi = i - 1; } } // If none found, then a negative number that is the bitwise complement // of the index of the next element that is larger than or, if there is // no larger element, the bitwise complement of `length`, which // is `lo` at this point. return(~lo); } }
internal static int BinarySearchHybrid <T>(ref T searchSpace, int offset, int length, T value, KeyComparer <T> comparer = default) { unchecked { int c; int lo = offset; int hi = offset + length - 1; while (hi - lo > 7) { int i = (int)(((uint)hi + (uint)lo) >> 1); c = comparer.Compare(value, UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, i))); if (c > 0) { lo = i + 1; } else { if (c == 0) { goto RETURN; } hi = i - 1; } } while ((c = comparer.Compare(value, UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, lo)))) > 0 && ++lo <= hi) { } RETURN: var ceq1 = -UnsafeEx.Ceq(c, 0); return((ceq1 & lo) | (~ceq1 & ~lo)); } }
public static TValue Unpack <TValue>(ref TValue value, int from, int to) { if (typeof(TValue) == typeof(sbyte) || typeof(TValue) == typeof(byte)) { return(UnsafeEx.As <byte, TValue>(BitHelper.Unpack(Unsafe.As <TValue, byte>(ref value), from, to))); } else if (typeof(TValue) == typeof(short) || typeof(TValue) == typeof(ushort)) { return(UnsafeEx.As <ushort, TValue>(BitHelper.Unpack(Unsafe.As <TValue, ushort>(ref value), from, to))); } else if (typeof(TValue) == typeof(int) || typeof(TValue) == typeof(uint)) { return(UnsafeEx.As <uint, TValue>(BitHelper.Unpack(Unsafe.As <TValue, uint>(ref value), from, to))); } else if (typeof(TValue) == typeof(long) || typeof(TValue) == typeof(ulong)) { return(UnsafeEx.As <ulong, TValue>(BitHelper.Unpack(Unsafe.As <TValue, ulong>(ref value), from, to))); } else { throw new ArgumentException(nameof(TValue)); } }
// TODO WTF is this externallyOwned on slice? See RdM Clone /// <summary> /// Returns new VectorStorage instance with the same memory source but (optionally) different memory start and length. /// Increments underlying memory reference count unless <paramref name="externallyOwned"/> is true. /// </summary> public RetainedVec Clone(int start, int length, bool externallyOwned = false) { // see CLR Span.Slice comment if (IntPtr.Size == 8) { if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_length) { ThrowHelper.ThrowArgumentOutOfRangeException(); } } else { if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) { ThrowHelper.ThrowArgumentOutOfRangeException(); } } if (!externallyOwned) { _memoryOwner?.Increment(); } var slice = new RetainedVec( externallyOwned ? null : _memoryOwner, _array, UnsafeEx.Add(_pointerOrOffset, _array == null ? start * VecTypeHelper.GetInfo(_runtimeTypeId).ElemSize : start), length, _runtimeTypeId ); return(slice); }
public T Add(T value, long diff) { if (typeof(T) == typeof(byte)) { var value1 = (byte)(object)(value); return((T)(object)(checked ((byte)((sbyte)value1 + diff)))); } if (typeof(T) == typeof(sbyte)) { var value1 = (sbyte)(object)(value); return((T)(object)(checked ((sbyte)(value1 + diff)))); } if (typeof(T) == typeof(char)) { var value1 = (char)(object)(value); return((T)(object)(checked ((char)(value1 + diff)))); } if (typeof(T) == typeof(short)) { var value1 = (short)(object)(value); return((T)(object)(checked ((short)(value1 + diff)))); } if (typeof(T) == typeof(ushort)) { var value1 = (ushort)(object)(value); return((T)(object)(checked ((ushort)((short)value1 + diff)))); } if (typeof(T) == typeof(int)) { var value1 = (int)(object)(value); return((T)(object)(checked ((int)(value1 + diff)))); } if (typeof(T) == typeof(uint)) { var value1 = (uint)(object)(value); return((T)(object)(checked ((uint)((int)value1 + diff)))); } if (typeof(T) == typeof(long)) { var value1 = (long)(object)(value); return((T)(object)(checked (value1 + diff))); } if (typeof(T) == typeof(ulong)) { var value1 = (ulong)(object)(value); return((T)(object)(checked ((ulong)((long)value1 + diff)))); } if (typeof(T) == typeof(DateTime)) { var value1 = (DateTime)(object)(value); return((T)(object)value1.AddTicks(diff)); } if (typeof(T) == typeof(Timestamp)) { var value1 = (Timestamp)(object)(value); return((T)(object)(new Timestamp(value1.Nanos + diff))); } // TODO SmallDecimal if (IsIInt64Diffable) { return(UnsafeEx.AddLongConstrained(ref value, diff)); } if (_keyComparer != null) { return(AddViaInterface(value, diff)); } ThrowHelper.ThrowNotSupportedException(); return(default(T)); }
public int Compare(T x, T y) { // For our purposes this method is "normal", i.e. known types are compared normally // TODO (docs) It is easy to compare known types in a custom way - just implement a wrapper struct and IComparable<T>. This also will be faster than a comparable. if (typeof(T) == typeof(bool)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (bool)(object)(x); var y1 = (bool)(object)(y); return(x1.CompareTo(y1)); } if (typeof(T) == typeof(byte)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (byte)(object)(x); var y1 = (byte)(object)(y); return(x1 - y1); } if (typeof(T) == typeof(sbyte)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (sbyte)(object)(x); var y1 = (sbyte)(object)(y); return(x1 - y1); } if (typeof(T) == typeof(char)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (char)(object)(x); var y1 = (char)(object)(y); return(x1 - y1); } if (typeof(T) == typeof(short)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (short)(object)(x); var y1 = (short)(object)(y); return(x1 - y1); } if (typeof(T) == typeof(ushort)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (ushort)(object)(x); var y1 = (ushort)(object)(y); // ReSharper disable RedundantCast return((int)x1 - (int)y1); // ReSharper restore RedundantCast } if (typeof(T) == typeof(int)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (int)(object)(x); var y1 = (int)(object)(y); return(x1.CompareTo(y1)); } if (typeof(T) == typeof(uint)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (uint)(object)(x); var y1 = (uint)(object)(y); return(x1.CompareTo(y1)); } if (typeof(T) == typeof(long)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (long)(object)(x); var y1 = (long)(object)(y); return(x1.CompareTo(y1)); } if (typeof(T) == typeof(ulong)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (ulong)(object)(x); var y1 = (ulong)(object)(y); return(x1.CompareTo(y1)); } if (typeof(T) == typeof(float)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (float)(object)(x); var y1 = (float)(object)(y); if (x1 < y1) { return(-1); } if (x1 > y1) { return(1); } // ReSharper disable once CompareOfFloatsByEqualityOperator if (x1 == y1) { return(0); } // At least one of the values is NaN. if (float.IsNaN(x1)) { return(float.IsNaN(y1) ? 0 : -1); } else // f is NaN. { return(1); } } if (typeof(T) == typeof(double)) { // x1.CompareTo(y1) is not inlined, copy manually // https://github.com/dotnet/corefx/blob/5fe165ab631675273f5d19bebc15b5733ef1354d/src/Common/src/CoreLib/System/Double.cs#L147 Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (double)(object)(x); var y1 = (double)(object)(y); if (x1 < y1) { return(-1); } if (x1 > y1) { return(1); } // ReSharper disable once CompareOfFloatsByEqualityOperator if (x1 == y1) { return(0); } // At least one of the values is NaN. if (double.IsNaN(x1)) { return(double.IsNaN(y1) ? 0 : -1); } else { return(1); } } if (typeof(T) == typeof(decimal)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (decimal)(object)(x); var y1 = (decimal)(object)(y); return(decimal.Compare(x1, y1)); } if (typeof(T) == typeof(DateTime)) { Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = (DateTime)(object)(x); var y1 = (DateTime)(object)(y); return(DateTime.Compare(x1, y1)); } if (typeof(T) == typeof(Timestamp)) { // TODO for TS we could use sub in normal cases Debug.Assert(_keyComparer == null, "Known types should not have a comparer"); var x1 = ((Timestamp)(object)(x)).Nanos; var y1 = ((Timestamp)(object)(y)).Nanos; return(x1.CompareTo(y1)); } // NB all primitive types are IComparable, all custom types could be easily made such // This optimization using Spreads.Unsafe package works for any type that implements // the interface and is as fast as `typeof(T) == typeof(...)` approach. // The special cases above are left for scenarios when the "static readonly" optimization // doesn't work, e.g. AOT. See discussion #100. if (IsIComparable) { return(UnsafeEx.CompareToConstrained(ref x, ref y)); } if (_keyComparer != null) { return(_keyComparer.Compare(x, y)); } return(CompareSlow(x, y)); }
internal static int InterpolationSearchGeneric <T>(ref T searchSpace, int offset, int length, T value, KeyComparer <T> comparer = default) { // Try interpolation only for big-enough lengths and do minimal job, // just find the range with exponential search with minimal branches // and switch to binary search. unchecked { int i; int lo = offset; int hi = offset + length - 1; if (hi - lo > 16) { var vlo = UnsafeEx.ReadUnaligned(ref searchSpace); var vhi = UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, hi)); int range = hi - lo; long vRange = comparer.Diff(vhi, vlo); // (hi - lo) <= int32.MaxValue // vlo could be zero while value could easily be close to int64.MaxValue (nanos in unix time, we are now between 60 and 61 bit at 60.4) // convert to double here to avoid overflow and for much faster calculations // (only 4 cycles vs 25 cycles https://lemire.me/blog/2017/11/16/fast-exact-integer-divisions-using-floating-point-operations/) double nominator = (hi - lo) * (double)comparer.Diff(value, vlo); i = (int)(nominator / vRange); if ((uint)i > range) { i = i < 0 ? 0 : range; } // make i relative to vecStart i += lo; int c = comparer.Compare(value, UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, i))); if (c == 0) { goto FOUND; } var step = 1; if (c > 0) { while (true) { i += step; if (i > hi) { break; } c = comparer.Compare(value, UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, i))); if (c <= 0) { if (c == 0) { goto FOUND; } hi = i - 1; break; } step <<= 1; } lo = i - step + 1; } else { while (true) { i -= step; if (i < lo) { break; } c = comparer.Compare(value, UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, i))); if (c >= 0) { if (c == 0) { goto FOUND; } lo = i + 1; break; } step <<= 1; } hi = i + step - 1; } } return(BinarySearch(ref searchSpace, lo, 1 + hi - lo, value)); FOUND: return(i); } }
public static int SearchToLookup <T>(int offset, int length, Lookup lookup, int i, ref T searchSpace, ref T value) { if (i >= offset) { if (lookup.IsEqualityOK()) { goto RETURN; } if (lookup == Lookup.LT) { if (i == offset) { goto RETURN_O; } i--; } else // depends on if (eqOk) above { Debug.Assert(lookup == Lookup.GT); if (i == offset + length - 1) { goto RETURN_OL; } i++; } } else { if (lookup == Lookup.EQ) { goto RETURN; } i = ~i; // LT or LE if (((uint)lookup & (uint)Lookup.LT) != 0) { // i is idx of element that is larger, nothing here for LE/LT if (i == offset) { goto RETURN_O; } i--; } else { Debug.Assert(((uint)lookup & (uint)Lookup.GT) != 0); Debug.Assert(i <= offset + length); // if was negative, if it was ~length then there are no more elements for GE/GT if (i == offset + length) { goto RETURN_OL; } // i is the same, ~i is idx of element that is GT the value } } value = UnsafeEx.ReadUnaligned(ref Unsafe.Add(ref searchSpace, i)); RETURN: Debug.Assert(unchecked ((uint)i) - offset < unchecked ((uint)length)); return(i); RETURN_O: return(~offset); RETURN_OL: return(~(offset + length)); }
public void GetValuesViaCalli <K, V>(DataBlock block, int index, ref K key, ref V value) { var dlg = Unsafe.As <Getter <K, V> >(Getter); UnsafeEx.CalliDataBlock(dlg, block, index, ref key, ref value, GetterPtr); }
public long Diff(T minuend, T subtrahend) { // ReSharper disable RedundantCast if (typeof(T) == typeof(byte)) { var x1 = (byte)(object)(minuend); var y1 = (byte)(object)(subtrahend); return(checked ((long)(x1) - (long)y1)); } if (typeof(T) == typeof(sbyte)) { var x1 = (sbyte)(object)(minuend); var y1 = (sbyte)(object)(subtrahend); return(checked ((long)(x1) - (long)y1)); } if (typeof(T) == typeof(char)) { var x1 = (char)(object)(minuend); var y1 = (char)(object)(subtrahend); return(checked ((long)(x1) - (long)y1)); } if (typeof(T) == typeof(short)) { var x1 = (short)(object)(minuend); var y1 = (short)(object)(subtrahend); return(checked ((long)(x1) - (long)y1)); } if (typeof(T) == typeof(ushort)) { var x1 = (ushort)(object)(minuend); var y1 = (ushort)(object)(subtrahend); return(checked ((long)(x1) - (long)y1)); } if (typeof(T) == typeof(int)) { var x1 = (int)(object)(minuend); var y1 = (int)(object)(subtrahend); return(checked ((long)(x1) - (long)y1)); } if (typeof(T) == typeof(uint)) { var x1 = (uint)(object)(minuend); var y1 = (uint)(object)(subtrahend); return(checked ((long)(x1) - y1)); } if (typeof(T) == typeof(long)) { var x1 = (long)(object)(minuend); var y1 = (long)(object)(subtrahend); return(checked (x1 - y1)); } if (typeof(T) == typeof(ulong)) { var x1 = (ulong)(object)(minuend); var y1 = (ulong)(object)(subtrahend); return(checked ((long)(x1) - (long)y1)); } if (typeof(T) == typeof(DateTime)) { var x1 = (DateTime)(object)(minuend); var y1 = (DateTime)(object)(subtrahend); return(checked (x1.Ticks - y1.Ticks)); } if (typeof(T) == typeof(Timestamp)) { var x1 = (Timestamp)(object)(minuend); var y1 = (Timestamp)(object)(subtrahend); return(checked (x1.Nanos - y1.Nanos)); } // ReSharper restore RedundantCast // TODO SmallDecimal if (IsIInt64Diffable) { return(UnsafeEx.DiffLongConstrained(ref minuend, ref subtrahend)); } if (_keyComparer != null) { return(DiffViaInterface(minuend, subtrahend)); } ThrowHelper.ThrowNotSupportedException(); return(0L); }