1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2013 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf.nano; 32 33 import java.io.IOException; 34 import java.util.Arrays; 35 36 /** 37 * Abstract interface implemented by Protocol Message objects. 38 * 39 * @author wink@google.com Wink Saville 40 */ 41 public abstract class MessageNano { 42 protected volatile int cachedSize = -1; 43 44 /** 45 * Get the number of bytes required to encode this message. 46 * Returns the cached size or calls getSerializedSize which 47 * sets the cached size. This is used internally when serializing 48 * so the size is only computed once. If a member is modified 49 * then this could be stale call getSerializedSize if in doubt. 50 */ getCachedSize()51 public int getCachedSize() { 52 if (cachedSize < 0) { 53 // getSerializedSize sets cachedSize 54 getSerializedSize(); 55 } 56 return cachedSize; 57 } 58 59 /** 60 * Computes the number of bytes required to encode this message. 61 * The size is cached and the cached result can be retrieved 62 * using getCachedSize(). 63 */ getSerializedSize()64 public int getSerializedSize() { 65 int size = computeSerializedSize(); 66 cachedSize = size; 67 return size; 68 } 69 70 /** 71 * Computes the number of bytes required to encode this message. This does not update the 72 * cached size. 73 */ computeSerializedSize()74 protected int computeSerializedSize() { 75 // This is overridden if the generated message has serialized fields. 76 return 0; 77 } 78 79 /** 80 * Serializes the message and writes it to {@code output}. 81 * 82 * @param output the output to receive the serialized form. 83 * @throws IOException if an error occurred writing to {@code output}. 84 */ writeTo(CodedOutputByteBufferNano output)85 public void writeTo(CodedOutputByteBufferNano output) throws IOException { 86 // Does nothing by default. Overridden by subclasses which have data to write. 87 } 88 89 /** 90 * Parse {@code input} as a message of this type and merge it with the 91 * message being built. 92 */ mergeFrom(CodedInputByteBufferNano input)93 public abstract MessageNano mergeFrom(CodedInputByteBufferNano input) throws IOException; 94 95 /** 96 * Serialize to a byte array. 97 * @return byte array with the serialized data. 98 */ toByteArray(MessageNano msg)99 public static final byte[] toByteArray(MessageNano msg) { 100 final byte[] result = new byte[msg.getSerializedSize()]; 101 toByteArray(msg, result, 0, result.length); 102 return result; 103 } 104 105 /** 106 * Serialize to a byte array starting at offset through length. The 107 * method getSerializedSize must have been called prior to calling 108 * this method so the proper length is know. If an attempt to 109 * write more than length bytes OutOfSpaceException will be thrown 110 * and if length bytes are not written then IllegalStateException 111 * is thrown. 112 */ toByteArray(MessageNano msg, byte[] data, int offset, int length)113 public static final void toByteArray(MessageNano msg, byte[] data, int offset, int length) { 114 try { 115 final CodedOutputByteBufferNano output = 116 CodedOutputByteBufferNano.newInstance(data, offset, length); 117 msg.writeTo(output); 118 output.checkNoSpaceLeft(); 119 } catch (IOException e) { 120 throw new RuntimeException("Serializing to a byte array threw an IOException " 121 + "(should never happen).", e); 122 } 123 } 124 125 /** 126 * Parse {@code data} as a message of this type and merge it with the 127 * message being built. 128 */ mergeFrom(T msg, final byte[] data)129 public static final <T extends MessageNano> T mergeFrom(T msg, final byte[] data) 130 throws InvalidProtocolBufferNanoException { 131 return mergeFrom(msg, data, 0, data.length); 132 } 133 134 /** 135 * Parse {@code data} as a message of this type and merge it with the 136 * message being built. 137 */ mergeFrom(T msg, final byte[] data, final int off, final int len)138 public static final <T extends MessageNano> T mergeFrom(T msg, final byte[] data, 139 final int off, final int len) throws InvalidProtocolBufferNanoException { 140 try { 141 final CodedInputByteBufferNano input = 142 CodedInputByteBufferNano.newInstance(data, off, len); 143 msg.mergeFrom(input); 144 input.checkLastTagWas(0); 145 return msg; 146 } catch (InvalidProtocolBufferNanoException e) { 147 throw e; 148 } catch (IOException e) { 149 throw new RuntimeException("Reading from a byte array threw an IOException (should " 150 + "never happen)."); 151 } 152 } 153 154 /** 155 * Compares two {@code MessageNano}s and returns true if the message's are the same class and 156 * have serialized form equality (i.e. all of the field values are the same). 157 */ messageNanoEquals(MessageNano a, MessageNano b)158 public static final boolean messageNanoEquals(MessageNano a, MessageNano b) { 159 if (a == b) { 160 return true; 161 } 162 if (a == null || b == null) { 163 return false; 164 } 165 if (a.getClass() != b.getClass()) { 166 return false; 167 } 168 final int serializedSize = a.getSerializedSize(); 169 if (b.getSerializedSize() != serializedSize) { 170 return false; 171 } 172 final byte[] aByteArray = new byte[serializedSize]; 173 final byte[] bByteArray = new byte[serializedSize]; 174 toByteArray(a, aByteArray, 0, serializedSize); 175 toByteArray(b, bByteArray, 0, serializedSize); 176 return Arrays.equals(aByteArray, bByteArray); 177 } 178 179 /** 180 * Returns a string that is (mostly) compatible with ProtoBuffer's TextFormat. Note that groups 181 * (which are deprecated) are not serialized with the correct field name. 182 * 183 * <p>This is implemented using reflection, so it is not especially fast nor is it guaranteed 184 * to find all fields if you have method removal turned on for proguard. 185 */ 186 @Override toString()187 public String toString() { 188 return MessageNanoPrinter.print(this); 189 } 190 191 /** 192 * Provides support for cloning. This only works if you specify the generate_clone method. 193 */ 194 @Override clone()195 public MessageNano clone() throws CloneNotSupportedException { 196 return (MessageNano) super.clone(); 197 } 198 } 199