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
31package com.google.protobuf.nano;
32
33import java.io.IOException;
34import java.lang.reflect.Array;
35import java.util.ArrayList;
36import java.util.List;
37
38/**
39 * Represents an extension.
40 *
41 * @author bduff@google.com (Brian Duff)
42 * @author maxtroy@google.com (Max Cai)
43 * @param <M> the type of the extendable message this extension is for.
44 * @param <T> the Java type of the extension; see {@link #clazz}.
45 */
46public class Extension<M extends ExtendableMessageNano<M>, T> {
47
48    /*
49     * Because we typically only define message-typed extensions, the Extension class hierarchy is
50     * designed as follows, to allow a big amount of code in this file to be removed by ProGuard:
51     *
52     *            Extension          // ready to use for message/group typed extensions
53     *                Δ
54     *                |
55     *       PrimitiveExtension      // for primitive/enum typed extensions
56     */
57
58    public static final int TYPE_DOUBLE   = InternalNano.TYPE_DOUBLE;
59    public static final int TYPE_FLOAT    = InternalNano.TYPE_FLOAT;
60    public static final int TYPE_INT64    = InternalNano.TYPE_INT64;
61    public static final int TYPE_UINT64   = InternalNano.TYPE_UINT64;
62    public static final int TYPE_INT32    = InternalNano.TYPE_INT32;
63    public static final int TYPE_FIXED64  = InternalNano.TYPE_FIXED64;
64    public static final int TYPE_FIXED32  = InternalNano.TYPE_FIXED32;
65    public static final int TYPE_BOOL     = InternalNano.TYPE_BOOL;
66    public static final int TYPE_STRING   = InternalNano.TYPE_STRING;
67    public static final int TYPE_GROUP    = InternalNano.TYPE_GROUP;
68    public static final int TYPE_MESSAGE  = InternalNano.TYPE_MESSAGE;
69    public static final int TYPE_BYTES    = InternalNano.TYPE_BYTES;
70    public static final int TYPE_UINT32   = InternalNano.TYPE_UINT32;
71    public static final int TYPE_ENUM     = InternalNano.TYPE_ENUM;
72    public static final int TYPE_SFIXED32 = InternalNano.TYPE_SFIXED32;
73    public static final int TYPE_SFIXED64 = InternalNano.TYPE_SFIXED64;
74    public static final int TYPE_SINT32   = InternalNano.TYPE_SINT32;
75    public static final int TYPE_SINT64   = InternalNano.TYPE_SINT64;
76
77    /**
78     * Creates an {@code Extension} of the given message type and tag number.
79     * Should be used by the generated code only.
80     *
81     * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
82     * @deprecated use {@link #createMessageTyped(int, Class, long)} instead.
83     */
84    @Deprecated
85    public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
86            Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) {
87        return new Extension<M, T>(type, clazz, tag, false);
88    }
89
90    // Note: these create...() methods take a long for the tag parameter,
91    // because tags are represented as unsigned ints, and these values exist
92    // in generated code as long values. However, they can fit in 32-bits, so
93    // it's safe to cast them to int without loss of precision.
94
95    /**
96     * Creates an {@code Extension} of the given message type and tag number.
97     * Should be used by the generated code only.
98     *
99     * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
100     */
101    public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
102            Extension<M, T> createMessageTyped(int type, Class<T> clazz, long tag) {
103        return new Extension<M, T>(type, clazz, (int) tag, false);
104    }
105
106    /**
107     * Creates a repeated {@code Extension} of the given message type and tag number.
108     * Should be used by the generated code only.
109     *
110     * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP}
111     */
112    public static <M extends ExtendableMessageNano<M>, T extends MessageNano>
113            Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, long tag) {
114        return new Extension<M, T[]>(type, clazz, (int) tag, true);
115    }
116
117    /**
118     * Creates an {@code Extension} of the given primitive type and tag number.
119     * Should be used by the generated code only.
120     *
121     * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP}
122     * @param clazz the boxed Java type of this extension
123     */
124    public static <M extends ExtendableMessageNano<M>, T>
125            Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, long tag) {
126        return new PrimitiveExtension<M, T>(type, clazz, (int) tag, false, 0, 0);
127    }
128
129    /**
130     * Creates a repeated {@code Extension} of the given primitive type and tag number.
131     * Should be used by the generated code only.
132     *
133     * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP}
134     * @param clazz the Java array type of this extension, with an unboxed component type
135     */
136    public static <M extends ExtendableMessageNano<M>, T>
137            Extension<M, T> createRepeatedPrimitiveTyped(
138                    int type, Class<T> clazz, long tag, long nonPackedTag, long packedTag) {
139        return new PrimitiveExtension<M, T>(type, clazz, (int) tag, true,
140            (int) nonPackedTag, (int) packedTag);
141    }
142
143    /**
144     * Protocol Buffer type of this extension; one of the {@code TYPE_} constants.
145     */
146    protected final int type;
147
148    /**
149     * Java type of this extension. For a singular extension, this is the boxed Java type for the
150     * Protocol Buffer {@link #type}; for a repeated extension, this is an array type whose
151     * component type is the unboxed Java type for {@link #type}. For example, for a singular
152     * {@code int32}/{@link #TYPE_INT32} extension, this equals {@code Integer.class}; for a
153     * repeated {@code int32} extension, this equals {@code int[].class}.
154     */
155    protected final Class<T> clazz;
156
157    /**
158     * Tag number of this extension. The data should be viewed as an unsigned 32-bit value.
159     */
160    public final int tag;
161
162    /**
163     * Whether this extension is repeated.
164     */
165    protected final boolean repeated;
166
167    private Extension(int type, Class<T> clazz, int tag, boolean repeated) {
168        this.type = type;
169        this.clazz = clazz;
170        this.tag = tag;
171        this.repeated = repeated;
172    }
173
174    /**
175     * Returns the value of this extension stored in the given list of unknown fields, or
176     * {@code null} if no unknown fields matches this extension.
177     *
178     * @param unknownFields a list of {@link UnknownFieldData}. All of the elements must have a tag
179     *                      that matches this Extension's tag.
180     *
181     */
182    final T getValueFrom(List<UnknownFieldData> unknownFields) {
183        if (unknownFields == null) {
184            return null;
185        }
186        return repeated ? getRepeatedValueFrom(unknownFields) : getSingularValueFrom(unknownFields);
187    }
188
189    private T getRepeatedValueFrom(List<UnknownFieldData> unknownFields) {
190        // For repeated extensions, read all matching unknown fields in their original order.
191        List<Object> resultList = new ArrayList<Object>();
192        for (int i = 0; i < unknownFields.size(); i++) {
193            UnknownFieldData data = unknownFields.get(i);
194            if (data.bytes.length != 0) {
195                readDataInto(data, resultList);
196            }
197        }
198
199        int resultSize = resultList.size();
200        if (resultSize == 0) {
201            return null;
202        } else {
203            T result = clazz.cast(Array.newInstance(clazz.getComponentType(), resultSize));
204            for (int i = 0; i < resultSize; i++) {
205                Array.set(result, i, resultList.get(i));
206            }
207            return result;
208        }
209    }
210
211    private T getSingularValueFrom(List<UnknownFieldData> unknownFields) {
212        // For singular extensions, get the last piece of data stored under this extension.
213        if (unknownFields.isEmpty()) {
214            return null;
215        }
216        UnknownFieldData lastData = unknownFields.get(unknownFields.size() - 1);
217        return clazz.cast(readData(CodedInputByteBufferNano.newInstance(lastData.bytes)));
218    }
219
220    protected Object readData(CodedInputByteBufferNano input) {
221        // This implementation is for message/group extensions.
222        Class<?> messageType = repeated ? clazz.getComponentType() : clazz;
223        try {
224            switch (type) {
225                case TYPE_GROUP:
226                    MessageNano group = (MessageNano) messageType.newInstance();
227                    input.readGroup(group, WireFormatNano.getTagFieldNumber(tag));
228                    return group;
229                case TYPE_MESSAGE:
230                    MessageNano message = (MessageNano) messageType.newInstance();
231                    input.readMessage(message);
232                    return message;
233                default:
234                    throw new IllegalArgumentException("Unknown type " + type);
235            }
236        } catch (InstantiationException e) {
237            throw new IllegalArgumentException(
238                    "Error creating instance of class " + messageType, e);
239        } catch (IllegalAccessException e) {
240            throw new IllegalArgumentException(
241                    "Error creating instance of class " + messageType, e);
242        } catch (IOException e) {
243            throw new IllegalArgumentException("Error reading extension field", e);
244        }
245    }
246
247    protected void readDataInto(UnknownFieldData data, List<Object> resultList) {
248        // This implementation is for message/group extensions.
249        resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes)));
250    }
251
252    void writeTo(Object value, CodedOutputByteBufferNano output) throws IOException {
253        if (repeated) {
254            writeRepeatedData(value, output);
255        } else {
256            writeSingularData(value, output);
257        }
258    }
259
260    protected void writeSingularData(Object value, CodedOutputByteBufferNano out) {
261        // This implementation is for message/group extensions.
262        try {
263            out.writeRawVarint32(tag);
264            switch (type) {
265                case TYPE_GROUP:
266                    MessageNano groupValue = (MessageNano) value;
267                    int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
268                    out.writeGroupNoTag(groupValue);
269                    // The endgroup tag must be included in the data payload.
270                    out.writeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP);
271                    break;
272                case TYPE_MESSAGE:
273                    MessageNano messageValue = (MessageNano) value;
274                    out.writeMessageNoTag(messageValue);
275                    break;
276                default:
277                    throw new IllegalArgumentException("Unknown type " + type);
278            }
279        } catch (IOException e) {
280            // Should not happen
281            throw new IllegalStateException(e);
282        }
283    }
284
285    protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) {
286        // This implementation is for non-packed extensions.
287        int arrayLength = Array.getLength(array);
288        for (int i = 0; i < arrayLength; i++) {
289            Object element = Array.get(array, i);
290            if (element != null) {
291                writeSingularData(element, output);
292            }
293        }
294    }
295
296    int computeSerializedSize(Object value) {
297        if (repeated) {
298            return computeRepeatedSerializedSize(value);
299        } else {
300            return computeSingularSerializedSize(value);
301        }
302    }
303
304    protected int computeRepeatedSerializedSize(Object array) {
305        // This implementation is for non-packed extensions.
306        int size = 0;
307        int arrayLength = Array.getLength(array);
308        for (int i = 0; i < arrayLength; i++) {
309            Object element = Array.get(array, i);
310            if (element != null) {
311                size += computeSingularSerializedSize(Array.get(array, i));
312            }
313        }
314        return size;
315    }
316
317    protected int computeSingularSerializedSize(Object value) {
318        // This implementation is for message/group extensions.
319        int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
320        switch (type) {
321            case TYPE_GROUP:
322                MessageNano groupValue = (MessageNano) value;
323                return CodedOutputByteBufferNano.computeGroupSize(fieldNumber, groupValue);
324            case TYPE_MESSAGE:
325                MessageNano messageValue = (MessageNano) value;
326                return CodedOutputByteBufferNano.computeMessageSize(fieldNumber, messageValue);
327            default:
328                throw new IllegalArgumentException("Unknown type " + type);
329        }
330    }
331
332    /**
333     * Represents an extension of a primitive (including enum) type. If there is no primitive
334     * extensions, this subclass will be removable by ProGuard.
335     */
336    private static class PrimitiveExtension<M extends ExtendableMessageNano<M>, T>
337            extends Extension<M, T> {
338
339        /**
340         * Tag of a piece of non-packed data from the wire compatible with this extension.
341         */
342        private final int nonPackedTag;
343
344        /**
345         * Tag of a piece of packed data from the wire compatible with this extension.
346         * 0 if the type of this extension is not packable.
347         */
348        private final int packedTag;
349
350        public PrimitiveExtension(int type, Class<T> clazz, int tag, boolean repeated,
351                int nonPackedTag, int packedTag) {
352            super(type, clazz, tag, repeated);
353            this.nonPackedTag = nonPackedTag;
354            this.packedTag = packedTag;
355        }
356
357        @Override
358        protected Object readData(CodedInputByteBufferNano input) {
359            try {
360              return input.readPrimitiveField(type);
361            } catch (IOException e) {
362                throw new IllegalArgumentException("Error reading extension field", e);
363            }
364        }
365
366        @Override
367        protected void readDataInto(UnknownFieldData data, List<Object> resultList) {
368            // This implementation is for primitive typed extensions,
369            // which can read both packed and non-packed data.
370            if (data.tag == nonPackedTag) {
371                resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes)));
372            } else {
373                CodedInputByteBufferNano buffer =
374                        CodedInputByteBufferNano.newInstance(data.bytes);
375                try {
376                    buffer.pushLimit(buffer.readRawVarint32()); // length limit
377                } catch (IOException e) {
378                    throw new IllegalArgumentException("Error reading extension field", e);
379                }
380                while (!buffer.isAtEnd()) {
381                    resultList.add(readData(buffer));
382                }
383            }
384        }
385
386        @Override
387        protected final void writeSingularData(Object value, CodedOutputByteBufferNano output) {
388            try {
389                output.writeRawVarint32(tag);
390                switch (type) {
391                    case TYPE_DOUBLE:
392                        Double doubleValue = (Double) value;
393                        output.writeDoubleNoTag(doubleValue);
394                        break;
395                    case TYPE_FLOAT:
396                        Float floatValue = (Float) value;
397                        output.writeFloatNoTag(floatValue);
398                        break;
399                    case TYPE_INT64:
400                        Long int64Value = (Long) value;
401                        output.writeInt64NoTag(int64Value);
402                        break;
403                    case TYPE_UINT64:
404                        Long uint64Value = (Long) value;
405                        output.writeUInt64NoTag(uint64Value);
406                        break;
407                    case TYPE_INT32:
408                        Integer int32Value = (Integer) value;
409                        output.writeInt32NoTag(int32Value);
410                        break;
411                    case TYPE_FIXED64:
412                        Long fixed64Value = (Long) value;
413                        output.writeFixed64NoTag(fixed64Value);
414                        break;
415                    case TYPE_FIXED32:
416                        Integer fixed32Value = (Integer) value;
417                        output.writeFixed32NoTag(fixed32Value);
418                        break;
419                    case TYPE_BOOL:
420                        Boolean boolValue = (Boolean) value;
421                        output.writeBoolNoTag(boolValue);
422                        break;
423                    case TYPE_STRING:
424                        String stringValue = (String) value;
425                        output.writeStringNoTag(stringValue);
426                        break;
427                    case TYPE_BYTES:
428                        byte[] bytesValue = (byte[]) value;
429                        output.writeBytesNoTag(bytesValue);
430                        break;
431                    case TYPE_UINT32:
432                        Integer uint32Value = (Integer) value;
433                        output.writeUInt32NoTag(uint32Value);
434                        break;
435                    case TYPE_ENUM:
436                        Integer enumValue = (Integer) value;
437                        output.writeEnumNoTag(enumValue);
438                        break;
439                    case TYPE_SFIXED32:
440                        Integer sfixed32Value = (Integer) value;
441                        output.writeSFixed32NoTag(sfixed32Value);
442                        break;
443                    case TYPE_SFIXED64:
444                        Long sfixed64Value = (Long) value;
445                        output.writeSFixed64NoTag(sfixed64Value);
446                        break;
447                    case TYPE_SINT32:
448                        Integer sint32Value = (Integer) value;
449                        output.writeSInt32NoTag(sint32Value);
450                        break;
451                    case TYPE_SINT64:
452                        Long sint64Value = (Long) value;
453                        output.writeSInt64NoTag(sint64Value);
454                        break;
455                    default:
456                        throw new IllegalArgumentException("Unknown type " + type);
457                }
458            } catch (IOException e) {
459                // Should not happen
460                throw new IllegalStateException(e);
461            }
462        }
463
464        @Override
465        protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) {
466            if (tag == nonPackedTag) {
467                // Use base implementation for non-packed data
468                super.writeRepeatedData(array, output);
469            } else if (tag == packedTag) {
470                // Packed. Note that the array element type is guaranteed to be primitive, so there
471                // won't be any null elements, so no null check in this block.
472                int arrayLength = Array.getLength(array);
473                int dataSize = computePackedDataSize(array);
474
475                try {
476                    output.writeRawVarint32(tag);
477                    output.writeRawVarint32(dataSize);
478                    switch (type) {
479                        case TYPE_BOOL:
480                            for (int i = 0; i < arrayLength; i++) {
481                                output.writeBoolNoTag(Array.getBoolean(array, i));
482                            }
483                            break;
484                        case TYPE_FIXED32:
485                            for (int i = 0; i < arrayLength; i++) {
486                                output.writeFixed32NoTag(Array.getInt(array, i));
487                            }
488                            break;
489                        case TYPE_SFIXED32:
490                            for (int i = 0; i < arrayLength; i++) {
491                                output.writeSFixed32NoTag(Array.getInt(array, i));
492                            }
493                            break;
494                        case TYPE_FLOAT:
495                            for (int i = 0; i < arrayLength; i++) {
496                                output.writeFloatNoTag(Array.getFloat(array, i));
497                            }
498                            break;
499                        case TYPE_FIXED64:
500                            for (int i = 0; i < arrayLength; i++) {
501                                output.writeFixed64NoTag(Array.getLong(array, i));
502                            }
503                            break;
504                        case TYPE_SFIXED64:
505                            for (int i = 0; i < arrayLength; i++) {
506                                output.writeSFixed64NoTag(Array.getLong(array, i));
507                            }
508                            break;
509                        case TYPE_DOUBLE:
510                            for (int i = 0; i < arrayLength; i++) {
511                                output.writeDoubleNoTag(Array.getDouble(array, i));
512                            }
513                            break;
514                        case TYPE_INT32:
515                            for (int i = 0; i < arrayLength; i++) {
516                                output.writeInt32NoTag(Array.getInt(array, i));
517                            }
518                            break;
519                        case TYPE_SINT32:
520                            for (int i = 0; i < arrayLength; i++) {
521                                output.writeSInt32NoTag(Array.getInt(array, i));
522                            }
523                            break;
524                        case TYPE_UINT32:
525                            for (int i = 0; i < arrayLength; i++) {
526                                output.writeUInt32NoTag(Array.getInt(array, i));
527                            }
528                            break;
529                        case TYPE_INT64:
530                            for (int i = 0; i < arrayLength; i++) {
531                                output.writeInt64NoTag(Array.getLong(array, i));
532                            }
533                            break;
534                        case TYPE_SINT64:
535                            for (int i = 0; i < arrayLength; i++) {
536                                output.writeSInt64NoTag(Array.getLong(array, i));
537                            }
538                            break;
539                        case TYPE_UINT64:
540                            for (int i = 0; i < arrayLength; i++) {
541                                output.writeUInt64NoTag(Array.getLong(array, i));
542                            }
543                            break;
544                        case TYPE_ENUM:
545                            for (int i = 0; i < arrayLength; i++) {
546                                output.writeEnumNoTag(Array.getInt(array, i));
547                            }
548                            break;
549                        default:
550                            throw new IllegalArgumentException("Unpackable type " + type);
551                    }
552                } catch (IOException e) {
553                    // Should not happen.
554                    throw new IllegalStateException(e);
555                }
556            } else {
557                throw new IllegalArgumentException("Unexpected repeated extension tag " + tag
558                        + ", unequal to both non-packed variant " + nonPackedTag
559                        + " and packed variant " + packedTag);
560            }
561        }
562
563        private int computePackedDataSize(Object array) {
564            int dataSize = 0;
565            int arrayLength = Array.getLength(array);
566            switch (type) {
567                case TYPE_BOOL:
568                    // Bools are stored as int32 but just as 0 or 1, so 1 byte each.
569                    dataSize = arrayLength;
570                    break;
571                case TYPE_FIXED32:
572                case TYPE_SFIXED32:
573                case TYPE_FLOAT:
574                    dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_32_SIZE;
575                    break;
576                case TYPE_FIXED64:
577                case TYPE_SFIXED64:
578                case TYPE_DOUBLE:
579                    dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_64_SIZE;
580                    break;
581                case TYPE_INT32:
582                    for (int i = 0; i < arrayLength; i++) {
583                        dataSize += CodedOutputByteBufferNano.computeInt32SizeNoTag(
584                                Array.getInt(array, i));
585                    }
586                    break;
587                case TYPE_SINT32:
588                    for (int i = 0; i < arrayLength; i++) {
589                        dataSize += CodedOutputByteBufferNano.computeSInt32SizeNoTag(
590                                Array.getInt(array, i));
591                    }
592                    break;
593                case TYPE_UINT32:
594                    for (int i = 0; i < arrayLength; i++) {
595                        dataSize += CodedOutputByteBufferNano.computeUInt32SizeNoTag(
596                                Array.getInt(array, i));
597                    }
598                    break;
599                case TYPE_INT64:
600                    for (int i = 0; i < arrayLength; i++) {
601                        dataSize += CodedOutputByteBufferNano.computeInt64SizeNoTag(
602                                Array.getLong(array, i));
603                    }
604                    break;
605                case TYPE_SINT64:
606                    for (int i = 0; i < arrayLength; i++) {
607                        dataSize += CodedOutputByteBufferNano.computeSInt64SizeNoTag(
608                                Array.getLong(array, i));
609                    }
610                    break;
611                case TYPE_UINT64:
612                    for (int i = 0; i < arrayLength; i++) {
613                        dataSize += CodedOutputByteBufferNano.computeUInt64SizeNoTag(
614                                Array.getLong(array, i));
615                    }
616                    break;
617                case TYPE_ENUM:
618                    for (int i = 0; i < arrayLength; i++) {
619                        dataSize += CodedOutputByteBufferNano.computeEnumSizeNoTag(
620                                Array.getInt(array, i));
621                    }
622                    break;
623                default:
624                    throw new IllegalArgumentException("Unexpected non-packable type " + type);
625            }
626            return dataSize;
627        }
628
629        @Override
630        protected int computeRepeatedSerializedSize(Object array) {
631            if (tag == nonPackedTag) {
632                // Use base implementation for non-packed data
633                return super.computeRepeatedSerializedSize(array);
634            } else if (tag == packedTag) {
635                // Packed.
636                int dataSize = computePackedDataSize(array);
637                int payloadSize =
638                        dataSize + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize);
639                return payloadSize + CodedOutputByteBufferNano.computeRawVarint32Size(tag);
640            } else {
641                throw new IllegalArgumentException("Unexpected repeated extension tag " + tag
642                        + ", unequal to both non-packed variant " + nonPackedTag
643                        + " and packed variant " + packedTag);
644            }
645        }
646
647        @Override
648        protected final int computeSingularSerializedSize(Object value) {
649            int fieldNumber = WireFormatNano.getTagFieldNumber(tag);
650            switch (type) {
651                case TYPE_DOUBLE:
652                    Double doubleValue = (Double) value;
653                    return CodedOutputByteBufferNano.computeDoubleSize(fieldNumber, doubleValue);
654                case TYPE_FLOAT:
655                    Float floatValue = (Float) value;
656                    return CodedOutputByteBufferNano.computeFloatSize(fieldNumber, floatValue);
657                case TYPE_INT64:
658                    Long int64Value = (Long) value;
659                    return CodedOutputByteBufferNano.computeInt64Size(fieldNumber, int64Value);
660                case TYPE_UINT64:
661                    Long uint64Value = (Long) value;
662                    return CodedOutputByteBufferNano.computeUInt64Size(fieldNumber, uint64Value);
663                case TYPE_INT32:
664                    Integer int32Value = (Integer) value;
665                    return CodedOutputByteBufferNano.computeInt32Size(fieldNumber, int32Value);
666                case TYPE_FIXED64:
667                    Long fixed64Value = (Long) value;
668                    return CodedOutputByteBufferNano.computeFixed64Size(fieldNumber, fixed64Value);
669                case TYPE_FIXED32:
670                    Integer fixed32Value = (Integer) value;
671                    return CodedOutputByteBufferNano.computeFixed32Size(fieldNumber, fixed32Value);
672                case TYPE_BOOL:
673                    Boolean boolValue = (Boolean) value;
674                    return CodedOutputByteBufferNano.computeBoolSize(fieldNumber, boolValue);
675                case TYPE_STRING:
676                    String stringValue = (String) value;
677                    return CodedOutputByteBufferNano.computeStringSize(fieldNumber, stringValue);
678                case TYPE_BYTES:
679                    byte[] bytesValue = (byte[]) value;
680                    return CodedOutputByteBufferNano.computeBytesSize(fieldNumber, bytesValue);
681                case TYPE_UINT32:
682                    Integer uint32Value = (Integer) value;
683                    return CodedOutputByteBufferNano.computeUInt32Size(fieldNumber, uint32Value);
684                case TYPE_ENUM:
685                    Integer enumValue = (Integer) value;
686                    return CodedOutputByteBufferNano.computeEnumSize(fieldNumber, enumValue);
687                case TYPE_SFIXED32:
688                    Integer sfixed32Value = (Integer) value;
689                    return CodedOutputByteBufferNano.computeSFixed32Size(fieldNumber,
690                            sfixed32Value);
691                case TYPE_SFIXED64:
692                    Long sfixed64Value = (Long) value;
693                    return CodedOutputByteBufferNano.computeSFixed64Size(fieldNumber,
694                            sfixed64Value);
695                case TYPE_SINT32:
696                    Integer sint32Value = (Integer) value;
697                    return CodedOutputByteBufferNano.computeSInt32Size(fieldNumber, sint32Value);
698                case TYPE_SINT64:
699                    Long sint64Value = (Long) value;
700                    return CodedOutputByteBufferNano.computeSInt64Size(fieldNumber, sint64Value);
701                default:
702                    throw new IllegalArgumentException("Unknown type " + type);
703            }
704        }
705    }
706}
707