0%

avro-c使用指南

avro C使用

  avro当前版本是1.7.7. 当前libavro版本是22:0:0.这份文档创建于2014-07-18。
  
##1. avro简介

  avro是一个数据序列化系统。
  avro支持:

  • 丰富的数据结构
  • 紧凑、快速的二进制数据结构
  • 作为容器的文件,用于持久化数据
  • 远程过程调用(RPC)

  该文档主要介绍acro的c实现。要了解更多关于avro的东西,请访问avro的网站

##2. avro c简介

f1

c程序就像某人拿着刮胡刀在新打过蜡的舞台跳的高速舞蹈
——Aaldi Ravens

  虽然c版本已经在MacOSX和Linux上经过检验,但将来会支持更多系统的。如果你在其他系统上使用Avro C,请告诉我们。Avro C没有依赖额外的库文件。我们只是把Jansson放进了Avro C中,方便把JSON数据解释成schema结构。

  C版本支持:

  • 编解码所有基础和复杂类型的二进制数据
  • 存放到一个Avro对象的容器文件中
  • schema解释,提升和投影
  • 写入Avro数据时的有效性检查和非检查模式

  C版本缺少:

  • RPC

  要了解更多关于API的内容,请查看本文档后边的例子和引用。
  我们一直需要志愿者,所以如果你是个C hacker,请尽情提交项目分支

##3. 错误报告

  Avro C库中大部分函数会返回一个int类型的状态码。遵循POSIX errno.h的传统,0代表成功,非零代表有错。有一些函数返回一个指针类型而不是int类型的状态码;对这些函数而言,返回NULL意味着有错。

  多数时候你可以用avro_strerror()函数得到关于最近的error值的字符串描述:

1
2
3
4
avro_schema_t schema = avro_schema_string();
if (schema == NULL){
fprintf(stderr, "Error was %s\n", avro_strerror());
}

##4. Avro值

  从1.6.0版本开始,Avro C库引入了一套新的API操作Avro的数据。为了便于区分这两种API, 我们会用legacydatum代表旧的API,用value代表新的API。(这些名字的由来是在相关API中表示Avro数据的C类型——avro_datum_tavro_value_t。)旧API还存在,但不建议使用——在新程序中不应该使用avro_datum_t类型或者avro_datum_*函数。

  新API的一大优势是你可以把现有的C类型都当成是Avro的value;你只需要提供一个value接口的通用操作就行。真好,我们就提供了一个genericvalue的实现;“generic”在这里意味着这个单独的实现能适用于任意Avro schema类型。最后,我们还为不建议使用的avro_datum_t类型提供了一个封装接口,好让你能方便的转到新的API。

###4.1. Avro value接口

  你可以通过value接口操作Avro的value, 这个接口定义了怎样设置和重新拿到一个Avro value的内容。avro_value_t类型的一个实例就是一个单独的value。

  这部分大致介绍了你能对一个avro_value_t实例进行的操作。实际中对value接口只有很少的操作,但并不是所有的操作都能作用于任意Avro schema类型。例如,你不能对一个Avro数组进行avro_value_set_boolean的操作。如果你尝试调用一个不正确的操作,会得到EINVAL错误码。

  注意这部分介绍的函数适用于所有的Avro value,不管值是否是用以下形式实现的(注:这句翻译的有问题。 ***Note that the functions in this section apply to all Avro values, regardless of which value implementation is used under the covers.***)。这部分不介绍怎么创建value的实例,因为这些构造函数需要依赖特定的value的实现。

####4.1.1 通用操作
  不管是哪种Avro schema的实例,对任意value都有一套方便的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stding.h>
#include <avro.h>

avro_type_t avro_value_get_type(const avro_value_t *value);
avro_schema_t avro_value_get_schema(const avro_value-t *value);

int avro_value_equal(const avro_value_t *v1, const avro_value_t *v2);
int avro_value_equal_fast(const avro_value_t *v1, const avro_value_t *v2);

int avro_value_copy(avro_value_t *dest, const avro_value_t *src);
int avro_value_copy_fast(avro_value_t *dest, const avro_value_t *src);

uint32_t avro_value_hash(avro_value_t *value);

int avro_value_reset(avro_value_t *value);

  get_typeget_schema操作用于获取已知的avro_value_t实例是哪种Avro值的信息。(对get_schema来说,你借用了value关于schema的引用;如果你要保存它以保证在作用域外使用该值,你需要对它进行avro_schema_incref操作。)

  equalequal_fast方法比较两个value是否相等。这里两个value不需要是同一个的value的实例,但必须是同一个schema的实例。(不是等同的schema,是同一个schema。)equal方法检查schema是否匹配;equal_fast假设它们匹配。

  copycopy_fast方法把一个Avro value的内容拷贝到另一个。(在某些地方,使用下边介绍的avro_wrapped_buffer_t函数可以不用真的去拷贝内容就能复制bytes, string, fixed value 的内容。)跟equal一样,这两个value必须有同一个schema;copy检查schema,copy_fast假设schema相同。

  hash操作返回给定的Avro value的hash值。这可以用于用Avro value作为key构建hash表。该函数对map类型也适用;它能产生不依赖map中值的排列顺序的hash值。hash值只是在比较value之间是否有相同的schema时才有意义。hash值不保证在不同平台或不同Avro库版本之间都能一致。也就是说你只能在一个单独的程序的单独一小部分安全的使用这些hash值。

  reset操作清空一个avro_value_t实例,保证它可以接收一个新value的内容。大多数时候,这是个无操作的方法,因为新value会覆盖旧的。对数组和map类型,这个操作会从容器中移除所有的元素,以便我们能添加新的元素。对record和union来说,这会递归的清空field或当前分支。

####4.1.2 标量value
  最简单的情况是操作标量的Avro schema类型实例。在Avro中,标量值是所有基本的schema类型,还包括enumfixed——也就是所有不能包含其他Avro value的类型。注意我们用标准的C99类型代表一个基本Avro标量的内容。

  从一个Avro标量中获取内容,你需要用以下getter方法的一种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdint.h>
#include <stdlib.h>
#include <avro.h>

int avro_value_get_boolean(const avro_value_t *value, int *dest);
int avro_value_get_bytes(const avro_value_t *value, const void **dest, size_t *size);

