001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.log.LogCounter
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.log;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025: import org.apache.derby.iapi.services.io.FormatIdUtil;
026: import org.apache.derby.iapi.services.io.StoredFormatIds;
027: import org.apache.derby.iapi.store.raw.log.LogInstant;
028: import org.apache.derby.iapi.store.access.DatabaseInstant;
029: import org.apache.derby.iapi.services.io.CompressedNumber;
030:
031: import java.io.IOException;
032: import java.io.ObjectInput;
033: import java.io.ObjectOutput;
034:
035: /**
036: A very simple log instant implementation.
037:
038: Within the stored log record a log counter is represented as a long,
039: hence the getValueAsLong() method. Outside the LogFactory the instant
040: is passed around as a LogCounter (through its LogInstant interface).
041:
042: The way the long is encoded is such that < == > correctly tells if
043: one log instant is lessThan, equals or greater than another.
044:
045: */
046: public class LogCounter implements LogInstant {
047:
048: /********************************************************
049: **
050: ** This class implements Formatable. That means that it
051: ** can write itself to and from a formatted stream. If
052: ** you add more fields to this class, make sure that you
053: ** also write/read them with the writeExternal()/readExternal()
054: ** methods.
055: **
056: ** If, between releases, you add more fields to this class,
057: ** then you should bump the version number emitted by the getTypeFormatId()
058: ** method.
059: **
060: ********************************************************/
061:
062: /** A well defined value of an invalid log instant. */
063: public static final long INVALID_LOG_INSTANT = 0;
064:
065: // max possible log file number in versions before 10.1 is 2^22 -1
066: public static final long DERBY_10_0_MAX_LOGFILE_NUMBER = (long) 0x003FFFFFL; // 4194303
067: // max possible log file number is 2^31 -1
068: public static final long MAX_LOGFILE_NUMBER = (long) 0x7FFFFFFFL; // 2147483647
069: // lower end of 32 bits in long type are used to store the log file position
070: private static final long FILE_NUMBER_SHIFT = 32;
071:
072: // reserve top 4 bits in log file size for future use
073: public static final long MAX_LOGFILE_SIZE = (long) 0x0FFFFFFFL; // 268435455
074: // 32 bits are used to store the log file postion
075: private static final long FILE_POSITION_MASK = (long) 0x7FFFFFFFL;
076:
077: private long fileNumber;
078: private long filePosition;
079:
080: // contructors
081: public LogCounter(long value) {
082: fileNumber = getLogFileNumber(value);
083: filePosition = getLogFilePosition(value);
084: }
085:
086: public LogCounter(long fileNumber, long position) {
087:
088: if (SanityManager.DEBUG) {
089: SanityManager.ASSERT(fileNumber > 0, "illegal fileNumber");
090: SanityManager.ASSERT(position > 0, "illegal file position");
091:
092: SanityManager.ASSERT(position < MAX_LOGFILE_SIZE,
093: "log file position exceeded max log file size");
094: SanityManager.ASSERT(fileNumber < MAX_LOGFILE_NUMBER,
095: "log file number exceeded max log file number");
096: }
097:
098: this .fileNumber = fileNumber;
099: this .filePosition = position;
100: }
101:
102: /**
103: * Public niladic constructor needed for Formatable interface.
104: */
105: public LogCounter() {
106: }
107:
108: /**
109: Static functions that can only be used inside the RawStore's log
110: factory which passes the log counter around encoded as a long
111: */
112:
113: // make a log instant from 2 longs and return a long which is the long
114: // representatin of a LogCounter
115: static public final long makeLogInstantAsLong(long filenum,
116: long filepos) {
117: if (SanityManager.DEBUG) {
118: SanityManager.ASSERT(filenum > 0, "illegal fileNumber");
119: SanityManager.ASSERT(filepos > 0, "illegal file position");
120:
121: SanityManager.ASSERT(filepos < MAX_LOGFILE_SIZE,
122: "log file position exceeded max log file size");
123: SanityManager.ASSERT(filenum < MAX_LOGFILE_NUMBER,
124: "log file number exceeded max log file number");
125: }
126:
127: return ((filenum << FILE_NUMBER_SHIFT) | filepos);
128: }
129:
130: static public final long getLogFilePosition(long valueAsLong) {
131: return valueAsLong & FILE_POSITION_MASK;
132: }
133:
134: static public final long getLogFileNumber(long valueAsLong) {
135: return valueAsLong >>> FILE_NUMBER_SHIFT;
136: }
137:
138: /** LogScan methods */
139:
140: public boolean lessThan(DatabaseInstant other) {
141: LogCounter compare = (LogCounter) other;
142:
143: return (fileNumber == compare.fileNumber) ? filePosition < compare.filePosition
144: : fileNumber < compare.fileNumber;
145: }
146:
147: public boolean equals(Object other) {
148: if (this == other)
149: return true;
150:
151: if (!(other instanceof LogCounter))
152: return false;
153:
154: LogCounter compare = (LogCounter) other;
155:
156: return fileNumber == compare.fileNumber
157: && filePosition == compare.filePosition;
158: }
159:
160: public DatabaseInstant next() {
161: return new LogCounter(makeLogInstantAsLong(fileNumber,
162: filePosition) + 1);
163: }
164:
165: public DatabaseInstant prior() {
166: return new LogCounter(makeLogInstantAsLong(fileNumber,
167: filePosition) - 1);
168: }
169:
170: public int hashCode() {
171: return (int) (filePosition ^ fileNumber);
172: }
173:
174: public String toString() {
175: return "(" + fileNumber + "," + filePosition + ")";
176: }
177:
178: public static String toDebugString(long instant) {
179: if (SanityManager.DEBUG)
180: return "(" + getLogFileNumber(instant) + ","
181: + getLogFilePosition(instant) + ")";
182: else
183: return null;
184: }
185:
186: /**
187: These following methods are only intended to be called by an
188: implementation of a log factory. All other uses of this object should
189: only see it as a log instant.
190: */
191: public long getValueAsLong() {
192: return makeLogInstantAsLong(fileNumber, filePosition);
193: }
194:
195: public long getLogFilePosition() {
196: return filePosition;
197: }
198:
199: public long getLogFileNumber() {
200: return fileNumber;
201: }
202:
203: /*
204: * methods for the Formatable interface
205: */
206:
207: /**
208: * Read this in.
209: * @exception IOException error reading from log stream
210: * @exception ClassNotFoundException corrupted log stream
211: */
212: public void readExternal(ObjectInput oi) throws IOException,
213: ClassNotFoundException {
214: fileNumber = CompressedNumber.readLong(oi);
215: filePosition = CompressedNumber.readLong(oi);
216: }
217:
218: /**
219: * Write this out.
220: * @exception IOException error writing to log stream
221: */
222: public void writeExternal(ObjectOutput oo) throws IOException {
223: CompressedNumber.writeLong(oo, fileNumber);
224: CompressedNumber.writeLong(oo, filePosition);
225: }
226:
227: /**
228: * Get the formatID which corresponds to this class.
229: *
230: * @return the formatID of this class
231: */
232: public int getTypeFormatId() {
233: return StoredFormatIds.LOG_COUNTER;
234: }
235:
236: }
|