Package org.voltdb.messaging

Source Code of org.voltdb.messaging.FastSerializer$BufferGrowCallback

/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB Inc.
*
* VoltDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VoltDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.voltdb.messaging;

import java.io.DataOutput;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.voltdb.VoltType;
import org.voltdb.types.TimestampType;
import org.voltdb.types.VoltDecimalHelper;
import org.voltdb.utils.DBBPool;
import org.voltdb.utils.Encoder;
import org.voltdb.utils.DBBPool.BBContainer;


/**
* <code>DataInputStream</code> subclass to write objects that implement
* the FastSerializable interface.
*
*/
public class FastSerializer implements DataOutput {
    /** callbacked when the internal buffer was grown. */
    public interface BufferGrowCallback {
        void onBufferGrow(FastSerializer obj);
    }

    public static final int INITIAL_ALLOCATION = 2048;
    private BBContainer buffer;
    private final BufferGrowCallback callback;
    private final DBBPool m_pool;
    private final boolean isDirect;

    /**
     * Create a <code>FastSerializer</code> that is BigEndian and uses a HeapByteBuffer
     */
    public FastSerializer() {
        this(true, false);
    }

    /**
     * Create a FastSerializer that will use the provided pool for all of its allocations
     * @param pool
     */
    public FastSerializer(DBBPool pool) {
        this(pool, INITIAL_ALLOCATION);
    }

    /**
     * Create a FastSerializer that will use the provided pool for all its allocations
     * with an initial allocation of the specified size
     * @param pool
     * @param initialAllocation
     */
    public FastSerializer(DBBPool pool, int initialAllocation) {
        this(true, false, null, pool, initialAllocation);
    }


    /** Warning: use direct ByteBuffers with caution, as they are generally slower. */
    public FastSerializer(boolean bigEndian, boolean isDirect) {
        this(bigEndian, isDirect, null, null, INITIAL_ALLOCATION);
    }

    /**
     * Create an FS that will use the provided pool for direct allocations or
     * will do its own direct allocations if the pool is null. The provided BufferGrowCallback will
     * be invoked every time the buffer grows.
     * @param bigEndian
     * @param callback
     * @param pool
     */
    public FastSerializer(boolean bigEndian, BufferGrowCallback callback, DBBPool pool) {
        this(bigEndian, pool == null ? true : false, callback, pool, INITIAL_ALLOCATION);
    }

    /**
     * Caveat. A pool won't always give back a direct byte buffer. If a direct byte buffer
     * is absolutely necessary for the serialized result then use isDirect and a null pool
     */
    public FastSerializer(boolean bigEndian, boolean isDirect, BufferGrowCallback callback, DBBPool pool, int initialAllocation) {
        assert(initialAllocation > 0);
        assert(pool == null && isDirect || pool != null && !isDirect || pool == null && !isDirect);
        this.isDirect = isDirect;
        if (pool != null) {
           m_pool = pool;
           buffer = pool.acquire(initialAllocation);
        } else if (isDirect) {
            assert(pool == null);
           m_pool = null;
           buffer = DBBPool.allocateDirect(initialAllocation);
        } else {
           buffer = DBBPool.wrapBB(ByteBuffer.allocate(initialAllocation));
           m_pool = null;
           assert(pool == null);
        }
        this.callback = callback;
        assert(buffer.b.order() == ByteOrder.BIG_ENDIAN);
    }
   
    public void reset() {
        if (m_pool != null) {
            buffer = m_pool.acquire(INITIAL_ALLOCATION);
        } else if (this.isDirect) {
            buffer = DBBPool.allocateDirect(INITIAL_ALLOCATION);
        } else {
            buffer = DBBPool.wrapBB(ByteBuffer.allocate(INITIAL_ALLOCATION));
        }
    }

    public int size() {
        return buffer.b.position();
    }

    /** Clears the contents of the underlying buffer, making it ready for more writes. */
    public void clear() {
        buffer.b.clear();
    }

    /** Resizes the internal byte buffer with a simple doubling policy, if needed. */
    private final void growIfNeeded(int minimumDesired) {
        if (buffer.b.remaining() < minimumDesired) {
            // Compute the size of the new buffer
            int newCapacity = buffer.b.capacity();
            int newRemaining = newCapacity - buffer.b.position();
            while (newRemaining < minimumDesired) {
                newRemaining += newCapacity;
                newCapacity *= 2;
            }

            // Allocate and copy
            BBContainer next;
            if (isDirect) {
                next = DBBPool.allocateDirect(newCapacity);
            } else if (m_pool != null) {
                next = m_pool.acquire(newCapacity);
            } else {
                next = DBBPool.wrapBB(ByteBuffer.allocate(newCapacity));
            }
            buffer.b.flip();
            next.b.put(buffer.b);
            assert next.b.remaining() == newRemaining;
            buffer.discard();
            buffer = next;
            if (callback != null) callback.onBufferGrow(this);
            assert(buffer.b.order() == ByteOrder.BIG_ENDIAN);
        }
    }