int avro_value_get_double(const avro_value_t *value, double *dest);
int avro_value_get_float(const avro_value_t *value, float *dest);
int avro_value_get_int(const avro_value_t *value, int32_t *dest);
int avro_value_get_long(const avro_value_t *value, int64_t *dest);
int avro_value_get_null(const avro_value_t *value);
int avro_value_get_string(const avro_value_t *value, const char **dest, size_t *size);
int avro_value_get_enum(const avro_value_t *value, int *dest);
int avro_value_get_fixed(const avro_value_t *value, const void **dest, size_t *size);

  对其中的大部分来说,这些操作都是自解释性的。对bytes, string, fixed value,指向下面内容的指针是const——你不能直接修改内容。我们保证一个string是以NUL结尾的,所以你可以放心把它当成C字符串来使用。对string对象来说,返回的size值包括了NUL结束符;它会比你使用strlen得到的长度多1。

  另外,对bytes, string, fixedvalue,destsize参数是可选的;如果你只想知道一个bytes value的长度,你可以用:

1
2
3
avro_value_t *value = /* from somewhere */;
size_t size;
avro_value_get_bytes(value, NULL, &size);

  要设置Avro标量中的内容,需要用以下setter方法中的一种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdint.h>
#include <stdlib.h>
#include <avro.h>

int avro_value_set_boolean(avro_value_t *value, int src);
int avro_value_set_bytes(avro_value_t *value, void *buf, size_t size);
int avro_value_set_double(avro_value_t *value, double src);
int avro_value_set_float(avro_value_t *value, float src);
int avro_value_set_int(avro_value_t *value, int32_t src);
int avro_value_set_long(avro_value_t *value, int64_t src);
int avro_value_set_null(avro_value_t *value);
int avro_value_set_string(avro_value_t *value, const char *src);
int avro_value_set_string_len(avro_value_t *value, const char *src, size_t size);
int avro_value_set_enum(avro_value_t *value, int src);
int avro_value_set_fixed(avro_value_t *value, void *buf, size_t size);

  这些都很直接。对bytes, string, fixed value,set操作会复制一份原始数据。对string value, 内容必须是以NUL结尾的值。如果你已经知道了字符串内容的长度,你可以使用set_string_len函数,传入的长度值应该包含NUl结束符。如果你用了set_string,我们会用strlen计算其长度。

  对fixed value, size的值必须跟相应fixed schema中规定的长度相一致;如果大小不匹配,你会的到错误码。

  如果你不希望复制bytes, string, string value, 你可以用givergrabber函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdint.h>
#include <stdlib.h>
#include <avro.h>

typedef void (*avro_buf_free-t)(void *ptr, size_t sz, void *user_data)

int avro_value_give_bytes(avro_value_t *value, avro_wrapped_buffer_t *src);
int avro_value_give_string_len(avro_value_t *value, avro_wrapped_buffer_t *src);
int avro_value_give_fixed(avro_value_t *value, avro_wrapped_buffer_t *src);

int avro_value_grab_bytes(const avro_value_t *value, avro_wrapped_buffer_t *dest);
int avro_value_grab_string(const avro_value_t *value, avro_wrapped_buffer_t *dest);
int avro_value_grab_fixed(const avro_value_t *value, avro_wrapped_buffer_t *dest);

typedef struct avro_wrapped_buffer{
const void *buf;
size_t size;
void (*free)(avro_wrapped_buffer_t *self);
int (*copy)(avro_wrapped_buffer_t *dest, avro_wrapped_buffer_t *src, size_t offset, size_t length);
int (*slice)(avro_wrapped_buffer_t *self, size_t offset, size_t length);
}avro_wrapped_buffer_t;

void avro_wrapped_buffer_free(avro_wrapped_buffer_t *buf);

int avro_wrapped_buffer_copy(avro_wrapped_buffer_t *dest, const avro_wrapped_buffer_t *src, size_t offset, size_t length);

int avro_wrapped_buffer_slice(avro_wrapped_buffer_t *self, size_t offset, size_t length);

  give函数把已经存在的缓冲区的控制权交给value。(在调用这个方法之后你不应该再去释放src。)grab函数通过指向一个Avro value的指针填充一个封装好的缓冲区。(当你用完它之后应该自己去释放掉dest。)

  avro_wrapped_buffer_t结构体封装了已经存在的缓冲区的位置和大小。它也封装了几个操作。如果缓冲区内容不再使用,就要调用free操作释放掉它。如果需要把指针更新到它先前指向内容的子集,需要调用slice方法。(这不会创建新的缓冲区,只会更新已经存在的那个。)如果需要复制内容,需要调用copy方法。需要注意的是如果你通过良好的引用计数特性封装了一个缓冲区,你不需要真正的执行复制动作;你只需要保证free函数在原始数据和副本中都能被调用,并且别把事情搞砸。

  generic value实现充分利用了这个特性;如果你用give方法传入到一个封装好的缓冲区,然后用grab方法重新得到它,然后我们就可以用封装好的缓冲区的copy操作填充dest参数。如果你的封装好了的缓冲区通过更新引用计数而不是真正的复制操作实现了slice方法,你就会得到一个获取Avro value内容的零拷贝操作。

####4.1.3. 复合value
  接下来的这部分描述了操作复合Avro value的gettersetter方法。所有的符合只都会他们的子存储区负责,这意味着没有一个方法让你把一个已经存在的avro_value_t添加到一个数组中。相反,有一个方法能让你创建一个新的空的avro_value_t,并把它加到数组中,然后根据需要返回给你实例让你去填充内容。

  你也不能释放这样创建来的子元素;容器value会负责子元素的整个生命周期。子元素的作用域跟容器value的一样长。通常你会在栈中定义一个avro_value_t,当你不再用它自己在超出作用域后失效:

1
2
3
4
5
6
7
avro_value_t *array = /* from somewhere else */;

{
avro_value_t child;
avro_value_get_by_index(array, 0, &child, NULL);
/* do something intersting with the array element */
}

####4.1.4. Arrays
  数组value 有三种操作:

1
2
3
4
5
6
#include <stdlib.h>
#include <avro.h>

int avro_value_get_size(const avro_value_t *array, size_t *size);
int avro_value_get_by_index(const avro_value_t *array, size_t *index, avro_value_t *element, const char **unused);
int avro_value_append(avro_value_t *array, avro_value_t *element, size_t *new_index);

  get_size操作会返回当前数组中元素的个数。get_by_index操作会让element指针指向给定index的数组元素。(你需要把unused参数设置为NULL, 在数组value中它是被忽略的。)

