1 /* pb_encode.c -- encode a protobuf using minimal resources
2  *
3  * 2011 Petteri Aimonen <jpa@kapsi.fi>
4  */
5 
6 #include "pb.h"
7 #include "pb_encode.h"
8 
9 /* Use the GCC warn_unused_result attribute to check that all return values
10  * are propagated correctly. On other compilers and gcc before 3.4.0 just
11  * ignore the annotation.
12  */
13 #if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
14     #define checkreturn
15 #else
16     #define checkreturn __attribute__((warn_unused_result))
17 #endif
18 
19 /**************************************
20  * Declarations internal to this file *
21  **************************************/
22 typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
23 
24 static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count);
25 static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func);
26 static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
27 static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
28 static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
29 static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
30 static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
31 static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
32 static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
33 static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
34 static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
35 static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
36 static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
37 
38 /* --- Function pointers to field encoders ---
39  * Order in the array must match pb_action_t LTYPE numbering.
40  */
41 static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
42     &pb_enc_varint,
43     &pb_enc_uvarint,
44     &pb_enc_svarint,
45     &pb_enc_fixed32,
46     &pb_enc_fixed64,
47 
48     &pb_enc_bytes,
49     &pb_enc_string,
50     &pb_enc_submessage,
51     NULL /* extensions */
52 };
53 
54 /*******************************
55  * pb_ostream_t implementation *
56  *******************************/
57 
buf_write(pb_ostream_t * stream,const uint8_t * buf,size_t count)58 static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
59 {
60     uint8_t *dest = (uint8_t*)stream->state;
61     stream->state = dest + count;
62 
63     while (count--)
64         *dest++ = *buf++;
65 
66     return true;
67 }
68 
pb_ostream_from_buffer(uint8_t * buf,size_t bufsize)69 pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize)
70 {
71     pb_ostream_t stream;
72 #ifdef PB_BUFFER_ONLY
73     stream.callback = (void*)1; /* Just a marker value */
74 #else
75     stream.callback = &buf_write;
76 #endif
77     stream.state = buf;
78     stream.max_size = bufsize;
79     stream.bytes_written = 0;
80 #ifndef PB_NO_ERRMSG
81     stream.errmsg = NULL;
82 #endif
83     return stream;
84 }
85 
pb_write(pb_ostream_t * stream,const uint8_t * buf,size_t count)86 bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
87 {
88     if (stream->callback != NULL)
89     {
90         if (stream->bytes_written + count > stream->max_size)
91             PB_RETURN_ERROR(stream, "stream full");
92 
93 #ifdef PB_BUFFER_ONLY
94         if (!buf_write(stream, buf, count))
95             PB_RETURN_ERROR(stream, "io error");
96 #else
97         if (!stream->callback(stream, buf, count))
98             PB_RETURN_ERROR(stream, "io error");
99 #endif
100     }
101 
102     stream->bytes_written += count;
103     return true;
104 }
105 
106 /*************************
107  * Encode a single field *
108  *************************/
109 
110 /* Encode a static array. Handles the size calculations and possible packing. */
encode_array(pb_ostream_t * stream,const pb_field_t * field,const void * pData,size_t count,pb_encoder_t func)111 static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
112                          const void *pData, size_t count, pb_encoder_t func)
113 {
114     size_t i;
115     const void *p;
116     size_t size;
117 
118     if (count == 0)
119         return true;
120 
121     if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
122         PB_RETURN_ERROR(stream, "array max size exceeded");
123 
124     /* We always pack arrays if the datatype allows it. */
125     if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
126     {
127         if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
128             return false;
129 
130         /* Determine the total size of packed array. */
131         if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
132         {
133             size = 4 * count;
134         }
135         else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
136         {
137             size = 8 * count;
138         }
139         else
140         {
141             pb_ostream_t sizestream = PB_OSTREAM_SIZING;
142             p = pData;
143             for (i = 0; i < count; i++)
144             {
145                 if (!func(&sizestream, field, p))
146                     return false;
147                 p = (const char*)p + field->data_size;
148             }
149             size = sizestream.bytes_written;
150         }
151 
152         if (!pb_encode_varint(stream, (uint64_t)size))
153             return false;
154 
155         if (stream->callback == NULL)
156             return pb_write(stream, NULL, size); /* Just sizing.. */
157 
158         /* Write the data */
159         p = pData;
160         for (i = 0; i < count; i++)
161         {
162             if (!func(stream, field, p))
163                 return false;
164             p = (const char*)p + field->data_size;
165         }
166     }
167     else
168     {
169         p = pData;
170         for (i = 0; i < count; i++)
171         {
172             if (!pb_encode_tag_for_field(stream, field))
173                 return false;
174 
175             /* Normally the data is stored directly in the array entries, but
176              * for pointer-type string and bytes fields, the array entries are
177              * actually pointers themselves also. So we have to dereference once
178              * more to get to the actual data. */
179             if (PB_ATYPE(field->type) == PB_ATYPE_POINTER &&
180                 (PB_LTYPE(field->type) == PB_LTYPE_STRING ||
181                  PB_LTYPE(field->type) == PB_LTYPE_BYTES))
182             {
183                 if (!func(stream, field, *(const void* const*)p))
184                     return false;
185             }
186             else
187             {
188                 if (!func(stream, field, p))
189                     return false;
190             }
191             p = (const char*)p + field->data_size;
192         }
193     }
194 
195     return true;
196 }
197 
198 /* Encode a field with static or pointer allocation, i.e. one whose data
199  * is available to the encoder directly. */
encode_basic_field(pb_ostream_t * stream,const pb_field_t * field,const void * pData)200 static bool checkreturn encode_basic_field(pb_ostream_t *stream,
201     const pb_field_t *field, const void *pData)
202 {
203     pb_encoder_t func;
204     const void *pSize;
205     bool implicit_has = true;
206 
207     func = PB_ENCODERS[PB_LTYPE(field->type)];
208 
209     if (field->size_offset)
210         pSize = (const char*)pData + field->size_offset;
211     else
212         pSize = &implicit_has;
213 
214     if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
215     {
216         /* pData is a pointer to the field, which contains pointer to
217          * the data. If the 2nd pointer is NULL, it is interpreted as if
218          * the has_field was false.
219          */
220 
221         pData = *(const void* const*)pData;
222         implicit_has = (pData != NULL);
223     }
224 
225     switch (PB_HTYPE(field->type))
226     {
227         case PB_HTYPE_REQUIRED:
228             if (!pData)
229                 PB_RETURN_ERROR(stream, "missing required field");
230             if (!pb_encode_tag_for_field(stream, field))
231                 return false;
232             if (!func(stream, field, pData))
233                 return false;
234             break;
235 
236         case PB_HTYPE_OPTIONAL:
237             if (*(const bool*)pSize)
238             {
239                 if (!pb_encode_tag_for_field(stream, field))
240                     return false;
241 
242                 if (!func(stream, field, pData))
243                     return false;
244             }
245             break;
246 
247         case PB_HTYPE_REPEATED:
248             if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
249                 return false;
250             break;
251 
252         default:
253             PB_RETURN_ERROR(stream, "invalid field type");
254     }
255 
256     return true;
257 }
258 
259 /* Encode a field with callback semantics. This means that a user function is
260  * called to provide and encode the actual data. */
encode_callback_field(pb_ostream_t * stream,const pb_field_t * field,const void * pData)261 static bool checkreturn encode_callback_field(pb_ostream_t *stream,
262     const pb_field_t *field, const void *pData)
263 {
264     const pb_callback_t *callback = (const pb_callback_t*)pData;
265 
266 #ifdef PB_OLD_CALLBACK_STYLE
267     const void *arg = callback->arg;
268 #else
269     void * const *arg = &(callback->arg);
270 #endif
271 
272     if (callback->funcs.encode != NULL)
273     {
274         if (!callback->funcs.encode(stream, field, arg))
275             PB_RETURN_ERROR(stream, "callback error");
276     }
277     return true;
278 }
279 
280 /* Encode a single field of any callback or static type. */
encode_field(pb_ostream_t * stream,const pb_field_t * field,const void * pData)281 static bool checkreturn encode_field(pb_ostream_t *stream,
282     const pb_field_t *field, const void *pData)
283 {
284     switch (PB_ATYPE(field->type))
285     {
286         case PB_ATYPE_STATIC:
287         case PB_ATYPE_POINTER:
288             return encode_basic_field(stream, field, pData);
289 
290         case PB_ATYPE_CALLBACK:
291             return encode_callback_field(stream, field, pData);
292 
293         default:
294             PB_RETURN_ERROR(stream, "invalid field type");
295     }
296 }
297 
298 /* Default handler for extension fields. Expects to have a pb_field_t
299  * pointer in the extension->type->arg field. */
default_extension_encoder(pb_ostream_t * stream,const pb_extension_t * extension)300 static bool checkreturn default_extension_encoder(pb_ostream_t *stream,
301     const pb_extension_t *extension)
302 {
303     const pb_field_t *field = (const pb_field_t*)extension->type->arg;
304     return encode_field(stream, field, extension->dest);
305 }
306 
307 /* Walk through all the registered extensions and give them a chance
308  * to encode themselves. */
encode_extension_field(pb_ostream_t * stream,const pb_field_t * field,const void * pData)309 static bool checkreturn encode_extension_field(pb_ostream_t *stream,
310     const pb_field_t *field, const void *pData)
311 {
312     const pb_extension_t *extension = *(const pb_extension_t* const *)pData;
313     UNUSED(field);
314 
315     while (extension)
316     {
317         bool status;
318         if (extension->type->encode)
319             status = extension->type->encode(stream, extension);
320         else
321             status = default_extension_encoder(stream, extension);
322 
323         if (!status)
324             return false;
325 
326         extension = extension->next;
327     }
328 
329     return true;
330 }
331 
332 /*********************
333  * Encode all fields *
334  *********************/
335 
pb_encode(pb_ostream_t * stream,const pb_field_t fields[],const void * src_struct)336 bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
337 {
338     const pb_field_t *field = fields;
339     const void *pData = src_struct;
340     size_t prev_size = 0;
341 
342     while (field->tag != 0)
343     {
344         pData = (const char*)pData + prev_size + field->data_offset;
345         if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
346             prev_size = sizeof(const void*);
347         else
348             prev_size = field->data_size;
349 
350         /* Special case for static arrays */
351         if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
352             PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
353         {
354             prev_size *= field->array_size;
355         }
356 
357         if (PB_LTYPE(field->type) == PB_LTYPE_EXTENSION)
358         {
359             /* Special case for the extension field placeholder */
360             if (!encode_extension_field(stream, field, pData))
361                 return false;
362         }
363         else
364         {
365             /* Regular field */
366             if (!encode_field(stream, field, pData))
367                 return false;
368         }
369 
370         field++;
371     }
372 
373     return true;
374 }
375 
pb_encode_delimited(pb_ostream_t * stream,const pb_field_t fields[],const void * src_struct)376 bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
377 {
378     return pb_encode_submessage(stream, fields, src_struct);
379 }
380 
pb_get_encoded_size(size_t * size,const pb_field_t fields[],const void * src_struct)381 bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct)
382 {
383     pb_ostream_t stream = PB_OSTREAM_SIZING;
384 
385     if (!pb_encode(&stream, fields, src_struct))
386         return false;
387 
388     *size = stream.bytes_written;
389     return true;
390 }
391 
392 /********************
393  * Helper functions *
394  ********************/
pb_encode_varint(pb_ostream_t * stream,uint64_t value)395 bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
396 {
397     uint8_t buffer[10];
398     size_t i = 0;
399 
400     if (value == 0)
401         return pb_write(stream, (uint8_t*)&value, 1);
402 
403     while (value)
404     {
405         buffer[i] = (uint8_t)((value & 0x7F) | 0x80);
406         value >>= 7;
407         i++;
408     }
409     buffer[i-1] &= 0x7F; /* Unset top bit on last byte */
410 
411     return pb_write(stream, buffer, i);
412 }
413 
pb_encode_svarint(pb_ostream_t * stream,int64_t value)414 bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
415 {
416     uint64_t zigzagged;
417     if (value < 0)
418         zigzagged = ~((uint64_t)value << 1);
419     else
420         zigzagged = (uint64_t)value << 1;
421 
422     return pb_encode_varint(stream, zigzagged);
423 }
424 
pb_encode_fixed32(pb_ostream_t * stream,const void * value)425 bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
426 {
427     #ifdef __BIG_ENDIAN__
428     const uint8_t *bytes = value;
429     uint8_t lebytes[4];
430     lebytes[0] = bytes[3];
431     lebytes[1] = bytes[2];
432     lebytes[2] = bytes[1];
433     lebytes[3] = bytes[0];
434     return pb_write(stream, lebytes, 4);
435     #else
436     return pb_write(stream, (const uint8_t*)value, 4);
437     #endif
438 }
439 
pb_encode_fixed64(pb_ostream_t * stream,const void * value)440 bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
441 {
442     #ifdef __BIG_ENDIAN__
443     const uint8_t *bytes = value;
444     uint8_t lebytes[8];
445     lebytes[0] = bytes[7];
446     lebytes[1] = bytes[6];
447     lebytes[2] = bytes[5];
448     lebytes[3] = bytes[4];
449     lebytes[4] = bytes[3];
450     lebytes[5] = bytes[2];
451     lebytes[6] = bytes[1];
452     lebytes[7] = bytes[0];
453     return pb_write(stream, lebytes, 8);
454     #else
455     return pb_write(stream, (const uint8_t*)value, 8);
456     #endif
457 }
458 
pb_encode_tag(pb_ostream_t * stream,pb_wire_type_t wiretype,uint32_t field_number)459 bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
460 {
461     uint64_t tag = ((uint64_t)field_number << 3) | wiretype;
462     return pb_encode_varint(stream, tag);
463 }
464 
pb_encode_tag_for_field(pb_ostream_t * stream,const pb_field_t * field)465 bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
466 {
467     pb_wire_type_t wiretype;
468     switch (PB_LTYPE(field->type))
469     {
470         case PB_LTYPE_VARINT:
471         case PB_LTYPE_UVARINT:
472         case PB_LTYPE_SVARINT:
473             wiretype = PB_WT_VARINT;
474             break;
475 
476         case PB_LTYPE_FIXED32:
477             wiretype = PB_WT_32BIT;
478             break;
479 
480         case PB_LTYPE_FIXED64:
481             wiretype = PB_WT_64BIT;
482             break;
483 
484         case PB_LTYPE_BYTES:
485         case PB_LTYPE_STRING:
486         case PB_LTYPE_SUBMESSAGE:
487             wiretype = PB_WT_STRING;
488             break;
489 
490         default:
491             PB_RETURN_ERROR(stream, "invalid field type");
492     }
493 
494     return pb_encode_tag(stream, wiretype, field->tag);
495 }
496 
pb_encode_string(pb_ostream_t * stream,const uint8_t * buffer,size_t size)497 bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size)
498 {
499     if (!pb_encode_varint(stream, (uint64_t)size))
500         return false;
501 
502     return pb_write(stream, buffer, size);
503 }
504 
pb_encode_submessage(pb_ostream_t * stream,const pb_field_t fields[],const void * src_struct)505 bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
506 {
507     /* First calculate the message size using a non-writing substream. */
508     pb_ostream_t substream = PB_OSTREAM_SIZING;
509     size_t size;
510     bool status;
511 
512     if (!pb_encode(&substream, fields, src_struct))
513     {
514 #ifndef PB_NO_ERRMSG
515         stream->errmsg = substream.errmsg;
516 #endif
517         return false;
518     }
519 
520     size = substream.bytes_written;
521 
522     if (!pb_encode_varint(stream, (uint64_t)size))
523         return false;
524 
525     if (stream->callback == NULL)
526         return pb_write(stream, NULL, size); /* Just sizing */
527 
528     if (stream->bytes_written + size > stream->max_size)
529         PB_RETURN_ERROR(stream, "stream full");
530 
531     /* Use a substream to verify that a callback doesn't write more than
532      * what it did the first time. */
533     substream.callback = stream->callback;
534     substream.state = stream->state;
535     substream.max_size = size;
536     substream.bytes_written = 0;
537 #ifndef PB_NO_ERRMSG
538     substream.errmsg = NULL;
539 #endif
540 
541     status = pb_encode(&substream, fields, src_struct);
542 
543     stream->bytes_written += substream.bytes_written;
544     stream->state = substream.state;
545 #ifndef PB_NO_ERRMSG
546     stream->errmsg = substream.errmsg;
547 #endif
548 
549     if (substream.bytes_written != size)
550         PB_RETURN_ERROR(stream, "submsg size changed");
551 
552     return status;
553 }
554 
555 /* Field encoders */
556 
pb_enc_varint(pb_ostream_t * stream,const pb_field_t * field,const void * src)557 static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
558 {
559     int64_t value = 0;
560 
561     /* Cases 1 and 2 are for compilers that have smaller types for bool
562      * or enums. */
563     switch (field->data_size)
564     {
565         case 1: value = *(const int8_t*)src; break;
566         case 2: value = *(const int16_t*)src; break;
567         case 4: value = *(const int32_t*)src; break;
568         case 8: value = *(const int64_t*)src; break;
569         default: PB_RETURN_ERROR(stream, "invalid data_size");
570     }
571 
572     return pb_encode_varint(stream, (uint64_t)value);
573 }
574 
pb_enc_uvarint(pb_ostream_t * stream,const pb_field_t * field,const void * src)575 static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
576 {
577     uint64_t value = 0;
578 
579     switch (field->data_size)
580     {
581         case 4: value = *(const uint32_t*)src; break;
582         case 8: value = *(const uint64_t*)src; break;
583         default: PB_RETURN_ERROR(stream, "invalid data_size");
584     }
585 
586     return pb_encode_varint(stream, value);
587 }
588 
pb_enc_svarint(pb_ostream_t * stream,const pb_field_t * field,const void * src)589 static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
590 {
591     int64_t value = 0;
592 
593     switch (field->data_size)
594     {
595         case 4: value = *(const int32_t*)src; break;
596         case 8: value = *(const int64_t*)src; break;
597         default: PB_RETURN_ERROR(stream, "invalid data_size");
598     }
599 
600     return pb_encode_svarint(stream, value);
601 }
602 
pb_enc_fixed64(pb_ostream_t * stream,const pb_field_t * field,const void * src)603 static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
604 {
605     UNUSED(field);
606     return pb_encode_fixed64(stream, src);
607 }
608 
pb_enc_fixed32(pb_ostream_t * stream,const pb_field_t * field,const void * src)609 static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
610 {
611     UNUSED(field);
612     return pb_encode_fixed32(stream, src);
613 }
614 
pb_enc_bytes(pb_ostream_t * stream,const pb_field_t * field,const void * src)615 static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
616 {
617     const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src;
618 
619     if (src == NULL)
620     {
621         /* Threat null pointer as an empty bytes field */
622         return pb_encode_string(stream, NULL, 0);
623     }
624 
625     if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
626         PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size)
627     {
628         PB_RETURN_ERROR(stream, "bytes size exceeded");
629     }
630 
631     return pb_encode_string(stream, bytes->bytes, bytes->size);
632 }
633 
pb_enc_string(pb_ostream_t * stream,const pb_field_t * field,const void * src)634 static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
635 {
636     /* strnlen() is not always available, so just use a loop */
637     size_t size = 0;
638     size_t max_size = field->data_size;
639     const char *p = (const char*)src;
640 
641     if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
642         max_size = (size_t)-1;
643 
644     if (src == NULL)
645     {
646         size = 0; /* Threat null pointer as an empty string */
647     }
648     else
649     {
650         while (size < max_size && *p != '\0')
651         {
652             size++;
653             p++;
654         }
655     }
656 
657     return pb_encode_string(stream, (const uint8_t*)src, size);
658 }
659 
pb_enc_submessage(pb_ostream_t * stream,const pb_field_t * field,const void * src)660 static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
661 {
662     if (field->ptr == NULL)
663         PB_RETURN_ERROR(stream, "invalid field descriptor");
664 
665     return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
666 }
667 
668