001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.internal.ix;
022:
023: import com.db4o.*;
024: import com.db4o.foundation.*;
025: import com.db4o.internal.*;
026:
027: /**
028: *
029: * @exclude
030: */
031: public class Index4 {
032:
033: public final Indexable4 _handler;
034:
035: static private int _version;
036:
037: public final MetaIndex _metaIndex;
038:
039: private IndexTransaction _globalIndexTransaction;
040:
041: private Collection4 _indexTransactions;
042:
043: private IxFileRangeReader _fileRangeReader;
044:
045: final boolean _nullHandling;
046:
047: public Index4(LocalTransaction systemTrans, Indexable4 handler,
048: MetaIndex metaIndex, boolean nullHandling) {
049: _metaIndex = metaIndex;
050: _handler = handler;
051: _globalIndexTransaction = new IndexTransaction(systemTrans,
052: this );
053: _nullHandling = nullHandling;
054: createGlobalFileRange();
055: }
056:
057: public IndexTransaction dirtyIndexTransaction(
058: LocalTransaction a_trans) {
059: IndexTransaction ift = new IndexTransaction(a_trans, this );
060: if (_indexTransactions == null) {
061: _indexTransactions = new Collection4();
062: } else {
063: IndexTransaction iftExisting = (IndexTransaction) _indexTransactions
064: .get(ift);
065: if (iftExisting != null) {
066: return iftExisting;
067: }
068: }
069: a_trans.addDirtyFieldIndex(ift);
070: ift.setRoot(Tree.deepClone(_globalIndexTransaction.getRoot(),
071: ift));
072: ift.i_version = ++_version;
073: _indexTransactions.add(ift);
074: return ift;
075: }
076:
077: public IndexTransaction globalIndexTransaction() {
078: return _globalIndexTransaction;
079: }
080:
081: public IndexTransaction indexTransactionFor(LocalTransaction a_trans) {
082: if (_indexTransactions != null) {
083: IndexTransaction ift = new IndexTransaction(a_trans, this );
084: ift = (IndexTransaction) _indexTransactions.get(ift);
085: if (ift != null) {
086: return ift;
087: }
088: }
089: return _globalIndexTransaction;
090: }
091:
092: private int[] freeForMetaIndex() {
093: return new int[] { _metaIndex.indexAddress,
094: _metaIndex.indexLength,
095: // _metaIndex.patchAddress,
096: // _metaIndex.patchLength
097: };
098: }
099:
100: private void doFree(int[] addressLength) {
101: LocalObjectContainer yf = file();
102: for (int i = 0; i < addressLength.length; i += 2) {
103: yf.free(addressLength[i], addressLength[i + 1]);
104: }
105: }
106:
107: /**
108: * solving a hen-egg problem: commit itself works with freespace
109: * so we have to do this all sequentially in the right way, working
110: * with with both indexes at the same time.
111: */
112: public void commitFreeSpace(Index4 other) {
113:
114: int entries = countEntries();
115:
116: // For the two freespace indexes themselves, we call
117: // the freespace system and we store two meta indexes. Potential effects:
118: // 2 x getSlot -2 to 0
119: // 4 x free -4 to + 4
120: //
121:
122: // Therefore we have to raise the value by 4, to make
123: // sure that there are enough.
124:
125: int length = (entries + 4) * lengthPerEntry();
126:
127: int mySlot = getSlot(length);
128: int otherSlot = getSlot(length);
129: doFree(freeForMetaIndex());
130: doFree(other.freeForMetaIndex());
131:
132: entries = writeToNewSlot(mySlot);
133: metaIndexSetMembers(entries, length, mySlot);
134: createGlobalFileRange();
135:
136: int otherEntries = other.writeToNewSlot(otherSlot);
137:
138: if (Deploy.debug) {
139: if (entries != otherEntries) {
140: throw new RuntimeException("Should never happen");
141: }
142: }
143:
144: other.metaIndexSetMembers(entries, length, otherSlot);
145: other.createGlobalFileRange();
146: }
147:
148: private int lengthPerEntry() {
149: return _handler.linkLength() + Const4.INT_LENGTH;
150: }
151:
152: private void metaIndexStore(int entries, int length, int address) {
153: Transaction transact = trans();
154: metaIndexSetMembers(entries, length, address);
155: transact.container()
156: .setInternal(transact, _metaIndex, 1, false);
157: }
158:
159: private void metaIndexSetMembers(int entries, int length,
160: int address) {
161: _metaIndex.indexEntries = entries;
162: _metaIndex.indexLength = length;
163: _metaIndex.indexAddress = address;
164: }
165:
166: private int writeToNewSlot(int slot) {
167: Tree root = getRoot();
168: final StatefulBuffer writer = new StatefulBuffer(trans(), slot,
169: lengthPerEntry());
170: final int[] entries = new int[] { 0 };
171: if (root != null) {
172: root.traverse(new Visitor4() {
173: public void visit(Object a_object) {
174: entries[0] += ((IxTree) a_object).write(_handler,
175: writer);
176: }
177: });
178: }
179: return entries[0];
180: }
181:
182: void commit(IndexTransaction ixTrans) {
183:
184: _indexTransactions.remove(ixTrans);
185: _globalIndexTransaction.merge(ixTrans);
186:
187: // TODO: Use more intelligent heuristic here to
188: // calculate when to flush the global index
189:
190: // int leaves = _globalIndexTransaction.countLeaves();
191: // boolean createNewFileRange = leaves > MAX_LEAVES;
192:
193: boolean createNewFileRange = true;
194:
195: if (createNewFileRange) {
196:
197: int entries = countEntries();
198: int length = countEntries() * lengthPerEntry();
199: int slot = getSlot(length);
200:
201: int[] free = freeForMetaIndex();
202:
203: metaIndexStore(entries, length, slot);
204:
205: writeToNewSlot(slot);
206:
207: IxFileRange newFileRange = createGlobalFileRange();
208:
209: if (_indexTransactions != null) {
210: Iterator4 i = _indexTransactions.iterator();
211: while (i.moveNext()) {
212: final IndexTransaction ft = (IndexTransaction) i
213: .current();
214: Tree clonedTree = newFileRange;
215: if (clonedTree != null) {
216: clonedTree = (Tree) clonedTree.deepClone(ft);
217: }
218: final Tree.ByRef tree = new Tree.ByRef(clonedTree);
219: ft.getRoot().traverseFromLeaves((new Visitor4() {
220:
221: public void visit(Object a_object) {
222: IxTree ixTree = (IxTree) a_object;
223: if (ixTree._version == ft.i_version) {
224: if (!(ixTree instanceof IxFileRange)) {
225: ixTree.beginMerge();
226: tree.value = Tree.add(tree.value,
227: ixTree);
228: }
229: }
230: }
231: }));
232: ft.setRoot(tree.value);
233: }
234: }
235:
236: doFree(free);
237:
238: } else {
239: Iterator4 i = _indexTransactions.iterator();
240: while (i.moveNext()) {
241: ((IndexTransaction) i.current()).merge(ixTrans);
242: }
243: }
244: }
245:
246: private IxFileRange createGlobalFileRange() {
247: IxFileRange fr = null;
248: if (_metaIndex.indexEntries > 0) {
249: fr = new IxFileRange(_globalIndexTransaction,
250: _metaIndex.indexAddress, 0, _metaIndex.indexEntries);
251: }
252: _globalIndexTransaction.setRoot(fr);
253: return fr;
254: }
255:
256: void rollback(IndexTransaction a_ft) {
257: _indexTransactions.remove(a_ft);
258: }
259:
260: IxFileRangeReader fileRangeReader() {
261: if (_fileRangeReader == null) {
262: _fileRangeReader = new IxFileRangeReader(_handler);
263: }
264: return _fileRangeReader;
265: }
266:
267: public String toString() {
268: if (!Debug4.prettyToStrings) {
269: return super .toString();
270: }
271: StringBuffer sb = new StringBuffer();
272: sb.append("IxField " + System.identityHashCode(this ));
273: if (_globalIndexTransaction != null) {
274: sb.append("\n Global \n ");
275: sb.append(_globalIndexTransaction.toString());
276: } else {
277: sb.append("\n no global index \n ");
278: }
279: if (_indexTransactions != null) {
280: Iterator4 i = _indexTransactions.iterator();
281: while (i.moveNext()) {
282: sb.append("\n");
283: sb.append(i.current().toString());
284: }
285: }
286: return sb.toString();
287: }
288:
289: private LocalTransaction trans() {
290: return _globalIndexTransaction.i_trans;
291: }
292:
293: private LocalObjectContainer file() {
294: return trans().file();
295: }
296:
297: private int getSlot(int length) {
298: return file().getSlot(length).address();
299: }
300:
301: private Tree getRoot() {
302: return _globalIndexTransaction.getRoot();
303: }
304:
305: private int countEntries() {
306: Tree root = getRoot();
307: return root == null ? 0 : root.size();
308: }
309:
310: }
|