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