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