1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.util.proto;
18
19import android.annotation.TestApi;
20import android.util.Log;
21
22import java.io.FileDescriptor;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.io.OutputStream;
26import java.io.UnsupportedEncodingException;
27
28/**
29 * Class to write to a protobuf stream.
30 *
31 * Each write method takes an ID code from the protoc generated classes
32 * and the value to write.  To make a nested object, call #start
33 * and then #end when you are done.
34 *
35 * The ID codes have type information embedded into them, so if you call
36 * the incorrect function you will get an IllegalArgumentException.
37 *
38 * To retrieve the encoded protobuf stream, call getBytes().
39 *
40 * TODO: Add a constructor that takes an OutputStream and write to that
41 * stream as the top-level objects are finished.
42 *
43 * @hide
44 */
45
46/* IMPLEMENTATION NOTES
47 *
48 * Because protobuf has inner values, and they are length prefixed, and
49 * those sizes themselves are stored with a variable length encoding, it
50 * is impossible to know how big an object will be in a single pass.
51 *
52 * The traditional way is to copy the in-memory representation of an object
53 * into the generated proto Message objects, do a traversal of those to
54 * cache the size, and then write the size-prefixed buffers.
55 *
56 * We are trying to avoid too much generated code here, but this class still
57 * needs to have a somewhat sane API.  We can't have the multiple passes be
58 * done by the calling code.  In addition, we want to avoid the memory high
59 * water mark of duplicating all of the values into the traditional in-memory
60 * Message objects. We need to find another way.
61 *
62 * So what we do here is to let the calling code write the data into a
63 * byte[] (actually a collection of them wrapped in the EncodedBuffer class),
64 * but not do the varint encoding of the sub-message sizes.  Then, we do a
65 * recursive traversal of the buffer itself, calculating the sizes (which are
66 * then knowable, although still not the actual sizes in the buffer because of
67 * possible further nesting).  Then we do a third pass, compacting the
68 * buffer and varint encoding the sizes.
69 *
70 * This gets us a relatively small number of fixed-size allocations,
71 * which is less likely to cause memory fragmentation or churn the GC, and
72 * the same number of data copies as we would have gotten with setting it
73 * field-by-field in generated code, and no code bloat from generated code.
74 * The final data copy is also done with System.arraycopy, which will be
75 * more efficient, in general, than doing the individual fields twice (as in
76 * the traditional way).
77 *
78 * To accomplish the multiple passes, whenever we write a
79 * WIRE_TYPE_LENGTH_DELIMITED field, we write the size occupied in our
80 * buffer as a fixed 32 bit int (called childRawSize), not a variable length
81 * one. We reserve another 32 bit slot for the computed size (called
82 * childEncodedSize).  If we know the size up front, as we do for strings
83 * and byte[], then we also put that into childEncodedSize, if we don't, we
84 * write the negative of childRawSize, as a sentinel that we need to
85 * compute it during the second pass and recursively compact it during the
86 * third pass.
87 *
88 * Unsigned size varints can be up to five bytes long, but we reserve eight
89 * bytes for overhead, so we know that when we compact the buffer, there
90 * will always be space for the encoded varint.
91 *
92 * When we can figure out the size ahead of time, we do, in order
93 * to save overhead with recalculating it, and with the later arraycopy.
94 *
95 * During the period between when the caller has called #start, but
96 * not yet called #end, we maintain a linked list of the tokens
97 * returned by #start, stored in those 8 bytes of size storage space.
98 * We use that linked list of tokens to ensure that the caller has
99 * correctly matched pairs of #start and #end calls, and issue
100 * errors if they are not matched.
101 */
102@TestApi
103public final class ProtoOutputStream {
104    public static final String TAG = "ProtoOutputStream";
105
106    public static final int FIELD_ID_SHIFT = 3;
107    public static final int WIRE_TYPE_MASK = (1<<FIELD_ID_SHIFT)-1;
108    public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK;
109
110    public static final int WIRE_TYPE_VARINT = 0;
111    public static final int WIRE_TYPE_FIXED64 = 1;
112    public static final int WIRE_TYPE_LENGTH_DELIMITED = 2;
113    public static final int WIRE_TYPE_START_GROUP = 3;
114    public static final int WIRE_TYPE_END_GROUP = 4;
115    public static final int WIRE_TYPE_FIXED32 = 5;
116
117    /**
118     * Position of the field type in a (long) fieldId.
119     */
120    public static final int FIELD_TYPE_SHIFT = 32;
121
122    /**
123     * Mask for the field types stored in a fieldId.  Leaves a whole
124     * byte for future expansion, even though there are currently only 17 types.
125     */
126    public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT;
127
128    public static final long FIELD_TYPE_UNKNOWN = 0;
129
130    /**
131     * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly,
132     * so no extra mapping needs to be maintained in this case.
133     */
134    public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
135    public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
136    public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT;
137    public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT;
138    public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT;
139    public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT;
140    public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT;
141    public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT;
142    public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT;
143//  public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated.
144    public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT;
145    public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT;
146    public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT;
147    public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT;
148    public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT;
149    public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT;
150    public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT;
151    public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT;
152
153    private static final String[] FIELD_TYPE_NAMES = new String[] {
154        "Double",
155        "Float",
156        "Int64",
157        "UInt64",
158        "Int32",
159        "Fixed64",
160        "Fixed32",
161        "Bool",
162        "String",
163        "Group",  // This field is deprecated but reserved here for indexing.
164        "Message",
165        "Bytes",
166        "UInt32",
167        "Enum",
168        "SFixed32",
169        "SFixed64",
170        "SInt32",
171        "SInt64",
172    };
173
174    //
175    // FieldId flags for whether the field is single, repeated or packed.
176    //
177    public static final int FIELD_COUNT_SHIFT = 40;
178    public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT;
179
180    public static final long FIELD_COUNT_UNKNOWN = 0;
181    public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
182    public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
183    public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT;
184
185    /**
186     * Our buffer.
187     */
188    private EncodedBuffer mBuffer;
189
190    /**
191     * Our stream.  If there is one.
192     */
193    private OutputStream mStream;
194
195    /**
196     * Current nesting depth of startObject calls.
197     */
198    private int mDepth;
199
200    /**
201     * An ID given to objects and returned in the token from startObject
202     * and stored in the buffer until endObject is called, where the two
203     * are checked.  Starts at -1 and becomes more negative, so the values
204     * aren't likely to alias with the size it will be overwritten with,
205     * which tend to be small, and we will be more likely to catch when
206     * the caller of endObject uses a stale token that they didn't intend
207     * to (e.g. copy and paste error).
208     */
209    private int mNextObjectId = -1;
210
211    /**
212     * The object token we are expecting in endObject.  If another call to
213     * startObject happens, this is written to that location, which gives
214     * us a stack, stored in the space for the as-yet unused size fields.
215     */
216    private long mExpectedObjectToken;
217
218    /**
219     * Index in mBuffer that we should start copying from on the next
220     * pass of compaction.
221     */
222    private int mCopyBegin;
223
224    /**
225     * Whether we've already compacted
226     */
227    private boolean mCompacted;
228
229    /**
230     * Construct a ProtoOutputStream with the default chunk size.
231     */
232    public ProtoOutputStream() {
233        this(0);
234    }
235
236    /**
237     * Construct a ProtoOutputStream with the given chunk size.
238     */
239    public ProtoOutputStream(int chunkSize) {
240        mBuffer = new EncodedBuffer(chunkSize);
241    }
242
243    /**
244     * Construct a ProtoOutputStream that sits on top of an OutputStream.
245     * @more
246     * The {@link #flush() flush()} method must be called when done writing
247     * to flush any remanining data, althought data *may* be written at intermediate
248     * points within the writing as well.
249     */
250    public ProtoOutputStream(OutputStream stream) {
251        this();
252        mStream = stream;
253    }
254
255    /**
256     * Construct a ProtoOutputStream that sits on top of a FileDescriptor.
257     * @more
258     * The {@link #flush() flush()} method must be called when done writing
259     * to flush any remanining data, althought data *may* be written at intermediate
260     * points within the writing as well.
261     */
262    public ProtoOutputStream(FileDescriptor fd) {
263        this(new FileOutputStream(fd));
264    }
265
266    /**
267     * Write a value for the given fieldId.
268     *
269     * Will automatically convert for the following field types, and
270     * throw an exception for others: double, float, int32, int64, uint32, uint64,
271     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
272     *
273     * @param fieldId The field identifier constant from the generated class.
274     * @param val The value.
275     */
276    public void write(long fieldId, double val) {
277        assertNotCompacted();
278        final int id = (int)fieldId;
279
280        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
281            // double
282            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
283                writeDoubleImpl(id, (double)val);
284                break;
285            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
286            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
287                writeRepeatedDoubleImpl(id, (double)val);
288                break;
289            // float
290            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
291                writeFloatImpl(id, (float)val);
292                break;
293            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
294            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
295                writeRepeatedFloatImpl(id, (float)val);
296                break;
297            // int32
298            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
299                writeInt32Impl(id, (int)val);
300                break;
301            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
302            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
303                writeRepeatedInt32Impl(id, (int)val);
304                break;
305            // int64
306            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
307                writeInt64Impl(id, (long)val);
308                break;
309            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
310            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
311                writeRepeatedInt64Impl(id, (long)val);
312                break;
313            // uint32
314            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
315                writeUInt32Impl(id, (int)val);
316                break;
317            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
318            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
319                writeRepeatedUInt32Impl(id, (int)val);
320                break;
321            // uint64
322            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
323                writeUInt64Impl(id, (long)val);
324                break;
325            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
326            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
327                writeRepeatedUInt64Impl(id, (long)val);
328                break;
329            // sint32
330            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
331                writeSInt32Impl(id, (int)val);
332                break;
333            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
334            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
335                writeRepeatedSInt32Impl(id, (int)val);
336                break;
337            // sint64
338            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
339                writeSInt64Impl(id, (long)val);
340                break;
341            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
342            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
343                writeRepeatedSInt64Impl(id, (long)val);
344                break;
345            // fixed32
346            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
347                writeFixed32Impl(id, (int)val);
348                break;
349            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
350            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
351                writeRepeatedFixed32Impl(id, (int)val);
352                break;
353            // fixed64
354            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
355                writeFixed64Impl(id, (long)val);
356                break;
357            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
358            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
359                writeRepeatedFixed64Impl(id, (long)val);
360                break;
361            // sfixed32
362            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
363                writeSFixed32Impl(id, (int)val);
364                break;
365            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
366            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
367                writeRepeatedSFixed32Impl(id, (int)val);
368                break;
369            // sfixed64
370            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
371                writeSFixed64Impl(id, (long)val);
372                break;
373            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
374            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
375                writeRepeatedSFixed64Impl(id, (long)val);
376                break;
377            // bool
378            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
379                writeBoolImpl(id, val != 0);
380                break;
381            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
382            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
383                writeRepeatedBoolImpl(id, val != 0);
384                break;
385            // enum
386            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
387                writeEnumImpl(id, (int)val);
388                break;
389            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
390            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
391                writeRepeatedEnumImpl(id, (int)val);
392                break;
393            // string, bytes, object not allowed here.
394            default: {
395                throw new IllegalArgumentException("Attempt to call write(long, double) with "
396                        + getFieldIdString(fieldId));
397            }
398        }
399    }
400
401    /**
402     * Write a value for the given fieldId.
403     *
404     * Will automatically convert for the following field types, and
405     * throw an exception for others: double, float, int32, int64, uint32, uint64,
406     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
407     *
408     * @param fieldId The field identifier constant from the generated class.
409     * @param val The value.
410     */
411    public void write(long fieldId, float val) {
412        assertNotCompacted();
413        final int id = (int)fieldId;
414
415        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
416            // double
417            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
418                writeDoubleImpl(id, (double)val);
419                break;
420            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
421            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
422                writeRepeatedDoubleImpl(id, (double)val);
423                break;
424            // float
425            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
426                writeFloatImpl(id, (float)val);
427                break;
428            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
429            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
430                writeRepeatedFloatImpl(id, (float)val);
431                break;
432            // int32
433            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
434                writeInt32Impl(id, (int)val);
435                break;
436            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
437            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
438                writeRepeatedInt32Impl(id, (int)val);
439                break;
440            // int64
441            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
442                writeInt64Impl(id, (long)val);
443                break;
444            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
445            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
446                writeRepeatedInt64Impl(id, (long)val);
447                break;
448            // uint32
449            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
450                writeUInt32Impl(id, (int)val);
451                break;
452            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
453            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
454                writeRepeatedUInt32Impl(id, (int)val);
455                break;
456            // uint64
457            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
458                writeUInt64Impl(id, (long)val);
459                break;
460            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
461            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
462                writeRepeatedUInt64Impl(id, (long)val);
463                break;
464            // sint32
465            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
466                writeSInt32Impl(id, (int)val);
467                break;
468            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
469            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
470                writeRepeatedSInt32Impl(id, (int)val);
471                break;
472            // sint64
473            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
474                writeSInt64Impl(id, (long)val);
475                break;
476            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
477            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
478                writeRepeatedSInt64Impl(id, (long)val);
479                break;
480            // fixed32
481            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
482                writeFixed32Impl(id, (int)val);
483                break;
484            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
485            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
486                writeRepeatedFixed32Impl(id, (int)val);
487                break;
488            // fixed64
489            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
490                writeFixed64Impl(id, (long)val);
491                break;
492            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
493            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
494                writeRepeatedFixed64Impl(id, (long)val);
495                break;
496            // sfixed32
497            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
498                writeSFixed32Impl(id, (int)val);
499                break;
500            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
501            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
502                writeRepeatedSFixed32Impl(id, (int)val);
503                break;
504            // sfixed64
505            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
506                writeSFixed64Impl(id, (long)val);
507                break;
508            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
509            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
510                writeRepeatedSFixed64Impl(id, (long)val);
511                break;
512            // bool
513            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
514                writeBoolImpl(id, val != 0);
515                break;
516            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
517            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
518                writeRepeatedBoolImpl(id, val != 0);
519                break;
520            // enum
521            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
522                writeEnumImpl(id, (int)val);
523                break;
524            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
525            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
526                writeRepeatedEnumImpl(id, (int)val);
527                break;
528            // string, bytes, object not allowed here.
529            default: {
530                throw new IllegalArgumentException("Attempt to call write(long, float) with "
531                        + getFieldIdString(fieldId));
532            }
533        }
534    }
535
536    /**
537     * Write a value for the given fieldId.
538     *
539     * Will automatically convert for the following field types, and
540     * throw an exception for others: double, float, int32, int64, uint32, uint64,
541     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
542     *
543     * @param fieldId The field identifier constant from the generated class.
544     * @param val The value.
545     */
546    public void write(long fieldId, int val) {
547        assertNotCompacted();
548        final int id = (int)fieldId;
549
550        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
551            // double
552            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
553                writeDoubleImpl(id, (double)val);
554                break;
555            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
556            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
557                writeRepeatedDoubleImpl(id, (double)val);
558                break;
559            // float
560            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
561                writeFloatImpl(id, (float)val);
562                break;
563            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
564            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
565                writeRepeatedFloatImpl(id, (float)val);
566                break;
567            // int32
568            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
569                writeInt32Impl(id, (int)val);
570                break;
571            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
572            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
573                writeRepeatedInt32Impl(id, (int)val);
574                break;
575            // int64
576            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
577                writeInt64Impl(id, (long)val);
578                break;
579            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
580            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
581                writeRepeatedInt64Impl(id, (long)val);
582                break;
583            // uint32
584            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
585                writeUInt32Impl(id, (int)val);
586                break;
587            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
588            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
589                writeRepeatedUInt32Impl(id, (int)val);
590                break;
591            // uint64
592            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
593                writeUInt64Impl(id, (long)val);
594                break;
595            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
596            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
597                writeRepeatedUInt64Impl(id, (long)val);
598                break;
599            // sint32
600            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
601                writeSInt32Impl(id, (int)val);
602                break;
603            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
604            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
605                writeRepeatedSInt32Impl(id, (int)val);
606                break;
607            // sint64
608            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
609                writeSInt64Impl(id, (long)val);
610                break;
611            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
612            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
613                writeRepeatedSInt64Impl(id, (long)val);
614                break;
615            // fixed32
616            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
617                writeFixed32Impl(id, (int)val);
618                break;
619            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
620            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
621                writeRepeatedFixed32Impl(id, (int)val);
622                break;
623            // fixed64
624            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
625                writeFixed64Impl(id, (long)val);
626                break;
627            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
628            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
629                writeRepeatedFixed64Impl(id, (long)val);
630                break;
631            // sfixed32
632            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
633                writeSFixed32Impl(id, (int)val);
634                break;
635            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
636            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
637                writeRepeatedSFixed32Impl(id, (int)val);
638                break;
639            // sfixed64
640            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
641                writeSFixed64Impl(id, (long)val);
642                break;
643            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
644            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
645                writeRepeatedSFixed64Impl(id, (long)val);
646                break;
647            // bool
648            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
649                writeBoolImpl(id, val != 0);
650                break;
651            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
652            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
653                writeRepeatedBoolImpl(id, val != 0);
654                break;
655            // enum
656            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
657                writeEnumImpl(id, (int)val);
658                break;
659            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
660            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
661                writeRepeatedEnumImpl(id, (int)val);
662                break;
663            // string, bytes, object not allowed here.
664            default: {
665                throw new IllegalArgumentException("Attempt to call write(long, int) with "
666                        + getFieldIdString(fieldId));
667            }
668        }
669    }
670
671    /**
672     * Write a value for the given fieldId.
673     *
674     * Will automatically convert for the following field types, and
675     * throw an exception for others: double, float, int32, int64, uint32, uint64,
676     * sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
677     *
678     * @param fieldId The field identifier constant from the generated class.
679     * @param val The value.
680     */
681    public void write(long fieldId, long val) {
682        assertNotCompacted();
683        final int id = (int)fieldId;
684
685        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
686            // double
687            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
688                writeDoubleImpl(id, (double)val);
689                break;
690            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
691            case (int)((FIELD_TYPE_DOUBLE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
692                writeRepeatedDoubleImpl(id, (double)val);
693                break;
694            // float
695            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
696                writeFloatImpl(id, (float)val);
697                break;
698            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
699            case (int)((FIELD_TYPE_FLOAT | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
700                writeRepeatedFloatImpl(id, (float)val);
701                break;
702            // int32
703            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
704                writeInt32Impl(id, (int)val);
705                break;
706            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
707            case (int)((FIELD_TYPE_INT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
708                writeRepeatedInt32Impl(id, (int)val);
709                break;
710            // int64
711            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
712                writeInt64Impl(id, (long)val);
713                break;
714            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
715            case (int)((FIELD_TYPE_INT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
716                writeRepeatedInt64Impl(id, (long)val);
717                break;
718            // uint32
719            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
720                writeUInt32Impl(id, (int)val);
721                break;
722            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
723            case (int)((FIELD_TYPE_UINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
724                writeRepeatedUInt32Impl(id, (int)val);
725                break;
726            // uint64
727            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
728                writeUInt64Impl(id, (long)val);
729                break;
730            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
731            case (int)((FIELD_TYPE_UINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
732                writeRepeatedUInt64Impl(id, (long)val);
733                break;
734            // sint32
735            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
736                writeSInt32Impl(id, (int)val);
737                break;
738            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
739            case (int)((FIELD_TYPE_SINT32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
740                writeRepeatedSInt32Impl(id, (int)val);
741                break;
742            // sint64
743            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
744                writeSInt64Impl(id, (long)val);
745                break;
746            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
747            case (int)((FIELD_TYPE_SINT64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
748                writeRepeatedSInt64Impl(id, (long)val);
749                break;
750            // fixed32
751            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
752                writeFixed32Impl(id, (int)val);
753                break;
754            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
755            case (int)((FIELD_TYPE_FIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
756                writeRepeatedFixed32Impl(id, (int)val);
757                break;
758            // fixed64
759            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
760                writeFixed64Impl(id, (long)val);
761                break;
762            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
763            case (int)((FIELD_TYPE_FIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
764                writeRepeatedFixed64Impl(id, (long)val);
765                break;
766            // sfixed32
767            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
768                writeSFixed32Impl(id, (int)val);
769                break;
770            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
771            case (int)((FIELD_TYPE_SFIXED32 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
772                writeRepeatedSFixed32Impl(id, (int)val);
773                break;
774            // sfixed64
775            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
776                writeSFixed64Impl(id, (long)val);
777                break;
778            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
779            case (int)((FIELD_TYPE_SFIXED64 | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
780                writeRepeatedSFixed64Impl(id, (long)val);
781                break;
782            // bool
783            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
784                writeBoolImpl(id, val != 0);
785                break;
786            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
787            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
788                writeRepeatedBoolImpl(id, val != 0);
789                break;
790            // enum
791            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
792                writeEnumImpl(id, (int)val);
793                break;
794            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
795            case (int)((FIELD_TYPE_ENUM | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
796                writeRepeatedEnumImpl(id, (int)val);
797                break;
798            // string, bytes, object not allowed here.
799            default: {
800                throw new IllegalArgumentException("Attempt to call write(long, long) with "
801                        + getFieldIdString(fieldId));
802            }
803        }
804    }
805
806    /**
807     * Write a boolean value for the given fieldId.
808     *
809     * If the field is not a bool field, an exception will be thrown.
810     *
811     * @param fieldId The field identifier constant from the generated class.
812     * @param val The value.
813     */
814    public void write(long fieldId, boolean val) {
815        assertNotCompacted();
816        final int id = (int)fieldId;
817
818        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
819            // bool
820            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
821                writeBoolImpl(id, val);
822                break;
823            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
824            case (int)((FIELD_TYPE_BOOL | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
825                writeRepeatedBoolImpl(id, val);
826                break;
827            // nothing else allowed
828            default: {
829                throw new IllegalArgumentException("Attempt to call write(long, boolean) with "
830                        + getFieldIdString(fieldId));
831            }
832        }
833    }
834
835    /**
836     * Write a string value for the given fieldId.
837     *
838     * If the field is not a string field, an exception will be thrown.
839     *
840     * @param fieldId The field identifier constant from the generated class.
841     * @param val The value.
842     */
843    public void write(long fieldId, String val) {
844        assertNotCompacted();
845        final int id = (int)fieldId;
846
847        switch ((int)((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
848            // string
849            case (int)((FIELD_TYPE_STRING | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
850                writeStringImpl(id, val);
851                break;
852            case (int)((FIELD_TYPE_STRING | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
853            case (int)((FIELD_TYPE_STRING | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
854                writeRepeatedStringImpl(id, val);
855                break;
856            // nothing else allowed
857            default: {
858                throw new IllegalArgumentException("Attempt to call write(long, String) with "
859                        + getFieldIdString(fieldId));
860            }
861        }
862    }
863
864    /**
865     * Write a byte[] value for the given fieldId.
866     *
867     * If the field is not a bytes or object field, an exception will be thrown.
868     *
869     * @param fieldId The field identifier constant from the generated class.
870     * @param val The value.
871     */
872    public void write(long fieldId, byte[] val) {
873        assertNotCompacted();
874        final int id = (int)fieldId;
875
876        switch ((int) ((fieldId & (FIELD_TYPE_MASK | FIELD_COUNT_MASK)) >> FIELD_TYPE_SHIFT)) {
877            // bytes
878            case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
879                writeBytesImpl(id, val);
880                break;
881            case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
882            case (int) ((FIELD_TYPE_BYTES | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
883                writeRepeatedBytesImpl(id, val);
884                break;
885            // Object
886            case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_SINGLE) >> FIELD_TYPE_SHIFT):
887                writeObjectImpl(id, val);
888                break;
889            case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED) >> FIELD_TYPE_SHIFT):
890            case (int) ((FIELD_TYPE_MESSAGE | FIELD_COUNT_PACKED) >> FIELD_TYPE_SHIFT):
891                writeRepeatedObjectImpl(id, val);
892                break;
893            // nothing else allowed
894            default: {
895                throw new IllegalArgumentException("Attempt to call write(long, byte[]) with "
896                        + getFieldIdString(fieldId));
897            }
898        }
899    }
900
901    /**
902     * Start a sub object.
903     */
904    public long start(long fieldId) {
905        assertNotCompacted();
906        final int id = (int)fieldId;
907
908        if ((fieldId & FIELD_TYPE_MASK) == FIELD_TYPE_MESSAGE) {
909            final long count = fieldId & FIELD_COUNT_MASK;
910            if (count == FIELD_COUNT_SINGLE) {
911                return startObjectImpl(id, false);
912            } else if (count == FIELD_COUNT_REPEATED || count == FIELD_COUNT_PACKED) {
913                return startObjectImpl(id, true);
914            }
915        }
916        throw new IllegalArgumentException("Attempt to call start(long) with "
917                + getFieldIdString(fieldId));
918    }
919
920    /**
921     * End the object started by start() that returned token.
922     */
923    public void end(long token) {
924        endObjectImpl(token, getRepeatedFromToken(token));
925    }
926
927    //
928    // proto3 type: double
929    // java type: double
930    // encoding: fixed64
931    // wire type: WIRE_TYPE_FIXED64
932    //
933
934    /**
935     * Write a single proto "double" type field value.
936     *
937     * @deprecated Use #write instead.
938     */
939    @Deprecated
940    public void writeDouble(long fieldId, double val) {
941        assertNotCompacted();
942        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE);
943
944        writeDoubleImpl(id, val);
945    }
946
947    private void writeDoubleImpl(int id, double val) {
948        if (val != 0) {
949            writeTag(id, WIRE_TYPE_FIXED64);
950            mBuffer.writeRawFixed64(Double.doubleToLongBits(val));
951        }
952    }
953
954    /**
955     * Write a single repeated proto "double" type field value.
956     *
957     * @deprecated Use #write instead.
958     */
959    @Deprecated
960    public void writeRepeatedDouble(long fieldId, double val) {
961        assertNotCompacted();
962        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_DOUBLE);
963
964        writeRepeatedDoubleImpl(id, val);
965    }
966
967    private void writeRepeatedDoubleImpl(int id, double val) {
968        writeTag(id, WIRE_TYPE_FIXED64);
969        mBuffer.writeRawFixed64(Double.doubleToLongBits(val));
970    }
971
972    /**
973     * Write a list of packed proto "double" type field values.
974     *
975     * @deprecated Use #write instead.
976     */
977    @Deprecated
978    public void writePackedDouble(long fieldId, double[] val) {
979        assertNotCompacted();
980        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_DOUBLE);
981
982        final int N = val != null ? val.length : 0;
983        if (N > 0) {
984            writeKnownLengthHeader(id, N * 8);
985            for (int i=0; i<N; i++) {
986                mBuffer.writeRawFixed64(Double.doubleToLongBits(val[i]));
987            }
988        }
989    }
990
991    //
992    // proto3 type: float
993    // java type: float
994    // encoding: fixed32
995    // wire type: WIRE_TYPE_FIXED32
996    //
997
998    /**
999     * Write a single proto "float" type field value.
1000     *
1001     * @deprecated Use #write instead.
1002     */
1003    @Deprecated
1004    public void writeFloat(long fieldId, float val) {
1005        assertNotCompacted();
1006        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT);
1007
1008        writeFloatImpl(id, val);
1009    }
1010
1011    private void writeFloatImpl(int id, float val) {
1012        if (val != 0) {
1013            writeTag(id, WIRE_TYPE_FIXED32);
1014            mBuffer.writeRawFixed32(Float.floatToIntBits(val));
1015        }
1016    }
1017
1018    /**
1019     * Write a single repeated proto "float" type field value.
1020     *
1021     * @deprecated Use #write instead.
1022     */
1023    @Deprecated
1024    public void writeRepeatedFloat(long fieldId, float val) {
1025        assertNotCompacted();
1026        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_FLOAT);
1027
1028        writeRepeatedFloatImpl(id, val);
1029    }
1030
1031    private void writeRepeatedFloatImpl(int id, float val) {
1032        writeTag(id, WIRE_TYPE_FIXED32);
1033        mBuffer.writeRawFixed32(Float.floatToIntBits(val));
1034    }
1035
1036    /**
1037     * Write a list of packed proto "float" type field value.
1038     *
1039     * @deprecated Use #write instead.
1040     */
1041    @Deprecated
1042    public void writePackedFloat(long fieldId, float[] val) {
1043        assertNotCompacted();
1044        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FLOAT);
1045
1046        final int N = val != null ? val.length : 0;
1047        if (N > 0) {
1048            writeKnownLengthHeader(id, N * 4);
1049            for (int i=0; i<N; i++) {
1050                mBuffer.writeRawFixed32(Float.floatToIntBits(val[i]));
1051            }
1052        }
1053    }
1054
1055    //
1056    // proto3 type: int32
1057    // java type: int
1058    // signed/unsigned: signed
1059    // encoding: varint
1060    // wire type: WIRE_TYPE_VARINT
1061    //
1062
1063    /**
1064     * Writes a java int as an usigned varint.
1065     *
1066     * The unadorned int32 type in protobuf is unfortunate because it
1067     * is stored in memory as a signed value, but encodes as unsigned
1068     * varints, which are formally always longs.  So here, we encode
1069     * negative values as 64 bits, which will get the sign-extension,
1070     * and positive values as 32 bits, which saves a marginal amount
1071     * of work in that it processes ints instead of longs.
1072     */
1073    private void writeUnsignedVarintFromSignedInt(int val) {
1074        if (val >= 0) {
1075            mBuffer.writeRawVarint32(val);
1076        } else {
1077            mBuffer.writeRawVarint64(val);
1078        }
1079    }
1080
1081    /**
1082     * Write a single proto "int32" type field value.
1083     *
1084     * Note that these are stored in memory as signed values and written as unsigned
1085     * varints, which if negative, are 10 bytes long. If you know the data is likely
1086     * to be negative, use "sint32".
1087     *
1088     * @deprecated Use #write instead.
1089     */
1090    @Deprecated
1091    public void writeInt32(long fieldId, int val) {
1092        assertNotCompacted();
1093        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_INT32);
1094
1095        writeInt32Impl(id, val);
1096    }
1097
1098    private void writeInt32Impl(int id, int val) {
1099        if (val != 0) {
1100            writeTag(id, WIRE_TYPE_VARINT);
1101            writeUnsignedVarintFromSignedInt(val);
1102        }
1103    }
1104
1105    /**
1106     * Write a single repeated proto "int32" type field value.
1107     *
1108     * Note that these are stored in memory as signed values and written as unsigned
1109     * varints, which if negative, are 10 bytes long. If you know the data is likely
1110     * to be negative, use "sint32".
1111     *
1112     * @deprecated Use #write instead.
1113     */
1114    @Deprecated
1115    public void writeRepeatedInt32(long fieldId, int val) {
1116        assertNotCompacted();
1117        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_INT32);
1118
1119        writeRepeatedInt32Impl(id, val);
1120    }
1121
1122    private void writeRepeatedInt32Impl(int id, int val) {
1123        writeTag(id, WIRE_TYPE_VARINT);
1124        writeUnsignedVarintFromSignedInt(val);
1125    }
1126
1127    /**
1128     * Write a list of packed proto "int32" type field value.
1129     *
1130     * Note that these are stored in memory as signed values and written as unsigned
1131     * varints, which if negative, are 10 bytes long. If you know the data is likely
1132     * to be negative, use "sint32".
1133     *
1134     * @deprecated Use #write instead.
1135     */
1136    @Deprecated
1137    public void writePackedInt32(long fieldId, int[] val) {
1138        assertNotCompacted();
1139        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_INT32);
1140
1141        final int N = val != null ? val.length : 0;
1142        if (N > 0) {
1143            int size = 0;
1144            for (int i=0; i<N; i++) {
1145                final int v = val[i];
1146                size += v >= 0 ? EncodedBuffer.getRawVarint32Size(v) : 10;
1147            }
1148            writeKnownLengthHeader(id, size);
1149            for (int i=0; i<N; i++) {
1150                writeUnsignedVarintFromSignedInt(val[i]);
1151            }
1152        }
1153    }
1154
1155    //
1156    // proto3 type: int64
1157    // java type: int
1158    // signed/unsigned: signed
1159    // encoding: varint
1160    // wire type: WIRE_TYPE_VARINT
1161    //
1162
1163    /**
1164     * Write a single proto "int64" type field value.
1165     *
1166     * @deprecated Use #write instead.
1167     */
1168    @Deprecated
1169    public void writeInt64(long fieldId, long val) {
1170        assertNotCompacted();
1171        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_INT64);
1172
1173        writeInt64Impl(id, val);
1174    }
1175
1176    private void writeInt64Impl(int id, long val) {
1177        if (val != 0) {
1178            writeTag(id, WIRE_TYPE_VARINT);
1179            mBuffer.writeRawVarint64(val);
1180        }
1181    }
1182
1183    /**
1184     * Write a single repeated proto "int64" type field value.
1185     *
1186     * @deprecated Use #write instead.
1187     */
1188    @Deprecated
1189    public void writeRepeatedInt64(long fieldId, long val) {
1190        assertNotCompacted();
1191        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_INT64);
1192
1193        writeRepeatedInt64Impl(id, val);
1194    }
1195
1196    private void writeRepeatedInt64Impl(int id, long val) {
1197        writeTag(id, WIRE_TYPE_VARINT);
1198        mBuffer.writeRawVarint64(val);
1199    }
1200
1201    /**
1202     * Write a list of packed proto "int64" type field value.
1203     *
1204     * @deprecated Use #write instead.
1205     */
1206    @Deprecated
1207    public void writePackedInt64(long fieldId, long[] val) {
1208        assertNotCompacted();
1209        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_INT64);
1210
1211        final int N = val != null ? val.length : 0;
1212        if (N > 0) {
1213            int size = 0;
1214            for (int i=0; i<N; i++) {
1215                size += EncodedBuffer.getRawVarint64Size(val[i]);
1216            }
1217            writeKnownLengthHeader(id, size);
1218            for (int i=0; i<N; i++) {
1219                mBuffer.writeRawVarint64(val[i]);
1220            }
1221        }
1222    }
1223
1224    //
1225    // proto3 type: uint32
1226    // java type: int
1227    // signed/unsigned: unsigned
1228    // encoding: varint
1229    // wire type: WIRE_TYPE_VARINT
1230    //
1231
1232    /**
1233     * Write a single proto "uint32" type field value.
1234     *
1235     * @deprecated Use #write instead.
1236     */
1237    @Deprecated
1238    public void writeUInt32(long fieldId, int val) {
1239        assertNotCompacted();
1240        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32);
1241
1242        writeUInt32Impl(id, val);
1243    }
1244
1245    private void writeUInt32Impl(int id, int val) {
1246        if (val != 0) {
1247            writeTag(id, WIRE_TYPE_VARINT);
1248            mBuffer.writeRawVarint32(val);
1249        }
1250    }
1251
1252    /**
1253     * Write a single repeated proto "uint32" type field value.
1254     *
1255     * @deprecated Use #write instead.
1256     */
1257    @Deprecated
1258    public void writeRepeatedUInt32(long fieldId, int val) {
1259        assertNotCompacted();
1260        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_UINT32);
1261
1262        writeRepeatedUInt32Impl(id, val);
1263    }
1264
1265    private void writeRepeatedUInt32Impl(int id, int val) {
1266        writeTag(id, WIRE_TYPE_VARINT);
1267        mBuffer.writeRawVarint32(val);
1268    }
1269
1270    /**
1271     * Write a list of packed proto "uint32" type field value.
1272     *
1273     * @deprecated Use #write instead.
1274     */
1275    @Deprecated
1276    public void writePackedUInt32(long fieldId, int[] val) {
1277        assertNotCompacted();
1278        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_UINT32);
1279
1280        final int N = val != null ? val.length : 0;
1281        if (N > 0) {
1282            int size = 0;
1283            for (int i=0; i<N; i++) {
1284                size += EncodedBuffer.getRawVarint32Size(val[i]);
1285            }
1286            writeKnownLengthHeader(id, size);
1287            for (int i=0; i<N; i++) {
1288                mBuffer.writeRawVarint32(val[i]);
1289            }
1290        }
1291    }
1292
1293    //
1294    // proto3 type: uint64
1295    // java type: int
1296    // signed/unsigned: unsigned
1297    // encoding: varint
1298    // wire type: WIRE_TYPE_VARINT
1299    //
1300
1301    /**
1302     * Write a single proto "uint64" type field value.
1303     *
1304     * @deprecated Use #write instead.
1305     */
1306    @Deprecated
1307    public void writeUInt64(long fieldId, long val) {
1308        assertNotCompacted();
1309        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64);
1310
1311        writeUInt64Impl(id, val);
1312    }
1313
1314    private void writeUInt64Impl(int id, long val) {
1315        if (val != 0) {
1316            writeTag(id, WIRE_TYPE_VARINT);
1317            mBuffer.writeRawVarint64(val);
1318        }
1319    }
1320
1321    /**
1322     * Write a single proto "uint64" type field value.
1323     *
1324     * @deprecated Use #write instead.
1325     */
1326    @Deprecated
1327    public void writeRepeatedUInt64(long fieldId, long val) {
1328        assertNotCompacted();
1329        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_UINT64);
1330
1331        writeRepeatedUInt64Impl(id, val);
1332    }
1333
1334    private void writeRepeatedUInt64Impl(int id, long val) {
1335        writeTag(id, WIRE_TYPE_VARINT);
1336        mBuffer.writeRawVarint64(val);
1337    }
1338
1339    /**
1340     * Write a single proto "uint64" type field value.
1341     *
1342     * @deprecated Use #write instead.
1343     */
1344    @Deprecated
1345    public void writePackedUInt64(long fieldId, long[] val) {
1346        assertNotCompacted();
1347        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_UINT64);
1348
1349        final int N = val != null ? val.length : 0;
1350        if (N > 0) {
1351            int size = 0;
1352            for (int i=0; i<N; i++) {
1353                size += EncodedBuffer.getRawVarint64Size(val[i]);
1354            }
1355            writeKnownLengthHeader(id, size);
1356            for (int i=0; i<N; i++) {
1357                mBuffer.writeRawVarint64(val[i]);
1358            }
1359        }
1360    }
1361
1362    //
1363    // proto3 type: sint32
1364    // java type: int
1365    // signed/unsigned: signed
1366    // encoding: zig-zag
1367    // wire type: WIRE_TYPE_VARINT
1368    //
1369
1370    /**
1371     * Write a single proto "sint32" type field value.
1372     *
1373     * @deprecated Use #write instead.
1374     */
1375    @Deprecated
1376    public void writeSInt32(long fieldId, int val) {
1377        assertNotCompacted();
1378        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32);
1379
1380        writeSInt32Impl(id, val);
1381    }
1382
1383    private void writeSInt32Impl(int id, int val) {
1384        if (val != 0) {
1385            writeTag(id, WIRE_TYPE_VARINT);
1386            mBuffer.writeRawZigZag32(val);
1387        }
1388    }
1389
1390    /**
1391     * Write a single repeated proto "sint32" type field value.
1392     *
1393     * @deprecated Use #write instead.
1394     */
1395    @Deprecated
1396    public void writeRepeatedSInt32(long fieldId, int val) {
1397        assertNotCompacted();
1398        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SINT32);
1399
1400        writeRepeatedSInt32Impl(id, val);
1401    }
1402
1403    private void writeRepeatedSInt32Impl(int id, int val) {
1404        writeTag(id, WIRE_TYPE_VARINT);
1405        mBuffer.writeRawZigZag32(val);
1406    }
1407
1408    /**
1409     * Write a list of packed proto "sint32" type field value.
1410     *
1411     * @deprecated Use #write instead.
1412     */
1413    @Deprecated
1414    public void writePackedSInt32(long fieldId, int[] val) {
1415        assertNotCompacted();
1416        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SINT32);
1417
1418        final int N = val != null ? val.length : 0;
1419        if (N > 0) {
1420            int size = 0;
1421            for (int i=0; i<N; i++) {
1422                size += EncodedBuffer.getRawZigZag32Size(val[i]);
1423            }
1424            writeKnownLengthHeader(id, size);
1425            for (int i=0; i<N; i++) {
1426                mBuffer.writeRawZigZag32(val[i]);
1427            }
1428        }
1429    }
1430
1431    //
1432    // proto3 type: sint64
1433    // java type: int
1434    // signed/unsigned: signed
1435    // encoding: zig-zag
1436    // wire type: WIRE_TYPE_VARINT
1437    //
1438
1439    /**
1440     * Write a single proto "sint64" type field value.
1441     *
1442     * @deprecated Use #write instead.
1443     */
1444    @Deprecated
1445    public void writeSInt64(long fieldId, long val) {
1446        assertNotCompacted();
1447        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64);
1448
1449        writeSInt64Impl(id, val);
1450    }
1451
1452    private void writeSInt64Impl(int id, long val) {
1453        if (val != 0) {
1454            writeTag(id, WIRE_TYPE_VARINT);
1455            mBuffer.writeRawZigZag64(val);
1456        }
1457    }
1458
1459    /**
1460     * Write a single repeated proto "sint64" type field value.
1461     *
1462     * @deprecated Use #write instead.
1463     */
1464    @Deprecated
1465    public void writeRepeatedSInt64(long fieldId, long val) {
1466        assertNotCompacted();
1467        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SINT64);
1468
1469        writeRepeatedSInt64Impl(id, val);
1470    }
1471
1472    private void writeRepeatedSInt64Impl(int id, long val) {
1473        writeTag(id, WIRE_TYPE_VARINT);
1474        mBuffer.writeRawZigZag64(val);
1475    }
1476
1477    /**
1478     * Write a list of packed proto "sint64" type field value.
1479     *
1480     * @deprecated Use #write instead.
1481     */
1482    @Deprecated
1483    public void writePackedSInt64(long fieldId, long[] val) {
1484        assertNotCompacted();
1485        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SINT64);
1486
1487        final int N = val != null ? val.length : 0;
1488        if (N > 0) {
1489            int size = 0;
1490            for (int i=0; i<N; i++) {
1491                size += EncodedBuffer.getRawZigZag64Size(val[i]);
1492            }
1493            writeKnownLengthHeader(id, size);
1494            for (int i=0; i<N; i++) {
1495                mBuffer.writeRawZigZag64(val[i]);
1496            }
1497        }
1498    }
1499
1500    //
1501    // proto3 type: fixed32
1502    // java type: int
1503    // encoding: little endian
1504    // wire type: WIRE_TYPE_FIXED32
1505    //
1506
1507    /**
1508     * Write a single proto "fixed32" type field value.
1509     *
1510     * @deprecated Use #write instead.
1511     */
1512    @Deprecated
1513    public void writeFixed32(long fieldId, int val) {
1514        assertNotCompacted();
1515        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32);
1516
1517        writeFixed32Impl(id, val);
1518    }
1519
1520    private void writeFixed32Impl(int id, int val) {
1521        if (val != 0) {
1522            writeTag(id, WIRE_TYPE_FIXED32);
1523            mBuffer.writeRawFixed32(val);
1524        }
1525    }
1526
1527    /**
1528     * Write a single repeated proto "fixed32" type field value.
1529     *
1530     * @deprecated Use #write instead.
1531     */
1532    @Deprecated
1533    public void writeRepeatedFixed32(long fieldId, int val) {
1534        assertNotCompacted();
1535        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_FIXED32);
1536
1537        writeRepeatedFixed32Impl(id, val);
1538    }
1539
1540    private void writeRepeatedFixed32Impl(int id, int val) {
1541        writeTag(id, WIRE_TYPE_FIXED32);
1542        mBuffer.writeRawFixed32(val);
1543    }
1544
1545    /**
1546     * Write a list of packed proto "fixed32" type field value.
1547     *
1548     * @deprecated Use #write instead.
1549     */
1550    @Deprecated
1551    public void writePackedFixed32(long fieldId, int[] val) {
1552        assertNotCompacted();
1553        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FIXED32);
1554
1555        final int N = val != null ? val.length : 0;
1556        if (N > 0) {
1557            writeKnownLengthHeader(id, N * 4);
1558            for (int i=0; i<N; i++) {
1559                mBuffer.writeRawFixed32(val[i]);
1560            }
1561        }
1562    }
1563
1564    //
1565    // proto3 type: fixed64
1566    // java type: long
1567    // encoding: fixed64
1568    // wire type: WIRE_TYPE_FIXED64
1569    //
1570
1571    /**
1572     * Write a single proto "fixed64" type field value.
1573     *
1574     * @deprecated Use #write instead.
1575     */
1576    @Deprecated
1577    public void writeFixed64(long fieldId, long val) {
1578        assertNotCompacted();
1579        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64);
1580
1581        writeFixed64Impl(id, val);
1582    }
1583
1584    private void writeFixed64Impl(int id, long val) {
1585        if (val != 0) {
1586            writeTag(id, WIRE_TYPE_FIXED64);
1587            mBuffer.writeRawFixed64(val);
1588        }
1589    }
1590
1591    /**
1592     * Write a single repeated proto "fixed64" type field value.
1593     *
1594     * @deprecated Use #write instead.
1595     */
1596    @Deprecated
1597    public void writeRepeatedFixed64(long fieldId, long val) {
1598        assertNotCompacted();
1599        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_FIXED64);
1600
1601        writeRepeatedFixed64Impl(id, val);
1602    }
1603
1604    private void writeRepeatedFixed64Impl(int id, long val) {
1605        writeTag(id, WIRE_TYPE_FIXED64);
1606        mBuffer.writeRawFixed64(val);
1607    }
1608
1609    /**
1610     * Write a list of packed proto "fixed64" type field value.
1611     *
1612     * @deprecated Use #write instead.
1613     */
1614    @Deprecated
1615    public void writePackedFixed64(long fieldId, long[] val) {
1616        assertNotCompacted();
1617        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FIXED64);
1618
1619        final int N = val != null ? val.length : 0;
1620        if (N > 0) {
1621            writeKnownLengthHeader(id, N * 8);
1622            for (int i=0; i<N; i++) {
1623                mBuffer.writeRawFixed64(val[i]);
1624            }
1625        }
1626    }
1627
1628    //
1629    // proto3 type: sfixed32
1630    // java type: int
1631    // encoding: little endian
1632    // wire type: WIRE_TYPE_FIXED32
1633    //
1634    /**
1635     * Write a single proto "sfixed32" type field value.
1636     *
1637     * @deprecated Use #write instead.
1638     */
1639    @Deprecated
1640    public void writeSFixed32(long fieldId, int val) {
1641        assertNotCompacted();
1642        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32);
1643
1644        writeSFixed32Impl(id, val);
1645    }
1646
1647    private void writeSFixed32Impl(int id, int val) {
1648        if (val != 0) {
1649            writeTag(id, WIRE_TYPE_FIXED32);
1650            mBuffer.writeRawFixed32(val);
1651        }
1652    }
1653
1654    /**
1655     * Write a single repeated proto "sfixed32" type field value.
1656     *
1657     * @deprecated Use #write instead.
1658     */
1659    @Deprecated
1660    public void writeRepeatedSFixed32(long fieldId, int val) {
1661        assertNotCompacted();
1662        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SFIXED32);
1663
1664        writeRepeatedSFixed32Impl(id, val);
1665    }
1666
1667    private void writeRepeatedSFixed32Impl(int id, int val) {
1668        writeTag(id, WIRE_TYPE_FIXED32);
1669        mBuffer.writeRawFixed32(val);
1670    }
1671
1672    /**
1673     * Write a list of packed proto "sfixed32" type field value.
1674     *
1675     * @deprecated Use #write instead.
1676     */
1677    @Deprecated
1678    public void writePackedSFixed32(long fieldId, int[] val) {
1679        assertNotCompacted();
1680        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SFIXED32);
1681
1682        final int N = val != null ? val.length : 0;
1683        if (N > 0) {
1684            writeKnownLengthHeader(id, N * 4);
1685            for (int i=0; i<N; i++) {
1686                mBuffer.writeRawFixed32(val[i]);
1687            }
1688        }
1689    }
1690
1691    //
1692    // proto3 type: sfixed64
1693    // java type: long
1694    // encoding: little endian
1695    // wire type: WIRE_TYPE_FIXED64
1696    //
1697
1698    /**
1699     * Write a single proto "sfixed64" type field value.
1700     *
1701     * @deprecated Use #write instead.
1702     */
1703    @Deprecated
1704    public void writeSFixed64(long fieldId, long val) {
1705        assertNotCompacted();
1706        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64);
1707
1708        writeSFixed64Impl(id, val);
1709    }
1710
1711    private void writeSFixed64Impl(int id, long val) {
1712        if (val != 0) {
1713            writeTag(id, WIRE_TYPE_FIXED64);
1714            mBuffer.writeRawFixed64(val);
1715        }
1716    }
1717
1718    /**
1719     * Write a single repeated proto "sfixed64" type field value.
1720     *
1721     * @deprecated Use #write instead.
1722     */
1723    @Deprecated
1724    public void writeRepeatedSFixed64(long fieldId, long val) {
1725        assertNotCompacted();
1726        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SFIXED64);
1727
1728        writeRepeatedSFixed64Impl(id, val);
1729    }
1730
1731    private void writeRepeatedSFixed64Impl(int id, long val) {
1732        writeTag(id, WIRE_TYPE_FIXED64);
1733        mBuffer.writeRawFixed64(val);
1734    }
1735
1736    /**
1737     * Write a list of packed proto "sfixed64" type field value.
1738     *
1739     * @deprecated Use #write instead.
1740     */
1741    @Deprecated
1742    public void writePackedSFixed64(long fieldId, long[] val) {
1743        assertNotCompacted();
1744        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SFIXED64);
1745
1746        final int N = val != null ? val.length : 0;
1747        if (N > 0) {
1748            writeKnownLengthHeader(id, N * 8);
1749            for (int i=0; i<N; i++) {
1750                mBuffer.writeRawFixed64(val[i]);
1751            }
1752        }
1753    }
1754
1755    //
1756    // proto3 type: bool
1757    // java type: boolean
1758    // encoding: varint
1759    // wire type: WIRE_TYPE_VARINT
1760    //
1761
1762    /**
1763     * Write a single proto "bool" type field value.
1764     *
1765     * @deprecated Use #write instead.
1766     */
1767    @Deprecated
1768    public void writeBool(long fieldId, boolean val) {
1769        assertNotCompacted();
1770        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL);
1771
1772        writeBoolImpl(id, val);
1773    }
1774
1775    private void writeBoolImpl(int id, boolean val) {
1776        if (val) {
1777            writeTag(id, WIRE_TYPE_VARINT);
1778            // 0 and 1 are the same as their varint counterparts
1779            mBuffer.writeRawByte((byte)1);
1780        }
1781    }
1782
1783    /**
1784     * Write a single repeated proto "bool" type field value.
1785     *
1786     * @deprecated Use #write instead.
1787     */
1788    @Deprecated
1789    public void writeRepeatedBool(long fieldId, boolean val) {
1790        assertNotCompacted();
1791        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_BOOL);
1792
1793        writeRepeatedBoolImpl(id, val);
1794    }
1795
1796    private void writeRepeatedBoolImpl(int id, boolean val) {
1797        writeTag(id, WIRE_TYPE_VARINT);
1798        mBuffer.writeRawByte((byte)(val ? 1 : 0));
1799    }
1800
1801    /**
1802     * Write a list of packed proto "bool" type field value.
1803     *
1804     * @deprecated Use #write instead.
1805     */
1806    @Deprecated
1807    public void writePackedBool(long fieldId, boolean[] val) {
1808        assertNotCompacted();
1809        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_BOOL);
1810
1811        final int N = val != null ? val.length : 0;
1812        if (N > 0) {
1813            // Write the header
1814            writeKnownLengthHeader(id, N);
1815
1816            // Write the data
1817            for (int i=0; i<N; i++) {
1818                // 0 and 1 are the same as their varint counterparts
1819                mBuffer.writeRawByte((byte)(val[i] ? 1 : 0));
1820            }
1821        }
1822    }
1823
1824    //
1825    // proto3 type: string
1826    // java type: String
1827    // encoding: utf-8
1828    // wire type: WIRE_TYPE_LENGTH_DELIMITED
1829    //
1830
1831    /**
1832     * Write a single proto "string" type field value.
1833     *
1834     * @deprecated Use #write instead.
1835     */
1836    @Deprecated
1837    public void writeString(long fieldId, String val) {
1838        assertNotCompacted();
1839        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_STRING);
1840
1841        writeStringImpl(id, val);
1842    }
1843
1844    private void writeStringImpl(int id, String val) {
1845        if (val != null && val.length() > 0) {
1846            writeUtf8String(id, val);
1847        }
1848    }
1849
1850    /**
1851     * Write a single repeated proto "string" type field value.
1852     *
1853     * @deprecated Use #write instead.
1854     */
1855    @Deprecated
1856    public void writeRepeatedString(long fieldId, String val) {
1857        assertNotCompacted();
1858        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_STRING);
1859
1860        writeRepeatedStringImpl(id, val);
1861    }
1862
1863    private void writeRepeatedStringImpl(int id, String val) {
1864        if (val == null || val.length() == 0) {
1865            writeKnownLengthHeader(id, 0);
1866        } else {
1867            writeUtf8String(id, val);
1868        }
1869    }
1870
1871    /**
1872     * Write a list of packed proto "string" type field value.
1873     */
1874    private void writeUtf8String(int id, String val) {
1875        // TODO: Is it worth converting by hand in order to not allocate?
1876        try {
1877            final byte[] buf = val.getBytes("UTF-8");
1878            writeKnownLengthHeader(id, buf.length);
1879            mBuffer.writeRawBuffer(buf);
1880        } catch (UnsupportedEncodingException ex) {
1881            throw new RuntimeException("not possible");
1882        }
1883    }
1884
1885    //
1886    // proto3 type: bytes
1887    // java type: byte[]
1888    // encoding: varint
1889    // wire type: WIRE_TYPE_VARINT
1890    //
1891
1892    /**
1893     * Write a single proto "bytes" type field value.
1894     *
1895     * @deprecated Use #write instead.
1896     */
1897    @Deprecated
1898    public void writeBytes(long fieldId, byte[] val) {
1899        assertNotCompacted();
1900        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES);
1901
1902        writeBytesImpl(id, val);
1903    }
1904
1905    private void writeBytesImpl(int id, byte[] val) {
1906        if (val != null && val.length > 0) {
1907            writeKnownLengthHeader(id, val.length);
1908            mBuffer.writeRawBuffer(val);
1909        }
1910    }
1911
1912    /**
1913     * Write a single repeated proto "bytes" type field value.
1914     *
1915     * @deprecated Use #write instead.
1916     */
1917    @Deprecated
1918    public void writeRepeatedBytes(long fieldId, byte[] val) {
1919        assertNotCompacted();
1920        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_BYTES);
1921
1922        writeRepeatedBytesImpl(id, val);
1923    }
1924
1925    private void writeRepeatedBytesImpl(int id, byte[] val) {
1926        writeKnownLengthHeader(id, val == null ? 0 : val.length);
1927        mBuffer.writeRawBuffer(val);
1928    }
1929
1930    //
1931    // proto3 type: enum
1932    // java type: int
1933    // signed/unsigned: unsigned
1934    // encoding: varint
1935    // wire type: WIRE_TYPE_VARINT
1936    //
1937
1938    /**
1939     * Write a single proto enum type field value.
1940     *
1941     * @deprecated Use #write instead.
1942     */
1943    @Deprecated
1944    public void writeEnum(long fieldId, int val) {
1945        assertNotCompacted();
1946        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM);
1947
1948        writeEnumImpl(id, val);
1949    }
1950
1951    private void writeEnumImpl(int id, int val) {
1952        if (val != 0) {
1953            writeTag(id, WIRE_TYPE_VARINT);
1954            writeUnsignedVarintFromSignedInt(val);
1955        }
1956    }
1957
1958    /**
1959     * Write a single repeated proto enum type field value.
1960     *
1961     * @deprecated Use #write instead.
1962     */
1963    @Deprecated
1964    public void writeRepeatedEnum(long fieldId, int val) {
1965        assertNotCompacted();
1966        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_ENUM);
1967
1968        writeRepeatedEnumImpl(id, val);
1969    }
1970
1971    private void writeRepeatedEnumImpl(int id, int val) {
1972        writeTag(id, WIRE_TYPE_VARINT);
1973        writeUnsignedVarintFromSignedInt(val);
1974    }
1975
1976    /**
1977     * Write a list of packed proto enum type field value.
1978     *
1979     * @deprecated Use #write instead.
1980     */
1981    @Deprecated
1982    public void writePackedEnum(long fieldId, int[] val) {
1983        assertNotCompacted();
1984        final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_ENUM);
1985
1986        final int N = val != null ? val.length : 0;
1987        if (N > 0) {
1988            int size = 0;
1989            for (int i=0; i<N; i++) {
1990                final int v = val[i];
1991                size += v >= 0 ? EncodedBuffer.getRawVarint32Size(v) : 10;
1992            }
1993            writeKnownLengthHeader(id, size);
1994            for (int i=0; i<N; i++) {
1995                writeUnsignedVarintFromSignedInt(val[i]);
1996            }
1997        }
1998    }
1999
2000    //
2001    // Child objects
2002    //
2003
2004    /**
2005     * Make a token.
2006     *  Bits 61-63 - tag size (So we can go backwards later if the object had not data)
2007     *                - 3 bits, max value 7, max value needed 5
2008     *  Bit  60    - true if the object is repeated (lets us require endObject or endRepeatedObject)
2009     *  Bits 59-51 - depth (For error checking)
2010     *                - 9 bits, max value 512, when checking, value is masked (if we really
2011     *                  are more than 512 levels deep)
2012     *  Bits 32-50 - objectId (For error checking)
2013     *                - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap
2014     *                  because of the overflow, and only the tokens are compared.
2015     *  Bits  0-31 - offset of the first size field in the buffer.
2016     */
2017    // VisibleForTesting
2018    public static long makeToken(int tagSize, boolean repeated, int depth, int objectId,
2019            int sizePos) {
2020        return ((0x07L & (long)tagSize) << 61)
2021                | (repeated ? (1L << 60) : 0)
2022                | (0x01ffL & (long)depth) << 51
2023                | (0x07ffffL & (long)objectId) << 32
2024                | (0x0ffffffffL & (long)sizePos);
2025    }
2026
2027    /**
2028     * Get the encoded tag size from the token.
2029     */
2030    public static int getTagSizeFromToken(long token) {
2031        return (int)(0x7 & (token >> 61));
2032    }
2033
2034    /**
2035     * Get whether this is a call to startObject (false) or startRepeatedObject (true).
2036     */
2037    public static boolean getRepeatedFromToken(long token) {
2038        return (0x1 & (token >> 60)) != 0;
2039    }
2040
2041    /**
2042     * Get the nesting depth of startObject calls from the token.
2043     */
2044    public static int getDepthFromToken(long token) {
2045        return (int)(0x01ff & (token >> 51));
2046    }
2047
2048    /**
2049     * Get the object ID from the token. The object ID is a serial number for the
2050     * startObject calls that have happened on this object.  The values are truncated
2051     * to 9 bits, but that is sufficient for error checking.
2052     */
2053    public static int getObjectIdFromToken(long token) {
2054        return (int)(0x07ffff & (token >> 32));
2055    }
2056
2057    /**
2058     * Get the location of the childRawSize (the first 32 bit size field) in this object.
2059     */
2060    public static int getSizePosFromToken(long token) {
2061        return (int)token;
2062    }
2063
2064    /**
2065     * Convert the object ID to the ordinal value -- the n-th call to startObject.
2066     * The object IDs start at -1 and count backwards, so that the value is unlikely
2067     * to alias with an actual size field that had been written.
2068     */
2069    public static int convertObjectIdToOrdinal(int objectId) {
2070        return (-1 & 0x07ffff) - objectId;
2071    }
2072
2073    /**
2074     * Return a debugging string of a token.
2075     */
2076    public static String token2String(long token) {
2077        if (token == 0L) {
2078            return "Token(0)";
2079        } else {
2080            return "Token(val=0x" + Long.toHexString(token)
2081                    + " depth=" + getDepthFromToken(token)
2082                    + " object=" + convertObjectIdToOrdinal(getObjectIdFromToken(token))
2083                    + " tagSize=" + getTagSizeFromToken(token)
2084                    + " sizePos=" + getSizePosFromToken(token)
2085                    + ')';
2086        }
2087    }
2088
2089    /**
2090     * Start a child object.
2091     *
2092     * Returns a token which should be passed to endObject.  Calls to endObject must be
2093     * nested properly.
2094     *
2095     * @deprecated Use #start() instead.
2096     */
2097    @Deprecated
2098    public long startObject(long fieldId) {
2099        assertNotCompacted();
2100        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE);
2101
2102        return startObjectImpl(id, false);
2103    }
2104
2105    /**
2106     * End a child object. Pass in the token from the correspoinding startObject call.
2107     *
2108     * @deprecated Use #end() instead.
2109     */
2110    @Deprecated
2111    public void endObject(long token) {
2112        assertNotCompacted();
2113
2114        endObjectImpl(token, false);
2115    }
2116
2117    /**
2118     * Start a repeated child object.
2119     *
2120     * Returns a token which should be passed to endObject.  Calls to endObject must be
2121     * nested properly.
2122     *
2123     * @deprecated Use #start() instead.
2124     */
2125    @Deprecated
2126    public long startRepeatedObject(long fieldId) {
2127        assertNotCompacted();
2128        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE);
2129
2130        return startObjectImpl(id, true);
2131    }
2132
2133    /**
2134     * End a child object. Pass in the token from the correspoinding startRepeatedObject call.
2135     *
2136     * @deprecated Use #end() instead.
2137     */
2138    @Deprecated
2139    public void endRepeatedObject(long token) {
2140        assertNotCompacted();
2141
2142        endObjectImpl(token, true);
2143    }
2144
2145    /**
2146     * Common implementation of startObject and startRepeatedObject.
2147     */
2148    private long startObjectImpl(final int id, boolean repeated) {
2149        writeTag(id, WIRE_TYPE_LENGTH_DELIMITED);
2150        final int sizePos = mBuffer.getWritePos();
2151        mDepth++;
2152        mNextObjectId--;
2153
2154        // Write the previous token, giving us a stack of expected tokens.
2155        // After endObject returns, the first fixed32 becomeschildRawSize (set in endObject)
2156        // and the second one becomes childEncodedSize (set in editEncodedSize).
2157        mBuffer.writeRawFixed32((int)(mExpectedObjectToken >> 32));
2158        mBuffer.writeRawFixed32((int)mExpectedObjectToken);
2159
2160        long old = mExpectedObjectToken;
2161
2162        mExpectedObjectToken = makeToken(getTagSize(id), repeated, mDepth, mNextObjectId, sizePos);
2163        return mExpectedObjectToken;
2164    }
2165
2166    /**
2167     * Common implementation of endObject and endRepeatedObject.
2168     */
2169    private void endObjectImpl(long token, boolean repeated) {
2170        // The upper 32 bits of the token is the depth of startObject /
2171        // endObject calls.  We could get aritrarily sophisticated, but
2172        // that's enough to prevent the common error of missing an
2173        // endObject somewhere.
2174        // The lower 32 bits of the token is the offset in the buffer
2175        // at which to write the size.
2176        final int depth = getDepthFromToken(token);
2177        final boolean expectedRepeated = getRepeatedFromToken(token);
2178        final int sizePos = getSizePosFromToken(token);
2179        final int childRawSize = mBuffer.getWritePos() - sizePos - 8;
2180
2181        if (repeated != expectedRepeated) {
2182            if (repeated) {
2183                throw new IllegalArgumentException("endRepeatedObject called where endObject should"
2184                        + " have been");
2185            } else {
2186                throw new IllegalArgumentException("endObject called where endRepeatedObject should"
2187                        + " have been");
2188            }
2189        }
2190
2191        // Check that we're getting the token and depth that we are expecting.
2192        if ((mDepth & 0x01ff) != depth || mExpectedObjectToken != token) {
2193            // This text of exception is united tested.  That test also implicity checks
2194            // that we're tracking the objectIds and depths correctly.
2195            throw new IllegalArgumentException("Mismatched startObject/endObject calls."
2196                    + " Current depth " + mDepth
2197                    + " token=" + token2String(token)
2198                    + " expectedToken=" + token2String(mExpectedObjectToken));
2199        }
2200
2201        // Get the next expected token that we stashed away in the buffer.
2202        mExpectedObjectToken = (((long)mBuffer.getRawFixed32At(sizePos)) << 32)
2203                | (0x0ffffffffL & (long)mBuffer.getRawFixed32At(sizePos+4));
2204
2205        mDepth--;
2206        if (childRawSize > 0) {
2207            mBuffer.editRawFixed32(sizePos, -childRawSize);
2208            mBuffer.editRawFixed32(sizePos+4, -1);
2209        } else if (repeated) {
2210            mBuffer.editRawFixed32(sizePos, 0);
2211            mBuffer.editRawFixed32(sizePos+4, 0);
2212        } else {
2213            // The object has no data.  Don't include it.
2214            mBuffer.rewindWriteTo(sizePos - getTagSizeFromToken(token));
2215        }
2216    }
2217
2218    /**
2219     * Write an object that has already been flattend.
2220     *
2221     * @deprecated Use #write instead.
2222     */
2223    @Deprecated
2224    public void writeObject(long fieldId, byte[] value) {
2225        assertNotCompacted();
2226        final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE);
2227
2228        writeObjectImpl(id, value);
2229    }
2230
2231    void writeObjectImpl(int id, byte[] value) {
2232        if (value != null && value.length != 0) {
2233            writeKnownLengthHeader(id, value.length);
2234            mBuffer.writeRawBuffer(value);
2235        }
2236    }
2237
2238    /**
2239     * Write an object that has already been flattend.
2240     *
2241     * @deprecated Use #write instead.
2242     */
2243    @Deprecated
2244    public void writeRepeatedObject(long fieldId, byte[] value) {
2245        assertNotCompacted();
2246        final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE);
2247
2248        writeRepeatedObjectImpl(id, value);
2249    }
2250
2251    void writeRepeatedObjectImpl(int id, byte[] value) {
2252        writeKnownLengthHeader(id, value == null ? 0 : value.length);
2253        mBuffer.writeRawBuffer(value);
2254    }
2255
2256    //
2257    // Tags
2258    //
2259
2260    /**
2261     * Combine a fieldId (the field keys in the proto file) and the field flags.
2262     * Mostly useful for testing because the generated code contains the fieldId
2263     * constants.
2264     */
2265    public static long makeFieldId(int id, long fieldFlags) {
2266        return fieldFlags | (((long)id) & 0x0ffffffffL);
2267    }
2268
2269    /**
2270     * Validates that the fieldId providied is of the type and count from expectedType.
2271     *
2272     * The type must match exactly to pass this check.
2273     *
2274     * The count must match according to this truth table to pass the check:
2275     *
2276     *                  expectedFlags
2277     *                  UNKNOWN     SINGLE      REPEATED    PACKED
2278     *    fieldId
2279     *    UNKNOWN       true        false       false       false
2280     *    SINGLE        x           true        false       false
2281     *    REPEATED      x           false       true        false
2282     *    PACKED        x           false       true        true
2283     *
2284     * @throws IllegalArgumentException if it is not.
2285     *
2286     * @return The raw ID of that field.
2287     */
2288    public static int checkFieldId(long fieldId, long expectedFlags) {
2289        final long fieldCount = fieldId & FIELD_COUNT_MASK;
2290        final long fieldType = fieldId & FIELD_TYPE_MASK;
2291        final long expectedCount = expectedFlags & FIELD_COUNT_MASK;
2292        final long expectedType = expectedFlags & FIELD_TYPE_MASK;
2293        if (((int)fieldId) == 0) {
2294            throw new IllegalArgumentException("Invalid proto field " + (int)fieldId
2295                    + " fieldId=" + Long.toHexString(fieldId));
2296        }
2297        if (fieldType != expectedType
2298                || !((fieldCount == expectedCount)
2299                    || (fieldCount == FIELD_COUNT_PACKED
2300                        && expectedCount == FIELD_COUNT_REPEATED))) {
2301            final String countString = getFieldCountString(fieldCount);
2302            final String typeString = getFieldTypeString(fieldType);
2303            if (typeString != null && countString != null) {
2304                final StringBuilder sb = new StringBuilder();
2305                if (expectedType == FIELD_TYPE_MESSAGE) {
2306                    sb.append("start");
2307                } else {
2308                    sb.append("write");
2309                }
2310                sb.append(getFieldCountString(expectedCount));
2311                sb.append(getFieldTypeString(expectedType));
2312                sb.append(" called for field ");
2313                sb.append((int)fieldId);
2314                sb.append(" which should be used with ");
2315                if (fieldType == FIELD_TYPE_MESSAGE) {
2316                    sb.append("start");
2317                } else {
2318                    sb.append("write");
2319                }
2320                sb.append(countString);
2321                sb.append(typeString);
2322                if (fieldCount == FIELD_COUNT_PACKED) {
2323                    sb.append(" or writeRepeated");
2324                    sb.append(typeString);
2325                }
2326                sb.append('.');
2327                throw new IllegalArgumentException(sb.toString());
2328            } else {
2329                final StringBuilder sb = new StringBuilder();
2330                if (expectedType == FIELD_TYPE_MESSAGE) {
2331                    sb.append("start");
2332                } else {
2333                    sb.append("write");
2334                }
2335                sb.append(getFieldCountString(expectedCount));
2336                sb.append(getFieldTypeString(expectedType));
2337                sb.append(" called with an invalid fieldId: 0x");
2338                sb.append(Long.toHexString(fieldId));
2339                sb.append(". The proto field ID might be ");
2340                sb.append((int)fieldId);
2341                sb.append('.');
2342                throw new IllegalArgumentException(sb.toString());
2343            }
2344        }
2345        return (int)fieldId;
2346    }
2347
2348    /**
2349     * Get the developer-usable name of a field type.
2350     */
2351    private static String getFieldTypeString(long fieldType) {
2352        int index = ((int)((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1;
2353        if (index >= 0 && index < FIELD_TYPE_NAMES.length) {
2354            return FIELD_TYPE_NAMES[index];
2355        } else {
2356            return null;
2357        }
2358    }
2359
2360    /**
2361     * Get the developer-usable name of a field count.
2362     */
2363    private static String getFieldCountString(long fieldCount) {
2364        if (fieldCount == FIELD_COUNT_SINGLE) {
2365            return "";
2366        } else if (fieldCount == FIELD_COUNT_REPEATED) {
2367            return "Repeated";
2368        } else if (fieldCount == FIELD_COUNT_PACKED) {
2369            return "Packed";
2370        } else {
2371            return null;
2372        }
2373    }
2374
2375    /**
2376     * Get a debug string for a fieldId.
2377     */
2378    private String getFieldIdString(long fieldId) {
2379        final long fieldCount = fieldId & FIELD_COUNT_MASK;
2380        String countString = getFieldCountString(fieldCount);
2381        if (countString == null) {
2382            countString = "fieldCount=" + fieldCount;
2383        }
2384        if (countString.length() > 0) {
2385            countString += " ";
2386        }
2387
2388        final long fieldType = fieldId & FIELD_TYPE_MASK;
2389        String typeString = getFieldTypeString(fieldType);
2390        if (typeString == null) {
2391            typeString = "fieldType=" + fieldType;
2392        }
2393
2394        return countString + typeString + " tag=" + ((int) fieldId)
2395                + " fieldId=0x" + Long.toHexString(fieldId);
2396    }
2397
2398    /**
2399     * Return how many bytes an encoded field tag will require.
2400     */
2401    private static int getTagSize(int id) {
2402        return EncodedBuffer.getRawVarint32Size(id << FIELD_ID_SHIFT);
2403    }
2404
2405    /**
2406     * Write a field tage to the stream.
2407     */
2408    public void writeTag(int id, int wireType) {
2409        mBuffer.writeRawVarint32((id << FIELD_ID_SHIFT) | wireType);
2410    }
2411
2412    /**
2413     * Write the header of a WIRE_TYPE_LENGTH_DELIMITED field for one where
2414     * we know the size in advance and do not need to compute and compact.
2415     */
2416    private void writeKnownLengthHeader(int id, int size) {
2417        // Write the tag
2418        writeTag(id, WIRE_TYPE_LENGTH_DELIMITED);
2419        // Size will be compacted later, but we know the size, so write it,
2420        // once for the rawSize and once for the encodedSize.
2421        mBuffer.writeRawFixed32(size);
2422        mBuffer.writeRawFixed32(size);
2423    }
2424
2425    //
2426    // Getting the buffer and compaction
2427    //
2428
2429    /**
2430     * Assert that the compact call has not already occured.
2431     *
2432     * TODO: Will change when we add the OutputStream version of ProtoOutputStream.
2433     */
2434    private void assertNotCompacted() {
2435        if (mCompacted) {
2436            throw new IllegalArgumentException("write called after compact");
2437        }
2438    }
2439
2440    /**
2441     * Finish the encoding of the data, and return a byte[] with
2442     * the protobuf formatted data.
2443     *
2444     * After this call, do not call any of the write* functions. The
2445     * behavior is undefined.
2446     */
2447    public byte[] getBytes() {
2448        compactIfNecessary();
2449
2450        return mBuffer.getBytes(mBuffer.getReadableSize());
2451    }
2452
2453    /**
2454     * If the buffer hasn't already had the nested object size fields compacted
2455     * and turned into an actual protobuf format, then do so.
2456     */
2457    private void compactIfNecessary() {
2458        if (!mCompacted) {
2459            if (mDepth != 0) {
2460                throw new IllegalArgumentException("Trying to compact with " + mDepth
2461                        + " missing calls to endObject");
2462            }
2463
2464            // The buffer must be compacted.
2465            mBuffer.startEditing();
2466            final int readableSize = mBuffer.getReadableSize();
2467
2468            // Cache the sizes of the objects
2469            editEncodedSize(readableSize);
2470
2471            // Re-write the buffer with the sizes as proper varints instead
2472            // of pairs of uint32s. We know this will always fit in the same
2473            // buffer because the pair of uint32s is exactly 8 bytes long, and
2474            // the single varint size will be no more than 5 bytes long.
2475            mBuffer.rewindRead();
2476            compactSizes(readableSize);
2477
2478            // If there is any data left over that wasn't copied yet, copy it.
2479            if (mCopyBegin < readableSize) {
2480                mBuffer.writeFromThisBuffer(mCopyBegin, readableSize - mCopyBegin);
2481            }
2482
2483            // Set the new readableSize
2484            mBuffer.startEditing();
2485
2486            // It's not valid to write to this object anymore. The write
2487            // pointers are off, and then some of the data would be compacted
2488            // and some not.
2489            mCompacted = true;
2490        }
2491    }
2492
2493    /**
2494     * First compaction pass.  Iterate through the data, and fill in the
2495     * nested object sizes so the next pass can compact them.
2496     */
2497    private int editEncodedSize(int rawSize) {
2498        int objectStart = mBuffer.getReadPos();
2499        int objectEnd = objectStart + rawSize;
2500        int encodedSize = 0;
2501        int tagPos;
2502
2503        while ((tagPos = mBuffer.getReadPos()) < objectEnd) {
2504            int tag = readRawTag();
2505            encodedSize += EncodedBuffer.getRawVarint32Size(tag);
2506
2507            final int wireType = tag & WIRE_TYPE_MASK;
2508            switch (wireType) {
2509                case WIRE_TYPE_VARINT:
2510                    encodedSize++;
2511                    while ((mBuffer.readRawByte() & 0x80) != 0) {
2512                        encodedSize++;
2513                    }
2514                    break;
2515                case WIRE_TYPE_FIXED64:
2516                    encodedSize += 8;
2517                    mBuffer.skipRead(8);
2518                    break;
2519                case WIRE_TYPE_LENGTH_DELIMITED: {
2520                    // This object is not of a fixed-size type.  So we need to figure
2521                    // out how big it should be.
2522                    final int childRawSize = mBuffer.readRawFixed32();
2523                    final int childEncodedSizePos = mBuffer.getReadPos();
2524                    int childEncodedSize = mBuffer.readRawFixed32();
2525                    if (childRawSize >= 0) {
2526                        // We know the size, just skip ahead.
2527                        if (childEncodedSize != childRawSize) {
2528                            throw new RuntimeException("Pre-computed size where the"
2529                                    + " precomputed size and the raw size in the buffer"
2530                                    + " don't match! childRawSize=" + childRawSize
2531                                    + " childEncodedSize=" + childEncodedSize
2532                                    + " childEncodedSizePos=" + childEncodedSizePos);
2533                        }
2534                        mBuffer.skipRead(childRawSize);
2535                    } else {
2536                        // We need to compute the size.  Recurse.
2537                        childEncodedSize = editEncodedSize(-childRawSize);
2538                        mBuffer.editRawFixed32(childEncodedSizePos, childEncodedSize);
2539                    }
2540                    encodedSize += EncodedBuffer.getRawVarint32Size(childEncodedSize)
2541                            + childEncodedSize;
2542                    break;
2543                }
2544                case WIRE_TYPE_START_GROUP:
2545                case WIRE_TYPE_END_GROUP:
2546                    throw new RuntimeException("groups not supported at index " + tagPos);
2547                case WIRE_TYPE_FIXED32:
2548                    encodedSize += 4;
2549                    mBuffer.skipRead(4);
2550                    break;
2551                default:
2552                    throw new ProtoParseException("editEncodedSize Bad tag tag=0x"
2553                            + Integer.toHexString(tag) + " wireType=" + wireType
2554                            + " -- " + mBuffer.getDebugString());
2555            }
2556        }
2557
2558        return encodedSize;
2559    }
2560
2561    /**
2562     * Second compaction pass.  Iterate through the data, and copy the data
2563     * forward in the buffer, converting the pairs of uint32s into a single
2564     * unsigned varint of the size.
2565     */
2566    private void compactSizes(int rawSize) {
2567        int objectStart = mBuffer.getReadPos();
2568        int objectEnd = objectStart + rawSize;
2569        int tagPos;
2570        while ((tagPos = mBuffer.getReadPos()) < objectEnd) {
2571            int tag = readRawTag();
2572
2573            // For all the non-length-delimited field types, just skip over them,
2574            // and we'll just System.arraycopy it later, either in the case for
2575            // WIRE_TYPE_LENGTH_DELIMITED or at the top of the stack in compactIfNecessary().
2576            final int wireType = tag & WIRE_TYPE_MASK;
2577            switch (wireType) {
2578                case WIRE_TYPE_VARINT:
2579                    while ((mBuffer.readRawByte() & 0x80) != 0) { }
2580                    break;
2581                case WIRE_TYPE_FIXED64:
2582                    mBuffer.skipRead(8);
2583                    break;
2584                case WIRE_TYPE_LENGTH_DELIMITED: {
2585                    // Copy everything up to now, including the tag for this field.
2586                    mBuffer.writeFromThisBuffer(mCopyBegin, mBuffer.getReadPos() - mCopyBegin);
2587                    // Write the new size.
2588                    final int childRawSize = mBuffer.readRawFixed32();
2589                    final int childEncodedSize = mBuffer.readRawFixed32();
2590                    mBuffer.writeRawVarint32(childEncodedSize);
2591                    // Next time, start copying from here.
2592                    mCopyBegin = mBuffer.getReadPos();
2593                    if (childRawSize >= 0) {
2594                        // This is raw data, not an object. Skip ahead by the size.
2595                        // Recurse into the child
2596                        mBuffer.skipRead(childEncodedSize);
2597                    } else {
2598                        compactSizes(-childRawSize);
2599                    }
2600                    break;
2601                    // TODO: What does regular proto do if the object would be 0 size
2602                    // (e.g. if it is all default values).
2603                }
2604                case WIRE_TYPE_START_GROUP:
2605                case WIRE_TYPE_END_GROUP:
2606                    throw new RuntimeException("groups not supported at index " + tagPos);
2607                case WIRE_TYPE_FIXED32:
2608                    mBuffer.skipRead(4);
2609                    break;
2610                default:
2611                    throw new ProtoParseException("compactSizes Bad tag tag=0x"
2612                            + Integer.toHexString(tag) + " wireType=" + wireType
2613                            + " -- " + mBuffer.getDebugString());
2614            }
2615        }
2616    }
2617
2618    /**
2619     * Write remaining data to the output stream.  If there is no output stream,
2620     * this function does nothing. Any currently open objects (i.e. ones that
2621     * have not had endObject called for them will not be written).  Whether this
2622     * writes objects that are closed if there are remaining open objects is
2623     * undefined (current implementation does not write it, future ones will).
2624     * For now, can either call getBytes() or flush(), but not both.
2625     */
2626    public void flush() {
2627        if (mStream == null) {
2628            return;
2629        }
2630        if (mDepth != 0) {
2631            // TODO: The compacting code isn't ready yet to compact unless we're done.
2632            // TODO: Fix that.
2633            return;
2634        }
2635        if (mCompacted) {
2636            // If we're compacted, we already wrote it finished.
2637            return;
2638        }
2639        compactIfNecessary();
2640        final byte[] data = mBuffer.getBytes(mBuffer.getReadableSize());
2641        try {
2642            mStream.write(data);
2643            mStream.flush();
2644        } catch (IOException ex) {
2645            throw new RuntimeException("Error flushing proto to stream", ex);
2646        }
2647    }
2648
2649    /**
2650     * Read a raw tag from the buffer.
2651     */
2652    private int readRawTag() {
2653        if (mBuffer.getReadPos() == mBuffer.getReadableSize()) {
2654            return 0;
2655        }
2656        return (int)mBuffer.readRawUnsigned();
2657    }
2658
2659    /**
2660     * Dump debugging data about the buffers with the given log tag.
2661     */
2662    public void dump(String tag) {
2663        Log.d(tag, mBuffer.getDebugString());
2664        mBuffer.dumpBuffers(tag);
2665    }
2666}
2667