| /*- |
| * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved. |
| * |
| * This file was distributed by Oracle as part of a version of Oracle Berkeley |
| * DB Java Edition made available at: |
| * |
| * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html |
| * |
| * Please see the LICENSE file included in the top-level directory of the |
| * appropriate version of Oracle Berkeley DB Java Edition for a copy of the |
| * license and additional information. |
| */ |
| |
| package com.sleepycat.je; |
| |
| import java.io.Serializable; |
| |
| import com.sleepycat.je.tree.Key; |
| import com.sleepycat.util.keyrange.KeyRange; |
| |
| /** |
| * Encodes database key and data items as a byte array. |
| * |
| * <p>Storage and retrieval for the {@link com.sleepycat.je.Database Database} |
| * and {@link com.sleepycat.je.Cursor Cursor} methods are based on key/data |
| * pairs. Both key and data items are represented by DatabaseEntry objects. |
| * Key and data byte arrays may refer to arrays of zero length up to arrays of |
| * essentially unlimited length.</p> |
| * |
| * <p>The DatabaseEntry class provides simple access to an underlying object |
| * whose elements can be examined or changed. DatabaseEntry objects can be |
| * subclassed, providing a way to associate with it additional data or |
| * references to other structures.</p> |
| * |
| * <p>Access to DatabaseEntry objects is not re-entrant. In particular, if |
| * multiple threads simultaneously access the same DatabaseEntry object using |
| * {@link com.sleepycat.je.Database Database} or {@link com.sleepycat.je.Cursor |
| * Cursor} methods, the results are undefined.</p> |
| * |
| * <p>DatabaseEntry objects may be used in conjunction with the object mapping |
| * support provided in the {@link com.sleepycat.bind} package.</p> |
| * |
| * <h3><a name="params">Input and Output Parameters</a></h3> |
| * |
| * <p>DatabaseEntry objects are used for both input values (for example, when |
| * writing to a database or specifying a search parameter) and output values |
| * (for example, when reading from a database). For every CRUD method |
| * ({@code get}, {@code put}, etc), each of the method's DatabaseEntry |
| * parameters ({@code key}, {@code data}, etc) may be input or output |
| * parameters, and this is specified by the method's documentation.</p> |
| * |
| * <h4><a name="inParam">Input Parameters</a></h4> |
| * |
| * <p>An input parameter is required by the JE method. The parameter may not be |
| * null, and the caller is also responsible for initializing the data of the |
| * DatabaseEntry to a non-null byte array.</p> |
| * |
| * <p>Input parameters normally may not be {@link #setPartial(int,int,boolean) |
| * partial}. However, this is allowed under certain circumstances, namely |
| * the {@link Cursor#putCurrent} method allows specifying a partial data |
| * parameter in order to update only part of the record's data value. Input |
| * parameters are NOT allowed to be partial unless this is explicitly stated in |
| * the method documentation.</p> |
| * |
| * <p>Although an input parameter is always used for input, in some cases it |
| * may be also used for output. For example, the {@link |
| * Cursor#getSearchKeyRange} method is passed a key parameter that is used as |
| * input, but since a record with a different key (greater or equal to the key |
| * given) may be found, the key parameter is also used to return the key |
| * that was found. Such parameters are documented as "input/output" |
| * parameters.</p> |
| * |
| * <p>Another example is when a custom key comparator is used and a key |
| * parameter is passed to a search method. The input parameter may match a |
| * record's key even if the bytes are not equal, and the key of the record |
| * found will be returned via the parameter. The same thing is true of data (or |
| * primary key) parameters when a custom duplicate comparator is used. Because |
| * of this, all input parameters of "get" methods can potentially be used for |
| * output, however, they are not explicitly documented to be input/output |
| * parameters.</p> |
| * |
| * <h4><a name="outParam">Output Parameters</a></h4> |
| * |
| * <p>An output parameter is not required by the JE method. It is used to |
| * optionally return a value to the caller. Null may be passed for the |
| * parameter if no returned value is needed. Passing null is a common way to |
| * optimize read operations when only the record's key, and not the record's |
| * data, is required. By passing null for the data parameter, a read from |
| * disk can be avoided when the data is not already cached. In addition, all |
| * output parameters may be {@link #setPartial(int,int,boolean) partial} to |
| * allow only returning a part of the data byte array. See <a |
| * href="Cursor.html#partialEntry">Using Null and Partial DatabaseEntry |
| * Parameters</a> for more information.</p> |
| * |
| * <p>For output parameters, the byte array specified by the caller will not be |
| * used and may be null. The JE method will will always allocate a new byte |
| * array. Therefore, after calling a method that returns output parameters, |
| * the application can safely keep a reference to the byte array returned by |
| * {@link #getData} without danger that the array will be overwritten in a |
| * subsequent call.</p> |
| * |
| * <p>Historical note: Prior to JE 7.0, null could not be passed for output |
| * parameters. Instead, {@code DatabaseEntry.setPartial(0, 0, true)} was called |
| * for a data parameter to avoid reading the record's data. Now, null can be |
| * passed instead.</p> |
| * |
| * <h3>Offset and Size Properties</h3> |
| * |
| * <p>By default the Offset property is zero and the Size property is the |
| * length of the byte array. However, to allow for optimizations involving the |
| * partial use of a byte array, the Offset and Size may be set to non-default |
| * values.</p> |
| * |
| * <p>For output parameters, the Size will always be set to the length of the |
| * byte array and the Offset will always be set to zero.</p> |
| * |
| * <p>However, for input parameters the Offset and Size are set to non-default |
| * values by the built-in tuple and serial bindings. For example, with a tuple |
| * or serial binding the byte array is grown dynamically as data is output, and |
| * the Size is set to the number of bytes actually used. For a serial binding, |
| * the Offset is set to a non-zero value in order to implement an optimization |
| * having to do with the serialization stream header.</p> |
| * |
| * <p>WARNING: In callbacks that are passed DatabaseEntry parameters, the |
| * application should always honor the Size and Offset properties, rather than |
| * assuming they have default values.</p> |
| */ |
| public class DatabaseEntry implements Serializable { |
| private static final long serialVersionUID = 1L; |
| |
| /* Currently, JE stores all data records as byte array */ |
| private byte[] data; |
| private int dlen = 0; |
| private int doff = 0; |
| private int offset = 0; |
| private int size = 0; |
| private boolean partial = false; |
| |
| /* FindBugs - ignore not "final" since a user can set this. */ |
| /** @hidden |
| * The maximum number of bytes to show when toString() is called. |
| */ |
| public static int MAX_DUMP_BYTES = 100; |
| |
| /** |
| * Returns all the attributes of the database entry in text form, including |
| * the underlying data. The maximum number of bytes that will be formatted |
| * is taken from the static variable DatabaseEntry.MAX_DUMP_BYTES, which |
| * defaults to 100. MAX_DUMP_BYTES may be changed by an application if it |
| * wishes to cause more bytes to be formatted. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder("<DatabaseEntry"); |
| if (partial) { |
| sb.append(" partial=\"true"); |
| sb.append("\" doff=\"").append(doff); |
| sb.append("\" dlen=\"").append(dlen); |
| sb.append("\""); |
| } |
| sb.append(" offset=\"").append(offset); |
| sb.append("\" size=\"").append(size); |
| sb.append("\" data=\"").append(dumpData()); |
| if ((size - 1) > MAX_DUMP_BYTES) { |
| sb.append(" ... ").append((size - MAX_DUMP_BYTES) + |
| " bytes not shown "); |
| } |
| sb.append("\"/>"); |
| return sb.toString(); |
| } |
| |
| /* |
| * Constructors |
| */ |
| |
| /** |
| * Constructs a DatabaseEntry with null data. The offset and size are set |
| * to zero. |
| */ |
| public DatabaseEntry() { |
| } |
| |
| /** |
| * Constructs a DatabaseEntry with a given byte array. The offset is set |
| * to zero; the size is set to the length of the array, or to zero if null |
| * is passed. |
| * |
| * @param data Byte array wrapped by the DatabaseEntry. |
| */ |
| public DatabaseEntry(byte[] data) { |
| this.data = data; |
| if (data != null) { |
| this.size = data.length; |
| } |
| } |
| |
| /** |
| * Constructs a DatabaseEntry with a given byte array, offset and size. |
| * |
| * @param data Byte array wrapped by the DatabaseEntry. |
| * |
| * @param offset Offset in the first byte in the byte array to be included. |
| * |
| * @param size Number of bytes in the byte array to be included. |
| */ |
| public DatabaseEntry(byte[] data, int offset, int size) { |
| this.data = data; |
| this.offset = offset; |
| this.size = size; |
| } |
| |
| /* |
| * Accessors |
| */ |
| |
| /** |
| * Returns the byte array. |
| * |
| * <p>For a DatabaseEntry that is used as an output parameter, the byte |
| * array will always be a newly allocated array. The byte array specified |
| * by the caller will not be used and may be null.</p> |
| * |
| * @return The byte array. |
| */ |
| public byte[] getData() { |
| return data; |
| } |
| |
| /** |
| * Sets the byte array. The offset is set to zero; the size is set to the |
| * length of the array, or to zero if null is passed. |
| * |
| * @param data Byte array wrapped by the DatabaseEntry. |
| */ |
| public void setData(byte[] data) { |
| this.data = data; |
| offset = 0; |
| size = (data == null) ? 0 : data.length; |
| } |
| |
| /** |
| * Sets the byte array, offset and size. |
| * |
| * @param data Byte array wrapped by the DatabaseEntry. |
| * |
| * @param offset Offset in the first byte in the byte array to be included. |
| * |
| * @param size Number of bytes in the byte array to be included. |
| */ |
| public void setData(byte[] data, int offset, int size) { |
| this.data = data; |
| this.offset = offset; |
| this.size = size; |
| } |
| |
| /** |
| * Configures this DatabaseEntry to read or write partial records. |
| * |
| * <p>By default the specified data (byte array, offset and size) |
| * corresponds to the full stored key or data item. Optionally, the |
| * Partial property can be set to true, and the PartialOffset and |
| * PartialLength properties are used to specify the portion of the key or |
| * data item to be read or written.</p> |
| * |
| * <p>Note that the Partial properties are set only by the caller. They |
| * will never be set by a Database or Cursor method, nor will they every be |
| * set by bindings. Therefore, the application can assume that the Partial |
| * properties are not set, unless the application itself sets them |
| * explicitly.</p> |
| * |
| * <p>All <a href="#outParam">output parameters</a> may be partial. If the |
| * calling application is doing a retrieval, length bytes specified by |
| * <tt>dlen</tt>, starting at the offset set by <tt>doff</tt> bytes from |
| * the beginning of the retrieved data record are returned as if they |
| * comprised the entire record. If any or all of the specified bytes do |
| * not exist in the record, the get is successful, and any existing bytes |
| * are returned.</p> |
| * |
| * <p>For example, if the data portion of a retrieved record was 100 bytes, |
| * and a partial retrieval was done using a DatabaseEntry having a partial |
| * length of 20 and a partial offset of 85, the retrieval would succeed and |
| * the retrieved data would be the last 15 bytes of the record.</p> |
| * |
| * <p>Input parameters normally may not be {@link |
| * #setPartial(int,int,boolean) partial}. However, this is allowed under |
| * certain circumstances, namely the {@link Cursor#putCurrent} method |
| * allows specifying a partial data parameter in order to update only part |
| * of the record's data value. Input parameters are NOT allowed to be |
| * partial unless this is explicitly stated in the method |
| * documentation.</p> |
| * |
| * <p>For storing an item using a partial parameter, length bytes specified |
| * by <tt>dlen</tt>, starting at the offset set by <tt>doff</tt> bytes from |
| * the beginning of the specified key's data item are replaced by the data |
| * specified by the DatabaseEntry. If the partial length is smaller than |
| * the data, the record will grow; if the partial length is larger than the |
| * data, the record will shrink. If the partial offset is greater than the |
| * length of the data, the record will be extended using zero bytes as |
| * necessary, and the store will succeed.</p> |
| * |
| * @param doff The offset of the partial record being read or written by |
| * the application, in bytes. |
| * |
| * @param dlen The byte length of the partial record being read or written |
| * by the application, in bytes. |
| * |
| * @param partial Whether this DatabaseEntry is configured to read or write |
| * partial records. |
| */ |
| public void setPartial(int doff, int dlen, boolean partial) { |
| setPartialOffset(doff); |
| setPartialLength(dlen); |
| setPartial(partial); |
| } |
| |
| /** |
| * Returns the byte length of the partial record being read or written by |
| * the application, in bytes. |
| * |
| * <p>Note that the Partial properties are set only by the caller. They |
| * will never be set by a Database or Cursor method.</p> |
| * |
| * @return The byte length of the partial record being read or written by |
| * the application, in bytes. |
| * |
| * @see #setPartial(int,int,boolean) |
| */ |
| public int getPartialLength() { |
| return dlen; |
| } |
| |
| /** |
| * Sets the byte length of the partial record being read or written by the |
| * application, in bytes. |
| * |
| * <p>Note that the Partial properties are set only by the caller. They |
| * will never be set by a Database or Cursor method.</p> |
| * |
| * @param dlen The byte length of the partial record being read or written |
| * by the |
| * |
| * @see #setPartial(int,int,boolean) |
| */ |
| public void setPartialLength(int dlen) { |
| this.dlen = dlen; |
| } |
| |
| /** |
| * Returns the offset of the partial record being read or written by the |
| * application, in bytes. |
| * |
| * <p>Note that the Partial properties are set only by the caller. They |
| * will never be set by a Database or Cursor method.</p> |
| * |
| * @return The offset of the partial record being read or written by the |
| * application, in bytes. |
| * |
| * @see #setPartial(int,int,boolean) |
| */ |
| public int getPartialOffset() { |
| return doff; |
| } |
| |
| /** |
| * Sets the offset of the partial record being read or written by the |
| * application, in bytes. |
| * |
| * <p>Note that the Partial properties are set only by the caller. They |
| * will never be set by a Database or Cursor method.</p> |
| * |
| * @param doff The offset of the partial record being read or written by |
| * the application, in bytes. |
| * |
| * @see #setPartial(int,int,boolean) |
| */ |
| public void setPartialOffset(int doff) { |
| this.doff = doff; |
| } |
| |
| /** |
| * Returns whether this DatabaseEntry is configured to read or write |
| * partial records. |
| * |
| * <p>Note that the Partial properties are set only by the caller. They |
| * will never be set by a Database or Cursor method.</p> |
| * |
| * @return Whether this DatabaseEntry is configured to read or write |
| * partial records. |
| * |
| * @see #setPartial(int,int,boolean) |
| */ |
| public boolean getPartial() { |
| return partial; |
| } |
| |
| /** |
| * Configures this DatabaseEntry to read or write partial records. |
| * |
| * <p>Note that the Partial properties are set only by the caller. They |
| * will never be set by a Database or Cursor method.</p> |
| * |
| * @param partial Whether this DatabaseEntry is configured to read or write |
| * partial records. |
| * |
| * @see #setPartial(int,int,boolean) |
| */ |
| public void setPartial(boolean partial) { |
| this.partial = partial; |
| } |
| |
| /** |
| * Returns the byte offset into the data array. |
| * |
| * <p>For a DatabaseEntry that is used as an output parameter, the offset |
| * will always be zero.</p> |
| * |
| * @return Offset in the first byte in the byte array to be included. |
| */ |
| public int getOffset() { |
| return offset; |
| } |
| |
| /** |
| * Sets the byte offset into the data array. |
| * |
| * ArrayIndexOutOfBoundsException if the data, offset, and size parameters |
| * refer to elements of the data array which do not exist. Note that this |
| * exception will not be thrown by setSize() or setOffset(), but will be |
| * thrown by varous JE methods if "this" is inconsistent and is used as an |
| * input parameter to those methods. It is the caller's responsibility to |
| * ensure that size, offset, and data.length are consistent. |
| * |
| * @param offset Offset in the first byte in the byte array to be included. |
| */ |
| public void setOffset(int offset) { |
| this.offset = offset; |
| } |
| |
| /** |
| * Returns the byte size of the data array. |
| * |
| * <p>For a DatabaseEntry that is used as an output parameter, the size |
| * will always be the length of the data array.</p> |
| * |
| * @return Number of bytes in the byte array to be included. |
| */ |
| public int getSize() { |
| return size; |
| } |
| |
| /** |
| * Sets the byte size of the data array. |
| * |
| * ArrayIndexOutOfBoundsException if the data, offset, and size parameters |
| * refer to elements of the data array which do not exist. Note that this |
| * exception will not be thrown by setSize() or setOffset(), but will be |
| * thrown by varous JE methods if "this" is inconsistent and is used as an |
| * input parameter to those methods. It is the caller's responsibility to |
| * ensure that size, offset, and data.length are consistent. |
| * |
| * @param size Number of bytes in the byte array to be included. |
| */ |
| public void setSize(int size) { |
| this.size = size; |
| } |
| |
| /** |
| * Dumps the data as a byte array, for tracing purposes |
| */ |
| String dumpData() { |
| return Key.DUMP_TYPE.dumpByteArray( |
| KeyRange.getByteArray(this, MAX_DUMP_BYTES)); |
| } |
| |
| /** |
| * Compares the data of two entries for byte-by-byte equality. |
| * |
| * <p>In either entry, if the offset is non-zero or the size is not equal |
| * to the data array length, then only the data bounded by these values is |
| * compared. The data array length and offset need not be the same in both |
| * entries for them to be considered equal.</p> |
| * |
| * <p>If the data array is null in one entry, then to be considered equal |
| * both entries must have a null data array.</p> |
| * |
| * <p>If the partial property is set in either entry, then to be considered |
| * equal both entries must have the same partial properties: partial, |
| * partialOffset and partialLength. |
| */ |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof DatabaseEntry)) { |
| return false; |
| } |
| DatabaseEntry e = (DatabaseEntry) o; |
| if (partial || e.partial) { |
| if (partial != e.partial || |
| dlen != e.dlen || |
| doff != e.doff) { |
| return false; |
| } |
| } |
| if (data == null && e.data == null) { |
| return true; |
| } |
| if (data == null || e.data == null) { |
| return false; |
| } |
| if (size != e.size) { |
| return false; |
| } |
| for (int i = 0; i < size; i += 1) { |
| if (data[offset + i] != e.data[e.offset + i]) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Returns a hash code based on the data value. |
| */ |
| @Override |
| public int hashCode() { |
| int hash = 0; |
| if (data != null) { |
| for (int i = 0; i < size; i += 1) { |
| hash += data[offset + i]; |
| } |
| } |
| return hash; |
| } |
| } |