001    /**
002     * JDBM LICENSE v1.00
003     *
004     * Redistribution and use of this software and associated documentation
005     * ("Software"), with or without modification, are permitted provided
006     * that the following conditions are met:
007     *
008     * 1. Redistributions of source code must retain copyright
009     *    statements and notices.  Redistributions must also contain a
010     *    copy of this document.
011     *
012     * 2. Redistributions in binary form must reproduce the
013     *    above copyright notice, this list of conditions and the
014     *    following disclaimer in the documentation and/or other
015     *    materials provided with the distribution.
016     *
017     * 3. The name "JDBM" must not be used to endorse or promote
018     *    products derived from this Software without prior written
019     *    permission of Cees de Groot.  For written permission,
020     *    please contact cg@cdegroot.com.
021     *
022     * 4. Products derived from this Software may not be called "JDBM"
023     *    nor may "JDBM" appear in their names without prior written
024     *    permission of Cees de Groot.
025     *
026     * 5. Due credit should be given to the JDBM Project
027     *    (http://jdbm.sourceforge.net/).
028     *
029     * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
030     * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
031     * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
032     * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
033     * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
034     * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
035     * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
036     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
037     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
038     * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
039     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
040     * OF THE POSSIBILITY OF SUCH DAMAGE.
041     *
042     * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
043     * Contributions are Copyright (C) 2000 by their associated contributors.
044     *
045     * $Id: BlockIo.java,v 1.2 2002/08/06 05:18:36 boisvert Exp $
046     */
047    
048    package jdbm.recman;
049    
050    import java.io.*;
051    
052    /**
053     *  This class wraps a page-sized byte array and provides methods
054     *  to read and write data to and from it. The readers and writers
055     *  are just the ones that the rest of the toolkit needs, nothing else.
056     *  Values written are compatible with java.io routines.
057     *
058     *  @see java.io.DataInput
059     *  @see java.io.DataOutput
060     */
061    public final class BlockIo implements java.io.Externalizable {
062    
063        public final static long serialVersionUID = 2L;
064    
065        private long blockId;
066    
067        private transient byte[] data; // work area
068        private transient BlockView view = null;
069        private transient boolean dirty = false;
070        private transient int transactionCount = 0;
071    
072        /**
073         * Default constructor for serialization
074         */
075        public BlockIo() {
076            // empty
077        }
078    
079        /**
080         *  Constructs a new BlockIo instance working on the indicated
081         *  buffer.
082         */
083        BlockIo(long blockId, byte[] data) {
084            // removeme for production version
085            if (blockId > 10000000000L)
086                throw new Error("bogus block id " + blockId);
087            this.blockId = blockId;
088            this.data = data;
089        }
090    
091        /**
092         *  Returns the underlying array
093         */
094        byte[] getData() {
095            return data;
096        }
097    
098        /**
099         *  Sets the block number. Should only be called by RecordFile.
100         */
101        void setBlockId(long id) {
102            if (isInTransaction())
103                throw new Error("BlockId assigned for transaction block");
104            // removeme for production version
105            if (id > 10000000000L)
106                throw new Error("bogus block id " + id);
107            blockId = id;
108        }
109    
110        /**
111         *  Returns the block number.
112         */
113        long getBlockId() {
114            return blockId;
115        }
116    
117        /**
118         *  Returns the current view of the block.
119         */
120        public BlockView getView() {
121            return view;
122        }
123    
124        /**
125         *  Sets the current view of the block.
126         */
127        public void setView(BlockView view) {
128            this.view = view;
129        }
130    
131        /**
132         *  Sets the dirty flag
133         */
134        void setDirty() {
135            dirty = true;
136        }
137    
138        /**
139         *  Clears the dirty flag
140         */
141        void setClean() {
142            dirty = false;
143        }
144    
145        /**
146         *  Returns true if the dirty flag is set.
147         */
148        boolean isDirty() {
149            return dirty;
150        }
151    
152        /**
153         *  Returns true if the block is still dirty with respect to the
154         *  transaction log.
155         */
156        boolean isInTransaction() {
157            return transactionCount != 0;
158        }
159    
160        /**
161         *  Increments transaction count for this block, to signal that this
162         *  block is in the log but not yet in the data file. The method also
163         *  takes a snapshot so that the data may be modified in new transactions.
164         */
165        synchronized void incrementTransactionCount() {
166            transactionCount++;
167            // @fixme(alex)
168            setClean();
169        }
170    
171        /**
172         *  Decrements transaction count for this block, to signal that this
173         *  block has been written from the log to the data file.
174         */
175        synchronized void decrementTransactionCount() {
176            transactionCount--;
177            if (transactionCount < 0)
178                throw new Error("transaction count on block "
179                                + getBlockId() + " below zero!");
180    
181        }
182    
183        /**
184         *  Reads a byte from the indicated position
185         */
186        public byte readByte(int pos) {
187            return data[pos];
188        }
189    
190        /**
191         *  Writes a byte to the indicated position
192         */
193        public void writeByte(int pos, byte value) {
194            data[pos] = value;
195            setDirty();
196        }
197    
198        /**
199         *  Reads a short from the indicated position
200         */
201        public short readShort(int pos) {
202            return (short)
203                (((short) (data[pos+0] & 0xff) << 8) |
204                 ((short) (data[pos+1] & 0xff) << 0));
205        }
206    
207        /**
208         *  Writes a short to the indicated position
209         */
210        public void writeShort(int pos, short value) {
211            data[pos+0] = (byte)(0xff & (value >> 8));
212            data[pos+1] = (byte)(0xff & (value >> 0));
213            setDirty();
214        }
215    
216        /**
217         *  Reads an int from the indicated position
218         */
219        public int readInt(int pos) {
220            return
221                (((int)(data[pos+0] & 0xff) << 24) |
222                 ((int)(data[pos+1] & 0xff) << 16) |
223                 ((int)(data[pos+2] & 0xff) <<  8) |
224                 ((int)(data[pos+3] & 0xff) <<  0));
225        }
226    
227        /**
228         *  Writes an int to the indicated position
229         */
230        public void writeInt(int pos, int value) {
231            data[pos+0] = (byte)(0xff & (value >> 24));
232            data[pos+1] = (byte)(0xff & (value >> 16));
233            data[pos+2] = (byte)(0xff & (value >>  8));
234            data[pos+3] = (byte)(0xff & (value >>  0));
235            setDirty();
236        }
237    
238        /**
239         *  Reads a long from the indicated position
240         */
241        public long readLong( int pos )
242        {
243            // Contributed by Erwin Bolwidt <ejb@klomp.org>
244            // Gives about 15% performance improvement
245            return
246                ( (long)( ((data[pos+0] & 0xff) << 24) |
247                          ((data[pos+1] & 0xff) << 16) |
248                          ((data[pos+2] & 0xff) <<  8) |
249                          ((data[pos+3] & 0xff)      ) ) << 32 ) |
250                ( (long)( ((data[pos+4] & 0xff) << 24) |
251                          ((data[pos+5] & 0xff) << 16) |
252                          ((data[pos+6] & 0xff) <<  8) |
253                          ((data[pos+7] & 0xff)      ) ) & 0xffffffff );
254            /* Original version by Alex Boisvert.  Might be faster on 64-bit JVMs.
255            return
256                (((long)(data[pos+0] & 0xff) << 56) |
257                 ((long)(data[pos+1] & 0xff) << 48) |
258                 ((long)(data[pos+2] & 0xff) << 40) |
259                 ((long)(data[pos+3] & 0xff) << 32) |
260                 ((long)(data[pos+4] & 0xff) << 24) |
261                 ((long)(data[pos+5] & 0xff) << 16) |
262                 ((long)(data[pos+6] & 0xff) <<  8) |
263                 ((long)(data[pos+7] & 0xff) <<  0));
264            */
265        }
266    
267        /**
268         *  Writes a long to the indicated position
269         */
270        public void writeLong(int pos, long value) {
271            data[pos+0] = (byte)(0xff & (value >> 56));
272            data[pos+1] = (byte)(0xff & (value >> 48));
273            data[pos+2] = (byte)(0xff & (value >> 40));
274            data[pos+3] = (byte)(0xff & (value >> 32));
275            data[pos+4] = (byte)(0xff & (value >> 24));
276            data[pos+5] = (byte)(0xff & (value >> 16));
277            data[pos+6] = (byte)(0xff & (value >>  8));
278            data[pos+7] = (byte)(0xff & (value >>  0));
279            setDirty();
280        }
281    
282        // overrides java.lang.Object
283    
284        public String toString() {
285            return "BlockIO("
286                + blockId + ","
287                + dirty + ","
288                + view + ")";
289        }
290    
291        // implement externalizable interface
292        public void readExternal(ObjectInput in)
293        throws IOException, ClassNotFoundException {
294            blockId = in.readLong();
295            int length = in.readInt();
296            data = new byte[length];
297            in.readFully(data);
298        }
299    
300        // implement externalizable interface
301        public void writeExternal(ObjectOutput out) throws IOException {
302            out.writeLong(blockId);
303            out.writeInt(data.length);
304            out.write(data);
305        }
306    
307    }