####4.1.5 Maps
  map value有四种操作:

1
2
3
4
5
6
7
#include <stdlib.h>
#include <avro.h>

int avro_value_get_size(cosnt avro_value_t *map, size_t *size);
int avro_value_get_by_name(const avro_value_t *map, const char *key, avro_value_t *element, size_t *index);
int avro_value_get_by_index(const avro_value_t *map, size_t index, avro_value_t *element, const char **key);
int avro_value_add(avro_value_t *map, const char *key, avro_value_t *element, size_t *index, int *is_new);

  get_size操作会返回当前map中子元素的个数。map的元素可以通过他们的key(get_by_name)或者他们的数值索引(get_by_index)得到。(map中的数值索引依赖于元素加入map的顺序。)不管哪种情况,这两个方法都提供了一个额外的输出参数让你能得到索引对应的key,或者反过来。

  如果给定的key没有存在,add操作会往map添加一个新的value。如果key已经存在了,会返回现有的value。如果提供了index参数,元素的索引会填充到其中。如果提供了is_new参数,可以用它检测这个value是否是新建的。

####4.1.6. Records
  record value有三种操作:

1
2
3
4
5
6
#include <stdlib.h>
#include <avro.h>

int avro_value_get_size(const avro_value_t *record, size_t *size);
int avro_value_get_by_index(const avro_value_t *record, size_t index, avro_value_t *element, const char **field_name);
int avro_value_get_by_name(const avro_value_t *record, const char *fiedl_name, avro_value_t *element, size_t *index);

  get_size操作会返回当前record中字段的个数。(你也可以通过查询value的schema得到该值,但对一些实现来说,该方法会更快些。)
  
  get_by_indexget_by_name函数可以得到record中的一个字段,不管是通过record中的相对位置还是通过指定的字段。跟map一样,这个方法会提供一个额外的参数让你能得到索引对应的字段名称,或者反过来 。

  如果可能,建议你通过数值索引获取字段。对大多数实现来说,这会更有效率。

####4.1.7. Unions
  union value有三种操作:

1
2
3
4
5
#include <avro.h>

int avro_value_get_discriminat(const avro_value_t *union_val, int *disc);
int avro_value_get_current_branch(const avro_value_t *union_val, avro_value_t *branch);
int avro_value_set_branch(avro_value_t *union_val, int discriminat, avro_value_t *branch);

  get_discriminatget_current_branch方法会返回当前union value的状态,不会改变当前被选择的分支。set_branch方法用于选择激活的分支,然后把branch值指向分支value的实例。(大多数实现会智能的检测希望选择的分支是否存在,所以除非你能保证希望选择的分支已经存在,最好总是使用这个方法。)

###4.2. 创建value的实例
  现在我们已经介绍了如何跟已有的value交互,但首先你怎么创建一个value呢?每种value接口的实现必须提供它自己的创建avro_value_t实例的函数。步骤如下:

  1. 为你想用的value实例给出一个实现的结构体。(用一个avro_value_iface_t指针表示。)
  2. 用构造函数创建value实现的实例。
  3. 做你需要对这个value的事(用前边部分介绍的关于avro_value_t的操作)。
  4. 释放value实例,如果需要,就使用实现的析构函数。
  5. 释放你创建value实例时创建的实现结构体。

 这些步骤使用了以下的函数:

1
2
3
4
#include <avro.h>

avro_value_iface_t *avro_value_iface_incref(avro_value_iface_t *iface);
void avro_value_iface_decref(avro_value_iface_t *iface);

  注意对大多数的value实现来说,对多个value重复使用同一个avro_value_t实例是可行的,只需要在每次使用新值前调用avro_value_reset函数。(这会减少你程序中使用mallocfree的次数。)

  我们提供了一个generic value实现,会有效的作用于任何Avro schema。

  对大多数应用来说,你不需要写你自己的value实现方式;Avro C库提供了一个有效的generic实现方式,其会支持所有的Avro schema类型。很多时候你只会想用这个实现,而不是用你自己写的。(用你自己写的实现的最主要原因是你想用C语法获取复合value的元素——例如,把一个Avro record转换成C 结构体。)你可以用下边的函数创建和操作一个针对特定schema的通用的value实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <avro.h>

avro_value_iface_t *avro_generic_class_from_schema(avro_schema_t schema);
int avro_generic_value_new(const avro_value_iface_t *iface, avro_value_t *dest);
void avro_generic_value_free(avro_value_t *self);

  把这些步骤合起来,就是下边这个代码片段:

avro_schema_t schema = avro_schema_long();
avro_value_iface_t *iface = avro_generic_class_from_schema(schema);

avro_value_t val;
avro_generic_value_new(iface, &val);

/* Generate Avro longs from 0-499 */
int i;
for (i = 0; i < 500; i++){
avro_value_reset(&val);
avro_value_set_long(&val, i);
/* do something with the value */
}

avro_generic_value_free(&val);
avro_value_iface_decref(iface);
avro_schema_decref(schema);

##5. 引用计数
  Avro C为所有的schema和数据对象进行引用计数。当引用的个数降到0,内存才会被释放。

  例如,要创建和释放一个字符串,你需要用:

1
2
3
4
avro_datum_t string = avro_string("This is my string");

....
avro_datum_decref(string);

  当用个更多精心设计的schema和数据结构时,事情会变得复杂点儿。
  例如,假如你创建一个只有单一字符串字段的record时:

1
2
3
4
5
6
7
avro_datum_t example = avro_record("Example");
avro_datum_t solo_field = avro_string("Example field value");

avro_record_set(example, "solo", solo_field);

...
avro_datum_decref(example);

  在这个例子中,solo_field数据不会被释放掉,因为它有两个引用:原始的引用和在Example record中的引用。avro_datum_decref(example)操作能把引用计数降到1.如果你不再使用solo_field schema,你需要用avor_schema_decref(solo_field)彻底解掉solo_field的引用然后销毁它。

##6. Wrap It and Give It
  你会注意到有些数据类型能被”wrapped”和”given”。这给了C程序员决定谁负责内存的自由。举个字符串的例子:
  要创建一个字符串datum, 你有三种不同的方法:

