All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.protostuff.ProtobufIOUtil Maven / Gradle / Ivy

The newest version!
//========================================================================
//Copyright 2007-2010 David Yu [email protected]
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at 
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================

package io.protostuff;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Protobuf ser/deser util for messages/objects.
 * 
 * @author David Yu
 * @created Oct 5, 2010
 */
public final class ProtobufIOUtil
{

    private ProtobufIOUtil()
    {
    }

    /**
     * Creates a protobuf pipe from a byte array.
     */
    public static Pipe newPipe(byte[] data)
    {
        return newPipe(data, 0, data.length);
    }

    /**
     * Creates a protobuf pipe from a byte array.
     */
    public static Pipe newPipe(byte[] data, int offset, int len)
    {
        final ByteArrayInput byteArrayInput = new ByteArrayInput(data, offset, len, false);
        return new Pipe()
        {
            @Override
            protected Input begin(Pipe.Schema pipeSchema) throws IOException
            {
                return byteArrayInput;
            }

            @Override
            protected void end(Pipe.Schema pipeSchema, Input input,
                    boolean cleanupOnly) throws IOException
            {
                if (cleanupOnly)
                    return;

                assert input == byteArrayInput;
            }
        };
    }

    /**
     * Creates a protobuf pipe from an {@link InputStream}.
     */
    public static Pipe newPipe(final InputStream in)
    {
        final CodedInput codedInput = new CodedInput(in, false);
        return new Pipe()
        {
            @Override
            protected Input begin(Pipe.Schema pipeSchema) throws IOException
            {
                return codedInput;
            }

            @Override
            protected void end(Pipe.Schema pipeSchema, Input input,
                    boolean cleanupOnly) throws IOException
            {
                if (cleanupOnly)
                    return;

                assert input == codedInput;
            }
        };
    }

    /**
     * Merges the {@code message} with the byte array using the given {@code schema}.
     */
    public static  void mergeFrom(byte[] data, T message, Schema schema)
    {
        IOUtil.mergeFrom(data, 0, data.length, message, schema, false);
    }

    /**
     * Merges the {@code message} with the byte array using the given {@code schema}.
     */
    public static  void mergeFrom(byte[] data, int offset, int length, T message,
            Schema schema)
    {
        IOUtil.mergeFrom(data, offset, length, message, schema, false);
    }

    /**
     * Merges the {@code message} from the {@link InputStream} using the given {@code schema}.
     */
    public static  void mergeFrom(InputStream in, T message, Schema schema)
            throws IOException
    {
        IOUtil.mergeFrom(in, message, schema, false);
    }