    /**
     * Get the byte version of object. This is a shortcut utility method when
     * you only need to serialize a single object.
     *
     * @return The byte array representation for <code>object</code>.
     */
    public static byte[] serialize(FastSerializable object) throws IOException {
        FastSerializer out = new FastSerializer();
        object.writeExternal(out);
        return out.getBBContainer().b.array();
    }

    public BBContainer writeObjectForMessaging(FastSerializable object) throws IOException {
        final int startPosition = buffer.b.position();
        buffer.b.putInt(0);
        object.writeExternal(this);
        final int len = buffer.b.position() - (4 + startPosition);
        buffer.b.rewind();
        buffer.b.putInt(len);
        buffer.b.position(len + 4);
        buffer.b.flip();
        return buffer;
    }

    /** @return a reference to the underlying ByteBuffer. */
    public BBContainer getBBContainer() {
        buffer.b.flip();
        return buffer;
    }

    /**
     * This method is slow and horrible. It entails an extra copy. Don't use it! Ever! Not even for test!
     * Just say no to test only code.
     * It will also leak the BBContainer if this FS is being used with a pool. <<- Is that true??
     */
    public byte[] getBytes() {
        byte[] retval = new byte[buffer.b.position()];
        int position = buffer.b.position();
        buffer.b.rewind();
        buffer.b.get(retval);
        assert position == buffer.b.position();
        return retval;
    }

    /**
     * Return a readOnly slice of this buffer. Flips the internal buffer.
     * May not be, usefully, invoked multiple times on the same internal
     * state.
     *
     * Only use this if using a non-direct ByteBuffer!
     */
    public ByteBuffer getBuffer() {
        assert(m_pool == null);
        assert(isDirect == false);
        assert(buffer.b.hasArray());
        assert(!buffer.b.isDirect());
        buffer.b.flip();
        return buffer.b.asReadOnlyBuffer();
    }

    /**
     * When a fast serializer is shared between Java and native
     * this is called to retrieve a reference to the to buffer without
     * flipping it. OnBufferGrowCallback needs this to update the pointer
     * to the shared buffer when the parameter buffer grows.
     */
    public BBContainer getContainerNoFlip() {
        assert(m_pool == null);
        assert(isDirect == true);
        assert(buffer.b.isDirect());
        return buffer;
    }

    /**
     * Get a ascii-string-safe version of the binary value using a
     * hex encoding.
     *
     * @return A hex-encoded string value representing the serialized
     * objects.
     */
    public String getHexEncodedBytes() {
        buffer.b.flip();
        byte bytes[] = new byte[buffer.b.remaining()];
        buffer.b.get(bytes);
        String hex = Encoder.hexEncode(bytes);
        buffer.discard();
        return hex;
    }

    /**
     * Write an object to the byte stream. Note: you need to know the
     * type to read it back out again.
     *
     * @param obj The <code>FastSerializable</code> object to be written.
     * @throws IOException Rethrows any IOExceptions thrown.
     */
    public void writeObject(FastSerializable obj) throws IOException {
        obj.writeExternal(this);
    }

    /**
     * Write a timestamp to a FastSerializer. Store the value as a long
     * representing microseconds from the epoch.
     *
     * @param timestamp The {@link org.voltdb.types.TimestampType TimestampType} to serialize.
     * @throws IOException Rethrows any IOExceptions thrown.
     */
    public void writeTimestamp(TimestampType timestamp) throws IOException {
        assert timestamp != null;
        long val = timestamp.getTime();
        writeLong(val);
    }