1
2
3
avro_datum_t avro_string(const char *str);
avro_datum_t avro_wrapstring(const char *str);
avro_datum_t avro_givestring(cosnt char *str);

  如果你使用了avro_string, Avro C会复制一个你的字符串并且当datum被解引用时会释放它。在一些情况下,特别是在操作大量数据时,你想要避免内存复制,这就是avro_wrapstringavro_givestring能帮上忙的地方了。
  如果你用了avro_wrapstring, Avro C不会做任何内存管理。它只会保存一个指向你的数据的指针,释放这个字符串是你自己的责任。
  警告:当用avro_wrapstring时,不要在你用avro_datum_decref()解这个字符串的引用前不要释放掉这个字符串。
  最后,如果你用avro_givestring, Avro C会在datum被解引用的时候释放掉字符串。也就是说,你把释放字符串的责任给了Avro C。
  警告:不要在你还没使用例如malloc 或 strdup操作申请堆内存的时候就把一个字符串”给“Avro C。不要像这样做:avro_datum_t bad_idea = avro_givestring("This isn't allocated on the heap");

##7. schema有效性
  如果你向写一个datum,你需要用到一下的函数:

1
int avro_write_data(avro_writer_t writer, avro_schema_t writers_schema, avro_datum_t datum);

  如果你传入了一个writers_schema,那么你的datum会在它被传入writer之前检查有效性。这个检查保证了你的数据有正确的格式。如果你确信你的数据格式是正确的,你可以把writers_schema参数设为NULL,那样Avro C就不会在写入前检查有效性。
  注意:写入到Avro 文件对象容器的数据总会被验证有效性。

##8. 例子
我都没希望过今天会走到这一步!
—— Dante Hicks

  想象你是个在New Jersey, Leonardo的自由的程序员,当地便利店那老板找到了你,他想让你帮忙设计个通讯录数据库,好让他能在休息日叫雇员工作。

  你可以用Avro C构建个像下边这样的简单的通讯录系统…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include <avro.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef DEFLATE_CODEC
#define QUICKSTOP_CODEC "deflate"
#else
#define QUICKSTOP_CODEC "null"
#endif

avro_schema_t person_schema;
int64_t id = 0;

/* A simple schema for our tutorial */
const char PERSON_SCHEMA[] =
"{\"type\":\"record\",\
\"name\":\"Person\",\
\"fields\":[\
{\"name\": \"ID\", \"type\": \"long\"},\
{\"name\": \"First\", \"type\": \"string\"},\
{\"name\": \"Last\", \"type\": \"string\"},\
{\"name\": \"Phone\", \"type\": \"string\"},\
{\"name\": \"Age\", \"type\": \"int\"}]}";

/* Parse schema into a schema data structure */
void init_schema(void)
{
if (avro_schema_from_json_literal(PERSON_SCHEMA, &person_schema)) {
fprintf(stderr, "Unable to parse person schema\n");
exit(EXIT_FAILURE);
}
}

/* Create a datum to match the person schema and save it */
void
add_person(avro_file_writer_t db, const char *first, const char *last,
const char *phone, int32_t age)
{
avro_datum_t person = avro_record(person_schema);

avro_datum_t id_datum = avro_int64(++id);
avro_datum_t first_datum = avro_string(first);
avro_datum_t last_datum = avro_string(last);
avro_datum_t age_datum = avro_int32(age);
avro_datum_t phone_datum = avro_string(phone);

if (avro_record_set(person, "ID", id_datum)
|| avro_record_set(person, "First", first_datum)
|| avro_record_set(person, "Last", last_datum)
|| avro_record_set(person, "Age", age_datum)
|| avro_record_set(person, "Phone", phone_datum)) {
fprintf(stderr, "Unable to create Person datum structure\n");
exit(EXIT_FAILURE);
}

if (avro_file_writer_append(db, person)) {
fprintf(stderr,
"Unable to write Person datum to memory buffer\nMessage: %s\n", avro_strerror());
exit(EXIT_FAILURE);
}

/* Decrement all our references to prevent memory from leaking */
avro_datum_decref(id_datum);
avro_datum_decref(first_datum);
avro_datum_decref(last_datum);
avro_datum_decref(age_datum);
avro_datum_decref(phone_datum);
avro_datum_decref(person);

//fprintf(stdout, "Successfully added %s, %s id=%"PRId64"\n", last, first, id);
}

int print_person(avro_file_reader_t db, avro_schema_t reader_schema)
{
int rval;
avro_datum_t person;

rval = avro_file_reader_read(db, reader_schema, &person);
if (rval == 0) {
int64_t i64;
int32_t i32;
char *p;
avro_datum_t id_datum, first_datum, last_datum, phone_datum,
age_datum;

if (avro_record_get(person, "ID", &id_datum) == 0) {
avro_int64_get(id_datum, &i64);
fprintf(stdout, "%"PRId64" | ", i64);
}
if (avro_record_get(person, "First", &first_datum) == 0) {
avro_string_get(first_datum, &p);
fprintf(stdout, "%15s | ", p);
}
if (avro_record_get(person, "Last", &last_datum) == 0) {
avro_string_get(last_datum, &p);
fprintf(stdout, "%15s | ", p);
}
if (avro_record_get(person, "Phone", &phone_datum) == 0) {
avro_string_get(phone_datum, &p);
fprintf(stdout, "%15s | ", p);
}
if (avro_record_get(person, "Age", &age_datum) == 0) {
avro_int32_get(age_datum, &i32);
fprintf(stdout, "%d", i32);
}
fprintf(stdout, "\n");

/* We no longer need this memory */
avro_datum_decref(person);
}
return rval;
}

