1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je.utilint;
9 
10 import java.io.File;
11 import java.util.Arrays;
12 
13 import com.sleepycat.je.EnvironmentFailureException;
14 import com.sleepycat.je.log.FileManager;
15 import com.sleepycat.je.tree.TreeUtils;
16 
17 /**
18  * DbLsn is a class that operates on Log Sequence Numbers (LSNs). An LSN is a
19  * long comprised of a file number (32b) and offset within that file (32b)
20  * which references a unique record in the database environment log.  While
21  * LSNs are represented as long's, we operate on them using an abstraction and
22  * return longs from these methods so that we don't have to worry about the
23  * lack of unsigned quantities.
24  */
25 public class DbLsn {
26     static final long INT_MASK = 0xFFFFFFFFL;
27 
28     public static final long MAX_FILE_OFFSET = 0xFFFFFFFFL;
29 
30     /* Signifies a transient LSN. */
31     private static final long MAX_FILE_NUM = 0xFFFFFFFFL;
32 
33     public static final long NULL_LSN = -1;
34 
DbLsn()35     private DbLsn() {
36     }
37 
makeLsn(long fileNumber, long fileOffset)38     public static long makeLsn(long fileNumber, long fileOffset) {
39         return fileOffset & INT_MASK |
40             ((fileNumber & INT_MASK) << 32);
41     }
42 
43     /**
44      * This flavor of makeLsn is used when the file offset has been stored
45      * in 32 bits, as is done in the VLSNBucket.
46      */
makeLsn(long fileNumber, int fileOffset)47     public static long makeLsn(long fileNumber, int fileOffset) {
48         return fileOffset & INT_MASK |
49             ((fileNumber & INT_MASK) << 32);
50     }
51 
52     /**
53      * For transient LSNs we use the MAX_FILE_NUM and the ascending sequence of
54      * offsets.
55      */
makeTransientLsn(long fileOffset)56     public static long makeTransientLsn(long fileOffset) {
57         return makeLsn(DbLsn.MAX_FILE_NUM, fileOffset);
58     }
59 
60     /**
61      * A transient LSN is defined as one with a file number of MAX_FILE_NUM.
62      */
isTransient(long lsn)63     public static boolean isTransient(long lsn) {
64         return getFileNumber(lsn) == MAX_FILE_NUM;
65     }
66 
isTransientOrNull(long lsn)67     public static boolean isTransientOrNull(long lsn) {
68         return lsn == NULL_LSN || isTransient(lsn);
69     }
70 
longToLsn(Long lsn)71     public static long longToLsn(Long lsn) {
72         if (lsn == null) {
73             return NULL_LSN;
74         }
75 
76         return lsn.longValue();
77     }
78 
79     /**
80      * Return the file number for this DbLsn.
81      * @return the number for this DbLsn.
82      */
getFileNumber(long lsn)83     public static long getFileNumber(long lsn) {
84         return (lsn >> 32) & INT_MASK;
85     }
86 
87     /**
88      * Return the file offset for this DbLsn.
89      * @return the offset for this DbLsn.
90      */
getFileOffset(long lsn)91     public static long getFileOffset(long lsn) {
92         return (lsn & INT_MASK);
93     }
94 
95     /*
96      * The file offset is really an unsigned int. If we are using the
97      * file offset as a value, we must be careful to manipulate it as a long
98      * in order not to lose the last bit of data. If we are only storing
99      * the file offset, we can treat it as an Integer in order to save
100      * 32 bits of space.
101      */
getFileOffsetAsInt(long lsn)102     public static int getFileOffsetAsInt(long lsn) {
103         return (int) getFileOffset(lsn);
104     }
105 
convertIntFileOffsetToLong(int storedLsn)106     public static long convertIntFileOffsetToLong(int storedLsn) {
107         return storedLsn & 0xffffffffL;
108     }
109 
compareLong(long l1, long l2)110     private static int compareLong(long l1, long l2) {
111         if (l1 < l2) {
112             return -1;
113         } else if (l1 > l2) {
114             return 1;
115         } else {
116             return 0;
117         }
118     }
119 
compareTo(long lsn1, long lsn2)120     public static int compareTo(long lsn1, long lsn2) {
121         if (lsn1 == NULL_LSN ||
122             lsn2 == NULL_LSN) {
123             throw EnvironmentFailureException.unexpectedState
124                 ("NULL_LSN lsn1=" + getNoFormatString(lsn1) +
125                  " lsn2=" + getNoFormatString(lsn2));
126         }
127 
128         long fileNumber1 = getFileNumber(lsn1);
129         long fileNumber2 = getFileNumber(lsn2);
130         if (fileNumber1 == fileNumber2) {
131             return compareLong(getFileOffset(lsn1), getFileOffset(lsn2));
132         }
133         return compareLong(fileNumber1, fileNumber2);
134     }
135 
toString(long lsn)136     public static String toString(long lsn) {
137         return "<DbLsn val=\"0x" +
138             Long.toHexString(getFileNumber(lsn)) +
139             "/0x" +
140             Long.toHexString(getFileOffset(lsn)) +
141             "\"/>";
142     }
143 
getNoFormatString(long lsn)144     public static String getNoFormatString(long lsn) {
145         return "0x" + Long.toHexString(getFileNumber(lsn)) + "/0x" +
146             Long.toHexString(getFileOffset(lsn));
147     }
148 
dumpString(long lsn, int nSpaces)149     public static String dumpString(long lsn, int nSpaces) {
150         StringBuilder sb = new StringBuilder();
151         sb.append(TreeUtils.indent(nSpaces));
152         sb.append(toString(lsn));
153         return sb.toString();
154     }
155 
156     /**
157      * Return the logsize in bytes between these two LSNs. This is an
158      * approximation; the logs might actually be a little more or less in
159      * size. This assumes that no log files have been cleaned.
160      */
getNoCleaningDistance(long thisLsn, long otherLsn, long logFileSize)161     public static long getNoCleaningDistance(long thisLsn,
162                                              long otherLsn,
163                                              long logFileSize) {
164         long diff = 0;
165 
166         assert thisLsn != NULL_LSN;
167         /* First figure out how many files lay between the two. */
168         long myFile = getFileNumber(thisLsn);
169         if (otherLsn == NULL_LSN) {
170             otherLsn = 0;
171         }
172         long otherFile = getFileNumber(otherLsn);
173         if (myFile == otherFile) {
174             diff = Math.abs(getFileOffset(thisLsn) - getFileOffset(otherLsn));
175         } else if (myFile > otherFile) {
176             diff = calcDiff(myFile - otherFile,
177                             logFileSize, thisLsn, otherLsn);
178         } else {
179             diff = calcDiff(otherFile - myFile,
180                             logFileSize, otherLsn, thisLsn);
181         }
182         return diff;
183     }
184 
185     /**
186      * Return the logsize in bytes between these two LSNs. This is an
187      * approximation; the logs might actually be a little more or less in
188      * size. This assumes that log files might have been cleaned.
189      */
getWithCleaningDistance(long thisLsn, long otherLsn, long logFileSize, FileManager fileManager)190     public static long getWithCleaningDistance(long thisLsn,
191                                                long otherLsn,
192                                                long logFileSize,
193                                                FileManager fileManager) {
194         long diff = 0;
195 
196         assert thisLsn != NULL_LSN;
197         /* First figure out how many files lay between the two. */
198         long myFile = getFileNumber(thisLsn);
199         if (otherLsn == NULL_LSN) {
200             otherLsn = 0;
201         }
202         long otherFile = getFileNumber(otherLsn);
203         if (myFile == otherFile) {
204             diff = Math.abs(getFileOffset(thisLsn) - getFileOffset(otherLsn));
205         } else {
206             /* Figure out how many files lie between. */
207             Long[] fileNums = fileManager.getAllFileNumbers();
208             int myFileIdx = Arrays.binarySearch(fileNums,
209                                                 Long.valueOf(myFile));
210             int otherFileIdx =
211                 Arrays.binarySearch(fileNums, Long.valueOf(otherFile));
212             if (myFileIdx > otherFileIdx) {
213                 diff = calcDiff(myFileIdx - otherFileIdx,
214                                 logFileSize, thisLsn, otherLsn);
215             } else {
216                 diff = calcDiff(otherFileIdx - myFileIdx,
217                                 logFileSize, otherLsn, thisLsn);
218             }
219         }
220         return diff;
221     }
222 
calcDiff(long fileDistance, long logFileSize, long laterLsn, long earlierLsn)223     private static long calcDiff(long fileDistance,
224                                  long logFileSize,
225                                  long laterLsn,
226                                  long earlierLsn) {
227         long diff = fileDistance * logFileSize;
228         diff += getFileOffset(laterLsn);
229         diff -= getFileOffset(earlierLsn);
230         return diff;
231     }
232 
233     /**
234      * Returns the number of bytes between two LSNs, counting the true size of
235      * each intermediate file. Assumes that all files in the LSN range are
236      * currently protected from cleaner deletion, e.g., during recovery. Uses
237      * File.length and does not perturb the FileManager's file handle cache.
238      */
getTrueDistance(final long thisLsn, final long otherLsn, final FileManager fileManager)239     public static long getTrueDistance(final long thisLsn,
240                                        final long otherLsn,
241                                        final FileManager fileManager) {
242 
243         final long lsn1;
244         final long lsn2;
245 
246         if (compareTo(thisLsn, otherLsn) < 0) {
247             lsn1 = thisLsn;
248             lsn2 = otherLsn;
249         } else {
250             lsn1 = otherLsn;
251             lsn2 = thisLsn;
252         }
253 
254         final long file1 = getFileNumber(lsn1);
255         final long file2 = getFileNumber(lsn2);
256 
257         long dist = getFileOffset(lsn2) - getFileOffset(lsn1);
258 
259         if (file1 == file2) {
260             return dist;
261         }
262 
263         final Long[] fileNums = fileManager.getAllFileNumbers();
264 
265         final int idx1 = Arrays.binarySearch(fileNums, file1);
266         final int idx2 = Arrays.binarySearch(fileNums, file2);
267 
268         /*
269          * File2 has already been counted, and we've already subtracted the
270          * offset of file1. Add lengths of file1 to file2-1.
271          */
272         for (int i = idx1; i < idx2; i += 1) {
273             final String path = fileManager.getFullFileName(fileNums[i]);
274             dist += new File(path).length();
275         }
276 
277         return dist;
278     }
279 }
280