mark PurpleImageClass as private
[pidgin-git.git] / libpurple / protocols / facebook / thrift.c
blobd9995ea7ce5b8c8dca9c4892f7bda40c7e20e8a2
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include <string.h>
24 #include "thrift.h"
26 typedef struct
28 GByteArray *bytes;
29 gboolean internal;
30 guint offset;
31 guint pos;
32 guint lastbool;
33 } FbThriftPrivate;
35 /**
36 * FbThrift:
38 * Represents a reader/writer for compact Thrift data.
40 struct _FbThrift
42 GObject parent;
43 FbThriftPrivate *priv;
46 G_DEFINE_TYPE_WITH_PRIVATE(FbThrift, fb_thrift, G_TYPE_OBJECT);
48 static void
49 fb_thrift_dispose(GObject *obj)
51 FbThriftPrivate *priv = FB_THRIFT(obj)->priv;
53 if (priv->internal) {
54 g_byte_array_free(priv->bytes, TRUE);
58 static void
59 fb_thrift_class_init(FbThriftClass *klass)
61 GObjectClass *gklass = G_OBJECT_CLASS(klass);
63 gklass->dispose = fb_thrift_dispose;
66 static void
67 fb_thrift_init(FbThrift *thft)
69 FbThriftPrivate *priv = fb_thrift_get_instance_private(thft);
71 thft->priv = priv;
74 FbThrift *
75 fb_thrift_new(GByteArray *bytes, guint offset)
77 FbThrift *thft;
78 FbThriftPrivate *priv;
80 thft = g_object_new(FB_TYPE_THRIFT, NULL);
81 priv = thft->priv;
83 if (bytes != NULL) {
84 priv->bytes = bytes;
85 priv->offset = offset;
86 priv->pos = offset;
87 } else {
88 priv->bytes = g_byte_array_new();
89 priv->internal = TRUE;
92 return thft;
95 const GByteArray *
96 fb_thrift_get_bytes(FbThrift *thft)
98 FbThriftPrivate *priv;
100 g_return_val_if_fail(FB_IS_THRIFT(thft), NULL);
101 priv = thft->priv;
102 return priv->bytes;
105 guint
106 fb_thrift_get_pos(FbThrift *thft)
108 FbThriftPrivate *priv;
110 g_return_val_if_fail(FB_IS_THRIFT(thft), 0);
111 priv = thft->priv;
112 return priv->pos;
115 void
116 fb_thrift_set_pos(FbThrift *thft, guint pos)
118 FbThriftPrivate *priv;
120 g_return_if_fail(FB_IS_THRIFT(thft));
121 priv = thft->priv;
122 priv->pos = pos;
125 void
126 fb_thrift_reset(FbThrift *thft)
128 FbThriftPrivate *priv;
130 g_return_if_fail(FB_IS_THRIFT(thft));
131 priv = thft->priv;
132 priv->pos = priv->offset;
135 gboolean
136 fb_thrift_read(FbThrift *thft, gpointer data, guint size)
138 FbThriftPrivate *priv;
140 g_return_val_if_fail(FB_IS_THRIFT(thft), FALSE);
141 priv = thft->priv;
143 if ((priv->pos + size) > priv->bytes->len) {
144 return FALSE;
147 if ((data != NULL) && (size > 0)) {
148 memcpy(data, priv->bytes->data + priv->pos, size);
151 priv->pos += size;
152 return TRUE;
155 gboolean
156 fb_thrift_read_bool(FbThrift *thft, gboolean *value)
158 FbThriftPrivate *priv;
159 guint8 byte;
161 g_return_val_if_fail(FB_IS_THRIFT(thft), FALSE);
162 priv = thft->priv;
164 if ((priv->lastbool & 0x03) != 0x01) {
165 if (!fb_thrift_read_byte(thft, &byte)) {
166 return FALSE;
169 if (value != NULL) {
170 *value = (byte & 0x0F) == 0x01;
173 priv->lastbool = 0;
174 return TRUE;
177 if (value != NULL) {
178 *value = ((priv->lastbool & 0x04) >> 2) != 0;
181 priv->lastbool = 0;
182 return TRUE;
185 gboolean
186 fb_thrift_read_byte(FbThrift *thft, guint8 *value)
188 return fb_thrift_read(thft, value, sizeof *value);
191 gboolean
192 fb_thrift_read_dbl(FbThrift *thft, gdouble *value)
194 gint64 i64;
196 /* Almost always 8, but check anyways */
197 static const gsize size = MIN(sizeof value, sizeof i64);
199 if (!fb_thrift_read_i64(thft, &i64)) {
200 return FALSE;
203 if (value != NULL) {
204 memcpy(value, &i64, size);
207 return TRUE;
210 gboolean
211 fb_thrift_read_i16(FbThrift *thft, gint16 *value)
213 gint64 i64;
215 if (!fb_thrift_read_i64(thft, &i64)) {
216 return FALSE;
219 if (value != NULL) {
220 *value = i64;
223 return TRUE;
226 gboolean
227 fb_thrift_read_vi16(FbThrift *thft, guint16 *value)
229 guint64 u64;
231 if (!fb_thrift_read_vi64(thft, &u64)) {
232 return FALSE;
235 if (value != NULL) {
236 *value = u64;
239 return TRUE;
242 gboolean
243 fb_thrift_read_i32(FbThrift *thft, gint32 *value)
245 gint64 i64;
247 if (!fb_thrift_read_i64(thft, &i64)) {
248 return FALSE;
251 if (value != NULL) {
252 *value = i64;
255 return TRUE;
258 gboolean
259 fb_thrift_read_vi32(FbThrift *thft, guint32 *value)
261 guint64 u64;
263 if (!fb_thrift_read_vi64(thft, &u64)) {
264 return FALSE;
267 if (value != NULL) {
268 *value = u64;
271 return TRUE;
274 gboolean
275 fb_thrift_read_i64(FbThrift *thft, gint64 *value)
277 guint64 u64;
279 if (!fb_thrift_read_vi64(thft, &u64)) {
280 return FALSE;
283 if (value != NULL) {
284 /* Convert from zigzag to integer */
285 *value = (u64 >> 0x01) ^ -(u64 & 0x01);
288 return TRUE;
291 gboolean
292 fb_thrift_read_vi64(FbThrift *thft, guint64 *value)
294 guint i = 0;
295 guint8 byte;
296 guint64 u64 = 0;
298 do {
299 if (!fb_thrift_read_byte(thft, &byte)) {
300 return FALSE;
303 u64 |= ((guint64) (byte & 0x7F)) << i;
304 i += 7;
305 } while ((byte & 0x80) == 0x80);
307 if (value != NULL) {
308 *value = u64;
311 return TRUE;
314 gboolean
315 fb_thrift_read_str(FbThrift *thft, gchar **value)
317 guint8 *data;
318 guint32 size;
320 if (!fb_thrift_read_vi32(thft, &size)) {
321 return FALSE;
324 if (value != NULL) {
325 data = g_new(guint8, size + 1);
326 data[size] = 0;
327 } else {
328 data = NULL;
331 if (!fb_thrift_read(thft, data, size)) {
332 g_free(data);
333 return FALSE;
336 if (value != NULL) {
337 *value = (gchar *) data;
340 return TRUE;
343 gboolean
344 fb_thrift_read_field(FbThrift *thft, FbThriftType *type, gint16 *id,
345 gint16 lastid)
347 FbThriftPrivate *priv;
348 gint16 i16;
349 guint8 byte;
351 g_return_val_if_fail(FB_IS_THRIFT(thft), FALSE);
352 g_return_val_if_fail(type != NULL, FALSE);
353 g_return_val_if_fail(id != NULL, FALSE);
354 priv = thft->priv;
356 if (!fb_thrift_read_byte(thft, &byte)) {
357 return FALSE;
360 if (byte == FB_THRIFT_TYPE_STOP) {
361 *type = FB_THRIFT_TYPE_STOP;
362 return FALSE;
365 *type = fb_thrift_ct2t(byte & 0x0F);
366 i16 = (byte & 0xF0) >> 4;
368 if (i16 == 0) {
369 if (!fb_thrift_read_i16(thft, id)) {
370 return FALSE;
372 } else {
373 *id = lastid + i16;
376 if (*type == FB_THRIFT_TYPE_BOOL) {
377 priv->lastbool = 0x01;
379 if ((byte & 0x0F) == 0x01) {
380 priv->lastbool |= 0x01 << 2;
384 return TRUE;
387 gboolean
388 fb_thrift_read_stop(FbThrift *thft)
390 guint8 byte;
392 return fb_thrift_read_byte(thft, &byte) &&
393 (byte == FB_THRIFT_TYPE_STOP);
396 gboolean
397 fb_thrift_read_isstop(FbThrift *thft)
399 FbThriftPrivate *priv;
400 guint8 byte;
402 g_return_val_if_fail(FB_IS_THRIFT(thft), FALSE);
403 priv = thft->priv;
405 if (!fb_thrift_read_byte(thft, &byte)) {
406 return FALSE;
409 priv->pos--;
410 return byte == FB_THRIFT_TYPE_STOP;
413 gboolean
414 fb_thrift_read_list(FbThrift *thft, FbThriftType *type, guint *size)
416 guint8 byte;
417 guint32 u32;
419 g_return_val_if_fail(type != NULL, FALSE);
420 g_return_val_if_fail(size != NULL, FALSE);
422 if (!fb_thrift_read_byte(thft, &byte)) {
423 return FALSE;
426 *type = fb_thrift_ct2t(byte & 0x0F);
427 *size = (byte & 0xF0) >> 4;
429 if (*size == 0x0F) {
430 if (!fb_thrift_read_vi32(thft, &u32)) {
431 return FALSE;
434 *size = u32;
437 return TRUE;
440 gboolean
441 fb_thrift_read_map(FbThrift *thft, FbThriftType *ktype, FbThriftType *vtype,
442 guint *size)
444 gint32 i32;
445 guint8 byte;
447 g_return_val_if_fail(ktype != NULL, FALSE);
448 g_return_val_if_fail(vtype != NULL, FALSE);
449 g_return_val_if_fail(size != NULL, FALSE);
451 if (!fb_thrift_read_i32(thft, &i32)) {
452 return FALSE;
455 if (i32 != 0) {
456 if (!fb_thrift_read_byte(thft, &byte)) {
457 return FALSE;
460 *ktype = fb_thrift_ct2t((byte & 0xF0) >> 4);
461 *vtype = fb_thrift_ct2t(byte & 0x0F);
462 } else {
463 *ktype = 0;
464 *vtype = 0;
467 *size = i32;
468 return TRUE;
471 gboolean
472 fb_thrift_read_set(FbThrift *thft, FbThriftType *type, guint *size)
474 return fb_thrift_read_list(thft, type, size);
477 void
478 fb_thrift_write(FbThrift *thft, gconstpointer data, guint size)
480 FbThriftPrivate *priv;
482 g_return_if_fail(FB_IS_THRIFT(thft));
483 priv = thft->priv;
485 g_byte_array_append(priv->bytes, data, size);
486 priv->pos += size;
489 void
490 fb_thrift_write_bool(FbThrift *thft, gboolean value)
492 FbThriftPrivate *priv;
493 guint pos;
495 g_return_if_fail(FB_IS_THRIFT(thft));
496 priv = thft->priv;
498 if ((priv->lastbool & 0x03) != 0x02) {
499 fb_thrift_write_byte(thft, value ? 0x01 : 0x02);
500 return;
503 pos = priv->lastbool >> 3;
504 priv->lastbool = 0;
506 if ((pos >= priv->offset) && (pos < priv->bytes->len)) {
507 priv->bytes->data[pos] &= ~0x0F;
508 priv->bytes->data[pos] |= value ? 0x01 : 0x02;
512 void
513 fb_thrift_write_byte(FbThrift *thft, guint8 value)
515 fb_thrift_write(thft, &value, sizeof value);
518 void
519 fb_thrift_write_dbl(FbThrift *thft, gdouble value)
521 gint64 i64;
523 /* Almost always 8, but check anyways */
524 static const gsize size = MIN(sizeof value, sizeof i64);
526 memcpy(&i64, &value, size);
527 fb_thrift_write_i64(thft, i64);
530 void
531 fb_thrift_write_i16(FbThrift *thft, gint16 value)
533 fb_thrift_write_i64(thft, value);
536 void
537 fb_thrift_write_vi16(FbThrift *thft, guint16 value)
539 fb_thrift_write_vi64(thft, value);
542 void
543 fb_thrift_write_i32(FbThrift *thft, gint32 value)
545 value = (value << 1) ^ (value >> 31);
546 fb_thrift_write_vi64(thft, value);
549 void
550 fb_thrift_write_vi32(FbThrift *thft, guint32 value)
552 fb_thrift_write_vi64(thft, value);
555 void
556 fb_thrift_write_i64(FbThrift *thft, gint64 value)
558 value = (value << 1) ^ (value >> 63);
559 fb_thrift_write_vi64(thft, value);
562 void
563 fb_thrift_write_vi64(FbThrift *thft, guint64 value)
565 gboolean last;
566 guint8 byte;
568 do {
569 last = (value & ~0x7F) == 0;
570 byte = value & 0x7F;
572 if (!last) {
573 byte |= 0x80;
574 value >>= 7;
577 fb_thrift_write_byte(thft, byte);
578 } while (!last);
581 void
582 fb_thrift_write_str(FbThrift *thft, const gchar *value)
584 guint32 size;
586 g_return_if_fail(value != NULL);
588 size = strlen(value);
589 fb_thrift_write_vi32(thft, size);
590 fb_thrift_write(thft, value, size);
593 void
594 fb_thrift_write_field(FbThrift *thft, FbThriftType type, gint16 id,
595 gint16 lastid)
597 FbThriftPrivate *priv;
598 gint16 diff;
600 g_return_if_fail(FB_IS_THRIFT(thft));
601 priv = thft->priv;
603 if (type == FB_THRIFT_TYPE_BOOL) {
604 priv->lastbool = (priv->pos << 3) | 0x02;
607 type = fb_thrift_t2ct(type);
608 diff = id - lastid;
610 if ((id <= lastid) || (diff > 0x0F)) {
611 fb_thrift_write_byte(thft, type);
612 fb_thrift_write_i16(thft, id);
613 } else {
614 fb_thrift_write_byte(thft, (diff << 4) | type);
618 void
619 fb_thrift_write_stop(FbThrift *thft)
621 fb_thrift_write_byte(thft, FB_THRIFT_TYPE_STOP);
624 void
625 fb_thrift_write_list(FbThrift *thft, FbThriftType type, guint size)
627 type = fb_thrift_t2ct(type);
629 if (size <= 14) {
630 fb_thrift_write_byte(thft, (size << 4) | type);
631 return;
634 fb_thrift_write_vi32(thft, size);
635 fb_thrift_write_byte(thft, 0xF0 | type);
638 void
639 fb_thrift_write_map(FbThrift *thft, FbThriftType ktype, FbThriftType vtype,
640 guint size)
642 if (size == 0) {
643 fb_thrift_write_byte(thft, 0);
644 return;
647 ktype = fb_thrift_t2ct(ktype);
648 vtype = fb_thrift_t2ct(vtype);
650 fb_thrift_write_vi32(thft, size);
651 fb_thrift_write_byte(thft, (ktype << 4) | vtype);
654 void
655 fb_thrift_write_set(FbThrift *thft, FbThriftType type, guint size)
657 fb_thrift_write_list(thft, type, size);
660 guint8
661 fb_thrift_t2ct(FbThriftType type)
663 static const guint8 types[] = {
664 [FB_THRIFT_TYPE_STOP] = 0,
665 [FB_THRIFT_TYPE_VOID] = 0,
666 [FB_THRIFT_TYPE_BOOL] = 2,
667 [FB_THRIFT_TYPE_BYTE] = 3,
668 [FB_THRIFT_TYPE_DOUBLE] = 7,
669 [5] = 0,
670 [FB_THRIFT_TYPE_I16] = 4,
671 [7] = 0,
672 [FB_THRIFT_TYPE_I32] = 5,
673 [9] = 0,
674 [FB_THRIFT_TYPE_I64] = 6,
675 [FB_THRIFT_TYPE_STRING] = 8,
676 [FB_THRIFT_TYPE_STRUCT] = 12,
677 [FB_THRIFT_TYPE_MAP] = 11,
678 [FB_THRIFT_TYPE_SET] = 10,
679 [FB_THRIFT_TYPE_LIST] = 9
682 g_return_val_if_fail(type < G_N_ELEMENTS(types), 0);
683 return types[type];
686 FbThriftType
687 fb_thrift_ct2t(guint8 type)
689 static const guint8 types[] = {
690 [0] = FB_THRIFT_TYPE_STOP,
691 [1] = FB_THRIFT_TYPE_BOOL,
692 [2] = FB_THRIFT_TYPE_BOOL,
693 [3] = FB_THRIFT_TYPE_BYTE,
694 [4] = FB_THRIFT_TYPE_I16,
695 [5] = FB_THRIFT_TYPE_I32,
696 [6] = FB_THRIFT_TYPE_I64,
697 [7] = FB_THRIFT_TYPE_DOUBLE,
698 [8] = FB_THRIFT_TYPE_STRING,
699 [9] = FB_THRIFT_TYPE_LIST,
700 [10] = FB_THRIFT_TYPE_SET,
701 [11] = FB_THRIFT_TYPE_MAP,
702 [12] = FB_THRIFT_TYPE_STRUCT
705 g_return_val_if_fail(type < G_N_ELEMENTS(types), 0);
706 return types[type];