int main(void)
{
int rval;
avro_file_reader_t dbreader;
avro_file_writer_t db;
avro_schema_t projection_schema, first_name_schema, phone_schema;
int64_t i;
const char *dbname = "quickstop.db";
char number[15] = {0};

/* Initialize the schema structure from JSON */
init_schema();

/* Delete the database if it exists */
remove(dbname);
/* Create a new database */
rval = avro_file_writer_create_with_codec
(dbname, person_schema, &db, QUICKSTOP_CODEC, 0);
if (rval) {
fprintf(stderr, "There was an error creating %s\n", dbname);
fprintf(stderr, " error message: %s\n", avro_strerror());
exit(EXIT_FAILURE);
}

/* Add lots of people to the database */
for (i = 0; i < 1000; i++)
{
sprintf(number, "(%d)", (int)i);
add_person(db, "Dante", "Hicks", number, 32);
add_person(db, "Randal", "Graves", "(555) 123-5678", 30);
add_person(db, "Veronica", "Loughran", "(555) 123-0987", 28);
add_person(db, "Caitlin", "Bree", "(555) 123-2323", 27);
add_person(db, "Bob", "Silent", "(555) 123-6422", 29);
add_person(db, "Jay", "???", number, 26);
}

/* Close the block and open a new one */
avro_file_writer_flush(db);
add_person(db, "Super", "Man", "123456", 31);

avro_file_writer_close(db);

fprintf(stdout, "\nNow let's read all the records back out\n");

/* Read all the records and print them */
if (avro_file_reader(dbname, &dbreader)) {
fprintf(stderr, "Error opening file: %s\n", avro_strerror());
exit(EXIT_FAILURE);
}
for (i = 0; i < id; i++) {
if (print_person(dbreader, NULL)) {
fprintf(stderr, "Error printing person\nMessage: %s\n", avro_strerror());
exit(EXIT_FAILURE);
}
}
avro_file_reader_close(dbreader);

/* You can also use projection, to only decode only the data you are
interested in. This is particularly useful when you have
huge data sets and you'll only interest in particular fields
e.g. your contacts First name and phone number */
projection_schema = avro_schema_record("Person", NULL);
first_name_schema = avro_schema_string();
phone_schema = avro_schema_string();
avro_schema_record_field_append(projection_schema, "First",
first_name_schema);
avro_schema_record_field_append(projection_schema, "Phone",
phone_schema);

/* Read only the record you're interested in */
fprintf(stdout,
"\n\nUse projection to print only the First name and phone numbers\n");
if (avro_file_reader(dbname, &dbreader)) {
fprintf(stderr, "Error opening file: %s\n", avro_strerror());
exit(EXIT_FAILURE);
}
for (i = 0; i < id; i++) {
if (print_person(dbreader, projection_schema)) {
fprintf(stderr, "Error printing person: %s\n",
avro_strerror());
exit(EXIT_FAILURE);
}
}
avro_file_reader_close(dbreader);
avro_schema_decref(first_name_schema);
avro_schema_decref(phone_schema);
avro_schema_decref(projection_schema);

/* We don't need this schema anymore */
avro_schema_decref(person_schema);
return 0;
}

  编译运行后,你应该得到下边的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Successfully added Hicks, Dante id=1
Successfully added Graves, Randal id=2
Successfully added Loughran, Veronica id=3
Successfully added Bree, Caitlin id=4
Successfully added Silent, Bob id=5
Successfully added ???, Jay id=6

Avro is compact. Here is the data for all 6 people.
| 02 0A 44 61 6E 74 65 0A | 48 69 63 6B 73 1C 28 35 | ..Dante.Hicks.(5
| 35 35 29 20 31 32 33 2D | 34 35 36 37 40 04 0C 52 | 55) 123-4567@..R
| 61 6E 64 61 6C 0C 47 72 | 61 76 65 73 1C 28 35 35 | andal.Graves.(55
| 35 29 20 31 32 33 2D 35 | 36 37 38 3C 06 10 56 65 | 5) 123-5678<..Ve
| 72 6F 6E 69 63 61 10 4C | 6F 75 67 68 72 61 6E 1C | ronica.Loughran.
| 28 35 35 35 29 20 31 32 | 33 2D 30 39 38 37 38 08 | (555) 123-09878.
| 0E 43 61 69 74 6C 69 6E | 08 42 72 65 65 1C 28 35 | .Caitlin.Bree.(5
| 35 35 29 20 31 32 33 2D | 32 33 32 33 36 0A 06 42 | 55) 123-23236..B
| 6F 62 0C 53 69 6C 65 6E | 74 1C 28 35 35 35 29 20 | ob.Silent.(555)
| 31 32 33 2D 36 34 32 32 | 3A 0C 06 4A 61 79 06 3F | 123-6422:..Jay.?
| 3F 3F 1C 28 35 35 35 29 | 20 31 32 33 2D 39 31 38 | ??.(555) 123-918
| 32 34 .. .. .. .. .. .. | .. .. .. .. .. .. .. .. | 24..............

Now let's read all the records back out
1 | Dante | Hicks | (555) 123-4567 | 32
2 | Randal | Graves | (555) 123-5678 | 30
3 | Veronica | Loughran | (555) 123-0987 | 28
4 | Caitlin | Bree | (555) 123-2323 | 27
5 | Bob | Silent | (555) 123-6422 | 29
6 | Jay | ??? | (555) 123-9182 | 26


Use projection to print only the First name and phone numbers
Dante | (555) 123-4567 |
Randal | (555) 123-5678 |
Veronica | (555) 123-0987 |
Caitlin | (555) 123-2323 |
Bob | (555) 123-6422 |
Jay | (555) 123-9182 |

  便利店老板很高兴,然后他让你为他的bulabula商店建个视频数据库…

##9. 引用文件

###9.1 avro.h
  avro.h头文件包含了Avro C的公共API。这些文档还不齐全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#ifndef AVRO_H
