1 /*
2 * Copyright (C) 2017 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 #define LOG_TAG "libprotoutil"
17
18 #include <inttypes.h>
19
20 #include <android/util/protobuf.h>
21 #include <android/util/ProtoOutputStream.h>
22 #include <cutils/log.h>
23
24 namespace android {
25 namespace util {
26
ProtoOutputStream()27 ProtoOutputStream::ProtoOutputStream()
28 :mBuffer(),
29 mCopyBegin(0),
30 mCompact(false),
31 mDepth(0),
32 mObjectId(0),
33 mExpectedObjectToken(UINT64_C(-1))
34 {
35 }
36
~ProtoOutputStream()37 ProtoOutputStream::~ProtoOutputStream()
38 {
39 }
40
41
42 void
clear()43 ProtoOutputStream::clear()
44 {
45 mBuffer.clear();
46 mCopyBegin = 0;
47 mCompact = false;
48 mDepth = 0;
49 mObjectId = 0;
50 mExpectedObjectToken = UINT64_C(-1);
51 }
52
53 bool
write(uint64_t fieldId,double val)54 ProtoOutputStream::write(uint64_t fieldId, double val)
55 {
56 if (mCompact) return false;
57 const uint32_t id = (uint32_t)fieldId;
58 switch (fieldId & FIELD_TYPE_MASK) {
59 case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
60 case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
61 case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
62 case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
63 case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
64 case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
65 case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
66 case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
67 case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
68 case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
69 case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
70 case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
71 default:
72 ALOGW("Field type %d is not supported when writing double val.",
73 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
74 return false;
75 }
76 return true;
77 }
78
79 bool
write(uint64_t fieldId,float val)80 ProtoOutputStream::write(uint64_t fieldId, float val)
81 {
82 if (mCompact) return false;
83 const uint32_t id = (uint32_t)fieldId;
84 switch (fieldId & FIELD_TYPE_MASK) {
85 case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
86 case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
87 case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
88 case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
89 case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
90 case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
91 case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
92 case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
93 case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
94 case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
95 case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
96 case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
97 default:
98 ALOGW("Field type %d is not supported when writing float val.",
99 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
100 return false;
101 }
102 return true;
103 }
104
105 bool
write(uint64_t fieldId,int val)106 ProtoOutputStream::write(uint64_t fieldId, int val)
107 {
108 if (mCompact) return false;
109 const uint32_t id = (uint32_t)fieldId;
110 switch (fieldId & FIELD_TYPE_MASK) {
111 case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
112 case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
113 case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
114 case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
115 case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
116 case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
117 case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
118 case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
119 case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
120 case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
121 case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
122 case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
123 case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
124 case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
125 default:
126 ALOGW("Field type %d is not supported when writing int val.",
127 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
128 return false;
129 }
130 return true;
131 }
132
133 bool
write(uint64_t fieldId,long long val)134 ProtoOutputStream::write(uint64_t fieldId, long long val)
135 {
136 if (mCompact) return false;
137 const uint32_t id = (uint32_t)fieldId;
138 switch (fieldId & FIELD_TYPE_MASK) {
139 case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
140 case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
141 case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
142 case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
143 case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
144 case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
145 case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
146 case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
147 case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
148 case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
149 case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
150 case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
151 case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
152 case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
153 default:
154 ALOGW("Field type %d is not supported when writing long long val.",
155 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
156 return false;
157 }
158 return true;
159 }
160
161 bool
write(uint64_t fieldId,bool val)162 ProtoOutputStream::write(uint64_t fieldId, bool val)
163 {
164 if (mCompact) return false;
165 const uint32_t id = (uint32_t)fieldId;
166 switch (fieldId & FIELD_TYPE_MASK) {
167 case FIELD_TYPE_BOOL:
168 writeBoolImpl(id, val);
169 return true;
170 default:
171 ALOGW("Field type %d is not supported when writing bool val.",
172 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
173 return false;
174 }
175 }
176
177 bool
write(uint64_t fieldId,std::string val)178 ProtoOutputStream::write(uint64_t fieldId, std::string val)
179 {
180 if (mCompact) return false;
181 const uint32_t id = (uint32_t)fieldId;
182 switch (fieldId & FIELD_TYPE_MASK) {
183 case FIELD_TYPE_STRING:
184 writeUtf8StringImpl(id, val.c_str(), val.size());
185 return true;
186 default:
187 ALOGW("Field type %d is not supported when writing string val.",
188 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
189 return false;
190 }
191 }
192
193 bool
write(uint64_t fieldId,const char * val,size_t size)194 ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size)
195 {
196 if (mCompact) return false;
197 const uint32_t id = (uint32_t)fieldId;
198 switch (fieldId & FIELD_TYPE_MASK) {
199 case FIELD_TYPE_STRING:
200 case FIELD_TYPE_BYTES:
201 writeUtf8StringImpl(id, val, size);
202 return true;
203 case FIELD_TYPE_MESSAGE:
204 // can directly write valid format of message bytes into ProtoOutputStream without calling start/end
205 writeMessageBytesImpl(id, val, size);
206 return true;
207 default:
208 ALOGW("Field type %d is not supported when writing char[] val.",
209 (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
210 return false;
211 }
212 }
213
214 /**
215 * Make a token.
216 * Bits 61-63 - tag size (So we can go backwards later if the object had not data)
217 * - 3 bits, max value 7, max value needed 5
218 * Bit 60 - true if the object is repeated
219 * Bits 59-51 - depth (For error checking)
220 * - 9 bits, max value 511, when checking, value is masked (if we really
221 * are more than 511 levels deep)
222 * Bits 32-50 - objectId (For error checking)
223 * - 19 bits, max value 524,287. that's a lot of objects. IDs will wrap
224 * because of the overflow, and only the tokens are compared.
225 * Bits 0-31 - offset of the first size field in the buffer.
226 */
227 static uint64_t
makeToken(uint32_t tagSize,bool repeated,uint32_t depth,uint32_t objectId,size_t sizePos)228 makeToken(uint32_t tagSize, bool repeated, uint32_t depth, uint32_t objectId, size_t sizePos) {
229 return ((UINT64_C(0x07) & (uint64_t)tagSize) << 61)
230 | (repeated ? (UINT64_C(1) << 60) : 0)
231 | (UINT64_C(0x01ff) & (uint64_t)depth) << 51
232 | (UINT64_C(0x07ffff) & (uint64_t)objectId) << 32
233 | (UINT64_C(0x0ffffffff) & (uint64_t)sizePos);
234 }
235
236 /**
237 * Get the encoded tag size from the token.
238 */
getTagSizeFromToken(uint64_t token)239 static uint32_t getTagSizeFromToken(uint64_t token) {
240 return 0x7 & (token >> 61);
241 }
242
243 /**
244 * Get the nesting depth of startObject calls from the token.
245 */
getDepthFromToken(uint64_t token)246 static uint32_t getDepthFromToken(uint64_t token) {
247 return 0x01ff & (token >> 51);
248 }
249
250 /**
251 * Get the location of the childRawSize (the first 32 bit size field) in this object.
252 */
getSizePosFromToken(uint64_t token)253 static uint32_t getSizePosFromToken(uint64_t token) {
254 return (uint32_t)token;
255 }
256
257 uint64_t
start(uint64_t fieldId)258 ProtoOutputStream::start(uint64_t fieldId)
259 {
260 if ((fieldId & FIELD_TYPE_MASK) != FIELD_TYPE_MESSAGE) {
261 ALOGE("Can't call start for non-message type field: 0x%" PRIx64, fieldId);
262 return 0;
263 }
264
265 uint32_t id = (uint32_t)fieldId;
266 size_t prevPos = mBuffer.wp()->pos();
267 mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
268 size_t sizePos = mBuffer.wp()->pos();
269
270 mDepth++;
271 mObjectId++;
272 mBuffer.writeRawFixed64(mExpectedObjectToken); // push previous token into stack.
273
274 mExpectedObjectToken = makeToken(sizePos - prevPos,
275 (bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos);
276 return mExpectedObjectToken;
277 }
278
279 void
end(uint64_t token)280 ProtoOutputStream::end(uint64_t token)
281 {
282 if (token != mExpectedObjectToken) {
283 ALOGE("Unexpected token: 0x%" PRIx64 ", should be 0x%" PRIx64, token, mExpectedObjectToken);
284 return;
285 }
286
287 uint32_t depth = getDepthFromToken(token);
288 if (depth != (mDepth & 0x01ff)) {
289 ALOGE("Unexpected depth: %" PRIu32 ", should be %" PRIu32, depth, mDepth);
290 return;
291 }
292 mDepth--;
293
294 uint32_t sizePos = getSizePosFromToken(token);
295 // number of bytes written in this start-end session.
296 int childRawSize = mBuffer.wp()->pos() - sizePos - 8;
297
298 // retrieve the old token from stack.
299 mBuffer.ep()->rewind()->move(sizePos);
300 mExpectedObjectToken = mBuffer.readRawFixed64();
301
302 // If raw size is larger than 0, write the negative value here to indicate a compact is needed.
303 if (childRawSize > 0) {
304 mBuffer.editRawFixed32(sizePos, -childRawSize);
305 mBuffer.editRawFixed32(sizePos+4, -1);
306 } else {
307 // reset wp which erase the header tag of the message when its size is 0.
308 mBuffer.wp()->rewind()->move(sizePos - getTagSizeFromToken(token));
309 }
310 }
311
312 size_t
bytesWritten()313 ProtoOutputStream::bytesWritten()
314 {
315 return mBuffer.size();
316 }
317
318 bool
compact()319 ProtoOutputStream::compact() {
320 if (mCompact) return true;
321 if (mDepth != 0) {
322 ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing calls to end.", mDepth);
323 return false;
324 }
325 // record the size of the original buffer.
326 size_t rawBufferSize = mBuffer.size();
327 if (rawBufferSize == 0) return true; // nothing to do if the buffer is empty;
328
329 // reset edit pointer and recursively compute encoded size of messages.
330 mBuffer.ep()->rewind();
331 if (editEncodedSize(rawBufferSize) == 0) {
332 ALOGE("Failed to editEncodedSize.");
333 return false;
334 }
335
336 // reset both edit pointer and write pointer, and compact recursively.
337 mBuffer.ep()->rewind();
338 mBuffer.wp()->rewind();
339 if (!compactSize(rawBufferSize)) {
340 ALOGE("Failed to compactSize.");
341 return false;
342 }
343 // copy the reset to the buffer.
344 if (mCopyBegin < rawBufferSize) {
345 mBuffer.copy(mCopyBegin, rawBufferSize - mCopyBegin);
346 }
347
348 // mark true means it is not legal to write to this ProtoOutputStream anymore
349 mCompact = true;
350 return true;
351 }
352
353 /**
354 * First compaction pass. Iterate through the data, and fill in the
355 * nested object sizes so the next pass can compact them.
356 */
357 size_t
editEncodedSize(size_t rawSize)358 ProtoOutputStream::editEncodedSize(size_t rawSize)
359 {
360 size_t objectStart = mBuffer.ep()->pos();
361 size_t objectEnd = objectStart + rawSize;
362 size_t encodedSize = 0;
363 int childRawSize, childEncodedSize;
364 size_t childEncodedSizePos;
365
366 while (mBuffer.ep()->pos() < objectEnd) {
367 uint32_t tag = (uint32_t)mBuffer.readRawVarint();
368 encodedSize += get_varint_size(tag);
369 switch (read_wire_type(tag)) {
370 case WIRE_TYPE_VARINT:
371 do {
372 encodedSize++;
373 } while ((mBuffer.readRawByte() & 0x80) != 0);
374 break;
375 case WIRE_TYPE_FIXED64:
376 encodedSize += 8;
377 mBuffer.ep()->move(8);
378 break;
379 case WIRE_TYPE_LENGTH_DELIMITED:
380 childRawSize = (int)mBuffer.readRawFixed32();
381 childEncodedSizePos = mBuffer.ep()->pos();
382 childEncodedSize = (int)mBuffer.readRawFixed32();
383 if (childRawSize >= 0 && childRawSize == childEncodedSize) {
384 mBuffer.ep()->move(childRawSize);
385 } else if (childRawSize < 0 && childEncodedSize == -1){
386 childEncodedSize = editEncodedSize(-childRawSize);
387 mBuffer.editRawFixed32(childEncodedSizePos, childEncodedSize);
388 } else {
389 ALOGE("Bad raw or encoded values: raw=%d, encoded=%d at %zu",
390 childRawSize, childEncodedSize, childEncodedSizePos);
391 return 0;
392 }
393 encodedSize += get_varint_size(childEncodedSize) + childEncodedSize;
394 break;
395 case WIRE_TYPE_FIXED32:
396 encodedSize += 4;
397 mBuffer.ep()->move(4);
398 break;
399 default:
400 ALOGE("Unexpected wire type %d in editEncodedSize at [%zu, %zu]",
401 read_wire_type(tag), objectStart, objectEnd);
402 return 0;
403 }
404 }
405 return encodedSize;
406 }
407
408 /**
409 * Second compaction pass. Iterate through the data, and copy the data
410 * forward in the buffer, converting the pairs of uint32s into a single
411 * unsigned varint of the size.
412 */
413 bool
compactSize(size_t rawSize)414 ProtoOutputStream::compactSize(size_t rawSize)
415 {
416 size_t objectStart = mBuffer.ep()->pos();
417 size_t objectEnd = objectStart + rawSize;
418 int childRawSize, childEncodedSize;
419
420 while (mBuffer.ep()->pos() < objectEnd) {
421 uint32_t tag = (uint32_t)mBuffer.readRawVarint();
422 switch (read_wire_type(tag)) {
423 case WIRE_TYPE_VARINT:
424 while ((mBuffer.readRawByte() & 0x80) != 0) {}
425 break;
426 case WIRE_TYPE_FIXED64:
427 mBuffer.ep()->move(8);
428 break;
429 case WIRE_TYPE_LENGTH_DELIMITED:
430 mBuffer.copy(mCopyBegin, mBuffer.ep()->pos() - mCopyBegin);
431
432 childRawSize = (int)mBuffer.readRawFixed32();
433 childEncodedSize = (int)mBuffer.readRawFixed32();
434 mCopyBegin = mBuffer.ep()->pos();
435
436 // write encoded size to buffer.
437 mBuffer.writeRawVarint32(childEncodedSize);
438 if (childRawSize >= 0 && childRawSize == childEncodedSize) {
439 mBuffer.ep()->move(childEncodedSize);
440 } else if (childRawSize < 0){
441 if (!compactSize(-childRawSize)) return false;
442 } else {
443 ALOGE("Bad raw or encoded values: raw=%d, encoded=%d",
444 childRawSize, childEncodedSize);
445 return false;
446 }
447 break;
448 case WIRE_TYPE_FIXED32:
449 mBuffer.ep()->move(4);
450 break;
451 default:
452 ALOGE("Unexpected wire type %d in compactSize at [%zu, %zu]",
453 read_wire_type(tag), objectStart, objectEnd);
454 return false;
455 }
456 }
457 return true;
458 }
459
460 size_t
size()461 ProtoOutputStream::size()
462 {
463 if (!compact()) {
464 ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
465 // TODO: handle this error
466 }
467 return mBuffer.size();
468 }
469
write_all(int fd,uint8_t const * buf,size_t size)470 static bool write_all(int fd, uint8_t const* buf, size_t size)
471 {
472 while (size > 0) {
473 ssize_t amt = ::write(fd, buf, size);
474 if (amt < 0) {
475 return false;
476 }
477 size -= amt;
478 buf += amt;
479 }
480 return true;
481 }
482
483 bool
flush(int fd)484 ProtoOutputStream::flush(int fd)
485 {
486 if (fd < 0) return false;
487 if (!compact()) return false;
488
489 EncodedBuffer::iterator it = mBuffer.begin();
490 while (it.readBuffer() != NULL) {
491 if (!write_all(fd, it.readBuffer(), it.currentToRead())) return false;
492 it.rp()->move(it.currentToRead());
493 }
494 return true;
495 }
496
497 EncodedBuffer::iterator
data()498 ProtoOutputStream::data()
499 {
500 if (!compact()) {
501 ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
502 // TODO: handle this error
503 }
504 return mBuffer.begin();
505 }
506
507 void
writeRawVarint(uint64_t varint)508 ProtoOutputStream::writeRawVarint(uint64_t varint)
509 {
510 mBuffer.writeRawVarint64(varint);
511 }
512
513 void
writeLengthDelimitedHeader(uint32_t id,size_t size)514 ProtoOutputStream::writeLengthDelimitedHeader(uint32_t id, size_t size)
515 {
516 mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
517 // reserves 64 bits for length delimited fields, if first field is negative, compact it.
518 mBuffer.writeRawFixed32(size);
519 mBuffer.writeRawFixed32(size);
520 }
521
522 void
writeRawByte(uint8_t byte)523 ProtoOutputStream::writeRawByte(uint8_t byte)
524 {
525 mBuffer.writeRawByte(byte);
526 }
527
528
529 // =========================================================================
530 // Private functions
531
532 /**
533 * bit_cast
534 */
535 template <class From, class To>
bit_cast(From const & from)536 inline To bit_cast(From const &from) {
537 To to;
538 memcpy(&to, &from, sizeof(to));
539 return to;
540 }
541
542 inline void
writeDoubleImpl(uint32_t id,double val)543 ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
544 {
545 mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
546 mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val));
547 }
548
549 inline void
writeFloatImpl(uint32_t id,float val)550 ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
551 {
552 mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
553 mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val));
554 }
555
556 inline void
writeInt64Impl(uint32_t id,long long val)557 ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
558 {
559 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
560 mBuffer.writeRawVarint64((uint64_t)val);
561 }
562
563 inline void
writeInt32Impl(uint32_t id,int val)564 ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
565 {
566 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
567 mBuffer.writeRawVarint32((uint32_t)val);
568 }
569
570 inline void
writeUint64Impl(uint32_t id,uint64_t val)571 ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
572 {
573 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
574 mBuffer.writeRawVarint64(val);
575 }
576
577 inline void
writeUint32Impl(uint32_t id,uint32_t val)578 ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
579 {
580 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
581 mBuffer.writeRawVarint32(val);
582 }
583
584 inline void
writeFixed64Impl(uint32_t id,uint64_t val)585 ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
586 {
587 mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
588 mBuffer.writeRawFixed64(val);
589 }
590
591 inline void
writeFixed32Impl(uint32_t id,uint32_t val)592 ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
593 {
594 mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
595 mBuffer.writeRawFixed32(val);
596 }
597
598 inline void
writeSFixed64Impl(uint32_t id,long long val)599 ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
600 {
601 mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
602 mBuffer.writeRawFixed64((uint64_t)val);
603 }
604
605 inline void
writeSFixed32Impl(uint32_t id,int val)606 ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
607 {
608 mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
609 mBuffer.writeRawFixed32((uint32_t)val);
610 }
611
612 inline void
writeZigzagInt64Impl(uint32_t id,long long val)613 ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
614 {
615 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
616 mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
617 }
618
619 inline void
writeZigzagInt32Impl(uint32_t id,int val)620 ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
621 {
622 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
623 mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
624 }
625
626 inline void
writeEnumImpl(uint32_t id,int val)627 ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
628 {
629 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
630 mBuffer.writeRawVarint32((uint32_t) val);
631 }
632
633 inline void
writeBoolImpl(uint32_t id,bool val)634 ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
635 {
636 mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
637 mBuffer.writeRawVarint32(val ? 1 : 0);
638 }
639
640 inline void
writeUtf8StringImpl(uint32_t id,const char * val,size_t size)641 ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
642 {
643 if (val == NULL) return;
644 writeLengthDelimitedHeader(id, size);
645 for (size_t i=0; i<size; i++) {
646 mBuffer.writeRawByte((uint8_t)val[i]);
647 }
648 }
649
650 inline void
writeMessageBytesImpl(uint32_t id,const char * val,size_t size)651 ProtoOutputStream::writeMessageBytesImpl(uint32_t id, const char* val, size_t size)
652 {
653 if (val == NULL) return;
654 writeLengthDelimitedHeader(id, size);
655 for (size_t i=0; i<size; i++) {
656 mBuffer.writeRawByte(val[i]);
657 }
658 }
659
660 } // util
661 } // android
662
663