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