001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.backends.jeb; 028 029import org.forgerock.opendj.ldap.ByteStringBuilder; 030import org.opends.server.types.DN; 031 032/** 033 * Handles the disk representation of LDAP data. 034 */ 035public class JebFormat 036{ 037 038 /** 039 * The format version used by this class to encode and decode a DatabaseEntry. 040 */ 041 public static final byte FORMAT_VERSION = 0x01; 042 043 /** 044 * The ASN1 tag for the DatabaseEntry type. 045 */ 046 public static final byte TAG_DATABASE_ENTRY = 0x60; 047 048 /** 049 * The ASN1 tag for the DirectoryServerEntry type. 050 */ 051 public static final byte TAG_DIRECTORY_SERVER_ENTRY = 0x61; 052 053 /** 054 * Decode an entry ID value from its database representation. Note that 055 * this method will throw an ArrayIndexOutOfBoundsException if the bytes 056 * array length is less than 8. 057 * 058 * @param bytes The database value of the entry ID. 059 * @return The entry ID value. 060 * @see #entryIDToDatabase(long) 061 */ 062 public static long entryIDFromDatabase(byte[] bytes) 063 { 064 return toLong(bytes, 0, 8); 065 } 066 067 /** 068 * Decode a long from a byte array, starting at start index and ending at end 069 * index. 070 * 071 * @param bytes 072 * The bytes value of the long. 073 * @param start 074 * the array index where to start computing the long 075 * @param end 076 * the array index exclusive where to end computing the long 077 * @return the long representation of the read bytes. 078 * @throws ArrayIndexOutOfBoundsException 079 * if the bytes array length is less than end. 080 */ 081 public static long toLong(byte[] bytes, int start, int end) 082 throws ArrayIndexOutOfBoundsException 083 { 084 long v = 0; 085 for (int i = start; i < end; i++) 086 { 087 v <<= 8; 088 v |= bytes[i] & 0xFF; 089 } 090 return v; 091 } 092 093 /** 094 * Decode an entry ID count from its database representation. 095 * 096 * @param bytes The database value of the entry ID count. 097 * @return The entry ID count. 098 * Cannot be negative if encoded with #entryIDUndefinedSizeToDatabase(long) 099 * @see #entryIDUndefinedSizeToDatabase(long) 100 */ 101 public static long entryIDUndefinedSizeFromDatabase(byte[] bytes) 102 { 103 if(bytes == null) 104 { 105 return 0; 106 } 107 108 if(bytes.length == 8) 109 { 110 long v = 0; 111 v |= bytes[0] & 0x7F; 112 for (int i = 1; i < 8; i++) 113 { 114 v <<= 8; 115 v |= bytes[i] & 0xFF; 116 } 117 return v; 118 } 119 return Long.MAX_VALUE; 120 } 121 122 /** 123 * Decode an array of entry ID values from its database representation. 124 * 125 * @param bytes The raw database value, null if there is no value and 126 * hence no entry IDs. Note that this method will throw an 127 * ArrayIndexOutOfBoundsException if the bytes array length is 128 * not a multiple of 8. 129 * @return An array of entry ID values. 130 * @see #entryIDListToDatabase(long[]) 131 */ 132 public static long[] entryIDListFromDatabase(byte[] bytes) 133 { 134 byte[] decodedBytes = bytes; 135 136 int count = decodedBytes.length / 8; 137 long[] entryIDList = new long[count]; 138 for (int pos = 0, i = 0; i < count; i++) 139 { 140 long v = 0; 141 v |= (decodedBytes[pos++] & 0xFFL) << 56; 142 v |= (decodedBytes[pos++] & 0xFFL) << 48; 143 v |= (decodedBytes[pos++] & 0xFFL) << 40; 144 v |= (decodedBytes[pos++] & 0xFFL) << 32; 145 v |= (decodedBytes[pos++] & 0xFFL) << 24; 146 v |= (decodedBytes[pos++] & 0xFFL) << 16; 147 v |= (decodedBytes[pos++] & 0xFFL) << 8; 148 v |= decodedBytes[pos++] & 0xFFL; 149 entryIDList[i] = v; 150 } 151 152 return entryIDList; 153 } 154 155 /** 156 * Decode a integer array using the specified byte array read from DB. 157 * 158 * @param bytes The byte array. 159 * @return An integer array. 160 */ 161 public static int[] intArrayFromDatabaseBytes(byte[] bytes) { 162 byte[] decodedBytes = bytes; 163 164 int count = decodedBytes.length / 8; 165 int[] entryIDList = new int[count]; 166 for (int pos = 0, i = 0; i < count; i++) { 167 int v = 0; 168 pos +=4; 169 v |= (decodedBytes[pos++] & 0xFFL) << 24; 170 v |= (decodedBytes[pos++] & 0xFFL) << 16; 171 v |= (decodedBytes[pos++] & 0xFFL) << 8; 172 v |= decodedBytes[pos++] & 0xFFL; 173 entryIDList[i] = v; 174 } 175 176 return entryIDList; 177 } 178 179 /** 180 * Encode an entry ID value to its database representation. 181 * 182 * @param id The entry ID value to be encoded. 183 * @return The encoded database value of the entry ID. 184 * @see #entryIDFromDatabase(byte[]) 185 */ 186 public static byte[] entryIDToDatabase(long id) 187 { 188 byte[] bytes = new byte[8]; 189 long v = id; 190 for (int i = 7; i >= 0; i--) 191 { 192 bytes[i] = (byte) (v & 0xFF); 193 v >>>= 8; 194 } 195 return bytes; 196 } 197 198 /** 199 * Encode an entry ID set count to its database representation. 200 * 201 * @param count The entry ID set count to be encoded. 202 * @return The encoded database value of the entry ID set count. 203 * @see #entryIDUndefinedSizeFromDatabase(byte[]) 204 */ 205 public static byte[] entryIDUndefinedSizeToDatabase(long count) 206 { 207 byte[] bytes = new byte[8]; 208 long v = count; 209 for (int i = 7; i >= 1; i--) 210 { 211 bytes[i] = (byte) (v & 0xFF); 212 v >>>= 8; 213 } 214 bytes[0] = (byte) ((v | 0x80) & 0xFF); 215 return bytes; 216 } 217 218 /** 219 * Encode an array of entry ID values to its database representation. 220 * 221 * @param entryIDArray An array of entry ID values. 222 * @return The encoded database value. 223 * @see #entryIDListFromDatabase(byte[]) 224 */ 225 public static byte[] entryIDListToDatabase(long[] entryIDArray) 226 { 227 if (entryIDArray.length == 0) 228 { 229 // Zero values 230 return null; 231 } 232 233 byte[] bytes = new byte[8*entryIDArray.length]; 234 for (int pos = 0, i = 0; i < entryIDArray.length; i++) 235 { 236 long v = entryIDArray[i]; 237 bytes[pos++] = (byte) ((v >>> 56) & 0xFF); 238 bytes[pos++] = (byte) ((v >>> 48) & 0xFF); 239 bytes[pos++] = (byte) ((v >>> 40) & 0xFF); 240 bytes[pos++] = (byte) ((v >>> 32) & 0xFF); 241 bytes[pos++] = (byte) ((v >>> 24) & 0xFF); 242 bytes[pos++] = (byte) ((v >>> 16) & 0xFF); 243 bytes[pos++] = (byte) ((v >>> 8) & 0xFF); 244 bytes[pos++] = (byte) (v & 0xFF); 245 } 246 247 return bytes; 248 } 249 250 /** 251 * Find the length of bytes that represents the superior DN of the given 252 * DN key. The superior DN is represented by the initial bytes of the DN key. 253 * 254 * @param dnKey The database key value of the DN. 255 * @return The length of the superior DN or -1 if the given dn is the 256 * root DN or 0 if the superior DN is removed. 257 */ 258 public static int findDNKeyParent(byte[] dnKey) 259 { 260 return findDNKeyParent(dnKey, 0, dnKey.length); 261 } 262 263 /** 264 * Find the length of bytes that represents the superior DN of the given 265 * DN key. The superior DN is represented by the initial bytes of the DN key. 266 * 267 * @param dnKey The database key value of the DN. 268 * @param offset Starting position in the database key data. 269 * @param length The length of the database key data. 270 * @return The length of the superior DN or -1 if the given dn is the 271 * root DN or 0 if the superior DN is removed. 272 */ 273 public static int findDNKeyParent(byte[] dnKey, int offset, int length) 274 { 275 if (length == 0) 276 { 277 // This is the root or base DN 278 return -1; 279 } 280 281 // We will walk backwards through the buffer and 282 // find the first unescaped NORMALIZED_RDN_SEPARATOR 283 for (int i = offset+length - 1; i >= offset; i--) 284 { 285 if (dnKey[i] == DN.NORMALIZED_RDN_SEPARATOR && i-1 >= offset && dnKey[i-1] != DN.NORMALIZED_ESC_BYTE) 286 { 287 return i; 288 } 289 } 290 return offset; 291 } 292 293 /** 294 * Create a DN database key from an entry DN. 295 * 296 * @param dn The entry DN. 297 * @param prefixRDNs The number of prefix RDNs to remove from the encoded 298 * representation. 299 * @return A DatabaseEntry containing the key. 300 */ 301 public static byte[] dnToDNKey(DN dn, int prefixRDNs) 302 { 303 ByteStringBuilder builder = new ByteStringBuilder(); 304 int startSize = dn.size() - prefixRDNs - 1; 305 for (int i = startSize; i >= 0; i--) 306 { 307 builder.append(DN.NORMALIZED_RDN_SEPARATOR); 308 dn.getRDN(i).toNormalizedByteString(builder); 309 } 310 311 return builder.toByteArray(); 312 } 313 314 315}