DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
BigArray_T.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Threading;
21 
22 namespace Deveel.Data.Util {
23  public sealed class BigArray<T> : IBigList<T>, IDisposable {
24  internal static readonly IEqualityComparer<T> _comparer = EqualityComparer<T>.Default;
25 
26  private static readonly T[][] _emptyArray = new T[0][];
27 
28  private readonly int _blockLength;
29  private readonly T _defaultValue;
30  private T[][] _firstArray;
31  private int _lastBlockLength;
32 
33  public BigArray(long length)
34  : this(length, default(T)) {
35  }
36 
37  public BigArray(long length, T defaultValue)
38  : this(length, defaultValue, BigArray.DefaultBlockLength) {
39  }
40 
41  public BigArray(long length, int blockLength)
42  : this(length, default(T), blockLength) {
43  }
44 
45  public BigArray(long length, T defaultValue, int blockLength) {
46  if (blockLength < 1024 * 1024)
47  throw new ArgumentOutOfRangeException("blockSize");
48 
49  _defaultValue = defaultValue;
50  _blockLength = blockLength;
51  _length = length;
52 
53  if (length == 0) {
54  _firstArray = _emptyArray;
55  return;
56  }
57 
58  int numBlocks;
59  int lastBlockSize;
60 
61  checked {
62  numBlocks = (int)(length / blockLength);
63  lastBlockSize = (int)(length % blockLength);
64 
65  if (lastBlockSize > 0)
66  numBlocks++;
67  else
68  lastBlockSize = blockLength;
69  }
70 
71  _lastBlockLength = lastBlockSize;
72  _firstArray = new T[numBlocks][];
73  }
74 
75  public void Dispose() {
76  _firstArray = null;
77  }
78 
79  internal long _length;
80 
81  public long Length {
82  get { return _length; }
83  }
84 
85  public int BlockLength {
86  get { return _blockLength; }
87  }
88 
90  get { return Length; }
91  }
92 
93  void IBigList<T>.Insert(long offset, T item) {
94  throw new NotImplementedException();
95  }
96 
97  void IBigList<T>.RemoveAt(long index) {
98  throw new NotImplementedException();
99  }
100 
101  public T this[long index] {
102  get {
103  var block = _firstArray[index / _blockLength];
104  if (block == null)
105  return _defaultValue;
106 
107  return block[index % _blockLength];
108  }
109  set {
110  long blockIndex = index / _blockLength;
111 
112  var block = _firstArray[blockIndex];
113  if (block == null) {
114  if (_comparer.Equals(value, _defaultValue))
115  return;
116 
117  if (blockIndex == _firstArray.Length - 1)
118  block = new T[_lastBlockLength];
119  else
120  block = new T[_blockLength];
121 
122  if (!_comparer.Equals(default(T), _defaultValue))
123  for (int i = 0; i < block.Length; i++)
124  block[i] = _defaultValue;
125 
126  _firstArray[blockIndex] = block;
127  }
128 
129  block[index % _blockLength] = value;
130  }
131  }
132 
133  public void Resize(long newLength) {
134  if (newLength < 0)
135  throw new ArgumentOutOfRangeException("newLength", "newLength can't be negative.");
136 
137  if (newLength == 0) {
138  _firstArray = _emptyArray;
139  _lastBlockLength = 0;
140  _length = 0;
141  return;
142  }
143 
144  int numBlocks;
145  int lastBlockSize;
146  checked {
147  numBlocks = (int)(newLength / _blockLength);
148  lastBlockSize = (int)(newLength % _blockLength);
149 
150  if (lastBlockSize > 0)
151  numBlocks++;
152  else
153  lastBlockSize = _blockLength;
154  }
155 
156  _lastBlockLength = lastBlockSize;
157 
158  T[][] newArray = _firstArray;
159  if (numBlocks != _firstArray.Length) {
160  int minBlocks = System.Math.Min(numBlocks, _firstArray.Length);
161  newArray = new T[numBlocks][];
162  for (int i = 0; i < minBlocks; i++)
163  newArray[i] = _firstArray[i];
164  }
165 
166  var block = newArray[numBlocks - 1];
167  if (block != null) {
168  int oldLength = block.Length;
169  Array.Resize(ref block, _lastBlockLength);
170  newArray[numBlocks - 1] = block;
171 
172  if (!_comparer.Equals(default(T), _defaultValue))
173  for (int i = oldLength; i < _lastBlockLength; i++)
174  block[i] = _defaultValue;
175  }
176 
177  if (_firstArray.Length > 0 && newLength > _length && newArray.Length != _firstArray.Length) {
178  int oldLastBlockIndex = _firstArray.Length - 1;
179  if (newArray[oldLastBlockIndex] != null)
180  Array.Resize(ref newArray[oldLastBlockIndex], _blockLength);
181  }
182 
183  _firstArray = newArray;
184  _length = newLength;
185  }
186 
187  void ICollection<T>.Add(T item) {
188  throw new NotImplementedException();
189  }
190 
191  void ICollection<T>.Clear() {
192  throw new NotImplementedException();
193  }
194 
195  public bool Contains(T item) {
196  return IndexOf(item) != -1;
197  }
198 
199  void ICollection<T>.CopyTo(T[] array, int arrayIndex) {
200  throw new NotImplementedException();
201  }
202 
203  bool ICollection<T>.Remove(T item) {
204  throw new NotSupportedException();
205  }
206 
207  int ICollection<T>.Count {
208  get { return (int) Length; }
209  }
210 
211  bool ICollection<T>.IsReadOnly {
212  get { return false; }
213  }
214 
215  public bool Contains(T item, long startIndex, long count) {
216  return IndexOf(item, startIndex, count) != -1;
217  }
218 
219  public long IndexOf(T item) {
220  return IndexOf(item, 0, _length);
221  }
222 
223  void IList<T>.Insert(int index, T item) {
224  throw new NotImplementedException();
225  }
226 
227  void IList<T>.RemoveAt(int index) {
228  throw new NotImplementedException();
229  }
230 
231  T IList<T>.this[int index] {
232  get { return this[index]; }
233  set { this[index] = value; }
234  }
235 
236  int IList<T>.IndexOf(T item) {
237  return (int) IndexOf(item);
238  }
239 
240  public long IndexOf(T item, long startIndex, long count) {
241  if (startIndex < 0 || startIndex > _length)
242  throw new ArgumentOutOfRangeException("startIndex");
243 
244  if (count == 0)
245  return -1;
246 
247  long end = startIndex + count;
248  if (count < 0 || end > _length)
249  throw new ArgumentOutOfRangeException("count");
250 
251  long blockIndex = startIndex / _blockLength;
252  long positionInBlock = startIndex % _blockLength;
253  T[] block = _firstArray[blockIndex];
254  long position = startIndex;
255 
256  while (true) {
257  if (block == null) {
258  position += _blockLength - positionInBlock;
259 
260  if (position >= end)
261  return -1;
262 
263  blockIndex++;
264  positionInBlock = 0;
265  block = _firstArray[blockIndex];
266  continue;
267  }
268 
269  if (_comparer.Equals(block[positionInBlock], item))
270  return position;
271 
272  position++;
273 
274  if (position >= end)
275  return -1;
276 
277  if (positionInBlock > block.Length) {
278  blockIndex++;
279  positionInBlock = 0;
280  block = _firstArray[blockIndex];
281  }
282  }
283  }
284 
285  public long BinarySearch(T item) {
286  return _BinarySearch(item, 0, _length, Comparer<T>.Default.Compare);
287  }
288 
289  public long BinarySearch(T item, Comparison<T> comparer) {
290  if (comparer == null)
291  comparer = Comparer<T>.Default.Compare;
292 
293  return _BinarySearch(item, 0, _length, comparer);
294  }
295 
296  public long BinarySearch(T item, long startIndex, long count, Comparison<T> comparer = null) {
297  if (startIndex < 0 || startIndex > _length)
298  throw new ArgumentOutOfRangeException("startIndex");
299 
300  if (count == 0)
301  return ~startIndex;
302 
303  long end = startIndex + count;
304  if (count < 0 || end > _length)
305  throw new ArgumentOutOfRangeException("count");
306 
307  if (comparer == null)
308  comparer = Comparer<T>.Default.Compare;
309 
310  return _BinarySearch(item, startIndex, count, comparer);
311  }
312 
313  private long _BinarySearch(T item, long startIndex, long count, Comparison<T> comparer) {
314  while (true) {
315  if (count == 0)
316  return ~startIndex;
317 
318  long middle = count / 2;
319  long position = startIndex + middle;
320  int comparison = comparer(item, this[position]);
321  if (comparison == 0)
322  return position;
323 
324  if (comparison < 0) {
325  count = middle;
326  } else {
327  middle++;
328  startIndex += middle;
329  count -= middle;
330  }
331  }
332  }
333 
334  public void Sort(Comparison<T> comparer = null) {
335  if (comparer == null)
336  comparer = Comparer<T>.Default.Compare;
337 
338  var parallelSort = new BigArrayParallelSort();
339 
340  _Sort(parallelSort, 0, _length, comparer);
341 
342  parallelSort.Wait();
343  }
344 
345  public void Sort(long startIndex, long count, Comparison<T> comparer) {
346  if (startIndex < 0 || startIndex > _length)
347  throw new ArgumentOutOfRangeException("startIndex");
348 
349  if (count == 0)
350  return;
351 
352  long end = startIndex + count;
353  if (count < 0 || end > _length)
354  throw new ArgumentOutOfRangeException("count");
355 
356  if (comparer == null)
357  comparer = Comparer<T>.Default.Compare;
358 
359  var parallelSort = new BigArrayParallelSort();
360 
361  _Sort(parallelSort, startIndex, count, comparer);
362 
363  parallelSort.Wait();
364  }
365 
366  private void _Sort(BigArrayParallelSort parallelSort, long startIndex, long count, Comparison<T> comparer) {
367  if (count <= 1)
368  return;
369 
370  long pivotOffset = _Partition(startIndex, count, comparer);
371 
372  // if we don't have more than 10k items, we don't need to try to run in parallel.
373  if (parallelSort == null || count < 10000)
374  _Sort(parallelSort, startIndex, pivotOffset, comparer);
375  else {
376  // before putting another task to the threadpool, we verify if the amount of parallel
377  // work is not exceeding the number of CPUs.
378  // Even if the threadpool can be bigger than the number of CPUs, sorting is a no-wait
379  // operation and so putting an extra work to do will only increase the number of task
380  // switches.
381  int parallelCount = Interlocked.Increment(ref BigArrayParallelSort.ParallelSortCount);
382  if (parallelCount >= Environment.ProcessorCount) {
383  // we have too many threads in parallel
384  // (note that the first thread never stops, that's why I used >= operator).
385  Interlocked.Decrement(ref BigArrayParallelSort.ParallelSortCount);
386 
387  // do a normal sub-sort.
388  _Sort(parallelSort, startIndex, pivotOffset, comparer);
389  } else {
390  bool shouldProcessNormally = false;
391 
392  // ok, we have the right to process in parallel, so let's start by saying we
393  // are processing in parallel.
394  Interlocked.Increment(ref parallelSort.ExecutingCount);
395  try {
396  ThreadPool.QueueUserWorkItem
397  (
398  (x) => {
399  // ok, finally we can sort. But, if an exception is thrown, we should redirect it to the
400  // main thread.
401  try {
402  _Sort(parallelSort, startIndex, pivotOffset, comparer);
403  } catch (Exception exception) {
404  // here we store the exception.
405  lock (parallelSort) {
406  var exceptions = parallelSort.Exceptions;
407  if (exceptions == null) {
408  exceptions = new List<Exception>();
409  parallelSort.Exceptions = exceptions;
410  }
411 
412  exceptions.Add(exception);
413  }
414  } finally {
415  // Independent if we had an exception or not, we should decrement
416  // both counters.
417  Interlocked.Decrement(ref BigArrayParallelSort.ParallelSortCount);
418 
419  int parallelRemaining = Interlocked.Decrement(ref parallelSort.ExecutingCount);
420 
421  // if we were the last parallel thread, we must notify the main thread if it is waiting
422  // for us.
423  if (parallelRemaining == 0)
424  lock (parallelSort)
425  Monitor.Pulse(parallelSort);
426  }
427  }
428  );
429  } catch {
430  // if an exception was thrown trying to call the thread pool, we simple reduce the
431  // count number and do the sort normally.
432  // The sort is out of the catch in case an Abort is done.
433  Interlocked.Decrement(ref parallelSort.ExecutingCount);
434  Interlocked.Decrement(ref BigArrayParallelSort.ParallelSortCount);
435  shouldProcessNormally = true;
436  }
437 
438  if (shouldProcessNormally)
439  _Sort(parallelSort, startIndex, pivotOffset, comparer);
440  }
441  }
442 
443  _Sort(parallelSort, startIndex + pivotOffset + 1, count - pivotOffset - 1, comparer);
444  }
445  private long _Partition(long startIndex, long count, Comparison<T> comparer) {
446  long pivotIndex = startIndex + count / 2;
447  T pivotValue = this[pivotIndex];
448 
449  long right = startIndex + count - 1;
450  if (pivotIndex != right)
451  this[pivotIndex] = this[right];
452 
453  long storeIndex = startIndex;
454  for (long index = startIndex; index < right; index++) {
455  T valueAtIndex = this[index];
456  if (comparer(valueAtIndex, pivotValue) >= 0)
457  continue;
458 
459  if (index != storeIndex) {
460  this[index] = this[storeIndex];
461  this[storeIndex] = valueAtIndex;
462  }
463 
464  storeIndex++;
465  }
466 
467  if (right != storeIndex)
468  this[right] = this[storeIndex];
469 
470  this[storeIndex] = pivotValue;
471 
472  return storeIndex - startIndex;
473  }
474 
475  public void Swap(long position1, long position2) {
476  if (position1 < 0 || position1 >= _length)
477  throw new ArgumentOutOfRangeException("position1");
478 
479  if (position2 < 0 || position2 >= _length)
480  throw new ArgumentOutOfRangeException("position2");
481 
482  if (position1 == position2)
483  return;
484 
485  T value1 = this[position1];
486  this[position1] = this[position2];
487  this[position2] = value1;
488  }
489 
490  public IEnumerator<T> GetEnumerator() {
491  int lastBlockIndex = _firstArray.Length - 1;
492  for (int i = 0; i <= lastBlockIndex; i++) {
493  var block = _firstArray[i];
494 
495  if (block == null) {
496  int numberOfFakes;
497  if (i == lastBlockIndex)
498  numberOfFakes = _lastBlockLength;
499  else
500  numberOfFakes = _blockLength;
501 
502  for (int j = 0; j < numberOfFakes; j++)
503  yield return _defaultValue;
504  } else {
505  foreach (var value in block)
506  yield return value;
507  }
508  }
509  }
510 
511  #region BigArrayParallelSort
512 
513  private sealed class BigArrayParallelSort {
514  public static int ParallelSortCount;
515  public int ExecutingCount;
516  public List<Exception> Exceptions;
517 
518  public void Wait() {
519  lock (this)
520  while (ExecutingCount > 0)
521  Monitor.Wait(this);
522 
523  // TODO: An improvement is to have an aggregate exception like for 4.5
524  if (Exceptions != null)
525  throw Exceptions[0];
526  }
527  }
528 
529  #endregion
530 
531  IEnumerator IEnumerable.GetEnumerator() {
532  return GetEnumerator();
533  }
534  }
535 }
bool Contains(T item, long startIndex, long count)
Definition: BigArray_T.cs:215
void Insert(long index, T item)
long _Partition(long startIndex, long count, Comparison< T > comparer)
Definition: BigArray_T.cs:445
long IndexOf(T item, long startIndex, long count)
Definition: BigArray_T.cs:240
readonly int _blockLength
Definition: BigArray_T.cs:28
BigArray(long length, T defaultValue, int blockLength)
Definition: BigArray_T.cs:45
void RemoveAt(long index)
long BinarySearch(T item, Comparison< T > comparer)
Definition: BigArray_T.cs:289
long BinarySearch(T item, long startIndex, long count, Comparison< T > comparer=null)
Definition: BigArray_T.cs:296
void Sort(long startIndex, long count, Comparison< T > comparer)
Definition: BigArray_T.cs:345
IEnumerator< T > GetEnumerator()
Definition: BigArray_T.cs:490
BigArray(long length, int blockLength)
Definition: BigArray_T.cs:41
void Resize(long newLength)
Definition: BigArray_T.cs:133
void _Sort(BigArrayParallelSort parallelSort, long startIndex, long count, Comparison< T > comparer)
Definition: BigArray_T.cs:366
void Sort(Comparison< T > comparer=null)
Definition: BigArray_T.cs:334
long BinarySearch(T item)
Definition: BigArray_T.cs:285
void Swap(long position1, long position2)
Definition: BigArray_T.cs:475
BigArray(long length, T defaultValue)
Definition: BigArray_T.cs:37
long _BinarySearch(T item, long startIndex, long count, Comparison< T > comparer)
Definition: BigArray_T.cs:313