RecordManager.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. //------------------------------------------------------------------------------
  2. // <copyright file="RecordManager.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data {
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Diagnostics;
  12. internal sealed class RecordManager {
  13. private readonly DataTable table;
  14. private int lastFreeRecord;
  15. private int minimumCapacity = 50;
  16. private int recordCapacity = 0;
  17. private readonly List<int> freeRecordList = new List<int>();
  18. DataRow[] rows;
  19. internal RecordManager(DataTable table) {
  20. if (table == null) {
  21. throw ExceptionBuilder.ArgumentNull("table");
  22. }
  23. this.table = table;
  24. }
  25. private void GrowRecordCapacity() {
  26. if (NewCapacity(recordCapacity) < NormalizedMinimumCapacity(minimumCapacity))
  27. RecordCapacity = NormalizedMinimumCapacity(minimumCapacity);
  28. else
  29. RecordCapacity = NewCapacity(recordCapacity);
  30. // set up internal map : record --> row
  31. DataRow[] newRows = table.NewRowArray(recordCapacity);
  32. if (rows != null) {
  33. Array.Copy(rows, 0, newRows, 0, Math.Min(lastFreeRecord, rows.Length));
  34. }
  35. rows = newRows;
  36. }
  37. internal int LastFreeRecord {
  38. get { return lastFreeRecord; }
  39. }
  40. internal int MinimumCapacity {
  41. get {
  42. return minimumCapacity;
  43. }
  44. set {
  45. if (minimumCapacity != value) {
  46. if (value < 0) {
  47. throw ExceptionBuilder.NegativeMinimumCapacity();
  48. }
  49. minimumCapacity = value;
  50. }
  51. }
  52. }
  53. internal int RecordCapacity {
  54. get {
  55. return recordCapacity;
  56. }
  57. set {
  58. if (recordCapacity != value) {
  59. for (int i = 0; i < table.Columns.Count; i++) {
  60. table.Columns[i].SetCapacity(value);
  61. }
  62. recordCapacity = value;
  63. }
  64. }
  65. }
  66. internal static int NewCapacity(int capacity) {
  67. return (capacity < 128) ? 128 : (capacity + capacity);
  68. }
  69. // Normalization: 64, 256, 1024, 2k, 3k, ....
  70. private int NormalizedMinimumCapacity(int capacity) {
  71. if (capacity < 1024 - 10) {
  72. if (capacity < 256 - 10) {
  73. if ( capacity < 54 )
  74. return 64;
  75. return 256;
  76. }
  77. return 1024;
  78. }
  79. return (((capacity + 10) >> 10) + 1) << 10;
  80. }
  81. internal int NewRecordBase() {
  82. int record;
  83. if (freeRecordList.Count != 0) {
  84. record = freeRecordList[freeRecordList.Count - 1];
  85. freeRecordList.RemoveAt(freeRecordList.Count - 1);
  86. }
  87. else {
  88. if (lastFreeRecord >= recordCapacity) {
  89. GrowRecordCapacity();
  90. }
  91. record = lastFreeRecord;
  92. lastFreeRecord++;
  93. }
  94. Debug.Assert(record >=0 && record < recordCapacity, "NewRecord: Invalid record");
  95. return record;
  96. }
  97. internal void FreeRecord(ref int record) {
  98. Debug.Assert(-1 <= record && record < recordCapacity, "invalid record");
  99. // Debug.Assert(record < lastFreeRecord, "Attempt to Free() <outofbounds> record");
  100. if (-1 != record) {
  101. this[record] = null;
  102. int count = table.columnCollection.Count;
  103. for(int i = 0; i < count; ++i) {
  104. table.columnCollection[i].FreeRecord(record);
  105. }
  106. // if freeing the last record, recycle it
  107. if (lastFreeRecord == record + 1) {
  108. lastFreeRecord--;
  109. }
  110. else if (record < lastFreeRecord) {
  111. // Debug.Assert(-1 == freeRecordList.IndexOf(record), "Attempt to double Free() record");
  112. freeRecordList.Add(record);
  113. }
  114. record = -1;
  115. }
  116. }
  117. internal void Clear(bool clearAll) {
  118. if (clearAll) {
  119. for(int record = 0; record < recordCapacity; ++record) {
  120. rows[record] = null;
  121. }
  122. int count = table.columnCollection.Count;
  123. for(int i = 0; i < count; ++i) {
  124. // SQLBU 415729: Serious performance issue when calling Clear()
  125. // this improves performance by caching the column instead of obtaining it for each row
  126. DataColumn column = table.columnCollection[i];
  127. for(int record = 0; record < recordCapacity; ++record) {
  128. column.FreeRecord(record);
  129. }
  130. }
  131. lastFreeRecord = 0;
  132. freeRecordList.Clear();
  133. }
  134. else { // just clear attached rows
  135. freeRecordList.Capacity = freeRecordList.Count + table.Rows.Count;
  136. for(int record = 0; record < recordCapacity; ++record) {
  137. if (rows[record]!= null && rows[record].rowID != -1) {
  138. int tempRecord = record;
  139. FreeRecord(ref tempRecord);
  140. }
  141. }
  142. }
  143. }
  144. internal DataRow this[int record] {
  145. get {
  146. Debug.Assert(record >= 0 && record < rows.Length, "Invalid record number");
  147. return rows[record];
  148. }
  149. set {
  150. Debug.Assert(record >= 0 && record < rows.Length, "Invalid record number");
  151. rows[record] = value;
  152. }
  153. }
  154. internal void SetKeyValues(int record, DataKey key, object[] keyValues) {
  155. for (int i = 0; i < keyValues.Length; i++) {
  156. key.ColumnsReference[i][record] = keyValues[i];
  157. }
  158. }
  159. // Increases AutoIncrementCurrent
  160. internal int ImportRecord(DataTable src, int record) {
  161. return CopyRecord(src, record, -1);
  162. }
  163. // No impact on AutoIncrementCurrent if over written
  164. internal int CopyRecord(DataTable src, int record, int copy) {
  165. Debug.Assert(src != null, "Can not Merge record without a table");
  166. if (record == -1) {
  167. return copy;
  168. }
  169. int newRecord = -1;
  170. try {
  171. if (copy == -1) {
  172. newRecord = table.NewUninitializedRecord();
  173. }
  174. else {
  175. newRecord = copy;
  176. }
  177. int count = table.Columns.Count;
  178. for (int i = 0; i < count; ++i) {
  179. DataColumn dstColumn = table.Columns[i];
  180. DataColumn srcColumn = src.Columns[dstColumn.ColumnName];
  181. if (null != srcColumn) {
  182. object value = srcColumn[record];
  183. ICloneable cloneableObject = value as ICloneable;
  184. if (null != cloneableObject) {
  185. dstColumn[newRecord] = cloneableObject.Clone();
  186. }
  187. else {
  188. dstColumn[newRecord] = value;
  189. }
  190. }
  191. else if (-1 == copy) {
  192. dstColumn.Init(newRecord);
  193. }
  194. }
  195. }
  196. catch (Exception e){
  197. //
  198. if (Common.ADP.IsCatchableOrSecurityExceptionType(e)) {
  199. if (-1 == copy) {
  200. FreeRecord(ref newRecord);
  201. }
  202. }
  203. throw;
  204. }
  205. return newRecord;
  206. }
  207. internal void SetRowCache(DataRow[] newRows) {
  208. rows = newRows;
  209. lastFreeRecord = rows.Length;
  210. recordCapacity = lastFreeRecord;
  211. }
  212. [Conditional("DEBUG")]
  213. internal void VerifyRecord(int record) {
  214. Debug.Assert((record < lastFreeRecord) && (-1 == freeRecordList.IndexOf(record)), "accesing free record");
  215. Debug.Assert((null == rows[record]) ||
  216. (record == rows[record].oldRecord) ||
  217. (record == rows[record].newRecord) ||
  218. (record == rows[record].tempRecord), "record of a different row");
  219. }
  220. [Conditional("DEBUG")]
  221. internal void VerifyRecord(int record, DataRow row) {
  222. Debug.Assert((record < lastFreeRecord) && (-1 == freeRecordList.IndexOf(record)), "accesing free record");
  223. Debug.Assert((null == rows[record]) || (row == rows[record]), "record of a different row");
  224. }
  225. }
  226. }