    /**
     * Write a string in the standard VoltDB way without
     * wrapping the byte buffer.
     */
    public static void writeString(String string, ByteBuffer buffer) throws IOException {
        final int MAX_LENGTH = VoltType.MAX_VALUE_LENGTH;
        final int NULL_STRING_INDICATOR = -1;
        if (string == null) {
            buffer.putInt(NULL_STRING_INDICATOR);
            return;
        }

        int len = 0;
        byte[] strbytes = {};
        try {
            strbytes = string.getBytes("UTF-8");
            len = strbytes.length;
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        if (len > MAX_LENGTH) {
            throw new IOException("String exceeds maximum length of "
                                  + MAX_LENGTH + " bytes.");
        }
        buffer.putInt(len);
        buffer.put(strbytes);
    }

    /**
     * Write a string in the standard VoltDB way. That is, two
     * bytes of length info followed by the bytes of characters
     * encoded in UTF-8.
     *
     * @param string The string value to be serialized.
     * @throws IOException Rethrows any IOExceptions thrown.
     */
    public void writeString(String string) throws IOException {
        final int MAX_LENGTH = VoltType.MAX_VALUE_LENGTH;
        final int NULL_STRING_INDICATOR = -1;
        if (string == null) {
            writeInt(NULL_STRING_INDICATOR);
            return;
        }

        int len = 0;
        byte[] strbytes = {};
        try {
            strbytes = string.getBytes("UTF-8");
            len = strbytes.length;
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        if (len > MAX_LENGTH) {
            throw new IOException("String exceeds maximum length of "
                                  + MAX_LENGTH + " bytes.");
        }
        writeInt(len);
        write(strbytes);
    }

    // These writeArray() methods are tested in TestSQLTypesSuite.
    // If changing the max limits, please update testInvalidParameterSerializations.

    public void writeArray(FastSerializable[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            if (values[i] == null)
                throw new IOException("Array being fastserialized can't contain null values (position " + i + ")");
            writeObject(values[i]);
        }
    }
   
    public void writeArray(boolean[] values) throws IOException {
        if (values.length > VoltType.MAX_VALUE_LENGTH) {
            throw new IOException("Array exceeds maximum length of "
                                  + VoltType.MAX_VALUE_LENGTH + " bytes");
        }
        writeInt(values.length);
        for (int i = 0; i < values.length; ++i) {
            writeBoolean(values[i]);
        }
    }

    public void writeArray(byte[] values) throws IOException {
        if (values.length > VoltType.MAX_VALUE_LENGTH) {
            throw new IOException("Array exceeds maximum length of "
                                  + VoltType.MAX_VALUE_LENGTH + " bytes");
        }
        writeInt(values.length);
        for (int i = 0; i < values.length; ++i) {
            writeByte(values[i]);
        }
    }

    public void writeArray(short[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            writeShort(values[i]);
        }
    }

    public void writeArray(int[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            writeInt(values[i]);
        }
    }

    public void writeArray(long[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            writeLong(values[i]);
        }
    }

    public void writeArray(double[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            writeDouble(values[i]);
        }
    }

    public void writeArray(String[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            writeString(values[i]);
        }
    }

    public void writeArray(TimestampType[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            if (values[i] == null) writeLong(Long.MIN_VALUE);
            else writeLong(values[i].getTime());
        }
    }

    public void writeArray(BigDecimal[] values) throws IOException {
        if (values.length > Short.MAX_VALUE) {
            throw new IOException("Array exceeds maximum length of "
                                  + Short.MAX_VALUE + " bytes");
        }
        writeShort(values.length);
        for (int i = 0; i < values.length; ++i) {
            if (values[i] == null) {
                VoltDecimalHelper.serializeNull(this);
            }
            else {
                VoltDecimalHelper.serializeBigDecimal(values[i], this);
            }
        }
    }

    @Override
    public void write(int b) throws IOException {
        writeByte((byte) b);
    }

    @Override
    public void write(byte[] b) throws IOException {
        growIfNeeded(b.length);
        buffer.b.put(b);
    }

    public void write(ByteBuffer b) throws IOException {
        growIfNeeded(b.limit() - b.position());
        buffer.b.put(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        growIfNeeded(len);
        buffer.b.put(b, off, len);
    }

    @Override
    public void writeBoolean(boolean v) throws IOException {
        writeByte((byte) (v ? 1 : 0));
    }

    @Override
    public void writeByte(int v) throws IOException {
        growIfNeeded(Byte.SIZE/8);
        buffer.b.put((byte) v);
    }

    @Override
    public void writeBytes(String s) throws IOException {
        throw new UnsupportedOperationException("FastSerializer.writeBytes() not supported.");
    }

    @Override
    public void writeChar(int v) throws IOException {
        growIfNeeded(Character.SIZE/8);
        buffer.b.putChar((char) v);
    }

    @Override
    public void writeChars(String s) throws IOException {
        throw new UnsupportedOperationException("FastSerializer.writeChars() not supported.");
    }

    @Override
    public void writeDouble(double v) throws IOException {
        growIfNeeded(Double.SIZE/8);
        buffer.b.putDouble(v);
    }

    @Override
    public void writeFloat(float v) throws IOException {
        growIfNeeded(Float.SIZE/8);
        buffer.b.putFloat(v);
    }

    @Override
    public void writeInt(int v) throws IOException {
        growIfNeeded(Integer.SIZE/8);
        buffer.b.putInt(v);
    }

    @Override
    public void writeLong(long v) throws IOException {
        growIfNeeded(Long.SIZE/8);
        buffer.b.putLong(v);
    }

    @Override
    public void writeShort(int v) throws IOException {
        growIfNeeded(Short.SIZE/8);
        buffer.b.putShort((short) v);
    }

    @Override
    public void writeUTF(String str) throws IOException {
        throw new UnsupportedOperationException("FastSerializer.writeChars() not supported.");
    }

    /**
     * return Current position within the underlying buffer, for self-comparison only.
     */
    public int getPosition() {
        return buffer.b.position();
    }
}
TOP

Related Classes of org.voltdb.messaging.FastSerializer$BufferGrowCallback

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.