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