#define AVRO_H
#ifdef __cplusplus
extern "C" {
#define CLOSE_EXTERN }
#else
#define CLOSE_EXTERN
#endif

#include <avro/allocation.h>
#include <avro/basics.h>
#include <avro/consumer.h>
#include <avro/data.h>
#include <avro/errors.h>
#include <avro/generic.h>
#include <avro/io.h>
#include <avro/legacy.h>
#include <avro/platform.h>
#include <avro/resolver.h>
#include <avro/schema.h>
#include <avro/value.h>

CLOSE_EXTERN
#endif

  9.2 test_avro_data.c
  学习用Avro C编解码数据的好方法是查看test_avro_data.c单元测试。这个简单的单元测试能检查所有的avro类型都能被正确编解码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include "avro.h"
#include "avro_private.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

char buf[4096];
avro_reader_t reader;
avro_writer_t writer;

typedef int (*avro_test) (void);

/*
* Use a custom allocator that verifies that the size that we use to
* free an object matches the size that we use to allocate it.
*/

static void *
test_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
{
AVRO_UNUSED(ud);
AVRO_UNUSED(osize);

if (nsize == 0) {
size_t *size = ((size_t *) ptr) - 1;
if (osize != *size) {
fprintf(stderr,
"Error freeing %p:\n"
"Size passed to avro_free (%" PRIsz ") "
"doesn't match size passed to "
"avro_malloc (%" PRIsz ")\n",
ptr, osize, *size);
abort();
//exit(EXIT_FAILURE);
}
free(size);
return NULL;
} else {
size_t real_size = nsize + sizeof(size_t);
size_t *old_size = ptr? ((size_t *) ptr)-1: NULL;
size_t *size = (size_t *) realloc(old_size, real_size);
*size = nsize;
return (size + 1);
}
}

void init_rand(void)
{
srand(time(NULL));
}

double rand_number(double from, double to)
{
double range = to - from;
return from + ((double)rand() / (RAND_MAX + 1.0)) * range;
}

int64_t rand_int64(void)
{
return (int64_t) rand_number(LONG_MIN, LONG_MAX);
}

int32_t rand_int32(void)
{
return (int32_t) rand_number(INT_MIN, INT_MAX);
}

void
write_read_check(avro_schema_t writers_schema, avro_datum_t datum,
avro_schema_t readers_schema, avro_datum_t expected, char *type)
{
avro_datum_t datum_out;
int validate;

for (validate = 0; validate <= 1; validate++) {

reader = avro_reader_memory(buf, sizeof(buf));
writer = avro_writer_memory(buf, sizeof(buf));

if (!expected) {
expected = datum;
}

/* Validating read/write */
if (avro_write_data
(writer, validate ? writers_schema : NULL, datum)) {
fprintf(stderr, "Unable to write %s validate=%d\n %s\n",
type, validate, avro_strerror());
exit(EXIT_FAILURE);
}
int64_t size =
avro_size_data(writer, validate ? writers_schema : NULL,
datum);
if (size != avro_writer_tell(writer)) {
fprintf(stderr,
"Unable to calculate size %s validate=%d "
"(%"PRId64" != %"PRId64")\n %s\n",
type, validate, size, avro_writer_tell(writer),
avro_strerror());
exit(EXIT_FAILURE);
}
if (avro_read_data
(reader, writers_schema, readers_schema, &datum_out)) {
fprintf(stderr, "Unable to read %s validate=%d\n %s\n",
type, validate, avro_strerror());
fprintf(stderr, " %s\n", avro_strerror());
exit(EXIT_FAILURE);
}
if (!avro_datum_equal(expected, datum_out)) {
fprintf(stderr,
"Unable to encode/decode %s validate=%d\n %s\n",
type, validate, avro_strerror());
exit(EXIT_FAILURE);
}

avro_reader_dump(reader, stderr);
avro_datum_decref(datum_out);
avro_reader_free(reader);
avro_writer_free(writer);
}
}

static void test_json(avro_datum_t datum, const char *expected)
{
char *json = NULL;
avro_datum_to_json(datum, 1, &json);
if (strcmp(json, expected) != 0) {
fprintf(stderr, "Unexpected JSON encoding: %s\n", json);
exit(EXIT_FAILURE);
}
free(json);
}

static int test_string(void)
{
unsigned int i;
const char *strings[] = { "Four score and seven years ago",
"our father brought forth on this continent",
"a new nation", "conceived in Liberty",
"and dedicated to the proposition that all men are created equal."
};
avro_schema_t writer_schema = avro_schema_string();
for (i = 0; i < sizeof(strings) / sizeof(strings[0]); i++) {
avro_datum_t datum = avro_givestring(strings[i], NULL);
write_read_check(writer_schema, datum, NULL, NULL, "string");
avro_datum_decref(datum);
}

avro_datum_t datum = avro_givestring(strings[0], NULL);
test_json(datum, "\"Four score and seven years ago\"");
avro_datum_decref(datum);

// The following should bork if we don't copy the string value
// correctly (since we'll try to free a static string).

datum = avro_string("this should be copied");
avro_string_set(datum, "also this");
avro_datum_decref(datum);

avro_schema_decref(writer_schema);
return 0;
}

static int test_bytes(void)
{
char bytes[] = { 0xDE, 0xAD, 0xBE, 0xEF };
avro_schema_t writer_schema = avro_schema_bytes();
avro_datum_t datum;
avro_datum_t expected_datum;

datum = avro_givebytes(bytes, sizeof(bytes), NULL);
write_read_check(writer_schema, datum, NULL, NULL, "bytes");
test_json(datum, "\"\\u00de\\u00ad\\u00be\\u00ef\"");
avro_datum_decref(datum);
avro_schema_decref(writer_schema);

datum = avro_givebytes(NULL, 0, NULL);
avro_givebytes_set(datum, bytes, sizeof(bytes), NULL);
expected_datum = avro_givebytes(bytes, sizeof(bytes), NULL);
if (!avro_datum_equal(datum, expected_datum)) {
fprintf(stderr,
"Expected equal bytes instances.\n");
exit(EXIT_FAILURE);
}
avro_datum_decref(datum);
avro_datum_decref(expected_datum);

// The following should bork if we don't copy the bytes value
// correctly (since we'll try to free a static string).

datum = avro_bytes("original", 8);
avro_bytes_set(datum, "alsothis", 8);
avro_datum_decref(datum);

avro_schema_decref(writer_schema);
return 0;
}

static int test_int32(void)
{
int i;
avro_schema_t writer_schema = avro_schema_int();
avro_schema_t long_schema = avro_schema_long();
avro_schema_t float_schema = avro_schema_float();
avro_schema_t double_schema = avro_schema_double();
for (i = 0; i < 100; i++) {
int32_t value = rand_int32();
avro_datum_t datum = avro_int32(value);
avro_datum_t long_datum = avro_int64(value);
avro_datum_t float_datum = avro_float(value);
avro_datum_t double_datum = avro_double(value);
write_read_check(writer_schema, datum, NULL, NULL, "int");
write_read_check(writer_schema, datum,
long_schema, long_datum, "int->long");
write_read_check(writer_schema, datum,
float_schema, float_datum, "int->float");
write_read_check(writer_schema, datum,
double_schema, double_datum, "int->double");
avro_datum_decref(datum);
avro_datum_decref(long_datum);
avro_datum_decref(float_datum);
avro_datum_decref(double_datum);
}

avro_datum_t datum = avro_int32(10000);
test_json(datum, "10000");
avro_datum_decref(datum);

avro_schema_decref(writer_schema);
avro_schema_decref(long_schema);
avro_schema_decref(float_schema);
avro_schema_decref(double_schema);
return 0;
}

static int test_int64(void)
{
int i;
avro_schema_t writer_schema = avro_schema_long();
avro_schema_t float_schema = avro_schema_float();
avro_schema_t double_schema = avro_schema_double();
for (i = 0; i < 100; i++) {
int64_t value = rand_int64();
avro_datum_t datum = avro_int64(value);
avro_datum_t float_datum = avro_float(value);
avro_datum_t double_datum = avro_double(value);
write_read_check(writer_schema, datum, NULL, NULL, "long");
write_read_check(writer_schema, datum,
float_schema, float_datum, "long->float");
write_read_check(writer_schema, datum,
double_schema, double_datum, "long->double");
avro_datum_decref(datum);
avro_datum_decref(float_datum);
avro_datum_decref(double_datum);
}

avro_datum_t datum = avro_int64(10000);
test_json(datum, "10000");
avro_datum_decref(datum);

avro_schema_decref(writer_schema);
avro_schema_decref(float_schema);
avro_schema_decref(double_schema);
return 0;
}

static int test_double(void)
{
int i;
avro_schema_t schema = avro_schema_double();
for (i = 0; i < 100; i++) {
avro_datum_t datum = avro_double(rand_number(-1.0E10, 1.0E10));
write_read_check(schema, datum, NULL, NULL, "double");
avro_datum_decref(datum);
}

avro_datum_t datum = avro_double(2000.0);
test_json(datum, "2000.0");
avro_datum_decref(datum);

avro_schema_decref(schema);
return 0;
}

static int test_float(void)
{
int i;
avro_schema_t schema = avro_schema_float();
avro_schema_t double_schema = avro_schema_double();
for (i = 0; i < 100; i++) {
float value = rand_number(-1.0E10, 1.0E10);
avro_datum_t datum = avro_float(value);
avro_datum_t double_datum = avro_double(value);
write_read_check(schema, datum, NULL, NULL, "float");
write_read_check(schema, datum,
double_schema, double_datum, "float->double");
avro_datum_decref(datum);
avro_datum_decref(double_datum);
}

avro_datum_t datum = avro_float(2000.0);
test_json(datum, "2000.0");
avro_datum_decref(datum);

avro_schema_decref(schema);
avro_schema_decref(double_schema);
return 0;
}

static int test_boolean(void)
{
int i;
const char *expected_json[] = { "false", "true" };
avro_schema_t schema = avro_schema_boolean();
for (i = 0; i <= 1; i++) {
avro_datum_t datum = avro_boolean(i);
write_read_check(schema, datum, NULL, NULL, "boolean");
test_json(datum, expected_json[i]);
avro_datum_decref(datum);
}
avro_schema_decref(schema);
return 0;
}

static int test_null(void)
{
avro_schema_t schema = avro_schema_null();
avro_datum_t datum = avro_null();
write_read_check(schema, datum, NULL, NULL, "null");
test_json(datum, "null");
avro_datum_decref(datum);
return 0;
}

static int test_record(void)
{
avro_schema_t schema = avro_schema_record("person", NULL);
avro_schema_record_field_append(schema, "name", avro_schema_string());
avro_schema_record_field_append(schema, "age", avro_schema_int());

avro_datum_t datum = avro_record(schema);
avro_datum_t name_datum, age_datum;

name_datum = avro_givestring("Joseph Campbell", NULL);
age_datum = avro_int32(83);

avro_record_set(datum, "name", name_datum);
avro_record_set(datum, "age", age_datum);

write_read_check(schema, datum, NULL, NULL, "record");
test_json(datum, "{\"name\": \"Joseph Campbell\", \"age\": 83}");

int rc;
avro_record_set_field_value(rc, datum, int32, "age", 104);

int32_t age = 0;
avro_record_get_field_value(rc, datum, int32, "age", &age);
if (age != 104) {
fprintf(stderr, "Incorrect age value\n");
exit(EXIT_FAILURE);
}

avro_datum_decref(name_datum);
avro_datum_decref(age_datum);
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}

static int test_nested_record(void)
{
const char *json =
"{"
" \"type\": \"record\","
" \"name\": \"list\","
" \"fields\": ["
" { \"name\": \"x\", \"type\": \"int\" },"
" { \"name\": \"y\", \"type\": \"int\" },"
" { \"name\": \"next\", \"type\": [\"null\",\"list\"]}"
" ]"
"}";

int rval;

avro_schema_t schema = NULL;
avro_schema_error_t error;
avro_schema_from_json(json, strlen(json), &schema, &error);

avro_datum_t head = avro_datum_from_schema(schema);
avro_record_set_field_value(rval, head, int32, "x", 10);
avro_record_set_field_value(rval, head, int32, "y", 10);

avro_datum_t next = NULL;
avro_datum_t tail = NULL;

avro_record_get(head, "next", &next);
avro_union_set_discriminant(next, 1, &tail);
avro_record_set_field_value(rval, tail, int32, "x", 20);
avro_record_set_field_value(rval, tail, int32, "y", 20);

avro_record_get(tail, "next", &next);
avro_union_set_discriminant(next, 0, NULL);

write_read_check(schema, head, NULL, NULL, "nested record");

avro_schema_decref(schema);
avro_datum_decref(head);

return 0;
}

static int test_enum(void)
{
enum avro_languages {
AVRO_C,
AVRO_CPP,
AVRO_PYTHON,
AVRO_RUBY,
AVRO_JAVA
};
avro_schema_t schema = avro_schema_enum("language");
avro_datum_t datum = avro_enum(schema, AVRO_C);

avro_schema_enum_symbol_append(schema, "C");
avro_schema_enum_symbol_append(schema, "C++");
avro_schema_enum_symbol_append(schema, "Python");
avro_schema_enum_symbol_append(schema, "Ruby");
avro_schema_enum_symbol_append(schema, "Java");

if (avro_enum_get(datum) != AVRO_C) {
fprintf(stderr, "Unexpected enum value AVRO_C\n");
exit(EXIT_FAILURE);
}

if (strcmp(avro_enum_get_name(datum), "C") != 0) {
fprintf(stderr, "Unexpected enum value name C\n");
exit(EXIT_FAILURE);
}

write_read_check(schema, datum, NULL, NULL, "enum");
test_json(datum, "\"C\"");

avro_enum_set(datum, AVRO_CPP);
if (strcmp(avro_enum_get_name(datum), "C++") != 0) {
fprintf(stderr, "Unexpected enum value name C++\n");
exit(EXIT_FAILURE);
}

write_read_check(schema, datum, NULL, NULL, "enum");
test_json(datum, "\"C++\"");

avro_enum_set_name(datum, "Python");
if (avro_enum_get(datum) != AVRO_PYTHON) {
fprintf(stderr, "Unexpected enum value AVRO_PYTHON\n");
exit(EXIT_FAILURE);
}

write_read_check(schema, datum, NULL, NULL, "enum");
test_json(datum, "\"Python\"");

avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}

static int test_array(void)
{
int i, rval;
avro_schema_t schema = avro_schema_array(avro_schema_int());
avro_datum_t datum = avro_array(schema);

for (i = 0; i < 10; i++) {
avro_datum_t i32_datum = avro_int32(i);
rval = avro_array_append_datum(datum, i32_datum);
avro_datum_decref(i32_datum);
if (rval) {
exit(EXIT_FAILURE);
}
}

if (avro_array_size(datum) != 10) {
fprintf(stderr, "Unexpected array size");
exit(EXIT_FAILURE);
}

write_read_check(schema, datum, NULL, NULL, "array");
test_json(datum, "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}

static int test_map(void)
{
avro_schema_t schema = avro_schema_map(avro_schema_long());
avro_datum_t datum = avro_map(schema);
int64_t i = 0;
char *nums[] =
{ "zero", "one", "two", "three", "four", "five", "six", NULL };
while (nums[i]) {
avro_datum_t i_datum = avro_int64(i);
avro_map_set(datum, nums[i], i_datum);
avro_datum_decref(i_datum);
i++;
}

if (avro_array_size(datum) != 7) {
fprintf(stderr, "Unexpected map size\n");
exit(EXIT_FAILURE);
}

avro_datum_t value;
const char *key;
avro_map_get_key(datum, 2, &key);
avro_map_get(datum, key, &value);
int64_t val;
avro_int64_get(value, &val);

if (val != 2) {
fprintf(stderr, "Unexpected map value 2\n");
exit(EXIT_FAILURE);
}

int index;
if (avro_map_get_index(datum, "two", &index)) {
fprintf(stderr, "Can't get index for key \"two\": %s\n",
avro_strerror());
exit(EXIT_FAILURE);
}
if (index != 2) {
fprintf(stderr, "Unexpected index for key \"two\"\n");
exit(EXIT_FAILURE);
}
if (!avro_map_get_index(datum, "foobar", &index)) {
fprintf(stderr, "Unexpected index for key \"foobar\"\n");
exit(EXIT_FAILURE);
}

write_read_check(schema, datum, NULL, NULL, "map");
test_json(datum,
"{\"zero\": 0, \"one\": 1, \"two\": 2, \"three\": 3, "
"\"four\": 4, \"five\": 5, \"six\": 6}");
avro_datum_decref(datum);
avro_schema_decref(schema);
return 0;
}

static int test_union(void)
{
avro_schema_t schema = avro_schema_union();
avro_datum_t union_datum;
avro_datum_t datum;
avro_datum_t union_datum1;
avro_datum_t datum1;

avro_schema_union_append(schema, avro_schema_string());
avro_schema_union_append(schema, avro_schema_int());
avro_schema_union_append(schema, avro_schema_null());

datum = avro_givestring("Follow your bliss.", NULL);
union_datum = avro_union(schema, 0, datum);

if (avro_union_discriminant(union_datum) != 0) {
fprintf(stderr, "Unexpected union discriminant\n");
exit(EXIT_FAILURE);
}

if (avro_union_current_branch(union_datum) != datum) {
fprintf(stderr, "Unexpected union branch datum\n");
exit(EXIT_FAILURE);
}

union_datum1 = avro_datum_from_schema(schema);
avro_union_set_discriminant(union_datum1, 0, &datum1);
avro_givestring_set(datum1, "Follow your bliss.", NULL);

if (!avro_datum_equal(datum, datum1)) {
fprintf(stderr, "Union values should be equal\n");
exit(EXIT_FAILURE);
}

write_read_check(schema, union_datum, NULL, NULL, "union");
test_json(union_datum, "{\"string\": \"Follow your bliss.\"}");

avro_datum_decref(datum);
avro_union_set_discriminant(union_datum, 2, &datum);
test_json(union_datum, "null");

avro_datum_decref(union_datum);
avro_datum_decref(datum);
avro_datum_decref(union_datum1);
avro_schema_decref(schema);
return 0;
}

static int test_fixed(void)
{
char bytes[] = { 0xD, 0xA, 0xD, 0xA, 0xB, 0xA, 0xB, 0xA };
avro_schema_t schema = avro_schema_fixed("msg", sizeof(bytes));
avro_datum_t datum;
avro_datum_t expected_datum;

datum = avro_givefixed(schema, bytes, sizeof(bytes), NULL);
write_read_check(schema, datum, NULL, NULL, "fixed");
test_json(datum, "\"\\r\\n\\r\\n\\u000b\\n\\u000b\\n\"");
avro_datum_decref(datum);

datum = avro_givefixed(schema, NULL, sizeof(bytes), NULL);
avro_givefixed_set(datum, bytes, sizeof(bytes), NULL);
expected_datum = avro_givefixed(schema, bytes, sizeof(bytes), NULL);
if (!avro_datum_equal(datum, expected_datum)) {
fprintf(stderr,
"Expected equal fixed instances.\n");
exit(EXIT_FAILURE);
}
avro_datum_decref(datum);
avro_datum_decref(expected_datum);

// The following should bork if we don't copy the fixed value
// correctly (since we'll try to free a static string).

datum = avro_fixed(schema, "original", 8);
avro_fixed_set(datum, "alsothis", 8);
avro_datum_decref(datum);

avro_schema_decref(schema);
return 0;
}

int main(void)
{
avro_set_allocator(test_allocator, NULL);

unsigned int i;
struct avro_tests {
char *name;
avro_test func;
} tests[] = {
{
"string", test_string}, {
"bytes", test_bytes}, {
"int", test_int32}, {
"long", test_int64}, {
"float", test_float}, {
"double", test_double}, {
"boolean", test_boolean}, {
"null", test_null}, {
"record", test_record}, {
"nested_record", test_nested_record}, {
"enum", test_enum}, {
"array", test_array}, {
"map", test_map}, {
"fixed", test_fixed}, {
"union", test_union}
};

init_rand();
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
struct avro_tests *test = tests + i;
fprintf(stderr, "**** Running %s tests ****\n", test->name);
if (test->func() != 0) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}