`
wqtn22
  • 浏览: 101081 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

erlang抽象码与basho的protobuf(五)执行过程

 
阅读更多

上文介绍了代码生成过程,成功的从erlang抽象码生成了erlang源文件,抽象码的替换步骤很少,这主要得益于模板文件pokemon_pb.erl的设计。这里将继续分析pokemon_pb.erl的执行过程,从中学习它的编程技巧。

编码过程:

pokemon_pb.erl

 

encode_pikachu(Record) when is_record(Record, pikachu) ->

    encode(pikachu, Record).

encode(pikachu, Record) ->

    iolist_to_binary(iolist(pikachu, Record)).

iolist(pikachu, Record) ->

    [pack(1, required, with_default(Record#pikachu.abc, none), string, [])].

 

主要的编码工作交给了pack函数,因此iolist函数仅仅需要保存message各个域的域编号,操作类型(required等),默认值,域类型(string等)。

pack(_, optional, undefined, _, _) -> [];

pack(_, repeated, undefined, _, _) -> [];

pack(_, repeated_packed, undefined, _, _) -> [];

pack(_, repeated_packed, [], _, _) -> [];

pack(FNum, required, undefined, Type, _) ->

    exit({error, {required_field_is_undefined, FNum, Type}});

pack(_, repeated, [], _, Acc) -> 

    lists:reverse(Acc);

pack(FNum, repeated, [Head|Tail], Type, Acc) ->

    pack(FNum, repeated, Tail, Type, [pack(FNum, optional, Head, Type, [])|Acc]);

pack(FNum, repeated_packed, Data, Type, _) ->

    protobuffs:encode_packed(FNum, Data, Type);

pack(FNum, _, Data, _, _) when is_tuple(Data) ->

    [RecName|_] = tuple_to_list(Data),

    protobuffs:encode(FNum, encode(RecName, Data), bytes);

pack(FNum, _, Data, Type, _) when Type=:=bool;Type=:=int32;Type=:=uint32;

 Type=:=int64;Type=:=uint64;Type=:=sint32;

 Type=:=sint64;Type=:=fixed32;Type=:=sfixed32;

 Type=:=fixed64;Type=:=sfixed64;Type=:=string;

 Type=:=bytes;Type=:=float;Type=:=double ->

    protobuffs:encode(FNum, Data, Type);

pack(FNum, _, Data, Type, _) when is_atom(Data) ->

    protobuffs:encode(FNum, enum_to_int(Type,Data), enum).

 

enum_to_int(pikachu,value) ->

    1.

 

pack函数有五个地方值得注意:

1.对于optional的域,其值存在与否都不会有影响;

2.对于required的域,若值不存在,则应该引发异常;

3.对于repeated的域,由于其值是一个列表,因此在编码时,应该对列表的每一个元素都进行pack;

4.对于内建类型,直接调用protobuffs:encode进行编码,protobuffs:encode编码方式是protocol buffers编码方式的erlang实现,分析起来也比较枯燥,读者可以自行查阅;

5.对于枚举类型,需要先通过enum_to_int将枚举类型替换为具体的值,这个函数也是通过抽象码替换过程生成的,message的每个枚举定义都有一个独立的值。

解码过程:

 

decode_pikachu(Bytes) when is_binary(Bytes) ->

    decode(pikachu, Bytes).

decode(pikachu, Bytes) when is_binary(Bytes) ->

    Types = [{1, abc, int32, []}, {2, def, double, []}],

    Decoded = decode(Bytes, Types, []),

    to_record(pikachu, Decoded).

decode/2保存了解码时遇到的每一个域的处理方法,这相当于一个简单的语法定义,具体的解析过程由decode/3完成。

 

decode(<<>>, _, Acc) -> Acc;

decode(Bytes, Types, Acc) ->

    {ok, FNum} = protobuffs:next_field_num(Bytes),

    case lists:keysearch(FNum, 1, Types) of

        {value, {FNum, Name, Type, Opts}} ->

            {Value1, Rest1} = 

                case lists:member(is_record, Opts) of

                    true ->

                        {{FNum, V}, R} = protobuffs:decode(Bytes, bytes),

                        RecVal = decode(list_to_atom(string:to_lower(atom_to_list(Type))), V),

                        {RecVal, R};

                    false ->

case lists:member(repeated_packed, Opts) of

   true ->

{{FNum, V}, R} = protobuffs:decode_packed(Bytes, Type),

{V, R};

   false ->

{{FNum, V}, R} = protobuffs:decode(Bytes, Type),

{unpack_value(V, Type), R}

end

 

                end,

            case lists:member(repeated, Opts) of

                true ->

                    case lists:keytake(FNum, 1, Acc) of

                        {value, {FNum, Name, List}, Acc1} ->

                            decode(Rest1, Types, [{FNum, Name, lists:reverse([int_to_enum(Type,Value1) | lists:reverse(List)])} | Acc1]);

                        false ->

                            decode(Rest1, Types, [{FNum, Name, [int_to_enum(Type,Value1)]}|Acc])

                    end;

                false ->

   decode(Rest1, Types, [{FNum, Name, int_to_enum(Type,Value1)}|Acc])

            end;

        false ->

            exit({error, {unexpected_field_index, FNum}})

    end.

 

int_to_enum(_,Val) ->

    Val.

 

这个函数是最大的一个函数,Types即是语法定义,而该函数相当于一个词法分析器,通过protobuffs:decode/2得到的每一个“符号”,都将根据Types的语法定义进行相应的动作:

1.对于嵌套定义的message,将进行递归类型解码;

2.对于repeated的域,将合并到原先找到的域的值列表中;

3.对于枚举类型,需要先通过int_to_enum将枚举值替换为具体的atom,这个函数也是通过抽象码替换过程生成的,message的每个枚举定义都有一个独立的atom。

4.对于标量类型,将直接记录到message的域值中。

最终,decode/3将生成一个列表,记录了每一个message的各个域,一个message的所有域放置在一个列表内。

 

decode(pikachu, Bytes) when is_binary(Bytes) ->

    Types = [{1, abc, int32, []}, {2, def, double, []}],

    Decoded = decode(Bytes, Types, []),

    to_record(pikachu, Decoded).

 

to_record(pikachu, DecodedTuples) ->

    lists:foldl(

        fun({_FNum, Name, Val}, Record) ->

            set_record_field(record_info(fields, pikachu), Record, Name, Val)

        end, #pikachu{}, DecodedTuples).

set_record_field(Fields, Record, Field, Value) ->

    Index = list_index(Field, Fields),

    erlang:setelement(Index+1, Record, Value).

list_index(Target, List) -> list_index(Target, List, 1).

list_index(Target, [Target|_], Index) -> Index;

list_index(Target, [_|Tail], Index) -> list_index(Target, Tail, Index+1);

list_index(_, [], _) -> 0.

to_record是一个语义分析过程,根据Types定义,遍历decode/3生成的语法结构,提取每个message的每个域及其值的绑定关系,然后填充到具体的record中去,这个填充的过程也较为巧妙,由于record实质也是一个元组,to_record将遍历一个message的域值绑定列表,由于每一个域在proto文件中都有顺序编号,取出域值绑定关系时也就得到了域在message里面的未知,继而也就得到了域在record元组中的位置,set_record_field即用于填充元组指定字段的值。

至此,erlang的protocol buffers实现protobuffs就已经分析完毕了,其设计思路较为新颖,从中可以学到很多编程技巧,感兴趣的读者可以深挖并应用。

抽象码是连接erlang代码与erlang虚拟机代码的纽带,抽象码的执行过程即代表了虚拟机的执行过程,分析抽象码可以有效帮助我们理解erlang代码的执行方式、性能等,利用抽象码替换(可参考smerl的实现),可以实现erlang代码trace、profile插桩,帮助我们找到代码的bug,调试代码的性能。

分享到:
评论

相关推荐

    在erlang项目中使用protobuf例子

    1. **protobuf安装与编译**:首先,你需要在Erlang项目中安装protobuf的Erlang库,这通常通过rebar3或erlang.mk等构建工具完成。接着,你需要为protobuf消息定义一个`.proto`文件,例如`test.proto`,其中包含数据...

    erlang版本的protobuf(erl_protobuffs)

    **protobuf与Erlang** `protobuf`,全称Protocol Buffers,是Google开发的一种数据序列化协议,类似于XML和JSON,但更小巧、快速且高效。它允许开发者定义数据结构,然后生成相应的代码来轻松地读写这些数据,支持...

    改进erlang版的protobuf代码

    标题中的“改进erlang版的protobuf代码”指的是在Erlang编程语言中对Protocol Buffers(protobuf)进行了优化和改进的代码实现。Protocol Buffers是一种数据序列化协议,由Google开发,它允许开发者定义数据结构,...

    enif_protobuf:使用enif(Erlang nif)的Google Protobuf实现

    **enif_protobuf** 是一个基于 **Erlang NIF (Native Implemented Functions)** 的库,用于集成 **Google Protobuf**,使得在Erlang环境中能够方便地处理和序列化protobuf消息。Erlang NIF是一种机制,允许Erlang...

    erlang_protobuffs

    Erlang Protobuffs是一个用于Erlang的协议缓冲区(Protocol Buffers)实现,它允许Erlang程序与使用Google Protobuf编译器生成的消息格式进行交互。Google Protobuf是一种高效的数据序列化协议,广泛应用于跨语言的...

    ErlangB和ErlangC计算工具(exe可执行文件+excel两个)

    这两个模型由丹麦工程师Agner Krarup Erlang在20世纪初提出,至今仍广泛应用于现代通信网络的设计与优化。 Erlang B模型,也称为无阻塞模型,主要用于计算在给定呼叫到达率和系统容量下,系统不发生阻塞的概率。它...

    forms:一个简化使用Erlang抽象格式的库

    read/1与Erlang源文件(即带有.erl后缀的文件)和Erlang二进制文件(即带有.beam后缀的文件)一起使用。 下面的行将从Erlang的内部lists模块中读取表单。 forms : read ( lists ). 同样,以下行将从开发人员提供...

    使用erlang的动态执行编写DSL

    总结来说,这个话题涵盖了Erlang编程、DSL设计与实现、元编程以及业务逻辑的动态执行。通过深入研究这些内容,你将能够构建自己的Erlang DSL,从而提升软件的可读性和可维护性,更好地适应变化的业务需求。

    erlang程序设计与入门

    BEAM是Erlang的虚拟机,它执行Erlang字节码,并负责管理进程、内存和垃圾回收。BEAM的设计考虑了并发和实时性,确保了Erlang程序的高效运行。 10. **Erlang生态系统** Erlang有丰富的开源库和工具,如 Cowboy...

    erlang-protobuffs

    听说google proto buffer(以下简称protobuf)已经很久了,但是一直没有尝试使用它。其中一个原因是,项目组自己写了个打包和解包的工具,而且代码也简单,可以很方便的扩展到自动生成xml之类的配置文件,已经能很好...

    erlang编程 Introducing Erlang

    它负责解释Erlang字节码,提供内存管理、垃圾回收和并发调度等功能。 ### 10. 语言特性 Erlang的语法简洁,支持模式匹配、函数式编程、列表处理和递归等特性。它的动态类型系统和强大的类型推断让代码更加灵活。 ...

    Erlang与Mysql对接

    在Erlang中,与MySQL对接的关键在于选择合适的驱动程序,文件名为"erlang-mysql-driver"很可能是指一个Erlang的MySQL驱动库。这样的驱动库允许Erlang应用程序通过标准的数据库接口与MySQL进行通信。在Erlang中,这种...

    erlang25.0 windows版本

    3. **环境变量**:安装过程中,安装程序可能会自动添加Erlang的bin目录到系统的PATH环境变量中,确保命令行可以识别`erl`等Erlang命令。 4. **验证**:安装完成后,打开命令行窗口并输入`erl`,如果Erlang成功安装,...

    erlang_版本24.3.4.4

    这个过程可能需要一些时间,因为它会编译整个Erlang虚拟机和标准库。编译完成后,你可以通过运行`make tests`来验证Erlang的正确性。 5. **安装**:最后,使用`make install`将编译好的Erlang二进制文件安装到你的...

    rebar工具开发erlang工程项目和发布erlang工程项目

    【标题】: 使用rebar构建Erlang项目与发布流程详解 【描述】: 本文将详细介绍如何利用rebar工具来开发和发布Erlang工程,包括项目结构、配置文件的编写以及具体操作步骤。 【标签】: Erlang, rebar, 项目开发, ...

    最新最全rabbitmq与erlang版本匹配-2020-04-23.docx

    **RabbitMQ与Erlang版本匹配指南** RabbitMQ是一种广泛使用的开源消息代理和队列服务器,它基于Erlang编程语言构建。Erlang以其并发能力、容错性和分布式特性而闻名,是实现RabbitMQ的理想选择。正确地匹配RabbitMQ...

    C#与Erlang的群通信

    标题中的"C#与Erlang的群通信"指的是在编程领域中,使用C#和Erlang两种不同的编程语言进行集群或分布式系统间的通信。这两种语言各有特点,C#是微软开发的面向对象的编程语言,常用于Windows平台的开发,而Erlang则...

    Erlang Programming 导读.pdf

    - **官方资源**: 通过阅读《Programming Erlang》这本书来深入了解 Erlang 的语法和特性,虽然书中某些部分可能较为抽象或难以理解,但它是了解 Erlang 的重要途径。 - **实践项目**: 结合实际案例进行练习,比如...

    Erlang官网下载过慢

    Erlang的下载过程中,你可以考虑以下几点: 1. 检查网络连接:确保网络稳定且速度足够。 2. 使用代理:如果你在公司或特定区域,可能需要通过代理服务器访问外部资源。 3. 更换下载时间:避开网络高峰期进行下载。 4...

    erlang资源

    3. **过程和模块**:Erlang的组织方式,包括如何定义和调用函数,以及模块的使用。 4. **错误调试**:Erlang的错误处理机制,如shell的使用、日志和调试工具。 5. **REPL(Read-Eval-Print Loop)**:Erlang shell...

Global site tag (gtag.js) - Google Analytics