//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Dispatcher { using System; using System.Collections; using System.Collections.Generic; using System.Runtime; #if NO internal interface IQueryBufferPool { // Clear all pools void Reset(); // Trim pools void Trim(); } #endif // // Generic struct representing ranges within buffers // internal struct QueryRange { internal int end; // INCLUSIVE - the end of the range internal int start; // INCLUSIVE - the start of the range #if NO internal QueryRange(int offset, QueryRange range) { this.start = range.start + offset; this.end = range.end + offset; } #endif internal QueryRange(int start, int end) { this.start = start; this.end = end; } internal int Count { get { return this.end - this.start + 1; } } #if NO internal int this[int offset] { get { return this.start + offset; } } internal bool IsNotEmpty { get { return (this.end >= this.start); } } internal void Clear() { this.end = this.start - 1; } internal void Grow(int offset) { this.end += offset; } #endif internal bool IsInRange(int point) { return (this.start <= point && point <= this.end); } #if NO internal void Set(int start, int end) { this.start = start; this.end = end; } #endif internal void Shift(int offset) { this.start += offset; this.end += offset; } } /// /// Our own buffer management /// There are a few reasons why we don't reuse something in System.Collections.Generic /// 1. We want Clear() to NOT reallocate the internal array. We want it to simply set the Count = 0 /// This allows us to reuse buffers with impunity. /// 2. We want to be able to replace the internal buffer in a collection with a different one. Again, /// this is to help with pooling /// 3. We want to be able to control how fast buffers grow. /// 4. Does absolutely no bounds or null checking. As fast as we can make it. All checking should be done /// by whoever wraps this. Checking is unnecessary for many internal uses where we need optimal perf. /// 5. Does more precise trimming /// 6. AND this is a struct /// /// internal struct QueryBuffer { internal T[] buffer; // buffer of T. Frequently larger than count internal int count; // Actual # of items internal static T[] EmptyBuffer = new T[0]; /// /// Construct a new buffer /// /// internal QueryBuffer(int capacity) { if (0 == capacity) { this.buffer = QueryBuffer.EmptyBuffer; } else { this.buffer = new T[capacity]; } this.count = 0; } #if NO internal QueryBuffer(QueryBuffer buffer) { this.buffer = (T[]) buffer.buffer.Clone(); this.count = buffer.count; } internal QueryBuffer(T[] buffer) { Fx.Assert(null != buffer, ""); this.buffer = buffer; this.count = 0; } /// /// Get and set the internal buffer /// If you set the buffer, the count will automatically be set to 0 /// internal T[] Buffer { get { return this.buffer; } set { Fx.Assert(null != value, ""); this.buffer = value; this.count = 0; } } #endif /// /// # of items /// internal int Count { get { return this.count; } #if NO set { Fx.Assert(value >= 0 && value <= this.buffer.Length, ""); this.count = value; } #endif } #if NO /// /// How much can it hold /// internal int Capacity { get { return this.buffer.Length; } set { Fx.Assert(value >= this.count, ""); if (value > this.buffer.Length) { Array.Resize(ref this.buffer, value); } } } #endif internal T this[int index] { get { return this.buffer[index]; } set { this.buffer[index] = value; } } #if NO internal void Add() { if (this.count == this.buffer.Length) { Array.Resize(ref this.buffer, this.count > 0 ? this.count * 2 : 16); } this.count++; } #endif /// /// Add an element to the buffer /// internal void Add(T t) { if (this.count == this.buffer.Length) { Array.Resize(ref this.buffer, this.count > 0 ? this.count * 2 : 16); } this.buffer[this.count++] = t; } #if NO /// /// Useful when this is a buffer of structs /// internal void AddReference(ref T t) { if (this.count == this.buffer.Length) { Array.Resize(ref this.buffer, this.count > 0 ? this.count * 2 : 16); } this.buffer[this.count++] = t; } #endif /// /// Add all the elements in the given buffer to this one /// We can do this very efficiently using an Array Copy /// internal void Add(ref QueryBuffer addBuffer) { if (1 == addBuffer.count) { this.Add(addBuffer.buffer[0]); return; } int newCount = this.count + addBuffer.count; if (newCount >= this.buffer.Length) { this.Grow(newCount); } // Copy all the new elements in Array.Copy(addBuffer.buffer, 0, this.buffer, this.count, addBuffer.count); this.count = newCount; } #if NO internal void Add(T[] addBuffer, int startAt, int addCount) { int newCount = this.count + addCount; if (newCount >= this.buffer.Length) { this.Grow(newCount); } // Copy all the new elements in Array.Copy(addBuffer, startAt, this.buffer, this.count, addCount); this.count = newCount; } /// /// Add without attempting to grow the buffer. Faster, but must be used with care. /// Caller must ensure that the buffer is large enough. /// internal void AddOnly(T t) { this.buffer[this.count++] = t; } #endif /// /// Set the count to zero but do NOT get rid of the actual buffer /// internal void Clear() { this.count = 0; } #if NO // // Copy from one location in the buffer to another // internal void Copy(int from, int to) { this.buffer[to] = this.buffer[from]; } internal void Copy(int from, int to, int count) { Array.Copy(this.buffer, from, this.buffer, to, count); } #endif internal void CopyFrom(ref QueryBuffer addBuffer) { int addCount = addBuffer.count; switch (addCount) { default: if (addCount > this.buffer.Length) { this.buffer = new T[addCount]; } // Copy all the new elements in Array.Copy(addBuffer.buffer, 0, this.buffer, 0, addCount); this.count = addCount; break; case 0: this.count = 0; break; case 1: if (this.buffer.Length == 0) { this.buffer = new T[1]; } this.buffer[0] = addBuffer.buffer[0]; this.count = 1; break; } } internal void CopyTo(T[] dest) { Array.Copy(this.buffer, dest, this.count); } #if NO /// /// Ensure that the internal buffer has adequate capacity /// internal void EnsureCapacity(int capacity) { if (capacity > this.buffer.Length) { this.Grow(capacity); } } internal void Erase() { Array.Clear(this.buffer, 0, this.count); this.count = 0; } #endif void Grow(int capacity) { int newCapacity = this.buffer.Length * 2; Array.Resize(ref this.buffer, capacity > newCapacity ? capacity : newCapacity); } internal int IndexOf(T t) { for (int i = 0; i < this.count; ++i) { if (t.Equals(this.buffer[i])) { return i; } } return -1; } internal int IndexOf(T t, int startAt) { for (int i = startAt; i < this.count; ++i) { if (t.Equals(this.buffer[i])) { return i; } } return -1; } #if NO internal void InsertAt(T t, int at) { this.ReserveAt(at, 1); this.buffer[at] = t; } #endif internal bool IsValidIndex(int index) { return (index >= 0 && index < this.count); } #if NO internal T Pop() { Fx.Assert(this.count > 0, ""); return this.buffer[--this.count]; } internal void Push(T t) { this.Add(t); } #endif /// /// Reserve enough space for count elements /// internal void Reserve(int reserveCount) { int newCount = this.count + reserveCount; if (newCount >= this.buffer.Length) { this.Grow(newCount); } this.count = newCount; } internal void ReserveAt(int index, int reserveCount) { if (index == this.count) { this.Reserve(reserveCount); return; } int newCount; if (index > this.count) { // We want to reserve starting at a location past what is current committed. // No shifting needed newCount = index + reserveCount + 1; if (newCount >= this.buffer.Length) { this.Grow(newCount); } } else { // reserving space within an already allocated portion of the buffer // we'll ensure that the buffer can fit 'newCount' items, then shift by reserveCount starting at index newCount = this.count + reserveCount; if (newCount >= this.buffer.Length) { this.Grow(newCount); } // Move to make room Array.Copy(this.buffer, index, this.buffer, index + reserveCount, this.count - index); } this.count = newCount; } internal void Remove(T t) { int index = this.IndexOf(t); if (index >= 0) { this.RemoveAt(index); } } internal void RemoveAt(int index) { if (index < this.count - 1) { Array.Copy(this.buffer, index + 1, this.buffer, index, this.count - index - 1); } this.count--; } internal void Sort(IComparer comparer) { Array.Sort(this.buffer, 0, this.count, comparer); } #if NO /// /// Reduce the buffer capacity so that it is no greater than twice the element count /// internal void Trim() { int maxSize = this.count * 2; if (maxSize < this.buffer.Length / 2) { if (0 == maxSize) { this.buffer = QueryBuffer.EmptyBuffer; } else { T[] newBuffer = new T[maxSize]; Array.Copy(this.buffer, newBuffer, maxSize); } } } #endif /// /// Reduce the buffer capacity so that its size is exactly == to the element count /// internal void TrimToCount() { if (this.count < this.buffer.Length) { if (0 == this.count) { this.buffer = QueryBuffer.EmptyBuffer; } else { T[] newBuffer = new T[this.count]; Array.Copy(this.buffer, newBuffer, this.count); } } } } internal struct SortedBuffer where C : IComparer { int size; T[] buffer; static DefaultComparer Comparer; internal SortedBuffer(C comparerInstance) { this.size = 0; this.buffer = null; if (Comparer == null) { Comparer = new DefaultComparer(comparerInstance); } else { Fx.Assert(object.ReferenceEquals(DefaultComparer.Comparer, comparerInstance), "The SortedBuffer type has already been initialized with a different comparer instance."); } } internal T this[int index] { get { return GetAt(index); } } internal int Capacity { #if NO get { return this.buffer == null ? 0 : this.buffer.Length; } #endif set { if (this.buffer != null) { if (value != this.buffer.Length) { Fx.Assert(value >= this.size, "New capacity must be >= size"); if (value > 0) { Array.Resize(ref this.buffer, value); } else { this.buffer = null; } } } else { this.buffer = new T[value]; } } } internal int Count { get { return this.size; } } internal int Add(T item) { int i = Search(item); if (i < 0) { i = ~i; InsertAt(i, item); } return i; } #if NO internal void CopyTo(T[] array) { CopyTo(array, 0, this.size); } internal void CopyTo(T[] array, int start, int length) { Fx.Assert(array != null, ""); Fx.Assert(start >= 0, ""); Fx.Assert(length >= 0, ""); Fx.Assert(start + length < this.size, ""); Array.Copy(this.buffer, 0, array, start, length); } #endif internal void Clear() { this.size = 0; } #if NO internal bool Contains(T item) { return IndexOf(item) >= 0; } #endif internal void Exchange(T old, T replace) { if (Comparer.Compare(old, replace) == 0) { int i = IndexOf(old); if (i >= 0) { this.buffer[i] = replace; } else { Insert(replace); } } else { // PERF, Microsoft, can this be made more efficient? Does it need to be? Remove(old); Insert(replace); } } internal T GetAt(int index) { Fx.Assert(index < this.size, "Index is greater than size"); return this.buffer[index]; } internal int IndexOf(T item) { return Search(item); } internal int IndexOfKey(K key, IItemComparer itemComp) { return Search(key, itemComp); } internal int Insert(T item) { int i = Search(item); if (i >= 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new ArgumentException(SR.GetString(SR.QueryItemAlreadyExists))); } // If an item is not found, Search returns the bitwise negation of // the index an item should inserted at; InsertAt(~i, item); return ~i; } void InsertAt(int index, T item) { Fx.Assert(index >= 0 && index <= this.size, ""); if (this.buffer == null) { this.buffer = new T[1]; } else if (this.buffer.Length == this.size) { // PERF, Microsoft, how should we choose a new size? T[] tmp = new T[this.size + 1]; if (index == 0) { Array.Copy(this.buffer, 0, tmp, 1, this.size); } else if (index == this.size) { Array.Copy(this.buffer, 0, tmp, 0, this.size); } else { Array.Copy(this.buffer, 0, tmp, 0, index); Array.Copy(this.buffer, index, tmp, index + 1, this.size - index); } this.buffer = tmp; } else { Array.Copy(this.buffer, index, this.buffer, index + 1, this.size - index); } this.buffer[index] = item; ++this.size; } internal bool Remove(T item) { int i = IndexOf(item); if (i >= 0) { RemoveAt(i); return true; } return false; } internal void RemoveAt(int index) { Fx.Assert(index >= 0 && index < this.size, ""); if (index < this.size - 1) { Array.Copy(this.buffer, index + 1, this.buffer, index, this.size - index - 1); } this.buffer[--this.size] = default(T); } int Search(T item) { if (size == 0) return ~0; return Search(item, Comparer); } int Search(K key, IItemComparer comparer) { if (this.size <= 8) { return LinearSearch(key, comparer, 0, this.size); } else { return BinarySearch(key, comparer); } } int BinarySearch(K key, IItemComparer comparer) { // [low, high) int low = 0; int high = this.size; int mid, result; // Binary search is implemented here so we could look for a type that is different from the // buffer type. Also, the search switches to linear for 8 or fewer elements. while (high - low > 8) { mid = (high + low) / 2; result = comparer.Compare(key, this.buffer[mid]); if (result < 0) { high = mid; } else if (result > 0) { low = mid + 1; } else { return mid; } } return LinearSearch(key, comparer, low, high); } // [start, bound) int LinearSearch(K key, IItemComparer comparer, int start, int bound) { int result; for (int i = start; i < bound; ++i) { result = comparer.Compare(key, this.buffer[i]); if (result == 0) { return i; } if (result < 0) { // Return the bitwise negation of the insertion index return ~i; } } // Return the bitwise negation of the insertion index return ~bound; } #if NO internal T[] ToArray() { T[] tmp = new T[this.size]; Array.Copy(this.buffer, 0, tmp, 0, this.size); return tmp; } #endif internal void Trim() { this.Capacity = this.size; } internal class DefaultComparer : IItemComparer { public static IComparer Comparer; public DefaultComparer(C comparer) { Comparer = comparer; } public int Compare(T item1, T item2) { return Comparer.Compare(item1, item2); } } } internal interface IItemComparer { int Compare(K key, V value); } }