Index.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. //
  2. // System.Data.Common.Key.cs
  3. //
  4. // Author:
  5. // Boris Kirzner <[email protected]>
  6. // Konstantin Triger ([email protected])
  7. //
  8. /*
  9. * Copyright (c) 2002-2004 Mainsoft Corporation.
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a
  12. * copy of this software and associated documentation files (the "Software"),
  13. * to deal in the Software without restriction, including without limitation
  14. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  15. * and/or sell copies of the Software, and to permit persons to whom the
  16. * Software is furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  26. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  27. * DEALINGS IN THE SOFTWARE.
  28. */
  29. using System;
  30. using System.Collections;
  31. using System.Text;
  32. namespace System.Data.Common
  33. {
  34. enum IndexDuplicatesState
  35. {
  36. Unknown,
  37. True,
  38. False
  39. }
  40. /// <summary>
  41. /// Summary description for Index.
  42. /// </summary>
  43. internal class Index
  44. {
  45. #region Fields
  46. int [] _array;
  47. int _size;
  48. Key _key;
  49. int _refCount;
  50. IndexDuplicatesState _hasDuplicates;
  51. #endregion // Fields
  52. #region Constructors
  53. internal Index (Key key)
  54. {
  55. _key = key;
  56. Reset();
  57. }
  58. #endregion // Constructors
  59. #region Properties
  60. internal Key Key {
  61. get { return _key; }
  62. }
  63. internal int Size {
  64. get {
  65. EnsureArray();
  66. return _size;
  67. }
  68. }
  69. internal int RefCount {
  70. get { return _refCount; }
  71. }
  72. internal int IndexToRecord (int index)
  73. {
  74. return index < 0 ? index : Array[index];
  75. }
  76. private int [] Array {
  77. get {
  78. EnsureArray ();
  79. return _array;
  80. }
  81. }
  82. internal bool HasDuplicates {
  83. get {
  84. if (_array == null || _hasDuplicates == IndexDuplicatesState.Unknown) {
  85. EnsureArray ();
  86. if (_hasDuplicates == IndexDuplicatesState.Unknown) {
  87. // check for duplicates
  88. _hasDuplicates = IndexDuplicatesState.False;
  89. for (int i = 0; i < Size - 1; i++) {
  90. if (Key.CompareRecords (Array [i], Array [i + 1]) == 0) {
  91. _hasDuplicates = IndexDuplicatesState.True;
  92. break;
  93. }
  94. }
  95. }
  96. }
  97. return (_hasDuplicates == IndexDuplicatesState.True);
  98. }
  99. }
  100. #endregion // Properties
  101. #region Methods
  102. internal int [] Duplicates {
  103. get {
  104. if (!HasDuplicates)
  105. return null;
  106. ArrayList dups = new ArrayList();
  107. bool inRange = false;
  108. for (int i = 0; i < Size - 1; i++) {
  109. if (Key.CompareRecords (Array [i], Array [i + 1]) == 0) {
  110. if (!inRange) {
  111. dups.Add(Array[i]);
  112. inRange = true;
  113. }
  114. dups.Add (Array [i + 1]);
  115. }
  116. else
  117. inRange = false;
  118. }
  119. return (int []) dups.ToArray (typeof (int));
  120. }
  121. }
  122. private void EnsureArray ()
  123. {
  124. if (_array == null)
  125. RebuildIndex ();
  126. }
  127. internal int [] GetAll ()
  128. {
  129. return Array;
  130. }
  131. internal DataRow [] GetAllRows ()
  132. {
  133. DataRow [] list = new DataRow [Size];
  134. for (int i = 0; i < Size; ++i)
  135. list [i] = Key.Table.RecordCache [Array [i]];
  136. return list;
  137. }
  138. internal DataRow [] GetDistinctRows ()
  139. {
  140. ArrayList list = new ArrayList ();
  141. list.Add (Key.Table.RecordCache [Array [0]]);
  142. int currRecord = Array [0];
  143. for (int i = 1; i < Size; ++i) {
  144. if (Key.CompareRecords (currRecord, Array [i]) == 0)
  145. continue;
  146. list.Add (Key.Table.RecordCache [Array [i]]);
  147. currRecord = Array [i];
  148. }
  149. return (DataRow []) list.ToArray (typeof (DataRow));
  150. }
  151. internal void Reset()
  152. {
  153. _array = null;
  154. RebuildIndex ();
  155. }
  156. private void RebuildIndex()
  157. {
  158. // consider better capacity approximation
  159. _array = new int [Key.Table.RecordCache.CurrentCapacity];
  160. _size = 0;
  161. foreach (DataRow row in Key.Table.Rows) {
  162. int record = Key.GetRecord (row);
  163. if (record != -1)
  164. _array [_size++] = record;
  165. }
  166. _hasDuplicates = IndexDuplicatesState.False;
  167. // Note : MergeSort may update hasDuplicates to True
  168. Sort ();
  169. }
  170. private void Sort ()
  171. {
  172. //QuickSort(Array,0,Size-1);
  173. MergeSort (Array, Size);
  174. }
  175. /*
  176. * Returns record number of the record equal to the key values supplied
  177. * in the meaning of index key, or -1 if no equal record found.
  178. */
  179. internal int Find (object [] keys)
  180. {
  181. int index = FindIndex(keys);
  182. return IndexToRecord (index);
  183. }
  184. /*
  185. * Returns record index (location) of the record equal to the key values supplied
  186. * in the meaning of index key, or -1 if no equal record found.
  187. */
  188. internal int FindIndex (object [] keys)
  189. {
  190. if (keys == null || keys.Length != Key.Columns.Length)
  191. throw new ArgumentException("Expecting " + Key.Columns.Length + " value(s) for the key being indexed, " +
  192. "but received " + ((keys == null) ? 0 : keys.Length) + " value(s).");
  193. int tmp = Key.Table.RecordCache.NewRecord ();
  194. try {
  195. // init key values for temporal record
  196. for (int i = 0; i < Key.Columns.Length; i++)
  197. Key.Columns [i].DataContainer [tmp] = keys [i];
  198. return FindIndex (tmp);
  199. } finally {
  200. Key.Table.RecordCache.DisposeRecord (tmp);
  201. }
  202. }
  203. /*
  204. * Returns record number of the record equal to the record supplied
  205. * in the meaning of index key, or -1 if no equal record found.
  206. */
  207. internal int Find (int record)
  208. {
  209. int index = FindIndex (record);
  210. return IndexToRecord (index);
  211. }
  212. /*
  213. * Returns array of record numbers of the records equal equal to the key values supplied
  214. * in the meaning of index key, or -1 if no equal record found.
  215. */
  216. internal int[] FindAll (object [] keys)
  217. {
  218. int [] indexes = FindAllIndexes (keys);
  219. IndexesToRecords (indexes);
  220. return indexes;
  221. }
  222. /*
  223. * Returns array of indexes of the records inside the index equal equal to the key values supplied
  224. * in the meaning of index key, or -1 if no equal record found.
  225. */
  226. internal int [] FindAllIndexes (object [] keys)
  227. {
  228. if (keys == null || keys.Length != Key.Columns.Length)
  229. throw new ArgumentException("Expecting " + Key.Columns.Length + " value(s) for the key being indexed," +
  230. "but received " + ((keys == null) ? 0 : keys.Length) + " value(s).");
  231. int tmp = Key.Table.RecordCache.NewRecord ();
  232. try {
  233. // init key values for temporal record
  234. for (int i = 0; i < Key.Columns.Length; i++)
  235. Key.Columns [i].DataContainer [tmp] = keys [i];
  236. return FindAllIndexes (tmp);
  237. } catch(FormatException) {
  238. return new int [0];
  239. } catch(InvalidCastException) {
  240. return new int [0];
  241. } finally {
  242. Key.Table.RecordCache.DisposeRecord (tmp);
  243. }
  244. }
  245. /*
  246. * Returns array of record numbers of the records equal to the record supplied
  247. * in the meaning of index key, or empty list if no equal records found.
  248. */
  249. internal int [] FindAll (int record)
  250. {
  251. int [] indexes = FindAllIndexes (record);
  252. IndexesToRecords (indexes);
  253. return indexes;
  254. }
  255. /*
  256. * Returns array of indexes of the records inside the index that equal to the record supplied
  257. * in the meaning of index key, or empty list if no equal records found.
  258. */
  259. internal int [] FindAllIndexes (int record)
  260. {
  261. int index = FindIndex(record);
  262. if (index == -1)
  263. return new int[0];
  264. int startIndex = index++;
  265. int endIndex = index;
  266. for (; startIndex >= 0 && Key.CompareRecords (Array [startIndex], record) == 0; startIndex--) {
  267. }
  268. for (; endIndex < Size && Key.CompareRecords (Array [endIndex], record) == 0; endIndex++) {
  269. }
  270. int length = endIndex - startIndex - 1;
  271. int [] indexes = new int [length];
  272. for (int i = 0; i < length; i++)
  273. indexes [i] = ++startIndex;
  274. return indexes;
  275. }
  276. /*
  277. * Returns index inside the array where record number of the record equal to the record supplied
  278. * in the meaning of index key is sored, or -1 if no equal record found.
  279. */
  280. private int FindIndex (int record)
  281. {
  282. if (Size == 0)
  283. return -1;
  284. return BinarySearch (Array, 0, Size - 1, record);
  285. }
  286. /*
  287. * Finds exact location of the record specified
  288. */
  289. private int FindIndexExact (int record)
  290. {
  291. for (int i = 0, size = Size; i < size; i++)
  292. if (Array [i] == record)
  293. return i;
  294. return -1;
  295. }
  296. /*
  297. * Returns array of records from the indexes (locations) inside the index
  298. */
  299. private void IndexesToRecords (int [] indexes)
  300. {
  301. for (int i = 0; i < indexes.Length; i++)
  302. indexes [i] = Array [indexes [i]];
  303. }
  304. internal void Delete (DataRow row)
  305. {
  306. int oldRecord = Key.GetRecord (row);
  307. Delete (oldRecord);
  308. }
  309. internal void Delete (int oldRecord)
  310. {
  311. if (oldRecord == -1)
  312. return;
  313. int index = FindIndexExact (oldRecord);
  314. if (index != -1) {
  315. if (_hasDuplicates == IndexDuplicatesState.True) {
  316. int c1 = 1;
  317. int c2 = 1;
  318. if (index > 0)
  319. c1 = Key.CompareRecords (Array [index - 1], oldRecord);
  320. if (index < Size - 1)
  321. c2 = Key.CompareRecords (Array [index + 1], oldRecord);
  322. if (c1 == 0 ^ c2 == 0)
  323. _hasDuplicates = IndexDuplicatesState.Unknown;
  324. }
  325. Remove(index);
  326. }
  327. }
  328. private void Remove (int index)
  329. {
  330. if (Size > 1)
  331. System.Array.Copy (Array, index + 1, Array, index,Size - index - 1);
  332. _size--;
  333. }
  334. internal void Update (DataRow row, int oldRecord, DataRowVersion oldVersion, DataRowState oldState)
  335. {
  336. bool contains = Key.ContainsVersion (oldState, oldVersion);
  337. int newRecord = Key.GetRecord (row);
  338. // the record did not appeared in the index before update
  339. if (oldRecord == -1 || Size == 0 || !contains) {
  340. if (newRecord >= 0)
  341. if (FindIndexExact (newRecord) < 0)
  342. Add (row,newRecord);
  343. return;
  344. }
  345. // the record will not appeare in the index after update
  346. if (newRecord < 0 || !Key.CanContain (newRecord)) {
  347. Delete (oldRecord);
  348. return;
  349. }
  350. int oldIdx = FindIndexExact (oldRecord);
  351. if (oldIdx == -1) {
  352. Add (row, newRecord);
  353. return;
  354. }
  355. int newIdx = -1;
  356. int compare = Key.CompareRecords (Array [oldIdx], newRecord);
  357. int start, end;
  358. int c1 = 1;
  359. int c2 = 1;
  360. if (compare == 0) {
  361. if (Array [oldIdx] == newRecord) {
  362. // we deal with the same record that didn't change
  363. // in the context of current index.
  364. // so , do nothing.
  365. return;
  366. }
  367. } else {
  368. if (_hasDuplicates == IndexDuplicatesState.True) {
  369. if (oldIdx > 0)
  370. c1 = Key.CompareRecords (Array [oldIdx - 1], newRecord);
  371. if (oldIdx < Size - 1)
  372. c2 = Key.CompareRecords (Array [oldIdx + 1], newRecord);
  373. if ((c1 == 0 ^ c2 == 0) && compare != 0)
  374. _hasDuplicates = IndexDuplicatesState.Unknown;
  375. }
  376. }
  377. if ((oldIdx == 0 && compare > 0) || (oldIdx == (Size - 1) && compare < 0) || (compare == 0)) {
  378. // no need to switch cells
  379. newIdx = oldIdx;
  380. } else {
  381. if (compare < 0) {
  382. // search after the old place
  383. start = oldIdx + 1;
  384. end = Size - 1;
  385. } else {
  386. // search before the old palce
  387. start = 0;
  388. end = oldIdx - 1;
  389. }
  390. newIdx = LazyBinarySearch (Array, start, end, newRecord);
  391. if (oldIdx < newIdx) {
  392. System.Array.Copy (Array, oldIdx + 1, Array, oldIdx, newIdx - oldIdx);
  393. if (Key.CompareRecords (Array [newIdx], newRecord) > 0)
  394. --newIdx;
  395. } else if (oldIdx > newIdx){
  396. System.Array.Copy (Array, newIdx, Array, newIdx + 1, oldIdx - newIdx);
  397. if (Key.CompareRecords (Array [newIdx], newRecord) < 0)
  398. ++newIdx;
  399. }
  400. }
  401. Array[newIdx] = newRecord;
  402. if (compare != 0) {
  403. if (!(_hasDuplicates == IndexDuplicatesState.True)) {
  404. if (newIdx > 0)
  405. c1 = Key.CompareRecords (Array [newIdx - 1], newRecord);
  406. if (newIdx < Size - 1)
  407. c2 = Key.CompareRecords (Array [newIdx + 1], newRecord);
  408. if (c1 == 0 || c2 == 0)
  409. _hasDuplicates = IndexDuplicatesState.True;
  410. }
  411. }
  412. }
  413. internal void Add (DataRow row)
  414. {
  415. Add(row, Key.GetRecord (row));
  416. }
  417. private void Add (DataRow row,int newRecord)
  418. {
  419. int newIdx;
  420. if (newRecord < 0 || !Key.CanContain (newRecord))
  421. return;
  422. if (Size == 0) {
  423. newIdx = 0;
  424. } else {
  425. newIdx = LazyBinarySearch (Array, 0, Size - 1, newRecord);
  426. // if newl value is greater - insert afer old value
  427. // else - insert before old value
  428. if (Key.CompareRecords (Array [newIdx], newRecord) < 0)
  429. newIdx++;
  430. }
  431. Insert (newIdx, newRecord);
  432. int c1 = 1;
  433. int c2 = 1;
  434. if (!(_hasDuplicates == IndexDuplicatesState.True)) {
  435. if (newIdx > 0)
  436. c1 = Key.CompareRecords (Array [newIdx - 1], newRecord);
  437. if (newIdx < Size - 1)
  438. c2 = Key.CompareRecords (Array [newIdx + 1], newRecord);
  439. if (c1 == 0 || c2 == 0)
  440. _hasDuplicates = IndexDuplicatesState.True;
  441. }
  442. }
  443. private void Insert (int index,int r)
  444. {
  445. if (Array.Length == Size) {
  446. int [] tmp = (Size == 0) ? new int[16] : new int[Size << 1];
  447. System.Array.Copy (Array, 0, tmp, 0, index);
  448. tmp [index] = r;
  449. System.Array.Copy (Array, index, tmp, index + 1, Size - index);
  450. _array = tmp;
  451. } else {
  452. System.Array.Copy (Array, index, Array, index + 1, Size - index);
  453. Array [index] = r;
  454. }
  455. _size++;
  456. }
  457. private void MergeSort (int [] to, int length)
  458. {
  459. int [] from = new int [length];
  460. System.Array.Copy (to, 0, from, 0, from.Length);
  461. MergeSort (from, to, 0, from.Length);
  462. }
  463. private void MergeSort(int[] from, int[] to,int p, int r)
  464. {
  465. int q = (p + r) >> 1;
  466. if (q == p)
  467. return;
  468. MergeSort (to, from, p, q);
  469. MergeSort (to, from, q, r);
  470. // merge
  471. for (int middle = q, current = p;;) {
  472. int res = Key.CompareRecords (from[p], from[q]);
  473. if (res > 0) {
  474. to [current++] = from [q++];
  475. if (q == r) {
  476. while (p < middle)
  477. to[current++] = from[p++];
  478. break;
  479. }
  480. } else {
  481. if (res == 0)
  482. _hasDuplicates = IndexDuplicatesState.True;
  483. to [current++] = from [p++];
  484. if (p == middle) {
  485. while (q < r)
  486. to[current++] = from[q++];
  487. break;
  488. }
  489. }
  490. }
  491. }
  492. private void QuickSort (int [] a,int p,int r)
  493. {
  494. if (p < r) {
  495. int q = Partition (a, p, r);
  496. QuickSort (a, p, q);
  497. QuickSort (a, q + 1, r);
  498. }
  499. }
  500. private int Partition (int [] a,int p,int r)
  501. {
  502. int x = a [p];
  503. int i = p - 1;
  504. int j = r + 1;
  505. while (true) {
  506. // decrement upper limit while values are greater then border value
  507. do {
  508. j--;
  509. } while (Key.CompareRecords (a [j], x) > 0);
  510. do {
  511. i++;
  512. } while (Key.CompareRecords (a [i], x) < 0);
  513. if (i < j) {
  514. int tmp = a [j];
  515. a [j] = a [i];
  516. a [i] = tmp;
  517. } else {
  518. return j;
  519. }
  520. }
  521. }
  522. private int BinarySearch (int [] a, int p, int r,int b)
  523. {
  524. int i = LazyBinarySearch (a, p, r, b);
  525. return (Key.CompareRecords (a [i], b) == 0) ? i : -1;
  526. }
  527. // Lazy binary search only returns the cell number the search finished in,
  528. // but does not checks that the correct value was actually found
  529. private int LazyBinarySearch (int [] a, int p, int r, int b)
  530. {
  531. if (p == r)
  532. return p;
  533. int q = (p + r) >> 1;
  534. int compare = Key.CompareRecords (a [q], b);
  535. if (compare < 0)
  536. return LazyBinarySearch (a, q + 1, r, b);
  537. else if (compare > 0)
  538. return LazyBinarySearch (a, p, q, b);
  539. else
  540. return q;
  541. }
  542. internal void AddRef ()
  543. {
  544. _refCount++;
  545. }
  546. internal void RemoveRef ()
  547. {
  548. _refCount--;
  549. }
  550. /*
  551. // Prints indexes. For debugging.
  552. internal void Print ()
  553. {
  554. for (int i=0; i < Size; i++) {
  555. Console.Write ("Index {0} record {1}: ", i, Array [i]);
  556. for (int j=0; j < Key.Table.Columns.Count; j++) {
  557. DataColumn col = Key.Table.Columns [j];
  558. if (Array [i] >= 0)
  559. Console.Write ("{0,15} ", col [Array [i]]);
  560. }
  561. Console.WriteLine ();
  562. }
  563. }
  564. */
  565. #endregion // Methods
  566. }
  567. }