001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.AllocExtent
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.raw.data;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025: import org.apache.derby.iapi.error.StandardException;
026: import org.apache.derby.iapi.store.raw.ContainerHandle;
027: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
028:
029: import org.apache.derby.iapi.services.io.FormatableBitSet;
030:
031: import java.io.Externalizable;
032: import java.io.IOException;
033:
034: import java.io.ObjectOutput;
035: import java.io.ObjectInput;
036:
037: /**
038:
039: An allocation extent row manages the page status of page in the extent.
040: AllocExtent is externalizable and is written to the AllocPage directly,
041: without being converted to a row first.
042: <P>
043: <PRE>
044: @format_id none, format implied by AllocPage's format
045: @purpose manage page status of page in extent
046: @upgrade
047: @disk_layout
048: extentOffset(long) the begin physical byte offset of the first page of this extent
049: extentStart(long) the first logical page mananged by this extent.
050: extentEnd(long) the last page this extent can ever hope to manage
051: extentLength(int) the number of pages allocated in this extent
052: extentStatus(int) status bits for the whole extent.
053: HAS_DEALLOCATED - most likely, this extent has a deallocated
054: page somewhere
055: If !HAD_DEALLOCATED, the extent has no deallocated page
056: HAS_FREE - most likely, this extent has a free page somewhere
057: If !HAS_FREE, there is no free page in the extent
058: ALL_FREE - most likely, this extent only has free pages, good
059: candidate for shrinking the file.
060: If !ALL_FREE, the extent is not all free
061: HAS_UNFILLED_PAGES - most likely, this extent has unfilled pages.
062: if !HAS_UNFILLED_PAGES, all pages are filled
063: KEEP_UNFILLED_PAGES - this extent keeps track of unfilled pages
064: (post v1.3). If not set, this extent has no notion of
065: unfilled page and has no unFilledPage bitmap.
066: NO_DEALLOC_PAGE_MAP - this extents do not have a dealloc and a
067: free page bit maps. Prior to 2.0, there are 2 bit
068: maps, a deallocate page bit map and a free page bit
069: map. Cloudscape 2.0 and later merged the dealloc page
070: bit map into the free page bit map.
071: RETIRED - this extent contains only 'retired' pages, never use
072: any page from this extent. The pages don't actually
073: exist, i.e., it maps to nothing (physicalOffset is
074: garbage). The purpose of this extent is to blot out a
075: range of logical page numbers that no longer exists
076: for this container. Use this to reuse a physical page
077: when a logical page has exhausted all recordId or for
078: logical pages that has been shrunk out.
079: preAllocLength(int) the number of pages that have been preallocated
080: reserved1(int)
081: reserved2(long) reserved for future use
082: reserved3(long) reserved for future use
083: FreePages(bit) bitmap of free pages
084: Bit[i] is ON iff page i is free for immediate (re)use.
085: [
086: on disk version before 2.0
087: deAllocPages(bit) bitmap of deallocated pages
088: Bit[i] is ON iff page i has been deallocated.
089: ]
090: unFilledPages(bit) bitmap of pages that has free space
091: Bit[i] is ON if page i is likely to be < 1/2 full
092:
093: org.apache.derby.iapi.services.io.FormatableBitSet is used to store the bit map.
094: FormatableBitSet is an externalizable class.
095:
096: @end_format
097:
098: <PRE>
099: A page can have the following logical state:
100: <BR>Free - a page that is free to be used
101: <BR>Valid - a page that is currently in use
102: <P>
103: There is another type of transitional pages which pages that have been
104: allocated on disk but has not yet been used. These pages are Free.
105: <P>
106: Bit[K] freePages
107: Bit[i] is ON iff page i maybe free for reuse. User must get the
108: dealloc page lock on the free page to make sure the transaction.
109: <P>
110: K is the size of the bit array, it must be >= length.
111:
112: @see AllocPage
113: */
114:
115: public class AllocExtent implements Externalizable {
116: private long extentOffset; // begin physical offset
117: private long extentStart; // first logical page number
118: private long extentEnd; // last logical page number
119: // page[extentStart] to page[extentEnd] are the pages in the range of this
120: // alloc extent.
121: // page[exentStart] to page[extentStart+extentLength-1] are actually
122: // allocated in this extent
123: // when the extent is completely allocated,
124: // extentEnd == extentStart+extentLength -1
125:
126: private int extentLength; // number of pages allocated in the extent
127:
128: int extentStatus;
129:
130: private int preAllocLength;
131:
132: private int reserved1;
133: private long reserved2;
134: private long reserved3;
135:
136: // extent Status bits
137: private static final int HAS_DEALLOCATED = 0x1;
138: private static final int HAS_FREE = 0x2;
139: private static final int ALL_FREE = 0x4;
140: private static final int HAS_UNFILLED_PAGES = 0x10;
141: private static final int KEEP_UNFILLED_PAGES = 0x10000000;
142: private static final int NO_DEALLOC_PAGE_MAP = 0x20000000;
143: private static final int RETIRED = 0x8;
144:
145: /**
146: public Per Page status
147: */
148: protected static final int ALLOCATED_PAGE = 0;
149: protected static final int DEALLOCATED_PAGE = 1;
150: protected static final int FREE_PAGE = 2;
151:
152: // a page which is not a freePage is a regular old
153: // allocated page. Only an allocated page can be unFilled.
154: FormatableBitSet freePages;
155: FormatableBitSet unFilledPages;
156:
157: /**
158: Statically calculates how many pages this extent can manage given the
159: availspace number of bytes to store this extent in
160:
161: if read/writeExternal changes, this must change too
162: */
163: protected static int MAX_RANGE(int availspace) {
164: /* extent Offset, Start, End, Length, Status, preAllocLength, reserved1,2,3 */
165: int bookkeeping = 8 /* offset */+ 8 /* start */+ 8 /* end */
166: + 4 /* length */+ 4 /* status */+ 4 /* preAllocLength */
167: + 4 /* reserved1 */+ 8 /* reserved2 */+ 8 /* reserved3 */;
168: availspace -= bookkeeping;
169:
170: // each bit array is allowed to the 1/3 the remaining space
171: availspace /= 3;
172:
173: if (availspace <= 0)
174: return 0;
175:
176: // ask bit array how many bits it can store in this amount of space
177: return FormatableBitSet.maxBitsForSpace(availspace);
178: }
179:
180: /*
181: * methods
182: */
183:
184: /*
185: * ctors
186: */
187: protected AllocExtent(long offset, // physical offset
188: long start, // starting logical page number
189: int length, // how many pages are in this extent
190: int pagesize, // size of all the pages in the extent
191: int maxlength) // initial size of the bit map arrays
192: {
193: if (SanityManager.DEBUG) {
194: if (length > maxlength)
195: SanityManager.THROWASSERT("length " + length
196: + " > maxlength " + maxlength);
197: }
198:
199: this .extentOffset = offset;
200: this .extentStart = start;
201: this .extentEnd = start + maxlength - 1;
202:
203: this .extentLength = length;
204: preAllocLength = extentLength;
205:
206: if (length > 0)
207: extentStatus = HAS_FREE | ALL_FREE;
208: else
209: extentStatus = 0;
210:
211: extentStatus |= KEEP_UNFILLED_PAGES; // v1.3 or beyond
212: extentStatus |= NO_DEALLOC_PAGE_MAP; // v2.0 or beyond
213:
214: int numbits = (1 + (length / 8)) * 8;
215: if (numbits > maxlength)
216: numbits = maxlength;
217:
218: freePages = new FormatableBitSet(numbits);
219: unFilledPages = new FormatableBitSet(numbits);
220:
221: // by definition, all pages are free to begin with, no pages are
222: // deallocated and no page is unfilled
223: for (int i = 0; i < length; i++)
224: freePages.set(i);
225: }
226:
227: /*
228: Copy constructor
229: */
230: protected AllocExtent(AllocExtent original) {
231: extentOffset = original.extentOffset;
232: extentStart = original.extentStart;
233: extentEnd = original.extentEnd;
234: extentLength = original.extentLength;
235: extentStatus = original.extentStatus;
236: preAllocLength = original.preAllocLength;
237:
238: freePages = new FormatableBitSet(original.freePages);
239: unFilledPages = new FormatableBitSet(original.unFilledPages);
240: }
241:
242: /*
243: * Methods of Externalizable
244: */
245: public AllocExtent() {
246: }
247:
248: public void writeExternal(ObjectOutput out) throws IOException {
249:
250: // any change to this routine must change maxRange
251: out.writeLong(extentOffset);
252: out.writeLong(extentStart);
253: out.writeLong(extentEnd);
254: out.writeInt(extentLength);
255: out.writeInt(extentStatus);
256: out.writeInt(preAllocLength);
257: out.writeInt(0); // reserved1
258: out.writeLong(0); // reserved2
259: out.writeLong(0); // reserved3
260:
261: freePages.writeExternal(out);
262: unFilledPages.writeExternal(out);
263: }
264:
265: public void readExternal(ObjectInput in) throws IOException,
266: ClassNotFoundException {
267: // any change to this routine must change maxRange
268: extentOffset = in.readLong();
269: extentStart = in.readLong();
270: extentEnd = in.readLong();
271: extentLength = in.readInt();
272: extentStatus = in.readInt();
273: preAllocLength = in.readInt();
274: reserved1 = in.readInt();
275: reserved2 = in.readLong();
276: reserved3 = in.readLong();
277:
278: freePages = new FormatableBitSet(); // don't know how to point to it
279: freePages.readExternal(in);
280:
281: // this extent is created before 2.0
282: if ((extentStatus & NO_DEALLOC_PAGE_MAP) == 0) {
283: FormatableBitSet deAllocPages = new FormatableBitSet();
284: deAllocPages.readExternal(in);
285: // fold this into free page bit map
286: freePages.or(deAllocPages);
287: extentStatus |= NO_DEALLOC_PAGE_MAP; // dealloc page map has been merged
288: }
289:
290: if ((extentStatus & KEEP_UNFILLED_PAGES) == KEEP_UNFILLED_PAGES) {
291: unFilledPages = new FormatableBitSet();
292: unFilledPages.readExternal(in);
293: } else // before we keep track of unfilled pages pre 1.3
294: {
295: // make sure there are enough space
296: unFilledPages = new FormatableBitSet(freePages.getLength());
297: extentStatus |= KEEP_UNFILLED_PAGES; // now we keep track of them
298: }
299:
300: }
301:
302: /*
303: * methods specific to allocExtent
304: */
305:
306: /*
307: * write operation that is called underneath the log
308: *
309: * page goes thru the following transition:
310: * ALLOCATED_PAGE <-> deallocated page -> free page <-> ALLOCATED_PAGE
311: *
312: */
313:
314: /**
315: Allocate this page - this is called underneath the log record
316:
317: @exception StandardException Standard Cloudscape error policy
318: */
319: protected void allocPage(long pagenum) throws StandardException {
320: if (SanityManager.DEBUG) {
321: if (pagenum > getLastPagenum()) {
322: // adding a brand new page, it should be at most one off from the last page
323: if (pagenum > extentEnd)
324: SanityManager.THROWASSERT("pagenum " + pagenum
325: + " is out of beyond my range ("
326: + extentStart + "," + extentEnd + ")");
327: if (pagenum != getLastPagenum() + 1)
328: SanityManager
329: .THROWASSERT("skipping pages, lastPageNumber = "
330: + getLastPagenum()
331: + " pageNumber = " + pagenum + "\n");
332: } else {
333: // reuseing a page, make sure it is in range and is not already in use
334: checkInRange(pagenum);
335:
336: int bitnum = (int) (pagenum - extentStart);
337:
338: // either the pagenum is now free (do) or deallocated (undo)
339: if (!freePages.isSet(bitnum)) {
340: SanityManager
341: .THROWASSERT("trying to re-allocate a page ( "
342: + pagenum
343: + " ) that is already allocated ");
344: }
345: }
346: }
347:
348: // don't know if we are redoing (from free -> valid)
349: // or undoing (from dealloc -> valid), reset them both
350: int bitnum = (int) (pagenum - extentStart);
351:
352: if (bitnum >= freePages.getLength()) // expand the bit map
353: {
354: int numbits = (1 + (bitnum / 8)) * 8;
355: if (numbits > (int) (extentEnd - extentStart + 1))
356: numbits = (int) (extentEnd - extentStart + 1);
357:
358: freePages.grow(numbits);
359: unFilledPages.grow(numbits);
360: }
361:
362: // the first page to be allocated has pagenum == extentStart.
363: int numPageAlloced = (int) (pagenum - extentStart + 1);
364:
365: if (numPageAlloced > extentLength) {
366: extentLength = numPageAlloced;
367: }
368:
369: freePages.clear(bitnum);
370:
371: // do not set the unfilled bit on a newly allocated page because
372: // we only keep track of unfilled HEAD page, not unfilled overflow
373: // page.
374: }
375:
376: /**
377: Deallocate logical page pagenum - this is called underneath the log record.
378: pagenum must be a page managed by this extent and it must be valid
379:
380: @exception StandardException Standard Cloudscape error policy
381: */
382: protected void deallocPage(long pagenum) throws StandardException {
383: int bitnum = (int) (pagenum - extentStart);
384:
385: // the pagenum must now be either valid (do) or free (undo)
386: if (SanityManager.DEBUG) {
387: if (freePages.isSet(bitnum))
388: SanityManager
389: .THROWASSERT("trying to deallocate a deallocated page "
390: + pagenum);
391: }
392:
393: freePages.set(bitnum);
394: unFilledPages.clear(bitnum); // deallocated page is never unfilled
395:
396: setExtentFreePageStatus(true);
397: }
398:
399: /**
400: * Compress free pages at end of this extent.
401: * <p>
402: * Search backward from end of extent and prepare data structures
403: * to return pages at end of extent to the OS. Returns the lowest
404: * page that can be returned to the OS.
405: * <p>
406: *
407: * @return Return bit of page where all pages that follow can
408: * be returned to the OS.
409: **/
410: protected int compress(BaseContainerHandle owner,
411: RawTransaction ntt, AllocPage alloc_page)
412: throws StandardException {
413: int compress_bitnum = -1;
414: int num_pages_compressed = 0;
415:
416: for (int i = (extentLength - 1); i >= 0; i--) {
417: if (freePages.isSet(i)) {
418: compress_bitnum = i;
419: num_pages_compressed++;
420: } else {
421: break;
422: }
423: }
424:
425: // new_highest_page is the last page to remain in the file after
426: // the truncate, the above loop set compress_bitnum to lowest
427: // free page in the set of contiguous free pages at end of extent.
428: int new_highest_page = compress_bitnum - 1;
429:
430: if (num_pages_compressed > 0) {
431: if (SanityManager.DEBUG) {
432: for (int i = new_highest_page + 1; i < extentLength; i++) {
433: if (!freePages.isSet(i)) {
434:
435: SanityManager
436: .THROWASSERT("compressPages with nonfree pg to truncate,"
437: + "new_highest_page = "
438: + new_highest_page
439: + "num_pages_truncated = "
440: + num_pages_compressed
441: + ";extentLength = "
442: + extentLength
443: + ";extentStart = "
444: + extentStart
445: + ";freePages.isSet("
446: + i
447: + ") = "
448: + freePages.isSet(i)
449: + "\nextent:\n"
450: + toDebugString());
451: }
452: }
453:
454: SanityManager.ASSERT((new_highest_page
455: + num_pages_compressed + 1) == extentLength,
456: "truncate page count did not match: "
457: + ";new_highest_page = "
458: + new_highest_page
459: + ";num_pages_truncated = "
460: + num_pages_compressed
461: + ";extentLength = " + extentLength);
462:
463: // the following assert could be made invalid by a new type of
464: // access method, but currently page 1 of btree and heap contain
465: // control rows, so will never become free and thus should never
466: // be compressed.
467: if (extentStart == 1) {
468: SanityManager.ASSERT(new_highest_page >= 0);
469:
470: if (num_pages_compressed >= extentLength) {
471: SanityManager.THROWASSERT("new_highest_page = "
472: + new_highest_page
473: + "num_pages_compressed = "
474: + num_pages_compressed
475: + "; extentLength = " + extentLength
476: + "extent:\n" + toDebugString());
477: }
478: }
479: }
480:
481: /*
482: SanityManager.DEBUG_PRINT("AllocExtent",
483: "calling actionCompressSpaceOperation:" +
484: ";new_highest_page = " + new_highest_page +
485: ";num_pages_compressed = " + num_pages_compressed +
486: ";extentLength = " + extentLength +
487: ";extent: \n" + toDebugString());
488: */
489:
490: owner.getAllocationActionSet()
491: .actionCompressSpaceOperation(ntt, alloc_page,
492: new_highest_page, num_pages_compressed);
493: return (compress_bitnum);
494: } else {
495: return (-1);
496: }
497:
498: }
499:
500: protected void compressPages(int new_highest_page,
501: int num_pages_truncated) {
502: if (SanityManager.DEBUG) {
503: if (new_highest_page >= 0) {
504: for (int i = new_highest_page + 1; i < extentLength; i++) {
505: if (!freePages.isSet(i)) {
506: SanityManager
507: .THROWASSERT("compressPages with non free page to truncate,"
508: + "new_highest_page = "
509: + new_highest_page
510: + "num_pages_truncated = "
511: + num_pages_truncated
512: + ";extentLength = "
513: + extentLength
514: + ";extentStart = "
515: + extentStart
516: + ";freePages.isSet("
517: + i
518: + ") = "
519: + freePages.isSet(i)
520: + "\nextent:\n"
521: + toDebugString());
522: }
523: }
524: }
525:
526: SanityManager.ASSERT((new_highest_page
527: + num_pages_truncated + 1) == extentLength,
528: "truncate page count did not match: "
529: + ";new_highest_page = " + new_highest_page
530: + ";num_pages_truncated = "
531: + num_pages_truncated + ";extentLength = "
532: + extentLength);
533:
534: // the following assert could be made invalid by a new type of
535: // access method, but currently page 1 of btree and heap contain
536: // control rows, so will never become free and thus should never
537: // be compressed.
538: if (extentStart == 1) {
539: SanityManager.ASSERT(new_highest_page >= 0);
540: SanityManager
541: .ASSERT(num_pages_truncated < extentLength);
542: }
543: }
544:
545: if (new_highest_page >= 0) {
546: freePages.shrink(new_highest_page + 1);
547: unFilledPages.shrink(new_highest_page + 1);
548:
549: // This routine assumes the caller
550: // will be doing the truncate, and just updates the data structures.
551: preAllocLength = extentLength = (new_highest_page + 1);
552: }
553:
554: return;
555: }
556:
557: /**
558: * Undo the compress space operation.
559: * <p>
560: * Undo of this operation doesn't really "undo" the operation, it just
561: * makes sure the data structures are ok after the undo. We are
562: * guaranteed at the point of the transaction doing the
563: * Undo of the compress space operation fixes up the bit maps to
564: * only point at pages within the new_highest_page range.
565: * <p>
566: * Prior to logging the compress space operation all pages greater
567: * than
568: * There are only 2 possibilities at this point:
569: * 1) the truncate of pages greater than new_highest_page happened before
570: * the abort took place. W
571: * 2)
572: *
573: * @exception StandardException Standard exception policy.
574: **/
575: protected void undoCompressPages(int new_highest_page,
576: int num_pages_truncated) {
577: if (new_highest_page >= 0) {
578: freePages.shrink(new_highest_page + 1);
579: unFilledPages.shrink(new_highest_page + 1);
580: preAllocLength = extentLength = (new_highest_page + 1);
581: }
582:
583: return;
584: }
585:
586: protected long getExtentEnd() {
587: return extentEnd;
588: }
589:
590: /*
591: * read operation that is called above the log
592: */
593:
594: /**
595: Get a page number that is free
596: */
597: protected long getFreePageNumber(long pnum) {
598: // if we can reuse page, do so, otherwise add a brand new page
599: if (mayHaveFreePage()) {
600: // The last allocated page may be from a previous alloc extent, but
601: // if that extent is full and we are the first extent that can
602: // accomodate a new page, we may be picked. In that case, pnum may
603: // be before the start of this extent.
604: int i = (pnum < extentStart) ? freePages.anySetBit()
605: : freePages.anySetBit((int) (pnum - extentStart));
606:
607: if (i != -1) {
608: if (SanityManager.DEBUG) {
609: if (i >= extentLength)
610: SanityManager.THROWASSERT("returned bit = " + i
611: + " extent length = " + extentLength);
612: }
613:
614: return i + extentStart;
615: }
616:
617: // the hint is wrong, no free page in the extent
618: // do this unlogged, it is just a hint, don't care if it is lost
619: if (pnum < extentStart)
620: setExtentFreePageStatus(false);
621: }
622:
623: // maximally, we can have up to extendEnd page
624: if (SanityManager.DEBUG)
625: SanityManager
626: .ASSERT(extentStart + extentLength <= extentEnd);
627:
628: // need to add a brand new page, current end of extent is at page
629: // extentStart+extentLength-1;
630: return extentStart + extentLength;
631: }
632:
633: /**
634: Get the physical offset of pagenum.
635: If deallocOK is true, then even if pagenum is deallocated, it is OK.
636: If deallocOK is false, then an exception is thrown if pagenum is
637: deallocated.
638:
639: An exception is always thrown if pagenum is a free page
640:
641: @exception StandardException Standard Cloudscape error policy
642: */
643: protected long getPageOffset(long pagenum, int pagesize,
644: boolean deallocOK) throws StandardException {
645: return pagenum * pagesize;
646: }
647:
648: /**
649: Return the status of this extent
650: */
651: protected boolean isRetired() {
652: return ((extentStatus & RETIRED) != 0);
653: }
654:
655: private boolean mayHaveFreePage() {
656: return ((extentStatus & HAS_FREE) != 0);
657: }
658:
659: private void setExtentFreePageStatus(boolean hasFree) {
660: if (hasFree)
661: extentStatus |= HAS_FREE;
662: else
663: extentStatus &= ~HAS_FREE;
664: }
665:
666: protected boolean canAddFreePage(long lastAllocatedPage) {
667: // the last page to be allocated == extentEnd
668: if (extentStart + extentLength <= extentEnd)
669: return true;
670:
671: // else, check to see if this may have any free page
672: if (!mayHaveFreePage())
673: return false;
674:
675: // we may have a free page, but that is not certain, double check
676: if (lastAllocatedPage < extentStart)
677: return (freePages.anySetBit() != -1);
678: else
679: return ((freePages
680: .anySetBit((int) (lastAllocatedPage - extentStart))) != -1);
681: }
682:
683: /**
684: Return the status of a particular page
685: */
686: protected int getPageStatus(long pagenum) {
687: if (SanityManager.DEBUG)
688: checkInRange(pagenum);
689:
690: int status = 0;
691: int bitnum = (int) (pagenum - extentStart);
692:
693: if (freePages.isSet(bitnum))
694: status = FREE_PAGE;
695: else
696: status = ALLOCATED_PAGE;
697:
698: return status;
699: }
700:
701: /**
702: Get the first logical page number managed by this extent.
703: */
704: protected long getFirstPagenum() {
705: return extentStart;
706: }
707:
708: /**
709: Get the last logical page number managed by this extent.
710: */
711: protected long getLastPagenum() {
712: return extentStart + extentLength - 1;
713: }
714:
715: /**
716: * translate bit position in map to page number.
717: * <p>
718: *
719: * @return The page number of this "bit" in the extent map.
720: *
721: * @exception StandardException Standard exception policy.
722: **/
723: protected long getPagenum(int bit_pos) {
724: return (extentStart + bit_pos);
725: }
726:
727: /*
728: * page preallocation
729: */
730:
731: /**
732: * get the last preallocated pagenumber managed by this alloc page
733: */
734: protected long getLastPreallocPagenum() {
735: if (extentLength > preAllocLength)
736: preAllocLength = extentLength;
737:
738: return extentStart + preAllocLength - 1;
739: }
740:
741: /**
742: preallocated N pages, passed in the last preallocated page number.
743: */
744: protected void setLastPreallocPagenum(long preAllocPagenum) {
745: if (SanityManager.DEBUG)
746: SanityManager
747: .ASSERT(
748: preAllocPagenum >= getLastPreallocPagenum(),
749: "setLastPreallocPagenum set to small prealloc length than before");
750:
751: // cannot prealloc more than this extent can handle
752: if (preAllocPagenum > extentEnd)
753: preAllocPagenum = extentEnd;
754:
755: preAllocLength = (int) (preAllocPagenum - extentStart + 1);
756: }
757:
758: /*
759: Get the logical page number that is bigger than prevPageNumber
760: and is a valid page. If no such page in this extent, return
761: ContainerHandle.INVALID_PAGE_HANDLE
762: */
763: protected long getNextValidPageNumber(long prevPageNumber) {
764: long pageNum;
765: long lastpage = getLastPagenum();
766:
767: if (prevPageNumber < extentStart)
768: pageNum = extentStart;
769: else
770: pageNum = prevPageNumber + 1;
771:
772: while (pageNum <= lastpage) {
773: int status = getPageStatus(pageNum);
774: if (status == ALLOCATED_PAGE)
775: break;
776: pageNum++;
777: }
778:
779: if (pageNum > lastpage)
780: pageNum = ContainerHandle.INVALID_PAGE_NUMBER;
781: return pageNum;
782: }
783:
784: protected long getLastValidPageNumber() {
785: long pageNum = getLastPagenum();
786: while (pageNum >= extentStart) {
787: int status = getPageStatus(pageNum);
788: if (status == ALLOCATED_PAGE)
789: break;
790: pageNum--;
791: }
792: if (pageNum < extentStart)
793: pageNum = ContainerHandle.INVALID_PAGE_NUMBER;
794: return pageNum;
795: }
796:
797: private void checkInRange(long pagenum) {
798: if (SanityManager.DEBUG)
799: if (pagenum < extentStart
800: || pagenum >= extentStart + extentLength)
801: SanityManager.THROWASSERT("pagenum " + pagenum
802: + " out of range");
803: }
804:
805: protected void updateUnfilledPageInfo(AllocExtent inputExtent) {
806: if (SanityManager.DEBUG) {
807: if (inputExtent.unFilledPages.getLength() != unFilledPages
808: .getLength()) {
809: SanityManager
810: .THROWASSERT("inputExtent's unfilled page length "
811: + inputExtent.unFilledPages.getLength()
812: + " != extent's unfilled page length "
813: + unFilledPages.getLength());
814: }
815: }
816:
817: // just use the passed in inputExtent, we know (wink wink) that the
818: // unfilled page info is being updated just when the allocation cache
819: // is being invalidated. Nobody is going to have a reference to the
820: // inputExtent after this so is it save to share the FormatableBitSet.
821:
822: // if we cannot guarentee that the inputExtent will be unchanged by the
823: // caller, we need to copy it
824: // unFilledPages = new FormatableBitSet(inputExtent.unFilledPages);
825: // Right now, just reference it directly
826: unFilledPages = inputExtent.unFilledPages;
827:
828: if (unFilledPages.anySetBit() >= 0)
829: extentStatus |= HAS_UNFILLED_PAGES;
830: else
831: extentStatus &= ~HAS_UNFILLED_PAGES;
832: }
833:
834: /*
835: Keep track of unfilled pages, if the extent changed, returns true.
836: */
837: protected boolean trackUnfilledPage(long pagenumber,
838: boolean unfilled) {
839: checkInRange(pagenumber);
840:
841: int bitnum = (int) (pagenumber - extentStart);
842:
843: boolean bitSet = unFilledPages.isSet(bitnum);
844: if (unfilled != bitSet) {
845: if (unfilled) {
846: unFilledPages.set(bitnum);
847: extentStatus |= HAS_UNFILLED_PAGES;
848: } else
849: unFilledPages.clear(bitnum);
850: return true;
851: }
852:
853: return false;
854: }
855:
856: /**
857: Get a page number that is unfilled, pagenum is the last page that was
858: rejected.
859: */
860: protected long getUnfilledPageNumber(long pagenum) {
861: if ((extentStatus & HAS_UNFILLED_PAGES) == 0)
862: return ContainerHandle.INVALID_PAGE_NUMBER;
863:
864: int i = unFilledPages.anySetBit();
865:
866: if (i != -1) {
867: if (i + extentStart != pagenum)
868: return i + extentStart;
869: else {
870: // unfortunately, we found the same page number that
871: // was rejected. It would be unwise to unset bit
872: // pagenum because just because it was rejected does not mean
873: // the page is full, the row we are trying to insert may just
874: // be too big. If we unset it, we will never find that page
875: // again even though it may be a perfectly good page for any
876: // other row. Just get the next set bit.
877: i = unFilledPages.anySetBit(i);
878: if (i != -1)
879: return i + extentStart;
880: }
881: }
882:
883: return ContainerHandle.INVALID_PAGE_NUMBER;
884:
885: }
886:
887: /**
888: Get the number of used page in this extent
889: */
890: protected int getAllocatedPageCount() {
891: // allocated page is one which is not free or deallocated.
892: int allocatedPageCount = extentLength;
893:
894: if (!mayHaveFreePage())
895: return allocatedPageCount;
896:
897: byte[] free = freePages.getByteArray();
898: int numBytes = free.length;
899:
900: for (int i = 0; i < numBytes; i++) {
901: if (free[i] != 0) {
902: for (int j = 0; j < 8; j++) {
903: if (((1 << j) & free[i]) != 0) {
904: allocatedPageCount--;
905: }
906: }
907: }
908: }
909:
910: if (SanityManager.DEBUG) {
911: if (allocatedPageCount < 0) {
912: SanityManager
913: .THROWASSERT("number of allocated page < 0, val ="
914: + allocatedPageCount
915: + "\nextent = "
916: + toDebugString());
917: }
918: }
919:
920: return allocatedPageCount;
921: }
922:
923: /**
924: Get the number of unfilled pages in this extent
925: */
926: protected int getUnfilledPageCount() {
927: int unfilledPageCount = 0;
928: int freePagesSize = freePages.size();
929:
930: for (int i = 0; i < unFilledPages.size(); i++) {
931: if (unFilledPages.isSet(i)
932: && (i >= freePagesSize || !freePages.isSet(i)))
933: unfilledPageCount++;
934: }
935:
936: if (SanityManager.DEBUG)
937: SanityManager.ASSERT(unfilledPageCount >= 0,
938: "number of unfilled pages < 0");
939:
940: return unfilledPageCount;
941: }
942:
943: /**
944: Get the total number of pages in this extent
945: */
946: protected int getTotalPageCount() {
947: return extentLength;
948: }
949:
950: protected String toDebugString() {
951: if (SanityManager.DEBUG) {
952: String str = "------------------------------------------------------------------------------\n"
953: + "Extent map of from page "
954: + extentStart
955: + " to page " + extentEnd + "\n";
956:
957: for (long i = extentStart; i < extentStart + extentLength; i++) {
958: str += "\tpage " + i + ": ";
959: switch (getPageStatus(i)) {
960: case FREE_PAGE:
961: str += "free page\n";
962: break;
963: case ALLOCATED_PAGE:
964: str += "valid, in use page\n";
965: break;
966: }
967:
968: // int bitnum = (int)(i-extentStart);
969: // if (unFilledPages.isSet(bitnum))
970: // str += " page is estimated to be unfilled\n";
971: }
972:
973: if (getLastPagenum() < extentEnd)
974: str += "\tFrom " + getLastPagenum() + " to "
975: + extentEnd + " are un-allocated pages\n";
976:
977: str += "------------------------------------------------------------------------------\n";
978:
979: return str;
980: } else
981: return null;
982: }
983:
984: }
|