    /**
     * Merges the {@code message} from the {@link InputStream} using the given {@code schema}.
     * 

* The {@code buffer}'s internal byte array will be used for reading the message. */ public static void mergeFrom(InputStream in, T message, Schema schema, LinkedBuffer buffer) throws IOException { IOUtil.mergeFrom(in, buffer.buffer, message, schema, false); } /** * Merges the {@code message} (delimited) from the {@link InputStream} using the given {@code schema}. * * @return the size of the message */ public static int mergeDelimitedFrom(InputStream in, T message, Schema schema) throws IOException { return IOUtil.mergeDelimitedFrom(in, message, schema, false); } /** * Merges the {@code message} (delimited) from the {@link InputStream} using the given {@code schema}. *

* The delimited message size must not be larger than the {@code buffer}'s size/capacity. {@link ProtobufException} * "size limit exceeded" is thrown otherwise. * * @return the size of the message */ public static int mergeDelimitedFrom(InputStream in, T message, Schema schema, LinkedBuffer buffer) throws IOException { return IOUtil.mergeDelimitedFrom(in, buffer.buffer, message, schema, false); } /** * Used by the code generated messages that implement {@link java.io.Externalizable}. Merges from the * {@link DataInput}. * * @return the size of the message */ public static int mergeDelimitedFrom(DataInput in, T message, Schema schema) throws IOException { return IOUtil.mergeDelimitedFrom(in, message, schema, false); } /** * Serializes the {@code message} into a byte array using the given schema. * * @return the byte array containing the data. */ public static byte[] toByteArray(T message, Schema schema, LinkedBuffer buffer) { if (buffer.start != buffer.offset) throw new IllegalArgumentException("Buffer previously used and had not been reset."); final ProtobufOutput output = new ProtobufOutput(buffer); try { schema.writeTo(output, message); } catch (IOException e) { throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e); } return output.toByteArray(); } /** * Writes the {@code message} into the {@link LinkedBuffer} using the given schema. * * @return the size of the message */ public static int writeTo(LinkedBuffer buffer, T message, Schema schema) { if (buffer.start != buffer.offset) throw new IllegalArgumentException("Buffer previously used and had not been reset."); final ProtobufOutput output = new ProtobufOutput(buffer); try { schema.writeTo(output, message); } catch (IOException e) { throw new RuntimeException("Serializing to a LinkedBuffer threw an IOException " + "(should never happen).", e); } return output.getSize(); } /** * Serializes the {@code message} into an {@link OutputStream} using the given schema. * * @return the size of the message */ public static int writeTo(OutputStream out, T message, Schema schema, LinkedBuffer buffer) throws IOException { if (buffer.start != buffer.offset) throw new IllegalArgumentException("Buffer previously used and had not been reset."); final ProtobufOutput output = new ProtobufOutput(buffer); schema.writeTo(output, message); return LinkedBuffer.writeTo(out, buffer); } /** * Serializes the {@code message}, prefixed with its length, into an {@link OutputStream}. * * @return the size of the message */ public static int writeDelimitedTo(OutputStream out, T message, Schema schema, LinkedBuffer buffer) throws IOException { if (buffer.start != buffer.offset) throw new IllegalArgumentException("Buffer previously used and had not been reset."); final ProtobufOutput output = new ProtobufOutput(buffer); schema.writeTo(output, message); final int size = output.getSize(); ProtobufOutput.writeRawVarInt32Bytes(out, size); final int msgSize = LinkedBuffer.writeTo(out, buffer); assert size == msgSize; return size; } /** * Used by the code generated messages that implement {@link java.io.Externalizable}. Writes to the * {@link DataOutput} . * * @return the size of the message. */ public static int writeDelimitedTo(DataOutput out, T message, Schema schema) throws IOException { final LinkedBuffer buffer = new LinkedBuffer(LinkedBuffer.MIN_BUFFER_SIZE); final ProtobufOutput output = new ProtobufOutput(buffer); schema.writeTo(output, message); final int size = output.getSize(); ProtobufOutput.writeRawVarInt32Bytes(out, size); final int msgSize = LinkedBuffer.writeTo(out, buffer); assert size == msgSize; return size; } /** * Serializes the {@code messages} (delimited) into an {@link OutputStream} using the given schema. * * @return the total size of the messages (excluding the length prefix varint) */ public static int writeListTo(OutputStream out, List messages, Schema schema, LinkedBuffer buffer) throws IOException { if (buffer.start != buffer.offset) throw new IllegalArgumentException("Buffer previously used and had not been reset."); final ProtobufOutput output = new ProtobufOutput(buffer); int totalSize = 0; for (T m : messages) { schema.writeTo(output, m); final int size = output.getSize(); ProtobufOutput.writeRawVarInt32Bytes(out, size); final int msgSize = LinkedBuffer.writeTo(out, buffer); assert size == msgSize; totalSize += size; output.clear(); } return totalSize; } /** * Parses the {@code messages} (delimited) from the {@link InputStream} using the given {@code schema}. * * @return the list containing the messages. */ public static List parseListFrom(InputStream in, Schema schema) throws IOException { final ArrayList list = new ArrayList(); byte[] buf = null; int biggestLen = 0; LimitedInputStream lin = null; for (int size = in.read(); size != -1; size = in.read()) { final T message = schema.newMessage(); list.add(message); final int len = size < 0x80 ? size : CodedInput.readRawVarint32(in, size); if (len != 0) { // not an empty message if (len > CodedInput.DEFAULT_BUFFER_SIZE) { // message too big if (lin == null) lin = new LimitedInputStream(in); final CodedInput input = new CodedInput(lin.limit(len), false); schema.mergeFrom(input, message); input.checkLastTagWas(0); continue; } if (biggestLen < len) { // cannot reuse buffer, allocate a bigger buffer // discard the last one for gc buf = new byte[len]; biggestLen = len; } IOUtil.fillBufferFrom(in, buf, 0, len); final ByteArrayInput input = new ByteArrayInput(buf, 0, len, false); try { schema.mergeFrom(input, message); } catch (ArrayIndexOutOfBoundsException e) { throw ProtobufException.truncatedMessage(e); } input.checkLastTagWas(0); } } return list; } /** * Optimal/Optional mergeDelimitedFrom - If the message does not fit the buffer, no merge is done and this method * will return false. *

* This is strictly for reading a single message from the stream because the buffer is aggressively filled when * reading the delimited size (which could result into reading more bytes than it has to). *

* The remaining bytes will be drained (consumed and discared) when the message is too large. */ public static boolean optMergeDelimitedFrom(InputStream in, T message, Schema schema, LinkedBuffer buffer) throws IOException { return optMergeDelimitedFrom(in, message, schema, true, buffer); } /** * Optimal/Optional mergeDelimitedFrom - If the message does not fit the buffer, no merge is done and this method * will return false. *

* This is strictly for reading a single message from the stream because the buffer is aggressively filled when * reading the delimited size (which could result into reading more bytes than it has to). */ public static boolean optMergeDelimitedFrom(InputStream in, T message, Schema schema, boolean drainRemainingBytesIfTooLarge, LinkedBuffer buffer) throws IOException { if (buffer.start != buffer.offset) throw new IllegalArgumentException("Buffer previously used and had not been reset."); final int size = IOUtil.fillBufferWithDelimitedMessageFrom(in, drainRemainingBytesIfTooLarge, buffer); if (size == 0) { // empty message return true; } if (buffer.start == buffer.offset) { // read offset not set ... message too large return false; } final ByteArrayInput input = new ByteArrayInput(buffer.buffer, buffer.offset, size, false); try { schema.mergeFrom(input, message); input.checkLastTagWas(0); } catch (ArrayIndexOutOfBoundsException e) { throw ProtobufException.truncatedMessage(e); } finally { // reset buffer.offset = buffer.start; } return true; } /** * Optimal writeDelimitedTo - The varint32 prefix is written to the buffer instead of directly writing to * outputstream. * * @return the size of the message */ public static int optWriteDelimitedTo(OutputStream out, T message, Schema schema, LinkedBuffer buffer) throws IOException { if (buffer.start != buffer.offset) throw new IllegalArgumentException("Buffer previously used and had not been reset."); final ProtobufOutput output = new ProtobufOutput(buffer); // leave space for varint32 buffer.offset = buffer.start + 5; output.size += 5; schema.writeTo(output, message); final int size = output.size - 5; final int delimOffset = IOUtil.putVarInt32AndGetOffset(size, buffer.buffer, buffer.start); // write to stream out.write(buffer.buffer, delimOffset, buffer.offset - delimOffset); // flush remaining if (buffer.next != null) LinkedBuffer.writeTo(out, buffer.next); return size; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy