1 /* Copyright (C) 2004 - 2009  Versant Inc.  http://www.db4o.com */
2 
3 using System;
4 using Db4objects.Db4o;
5 using Db4objects.Db4o.Ext;
6 using Db4objects.Db4o.Foundation;
7 using Db4objects.Db4o.Internal;
8 using Db4objects.Db4o.Internal.Freespace;
9 using Db4objects.Db4o.Internal.Ids;
10 using Db4objects.Db4o.Internal.Slots;
11 using Sharpen.Lang;
12 
13 namespace Db4objects.Db4o.Internal.Ids
14 {
15 	/// <exclude></exclude>
16 	public class InMemoryIdSystem : IStackableIdSystem
17 	{
18 		private readonly LocalObjectContainer _container;
19 
20 		private IdSlotTree _ids;
21 
22 		private Slot _slot;
23 
24 		private readonly SequentialIdGenerator _idGenerator;
25 
26 		private int _childId;
27 
28 		/// <summary>for testing purposes only.</summary>
29 		/// <remarks>for testing purposes only.</remarks>
InMemoryIdSystem(LocalObjectContainer container, int maxValidId)30 		public InMemoryIdSystem(LocalObjectContainer container, int maxValidId)
31 		{
32 			_container = container;
33 			_idGenerator = new SequentialIdGenerator(new _IFunction4_32(this, maxValidId), _container
34 				.Handlers.LowestValidId(), maxValidId);
35 		}
36 
37 		private sealed class _IFunction4_32 : IFunction4
38 		{
_IFunction4_32(InMemoryIdSystem _enclosing, int maxValidId)39 			public _IFunction4_32(InMemoryIdSystem _enclosing, int maxValidId)
40 			{
41 				this._enclosing = _enclosing;
42 				this.maxValidId = maxValidId;
43 			}
44 
Apply(object start)45 			public object Apply(object start)
46 			{
47 				return this._enclosing.FindFreeId((((int)start)), maxValidId);
48 			}
49 
50 			private readonly InMemoryIdSystem _enclosing;
51 
52 			private readonly int maxValidId;
53 		}
54 
InMemoryIdSystem(LocalObjectContainer container)55 		public InMemoryIdSystem(LocalObjectContainer container) : this(container, int.MaxValue
56 			)
57 		{
58 			ReadThis();
59 		}
60 
ReadThis()61 		private void ReadThis()
62 		{
63 			SystemData systemData = _container.SystemData();
64 			_slot = systemData.IdSystemSlot();
65 			if (!Slot.IsNull(_slot))
66 			{
67 				ByteArrayBuffer buffer = _container.ReadBufferBySlot(_slot);
68 				_childId = buffer.ReadInt();
69 				_idGenerator.Read(buffer);
70 				_ids = (IdSlotTree)new TreeReader(buffer, new IdSlotTree(0, null)).Read();
71 			}
72 		}
73 
Close()74 		public virtual void Close()
75 		{
76 		}
77 
78 		// do nothing
Commit(IVisitable slotChanges, FreespaceCommitter freespaceCommitter )79 		public virtual void Commit(IVisitable slotChanges, FreespaceCommitter freespaceCommitter
80 			)
81 		{
82 			Slot oldSlot = _slot;
83 			Slot reservedSlot = AllocateSlot(false, EstimatedSlotLength(EstimateMappingCount(
84 				slotChanges)));
85 			// No more operations against the FreespaceManager.
86 			// Time to free old slots.
87 			freespaceCommitter.Commit();
88 			slotChanges.Accept(new _IVisitor4_69(this));
89 			WriteThis(reservedSlot);
90 			FreeSlot(oldSlot);
91 		}
92 
93 		private sealed class _IVisitor4_69 : IVisitor4
94 		{
_IVisitor4_69(InMemoryIdSystem _enclosing)95 			public _IVisitor4_69(InMemoryIdSystem _enclosing)
96 			{
97 				this._enclosing = _enclosing;
98 			}
99 
Visit(object slotChange)100 			public void Visit(object slotChange)
101 			{
102 				if (!((SlotChange)slotChange).SlotModified())
103 				{
104 					return;
105 				}
106 				if (((SlotChange)slotChange).RemoveId())
107 				{
108 					this._enclosing._ids = (IdSlotTree)Tree.RemoveLike(this._enclosing._ids, new TreeInt
109 						(((TreeInt)slotChange)._key));
110 					return;
111 				}
112 				if (DTrace.enabled)
113 				{
114 					DTrace.SlotCommitted.LogLength(((TreeInt)slotChange)._key, ((SlotChange)slotChange
115 						).NewSlot());
116 				}
117 				this._enclosing._ids = ((IdSlotTree)Tree.Add(this._enclosing._ids, new IdSlotTree
118 					(((TreeInt)slotChange)._key, ((SlotChange)slotChange).NewSlot())));
119 			}
120 
121 			private readonly InMemoryIdSystem _enclosing;
122 		}
123 
AllocateSlot(bool appendToFile, int slotLength)124 		private Slot AllocateSlot(bool appendToFile, int slotLength)
125 		{
126 			if (!appendToFile)
127 			{
128 				Slot slot = _container.FreespaceManager().AllocateSafeSlot(slotLength);
129 				if (slot != null)
130 				{
131 					return slot;
132 				}
133 			}
134 			return _container.AppendBytes(slotLength);
135 		}
136 
EstimateMappingCount(IVisitable slotChanges)137 		private int EstimateMappingCount(IVisitable slotChanges)
138 		{
139 			IntByRef count = new IntByRef();
140 			count.value = _ids == null ? 0 : _ids.Size();
141 			slotChanges.Accept(new _IVisitor4_103(count));
142 			return count.value;
143 		}
144 
145 		private sealed class _IVisitor4_103 : IVisitor4
146 		{
_IVisitor4_103(IntByRef count)147 			public _IVisitor4_103(IntByRef count)
148 			{
149 				this.count = count;
150 			}
151 
Visit(object slotChange)152 			public void Visit(object slotChange)
153 			{
154 				if (!((SlotChange)slotChange).SlotModified() || ((SlotChange)slotChange).RemoveId
155 					())
156 				{
157 					return;
158 				}
159 				count.value++;
160 			}
161 
162 			private readonly IntByRef count;
163 		}
164 
WriteThis(Slot reservedSlot)165 		private void WriteThis(Slot reservedSlot)
166 		{
167 			// We need a little dance here to keep filling free slots
168 			// with X bytes. The FreespaceManager would do it immediately
169 			// upon the free call, but then our CrashSimulatingTestCase
170 			// fails because we have the Xses in the file before flushing.
171 			Slot xByteSlot = null;
172 			int slotLength = SlotLength();
173 			if (reservedSlot.Length() >= slotLength)
174 			{
175 				_slot = reservedSlot;
176 				reservedSlot = null;
177 			}
178 			else
179 			{
180 				_slot = AllocateSlot(true, slotLength);
181 			}
182 			ByteArrayBuffer buffer = new ByteArrayBuffer(_slot.Length());
183 			buffer.WriteInt(_childId);
184 			_idGenerator.Write(buffer);
185 			TreeInt.Write(buffer, _ids);
186 			_container.WriteBytes(buffer, _slot.Address(), 0);
187 			_container.SystemData().IdSystemSlot(_slot);
188 			IRunnable commitHook = _container.CommitHook();
189 			_container.SyncFiles(commitHook);
190 			FreeSlot(reservedSlot);
191 		}
192 
FreeSlot(Slot slot)193 		private void FreeSlot(Slot slot)
194 		{
195 			if (Slot.IsNull(slot))
196 			{
197 				return;
198 			}
199 			IFreespaceManager freespaceManager = _container.FreespaceManager();
200 			if (freespaceManager == null)
201 			{
202 				return;
203 			}
204 			freespaceManager.FreeSafeSlot(slot);
205 		}
206 
SlotLength()207 		private int SlotLength()
208 		{
209 			return TreeInt.MarshalledLength(_ids) + _idGenerator.MarshalledLength() + Const4.
210 				IdLength;
211 		}
212 
EstimatedSlotLength(int estimatedCount)213 		private int EstimatedSlotLength(int estimatedCount)
214 		{
215 			IdSlotTree template = _ids;
216 			if (template == null)
217 			{
218 				template = new IdSlotTree(0, new Slot(0, 0));
219 			}
220 			return template.MarshalledLength(estimatedCount) + _idGenerator.MarshalledLength(
221 				) + Const4.IdLength;
222 		}
223 
CommittedSlot(int id)224 		public virtual Slot CommittedSlot(int id)
225 		{
226 			IdSlotTree idSlotMapping = (IdSlotTree)Tree.Find(_ids, new TreeInt(id));
227 			if (idSlotMapping == null)
228 			{
229 				throw new InvalidIDException(id);
230 			}
231 			return idSlotMapping.Slot();
232 		}
233 
CompleteInterruptedTransaction(int address, int length)234 		public virtual void CompleteInterruptedTransaction(int address, int length)
235 		{
236 		}
237 
238 		// do nothing
NewId()239 		public virtual int NewId()
240 		{
241 			int id = _idGenerator.NewId();
242 			_ids = ((IdSlotTree)Tree.Add(_ids, new IdSlotTree(id, Slot.Zero)));
243 			return id;
244 		}
245 
FindFreeId(int start, int end)246 		private int FindFreeId(int start, int end)
247 		{
248 			if (_ids == null)
249 			{
250 				return start;
251 			}
252 			IntByRef lastId = new IntByRef();
253 			IntByRef freeId = new IntByRef();
254 			Tree.Traverse(_ids, new TreeInt(start), new _ICancellableVisitor4_204(lastId, start
255 				, freeId));
256 			if (freeId.value > 0)
257 			{
258 				return freeId.value;
259 			}
260 			if (lastId.value < end)
261 			{
262 				return Math.Max(start, lastId.value + 1);
263 			}
264 			return 0;
265 		}
266 
267 		private sealed class _ICancellableVisitor4_204 : ICancellableVisitor4
268 		{
_ICancellableVisitor4_204(IntByRef lastId, int start, IntByRef freeId)269 			public _ICancellableVisitor4_204(IntByRef lastId, int start, IntByRef freeId)
270 			{
271 				this.lastId = lastId;
272 				this.start = start;
273 				this.freeId = freeId;
274 			}
275 
Visit(object node)276 			public bool Visit(object node)
277 			{
278 				int id = ((TreeInt)node)._key;
279 				if (lastId.value == 0)
280 				{
281 					if (id > start)
282 					{
283 						freeId.value = start;
284 						return false;
285 					}
286 					lastId.value = id;
287 					return true;
288 				}
289 				if (id > lastId.value + 1)
290 				{
291 					freeId.value = lastId.value + 1;
292 					return false;
293 				}
294 				lastId.value = id;
295 				return true;
296 			}
297 
298 			private readonly IntByRef lastId;
299 
300 			private readonly int start;
301 
302 			private readonly IntByRef freeId;
303 		}
304 
ReturnUnusedIds(IVisitable visitable)305 		public virtual void ReturnUnusedIds(IVisitable visitable)
306 		{
307 			visitable.Accept(new _IVisitor4_233(this));
308 		}
309 
310 		private sealed class _IVisitor4_233 : IVisitor4
311 		{
_IVisitor4_233(InMemoryIdSystem _enclosing)312 			public _IVisitor4_233(InMemoryIdSystem _enclosing)
313 			{
314 				this._enclosing = _enclosing;
315 			}
316 
Visit(object obj)317 			public void Visit(object obj)
318 			{
319 				this._enclosing._ids = (IdSlotTree)Tree.RemoveLike(this._enclosing._ids, new TreeInt
320 					((((int)obj))));
321 			}
322 
323 			private readonly InMemoryIdSystem _enclosing;
324 		}
325 
ChildId()326 		public virtual int ChildId()
327 		{
328 			return _childId;
329 		}
330 
ChildId(int id)331 		public virtual void ChildId(int id)
332 		{
333 			_childId = id;
334 		}
335 	}
336 }
337