1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: DatabaseEntry.java,v 12.13 2008/04/02 13:43:38 bschmeck Exp $ 7 */ 8 9package com.sleepycat.db; 10 11import com.sleepycat.db.internal.DbConstants; 12import com.sleepycat.db.internal.DbUtil; 13 14import java.nio.ByteBuffer; 15import java.lang.IllegalArgumentException; 16 17/** 18Encodes database key and data items as a byte array. 19<p> 20Storage and retrieval for the {@link com.sleepycat.db.Database Database} and {@link com.sleepycat.db.Cursor Cursor} methods 21are based on key/data pairs. Both key and data items are represented by 22DatabaseEntry objects. Key and data byte arrays may refer to arrays of zero 23length up to arrays of essentially unlimited length. 24<p> 25The DatabaseEntry class provides simple access to an underlying object whose 26elements can be examined or changed. DatabaseEntry objects can be 27subclassed, providing a way to associate with it additional data or 28references to other structures. 29<p> 30Access to DatabaseEntry objects is not re-entrant. In particular, if 31multiple threads simultaneously access the same DatabaseEntry object using 32{@link com.sleepycat.db.Database Database} or {@link com.sleepycat.db.Cursor Cursor} methods, the results are undefined. 33<p> 34DatabaseEntry objects may be used in conjunction with the object mapping 35support provided in the {@link com.sleepycat.bind} package. 36<p> 37<h3>Input and Output Parameters</h3> 38<p> 39DatabaseEntry objects are used for both input data (when writing to a 40database or specifying a search parameter) and output data (when reading 41from a database). For certain methods, one parameter may be an input 42parameter and another may be an output parameter. For example, the 43{@link Database#get} method has an input key parameter and an output 44data parameter. The documentation for each method describes whether its 45parameters are input or output parameters. 46<p> 47For DatabaseEntry input parameters, the caller is responsible for 48initializing the data array of the DatabaseEntry. For DatabaseEntry 49output parameters, the method called will initialize the data array. 50<p> 51For DatabaseEntry output parameters, by default the method called will 52reuse the byte array in the DatabaseEntry, if the data returned fits in 53the byte array. This behavior can be configured with {@link 54#setReuseBuffer} or {@link #setUserBuffer}. If an entry is configured to 55reuse the byte array (the default behavior), the length of the underlying 56byte array should not be used to determine the amount of data returned each 57time the entry is used as an output parameter, rather the {@link #getSize} 58call should be used. If an entry is configured to not reuse the byte array, 59a new array is allocated each time the entry is used as an output parameter, 60 so 61the application can safely keep a reference to the byte array returned 62by {@link #getData} without danger that the array will be overwritten in 63a subsequent call. 64<p> 65<h3>Offset and Size Properties</h3> 66<p> 67By default the Offset property is zero and the Size property is the length 68of the byte array. However, to allow for optimizations involving the 69partial use of a byte array, the Offset and Size may be set to non-default 70values. 71<p> 72For DatabaseEntry output parameters, the Size will always be set to the 73length of the returned data and 74the Offset will always be set to zero. 75<p> 76However, for DatabaseEntry input parameters the Offset and Size are set to 77non-default values by the built-in tuple and serial bindings. For example, 78with a tuple or serial binding the byte array is grown dynamically as data 79is output, and the Size is set to the number of bytes actually used. For a 80serial binding, the Offset is set to a non-zero value in order to implement 81an optimization having to do with the serialization stream header. 82<p> 83Therefore, for output DatabaseEntry parameters the application can assume 84that the Offset is zero and the Size is the length of the byte array. 85However, for input DatabaseEntry parameters the application should not make 86this assumption. In general, it is safest for the application to always 87honor the Size and Offset properties, rather than assuming they have default 88values. 89<p> 90<h3>Partial Offset and Length Properties</h3> 91<p> 92By default the specified data (byte array, offset and size) corresponds to 93the full stored key or data item. Optionally, the Partial property can be 94set to true, and the PartialOffset and PartialLength properties are used to 95specify the portion of the key or data item to be read or written. For 96details, see the {@link #setPartial(int,int,boolean)} method. 97<p> 98Note that the Partial properties are set only by the caller. They will 99never be set by a Database or Cursor method, nor will they every be set by 100bindings. Therefore, the application can assume that the Partial properties 101are not set, unless the application itself sets them explicitly. 102*/ 103public class DatabaseEntry { 104 105 /* Currently, JE stores all data records as byte array */ 106 /* package */ byte[] data; 107 /* package */ ByteBuffer data_nio; 108 /* package */ int dlen = 0; 109 /* package */ int doff = 0; 110 /* package */ int flags = 0; 111 /* package */ int offset = 0; 112 /* package */ int size = 0; 113 /* package */ int ulen = 0; 114 115 /* 116 * IGNORE is used to avoid returning data that is not needed. It may not 117 * be used as the key DBT in a put since the PARTIAL flag is not allowed; 118 * use UNUSED for that instead. 119 */ 120 121 /* package */ 122 static final DatabaseEntry IGNORE = new DatabaseEntry(); 123 static { 124 IGNORE.setUserBuffer(0, true); 125 IGNORE.setPartial(0, 0, true); // dlen == 0, so no data ever returned 126 } 127 /* package */ 128 static final DatabaseEntry UNUSED = new DatabaseEntry(); 129 130 /* package */ static final int INT32SZ = 4; 131 132 /* 133 * Constructors 134 */ 135 136 /** 137 Construct a DatabaseEntry with null data. The offset and size are set to 138 zero. 139 */ 140 public DatabaseEntry() { 141 } 142 143 /** 144 Construct a DatabaseEntry with a given byte array. The offset is 145 set to zero; the size is set to the length of the array, or to zero if 146 null is passed. 147 <p> 148 @param data 149 Byte array wrapped by the DatabaseEntry. 150 */ 151 public DatabaseEntry(final byte[] data) { 152 this.data = data; 153 if (data != null) { 154 this.size = data.length; 155 } 156 this.data_nio = null; 157 } 158 159 /** 160 Constructs a DatabaseEntry with a given byte array, offset and size. 161 <p> 162 @param data 163 Byte array wrapped by the DatabaseEntry. 164 @param offset 165 Offset in the first byte in the byte array to be included. 166 @param size 167 Number of bytes in the byte array to be included. 168 */ 169 public DatabaseEntry(final byte[] data, final int offset, final int size) { 170 this.data = data; 171 this.offset = offset; 172 this.size = size; 173 this.data_nio = null; 174 } 175 176 /** 177 Construct a DatabaseEntry with a given native I/O buffer. 178 <p> 179 @param data 180 NIO byte buffer wrapped by the DatabaseEntry. 181 */ 182 public DatabaseEntry(ByteBuffer data) { 183 if (data.isDirect()) { 184 this.data_nio = data; 185 if (data != null) { 186 this.size = this.ulen = data.limit(); 187 setUserBuffer(data.limit(), true); 188 } 189 } else if (data.hasArray()) { 190 /* The same as calling the DatabaseEntry(byte[]) constructor. */ 191 this.data = data.array(); 192 if (this.data != null) { 193 this.size = this.data.length; 194 } 195 this.data_nio = null; 196 } else { 197 throw new IllegalArgumentException("Attempting to use a " + 198 "non-direct ByteBuffer without a backing byte array."); 199 } 200 } 201 202 /* 203 * Accessors 204 */ 205 206 /** 207 Return the byte array. 208 <p> 209 For a DatabaseEntry that is used as an output parameter, the byte 210 array will always be a newly allocated array. The byte array specified 211 by the caller will not be used and may be null. 212 <p> 213 @return 214 The byte array. 215 */ 216 public byte[] getData() { 217 return data; 218 } 219 220 /** 221 Return the java.nio.ByteBuffer. 222 <p> 223 Used to access the underlying data when the DatabaseEntry is 224 configured to utilize a java.nio.ByteBuffer. 225 <p> 226 @return 227 The underlying java.nio.ByteBuffer. 228 */ 229 public ByteBuffer getDataNIO() { 230 return data_nio; 231 } 232 233 /** 234 Sets the byte array, offset and size. 235 <p> 236 @param data 237 Byte array wrapped by the DatabaseEntry. 238 @param offset 239 Offset in the first byte in the byte array to be included. 240 @param size 241 Number of bytes in the byte array to be included. 242 */ 243 public void setData(final byte[] data, final int offset, final int size) { 244 this.data = data; 245 this.offset = offset; 246 this.size = size; 247 248 this.data_nio = null; 249 } 250 251 /** 252 Sets the byte array. The offset is set to zero; the size is set to the 253 length of the array, or to zero if null is passed. 254 <p> 255 @param data 256 Byte array wrapped by the DatabaseEntry. 257 */ 258 public void setData(final byte[] data) { 259 setData(data, 0, (data == null) ? 0 : data.length); 260 } 261 262 /** 263 * 264 Sets the java.nio.ByteBuffer. The offset is set to zero; the size 265 is set to the length of the ByteBuffer, or to zero if null is passed. 266 <p> 267 @param data 268 java.nio.ByteBuffer wrapped by the DatabaseEntry. 269 @param offset 270 int offset into the ByteBuffer where the DatabaseEntry data begins. 271 @param size 272 int size of the ByteBuffer available. 273 */ 274 public void setDataNIO(final ByteBuffer data, final int offset, final int size) { 275 this.data_nio = data; 276 this.offset = offset; 277 this.size = this.ulen = size; 278 279 this.data = null; 280 flags = 0; 281 setUserBuffer(size, true); 282 } 283 284 /** 285 * 286 Sets the java.nio.ByteBuffer. The offset is set to zero; the size 287 is set to the length of the ByteBuffer, or to zero if null is passed. 288 <p> 289 @param data 290 java.nio.ByteBuffer wrapped by the DatabaseEntry. 291 */ 292 public void setDataNIO(final ByteBuffer data) { 293 setDataNIO(data, 0, (data == null) ? 0 : data.capacity()); 294 } 295 296 /** 297 * This method is called just before performing a get operation. It is 298 * overridden by Multiple*Entry classes to return the flags used for bulk 299 * retrieval. If non-zero is returned, this method should reset the entry 300 * position so that the next set of key/data can be returned. 301 */ 302 /* package */ 303 int getMultiFlag() { 304 return 0; 305 } 306 307 /** 308 Return the byte offset into the data array. 309 <p> 310 For a DatabaseEntry that is used as an output parameter, the offset 311 will always be zero. 312 <p> 313 @return 314 Offset in the first byte in the byte array to be included. 315 */ 316 public int getOffset() { 317 return offset; 318 } 319 320 /** 321 Set the byte offset into the data array. 322 <p> 323 @param offset 324 Offset in the first byte in the byte array to be included. 325 */ 326 public void setOffset(final int offset) { 327 this.offset = offset; 328 } 329 330 /** 331 Return the byte length of the partial record being read or written by 332 the application, in bytes. 333 <p> 334 Note that the Partial properties are set only by the caller. They 335 will never be set by a Database or Cursor method. 336 <p> 337 @return 338 The byte length of the partial record being read or written by the 339 application, in bytes. 340 <p> 341 @see #setPartial(int,int,boolean) 342 */ 343 public int getPartialLength() { 344 return dlen; 345 } 346 347 /** 348 Return the offset of the partial record being read or written by the 349 application, in bytes. 350 <p> 351 Note that the Partial properties are set only by the caller. They 352 will never be set by a Database or Cursor method. 353 <p> 354 @return 355 The offset of the partial record being read or written by the 356 application, in bytes. 357 <p> 358 @see #setPartial(int,int,boolean) 359 */ 360 public int getPartialOffset() { 361 return doff; 362 } 363 364 /** 365 Return whether this DatabaseEntry is configured to read or write partial 366 records. 367 <p> 368 Note that the Partial properties are set only by the caller. They 369 will never be set by a Database or Cursor method. 370 <p> 371 @return 372 Whether this DatabaseEntry is configured to read or write partial 373 records. 374 <p> 375 @see #setPartial(int,int,boolean) 376 */ 377 public boolean getPartial() { 378 return (flags & DbConstants.DB_DBT_PARTIAL) != 0; 379 } 380 381 /** 382 Set the offset of the partial record being read or written by the 383 application, in bytes. 384 <p> 385 Note that the Partial properties are set only by the caller. They 386 will never be set by a Database or Cursor method. 387 <p> 388 @param doff 389 The offset of the partial record being read or written by the 390 application, in bytes. 391 <p> 392 @see #setPartial(int,int,boolean) 393 */ 394 public void setPartialOffset(final int doff) { 395 this.doff = doff; 396 } 397 398 /** 399 Set the byte length of the partial record being read or written by 400 the application, in bytes. 401 <p> 402 Note that the Partial properties are set only by the caller. They 403 will never be set by a Database or Cursor method. 404 <p> 405 @param dlen 406 The byte length of the partial record being read or written by the 407 <p> 408 @see #setPartial(int,int,boolean) 409 application, in bytes. 410 */ 411 public void setPartialLength(final int dlen) { 412 this.dlen = dlen; 413 } 414 415 /** 416 Configure this DatabaseEntry to read or write partial records. 417 <p> 418 Note that the Partial properties are set only by the caller. They 419 will never be set by a Database or Cursor method. 420 <p> 421 @param partial 422 Whether this DatabaseEntry is configured to read or write partial 423 records. 424 <p> 425 @see #setPartial(int,int,boolean) 426 */ 427 public void setPartial(final boolean partial) { 428 if (partial) 429 flags |= DbConstants.DB_DBT_PARTIAL; 430 else 431 flags &= ~DbConstants.DB_DBT_PARTIAL; 432 } 433 434 /** 435 Configures this DatabaseEntry to read or write partial records. 436 <p> 437 Do partial retrieval or storage of an item. If the calling 438 application is doing a retrieval, length bytes specified by 439 <tt>dlen</tt>, starting at the offset set by <tt>doff</tt> bytes from 440 the beginning of the retrieved data record are returned as if they 441 comprised the entire record. If any or all of the specified bytes do 442 not exist in the record, the get is successful, and any existing bytes 443 are returned. 444 <p> 445 For example, if the data portion of a retrieved record was 100 bytes, 446 and a partial retrieval was done using a DatabaseEntry having a partial 447 length of 20 and a partial offset of 85, the retrieval would succeed and 448 the retrieved data would be the last 15 bytes of the record. 449 <p> 450 If the calling application is storing an item, length bytes specified 451 by <tt>dlen</tt>, starting at the offset set by <tt>doff</tt> 452 bytes from the beginning of the specified key's data item are replaced 453 by the data specified by the DatabaseEntry. If the partial length is 454 smaller than the data, the record will grow; if the partial length is 455 larger than the data, the record will shrink. If the specified bytes do 456 not exist, the record will be extended using nul bytes as necessary, and 457 the store will succeed. 458 <p> 459 It is an error to specify a partial key when performing a put 460 operation of any kind. 461 <p> 462 It is an error to attempt a partial store using the {@link com.sleepycat.db.Database#put Database.put} method in a database that supports duplicate records. Partial 463 stores in databases supporting duplicate records must be done using a 464 cursor method. 465 <p> 466 Note that the Partial properties are set only by the caller. They 467 will never be set by a Database or Cursor method. 468 <p> 469 @param doff 470 The offset of the partial record being read or written by the 471 application, in bytes. 472 <p> 473 @param dlen 474 The byte length of the partial record being read or written by the 475 application, in bytes. 476 <p> 477 @param partial 478 Whether this DatabaseEntry is configured to read or write partial 479 records. 480 */ 481 public void setPartial(final int doff, 482 final int dlen, 483 final boolean partial) { 484 setPartialOffset(doff); 485 setPartialLength(dlen); 486 setPartial(partial); 487 } 488 489 /** 490Return the record number encoded in this entry's buffer. 491<p> 492This method may be called at any time during the life of the application. 493<p> 494@return 495The record number encoded in this entry's buffer. 496 * 497 @return the decoded record number. 498 */ 499 public int getRecordNumber() { 500 return DbUtil.array2int(data, offset); 501 } 502 503 /** 504 Initialize the entry from a logical record number. Record numbers 505 are integer keys starting at 1. When this method is called the data, 506 size and offset fields are implicitly set to hold a byte array 507 representation of the integer key. 508 * 509 @param recno the record number to be encoded 510 */ 511 public void setRecordNumber(final int recno) { 512 if (data == null || data.length < INT32SZ) { 513 data = new byte[INT32SZ]; 514 size = INT32SZ; 515 ulen = 0; 516 offset = 0; 517 } 518 DbUtil.int2array(recno, data, 0); 519 } 520 521 /** 522Return true if the whether the entry is configured to reuse the buffer. 523<p> 524This method may be called at any time during the life of the application. 525<p> 526@return 527True if the whether the entry is configured to reuse the buffer. 528 */ 529 public boolean getReuseBuffer() { 530 return 0 == 531 (flags & (DbConstants.DB_DBT_MALLOC | DbConstants.DB_DBT_USERMEM)); 532 } 533 534 /** 535 Configures the entry to try to reuse the buffer before allocating a new 536 one. 537 <p> 538 @param reuse 539 whether to reuse the buffer 540 */ 541 public void setReuseBuffer(boolean reuse) { 542 if (data_nio != null) 543 throw new IllegalArgumentException("Can only set the reuse flag on" + 544 " DatabaseEntry classes with a underlying byte[] data"); 545 546 if (reuse) 547 flags &= ~(DbConstants.DB_DBT_MALLOC | DbConstants.DB_DBT_USERMEM); 548 else { 549 flags &= ~DbConstants.DB_DBT_USERMEM; 550 flags |= DbConstants.DB_DBT_MALLOC; 551 } 552 } 553 554 /** 555 Return the byte size of the data array. 556 <p> 557 For a DatabaseEntry that is used as an output parameter, the size 558 will always be the length of the data array. 559 <p> 560 @return 561 Number of bytes in the byte array to be included. 562 */ 563 public int getSize() { 564 return size; 565 } 566 567 /** 568 Set the byte size of the data array. 569 <p> 570 @param size 571 Number of bytes in the byte array to be included. 572 */ 573 public void setSize(final int size) { 574 this.size = size; 575 } 576 577 /** 578Return true if the whether the buffer in this entry is owned by the 579 application. 580<p> 581This method may be called at any time during the life of the application. 582<p> 583@return 584True if the whether the buffer in this entry is owned by the 585 application. 586 */ 587 public boolean getUserBuffer() { 588 return (flags & DbConstants.DB_DBT_USERMEM) != 0; 589 } 590 591 /** 592Return the length of the application's buffer. 593<p> 594This method may be called at any time during the life of the application. 595<p> 596@return 597The length of the application's buffer. 598 */ 599 public int getUserBufferLength() { 600 return ulen; 601 } 602 603 /** 604 Configures the entry with an application-owned buffer. 605 <p> 606 The <code>data</code> field of the entry must refer to a buffer that is 607 at least <code>length</code> bytes in length. 608 <p> 609 If the length of the requested item is less than or equal to that number 610 of bytes, the item is copied into the memory to which the 611 <code>data</code> field refers. Otherwise, the <code>size</code> field 612 is set to the length needed for the requested item, and a 613 {@link com.sleepycat.db.MemoryException MemoryException} is thrown. 614 <p> 615 Applications can determine the length of a record by setting 616 <code>length</code> to 0 and calling {@link com.sleepycat.db.DatabaseEntry#getSize DatabaseEntry.getSize} 617 on the return value. 618 <p> 619 @param length 620 the length of the buffer 621 <p> 622 @param usermem 623 whether the buffer is owned by the application 624 */ 625 public void setUserBuffer(final int length, final boolean usermem) { 626 627 this.ulen = length; 628 if (usermem) { 629 flags &= ~DbConstants.DB_DBT_MALLOC; 630 flags |= DbConstants.DB_DBT_USERMEM; 631 } else 632 flags &= ~DbConstants.DB_DBT_USERMEM; 633 } 634 635 /** 636 * Compares the data of two entries for byte-by-byte equality. 637 * 638 * <p>In either entry, if the offset is non-zero or the size is not equal 639 * to the data array length, then only the data bounded by these values is 640 * compared. The data array length and offset need not be the same in both 641 * entries for them to be considered equal.</p> 642 * 643 * <p>If the data array is null in one entry, then to be considered equal 644 * both entries must have a null data array.</p> 645 * 646 * <p>If the partial property is set in either entry, then to be considered 647 * equal both entries must have the same partial properties: partial, 648 * partialOffset and partialLength. 649 */ 650 public boolean equals(Object o) { 651 if (!(o instanceof DatabaseEntry)) { 652 return false; 653 } 654 DatabaseEntry e = (DatabaseEntry) o; 655 if (getPartial() || e.getPartial()) { 656 if (getPartial() != e.getPartial() || 657 dlen != e.dlen || 658 doff != e.doff) { 659 return false; 660 } 661 } 662 if (data == null && e.data == null) { 663 return true; 664 } 665 if (data == null || e.data == null) { 666 return false; 667 } 668 if (size != e.size) { 669 return false; 670 } 671 for (int i = 0; i < size; i += 1) { 672 if (data[offset + i] != e.data[e.offset + i]) { 673 return false; 674 } 675 } 676 return true; 677 } 678 679 /** 680 * Returns a hash code based on the data value. 681 */ 682 public int hashCode() { 683 int hash = 0; 684 if (data != null) { 685 for (int i = 0; i < size; i += 1) { 686 hash += data[offset + i]; 687 } 688 } 689 return hash; 690 } 691} 692