1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-29 08:12:21 +00:00

ch4-07: 完善

This commit is contained in:
chai2010 2018-07-17 00:10:44 +08:00
parent 1dedfa035e
commit 0785c9ec28
5 changed files with 587 additions and 0 deletions

View File

@ -1,5 +1,98 @@
# 4.7. Protobuf扩展语法和插件
在本章第二节我们已经展示过如何定制一个Protobuf代码生成插件。本节我们将继续深入挖掘Protobuf的高级特性通过Protobuf的扩展特性增加自定义的元数据信息。通过针对每个方法增加Rest接口元信息实现一个基于Protobuf的迷你Rest框架。
## Protobuf扩展语法
目前Protobuf相关的很多开源项目都使用到了Protobuf的扩展语法。在前一节中提到的验证器就是通过给结构体成员增加扩展元信息实现验证。在grpc-gateway项目中则是通过为服务的每个方法增加Http相关的映射规则实现对Rest接口的支持。这里我们将查看下Protobuf全部的扩展语法。
扩展语法也被用来实现Protobuf内置的某些特性比如针对不同语言的扩展选项和proto2中message成员的默认值特性其中文件扩展选项go_package为Go语言定义了当前包的路径和包的名称。message成员的default扩展为每个成员定义了默认值。go_package和proto2的default特性底层都是通过扩展语法实现。
Protobuf扩展语法有五种类型分别是针对文件的扩展信息、针对message的扩展信息、正对message成员的扩展信息、针对service的扩展信息和针对service方法的扩展信息。在使用扩展前首先需要通过extend关键字定义扩展的类型和可以用于扩展的成员。扩展成员也可以基础类型也可以是一个结构体类型。
为了简单我们假设采用标准库中的StringValue作为每个扩展成员的类型
```protobuf
import "google/protobuf/wrappers.proto";
message StringValue {
// The string value.
string value = 1;
}
```
我们先看看如何定义文件的扩展类型:
```protobuf
import "google/protobuf/descriptor.proto";
extend google.protobuf.FileOptions {
optional google.protobuf.StringValue file_option = 50000;
}
option (file_option) = {
value: "this is a file option"
};
```
然后是message和message成员的扩展方式
```protobuf
import "google/protobuf/descriptor.proto";
extend google.protobuf.MessageOptions {
optional google.protobuf.StringValue message_option = 50000;
}
extend google.protobuf.FieldOptions {
optional google.protobuf.StringValue filed_option = 50000;
}
message Message {
option (message_option) = {
value: "message option"
};
string name = 1 [
(filed_option) = {
value: ""
}
];
}
```
最后是service和service方法的扩展
```protobuf
import "google/protobuf/descriptor.proto";
extend google.protobuf.ServiceOptions {
optional String service_option = 50000;
}
extend google.protobuf.MethodOptions {
optional String method_option = 50000;
}
service HelloService {
option (service_option) = {
value: "message option"
};
rpc Hello(String) returns(String) {
option (method_option) = {
value: ""
};
}
}
```
如果是通用的扩展类型我们可以将扩展相关的内容放到一个独立的proto文件中。以后知道导入定义了扩展的proto文件就可以直接使用定义的扩展定义元数据了。
## 插件中读取扩展信息
TODO
## Rest模板框架
TODO
<!--

View File

@ -0,0 +1,10 @@
run:
@go build -o a.out && ./a.out
-@rm ./a.out
gen:
protoc -I . --go_out=plugins=grpc:. helloworld.proto
clean:
-rm *.pb.go

View File

@ -0,0 +1,255 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: helloworld.proto
package main
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Message struct {
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_helloworld_ed50fb97dddb84f0, []int{0}
}
func (m *Message) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message.Unmarshal(m, b)
}
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
}
func (dst *Message) XXX_Merge(src proto.Message) {
xxx_messageInfo_Message.Merge(dst, src)
}
func (m *Message) XXX_Size() int {
return xxx_messageInfo_Message.Size(m)
}
func (m *Message) XXX_DiscardUnknown() {
xxx_messageInfo_Message.DiscardUnknown(m)
}
var xxx_messageInfo_Message proto.InternalMessageInfo
func (m *Message) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
type String struct {
Value *string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *String) Reset() { *m = String{} }
func (m *String) String() string { return proto.CompactTextString(m) }
func (*String) ProtoMessage() {}
func (*String) Descriptor() ([]byte, []int) {
return fileDescriptor_helloworld_ed50fb97dddb84f0, []int{1}
}
func (m *String) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_String.Unmarshal(m, b)
}
func (m *String) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_String.Marshal(b, m, deterministic)
}
func (dst *String) XXX_Merge(src proto.Message) {
xxx_messageInfo_String.Merge(dst, src)
}
func (m *String) XXX_Size() int {
return xxx_messageInfo_String.Size(m)
}
func (m *String) XXX_DiscardUnknown() {
xxx_messageInfo_String.DiscardUnknown(m)
}
var xxx_messageInfo_String proto.InternalMessageInfo
func (m *String) GetValue() string {
if m != nil && m.Value != nil {
return *m.Value
}
return ""
}
var E_FileOption = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*String)(nil),
Field: 50000,
Name: "main.file_option",
Tag: "bytes,50000,opt,name=file_option,json=fileOption",
Filename: "helloworld.proto",
}
var E_MessageOption = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*String)(nil),
Field: 50000,
Name: "main.message_option",
Tag: "bytes,50000,opt,name=message_option,json=messageOption",
Filename: "helloworld.proto",
}
var E_FiledOption = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*String)(nil),
Field: 50000,
Name: "main.filed_option",
Tag: "bytes,50000,opt,name=filed_option,json=filedOption",
Filename: "helloworld.proto",
}
var E_ServiceOption = &proto.ExtensionDesc{
ExtendedType: (*descriptor.ServiceOptions)(nil),
ExtensionType: (*String)(nil),
Field: 50000,
Name: "main.service_option",
Tag: "bytes,50000,opt,name=service_option,json=serviceOption",
Filename: "helloworld.proto",
}
var E_MethodOption = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MethodOptions)(nil),
ExtensionType: (*String)(nil),
Field: 50000,
Name: "main.method_option",
Tag: "bytes,50000,opt,name=method_option,json=methodOption",
Filename: "helloworld.proto",
}
func init() {
proto.RegisterType((*Message)(nil), "main.Message")
proto.RegisterType((*String)(nil), "main.String")
proto.RegisterExtension(E_FileOption)
proto.RegisterExtension(E_MessageOption)
proto.RegisterExtension(E_FiledOption)
proto.RegisterExtension(E_ServiceOption)
proto.RegisterExtension(E_MethodOption)
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// HelloServiceClient is the client API for HelloService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloServiceClient interface {
Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error)
}
type helloServiceClient struct {
cc *grpc.ClientConn
}
func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient {
return &helloServiceClient{cc}
}
func (c *helloServiceClient) Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error) {
out := new(String)
err := c.cc.Invoke(ctx, "/main.HelloService/Hello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HelloServiceServer is the server API for HelloService service.
type HelloServiceServer interface {
Hello(context.Context, *String) (*String, error)
}
func RegisterHelloServiceServer(s *grpc.Server, srv HelloServiceServer) {
s.RegisterService(&_HelloService_serviceDesc, srv)
}
func _HelloService_Hello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(String)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HelloServiceServer).Hello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/main.HelloService/Hello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HelloServiceServer).Hello(ctx, req.(*String))
}
return interceptor(ctx, in, info, handler)
}
var _HelloService_serviceDesc = grpc.ServiceDesc{
ServiceName: "main.HelloService",
HandlerType: (*HelloServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Hello",
Handler: _HelloService_Hello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "helloworld.proto",
}
func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_helloworld_ed50fb97dddb84f0) }
var fileDescriptor_helloworld_ed50fb97dddb84f0 = []byte{
// 325 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x51, 0xcf, 0x4e, 0xc2, 0x30,
0x1c, 0x16, 0x05, 0x8c, 0x3f, 0x86, 0x21, 0x0d, 0xc6, 0x65, 0x2a, 0x36, 0x9c, 0x48, 0x4c, 0x4a,
0xc2, 0x71, 0xde, 0x38, 0x18, 0x2f, 0x44, 0x85, 0xc4, 0xab, 0x99, 0xec, 0xc7, 0x68, 0xd2, 0xae,
0xa4, 0x2d, 0x78, 0xe7, 0xe0, 0x1b, 0xf1, 0x1e, 0x3e, 0x92, 0xd9, 0x3a, 0x10, 0x22, 0xe2, 0xf1,
0x6b, 0xbf, 0x7e, 0xff, 0x0a, 0x8d, 0x29, 0x0a, 0xa1, 0x3e, 0x94, 0x16, 0x31, 0x9b, 0x69, 0x65,
0x15, 0x29, 0xcb, 0x88, 0xa7, 0x01, 0x4d, 0x94, 0x4a, 0x04, 0x76, 0xf3, 0xb3, 0xf7, 0xf9, 0xa4,
0x1b, 0xa3, 0x19, 0x6b, 0x3e, 0xb3, 0x4a, 0x3b, 0x5e, 0xfb, 0x1e, 0x4e, 0x07, 0x68, 0x4c, 0x94,
0x20, 0x09, 0xa0, 0x9c, 0x46, 0x12, 0xfd, 0x12, 0x2d, 0x75, 0xce, 0xfa, 0xd5, 0xe5, 0xca, 0x3f,
0x86, 0xa3, 0x61, 0x7e, 0x16, 0x36, 0x97, 0x2b, 0xbf, 0x01, 0xe7, 0xd2, 0x51, 0xa9, 0x9a, 0x59,
0xae, 0xd2, 0x76, 0x0b, 0xaa, 0x23, 0xab, 0x79, 0x9a, 0x90, 0x26, 0x54, 0x16, 0x91, 0x98, 0x17,
0x8f, 0x87, 0x0e, 0xf4, 0x5e, 0xc0, 0x7b, 0xcc, 0x82, 0x8d, 0x50, 0x2f, 0xf8, 0x18, 0xc9, 0x1d,
0x54, 0x72, 0x4c, 0x3c, 0x96, 0xc5, 0x63, 0xee, 0x71, 0xb0, 0x83, 0xda, 0x85, 0x71, 0xb0, 0xd7,
0x32, 0x1c, 0x40, 0x6d, 0xc2, 0x05, 0xbe, 0x39, 0x48, 0xae, 0x99, 0x6b, 0xc8, 0xd6, 0x0d, 0xd9,
0x03, 0x17, 0xf8, 0x94, 0x5f, 0x1a, 0xff, 0xeb, 0xf3, 0x84, 0x96, 0x3a, 0xb5, 0xde, 0x8e, 0xc1,
0x10, 0x26, 0x1b, 0x4a, 0xf8, 0xba, 0x31, 0x58, 0x2b, 0xde, 0xfe, 0x52, 0x2c, 0xf6, 0x39, 0x2c,
0x5a, 0x97, 0xdb, 0xac, 0xf0, 0x19, 0xbc, 0xcc, 0x25, 0x5e, 0xab, 0xde, 0xec, 0xc9, 0x89, 0x22,
0x3e, 0xac, 0x99, 0x37, 0x8d, 0x7f, 0x92, 0x1a, 0x37, 0xe3, 0xdf, 0x49, 0x8b, 0x9d, 0xff, 0x49,
0x6a, 0xb6, 0x59, 0xe1, 0x08, 0xea, 0x12, 0xed, 0x54, 0x6d, 0xa2, 0xb6, 0xf6, 0x0c, 0x90, 0xdd,
0x1f, 0x56, 0xf5, 0xe4, 0x16, 0xa9, 0x7f, 0xb5, 0x5c, 0xf9, 0x97, 0x70, 0x61, 0xa7, 0xdc, 0x50,
0x6e, 0x68, 0x44, 0xb3, 0x26, 0xc5, 0x17, 0x7e, 0x07, 0x00, 0x00, 0xff, 0xff, 0xe6, 0xb9, 0x93,
0x70, 0xad, 0x02, 0x00, 0x00,
}

View File

@ -0,0 +1,55 @@
syntax = "proto2";
package main;
import "google/protobuf/descriptor.proto";
extend google.protobuf.FileOptions {
optional String file_option = 50000;
}
extend google.protobuf.MessageOptions {
optional String message_option = 50000;
}
extend google.protobuf.FieldOptions {
optional String filed_option = 50000;
}
extend google.protobuf.ServiceOptions {
optional String service_option = 50000;
}
extend google.protobuf.MethodOptions {
optional String method_option = 50000;
}
option (file_option) = {
value: "this is a file option"
};
message Message {
option (message_option) = {
value: "message option"
};
optional string name = 1 [
(filed_option) = {
value: ""
}
];
}
service HelloService {
option (service_option) = {
value: "message option"
};
rpc Hello(String) returns(String) {
option (method_option) = {
value: ""
};
}
}
message String {
optional string value = 1;
}

View File

@ -0,0 +1,174 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: helloworld.proto
package main // import "main"
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Message0 struct {
Name *string `protobuf:"bytes,1,opt,name=name,def=gopher" json:"name,omitempty"`
Age *int32 `protobuf:"varint,2,opt,name=age,def=10" json:"age,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Message0) Reset() { *m = Message0{} }
func (m *Message0) String() string { return proto.CompactTextString(m) }
func (*Message0) ProtoMessage() {}
func (*Message0) Descriptor() ([]byte, []int) {
return fileDescriptor_helloworld_a2a557ac07ecc4b1, []int{0}
}
func (m *Message0) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message0.Unmarshal(m, b)
}
func (m *Message0) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Message0.Marshal(b, m, deterministic)
}
func (dst *Message0) XXX_Merge(src proto.Message) {
xxx_messageInfo_Message0.Merge(dst, src)
}
func (m *Message0) XXX_Size() int {
return xxx_messageInfo_Message0.Size(m)
}
func (m *Message0) XXX_DiscardUnknown() {
xxx_messageInfo_Message0.DiscardUnknown(m)
}
var xxx_messageInfo_Message0 proto.InternalMessageInfo
const Default_Message0_Name string = "gopher"
const Default_Message0_Age int32 = 10
func (m *Message0) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return Default_Message0_Name
}
func (m *Message0) GetAge() int32 {
if m != nil && m.Age != nil {
return *m.Age
}
return Default_Message0_Age
}
type Message2 struct {
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Age *int32 `protobuf:"varint,2,opt,name=age" json:"age,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Message2) Reset() { *m = Message2{} }
func (m *Message2) String() string { return proto.CompactTextString(m) }
func (*Message2) ProtoMessage() {}
func (*Message2) Descriptor() ([]byte, []int) {
return fileDescriptor_helloworld_a2a557ac07ecc4b1, []int{1}
}
func (m *Message2) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Message2.Unmarshal(m, b)
}
func (m *Message2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Message2.Marshal(b, m, deterministic)
}
func (dst *Message2) XXX_Merge(src proto.Message) {
xxx_messageInfo_Message2.Merge(dst, src)
}
func (m *Message2) XXX_Size() int {
return xxx_messageInfo_Message2.Size(m)
}
func (m *Message2) XXX_DiscardUnknown() {
xxx_messageInfo_Message2.DiscardUnknown(m)
}
var xxx_messageInfo_Message2 proto.InternalMessageInfo
func (m *Message2) GetName() string {
if m != nil && m.Name != nil {
return *m.Name
}
return ""
}
func (m *Message2) GetAge() int32 {
if m != nil && m.Age != nil {
return *m.Age
}
return 0
}
var E_Email = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*string)(nil),
Field: 50000,
Name: "main.email",
Tag: "bytes,50000,opt,name=email",
Filename: "helloworld.proto",
}
var E_DefaultString = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 50000,
Name: "main.default_string",
Tag: "bytes,50000,opt,name=default_string,json=defaultString",
Filename: "helloworld.proto",
}
var E_DefaultInt = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*int32)(nil),
Field: 50001,
Name: "main.default_int",
Tag: "varint,50001,opt,name=default_int,json=defaultInt",
Filename: "helloworld.proto",
}
func init() {
proto.RegisterType((*Message0)(nil), "main.Message0")
proto.RegisterType((*Message2)(nil), "main.Message2")
proto.RegisterExtension(E_Email)
proto.RegisterExtension(E_DefaultString)
proto.RegisterExtension(E_DefaultInt)
}
func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_helloworld_a2a557ac07ecc4b1) }
var fileDescriptor_helloworld_a2a557ac07ecc4b1 = []byte{
// 284 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0x41, 0x4b, 0xc3, 0x30,
0x18, 0x86, 0xd9, 0xd6, 0x8a, 0x8b, 0x28, 0x12, 0x54, 0xc2, 0x50, 0x29, 0xc3, 0xc3, 0x4e, 0xdd,
0x14, 0x4f, 0xd5, 0xc3, 0xe8, 0x61, 0xe0, 0x41, 0x84, 0x7a, 0xf3, 0x22, 0xb1, 0xfd, 0x96, 0x06,
0xd2, 0x7c, 0x25, 0x49, 0xf1, 0xbe, 0x83, 0xf8, 0x87, 0xfa, 0x3f, 0xf4, 0x1f, 0x49, 0xdb, 0x4d,
0xa6, 0x08, 0x5e, 0x02, 0x79, 0x79, 0x9f, 0x87, 0x17, 0x3e, 0x72, 0x98, 0x83, 0x52, 0xf8, 0x8a,
0x46, 0x65, 0x61, 0x69, 0xd0, 0x21, 0xf5, 0x0a, 0x2e, 0xf5, 0x28, 0x10, 0x88, 0x42, 0xc1, 0xb4,
0xcd, 0x5e, 0xaa, 0xe5, 0x34, 0x03, 0x9b, 0x1a, 0x59, 0x3a, 0x34, 0x5d, 0x6f, 0x7c, 0x4b, 0x76,
0xef, 0xc1, 0x5a, 0x2e, 0x60, 0x46, 0x47, 0xc4, 0xd3, 0xbc, 0x00, 0xd6, 0x0b, 0x7a, 0x93, 0x61,
0xb4, 0x23, 0xb0, 0xcc, 0xc1, 0x24, 0x6d, 0x46, 0x8f, 0xc8, 0x80, 0x0b, 0x60, 0xfd, 0xa0, 0x37,
0xf1, 0xa3, 0xfe, 0xe5, 0x2c, 0x69, 0xbe, 0xe3, 0xf8, 0x9b, 0xbe, 0xa2, 0xe7, 0xdb, 0x74, 0x4c,
0x56, 0x35, 0xfb, 0x69, 0x38, 0xd9, 0x32, 0xc4, 0xde, 0x7b, 0xcd, 0x48, 0xeb, 0x88, 0xae, 0x89,
0x0f, 0x05, 0x97, 0x8a, 0x9e, 0x86, 0xdd, 0xda, 0x70, 0xb3, 0x36, 0x5c, 0x48, 0x05, 0x0f, 0xa5,
0x93, 0xa8, 0x2d, 0xfb, 0x78, 0x1b, 0x34, 0xe2, 0xa4, 0x2b, 0x47, 0x0b, 0x72, 0x90, 0xc1, 0x92,
0x57, 0xca, 0x3d, 0x5b, 0x67, 0xa4, 0x16, 0xf4, 0xec, 0x0f, 0x1c, 0x54, 0xf6, 0x9b, 0xdf, 0x5f,
0x63, 0x8f, 0x2d, 0x15, 0xcd, 0xc9, 0xde, 0xc6, 0x23, 0xb5, 0xfb, 0x4f, 0xf2, 0xd9, 0x4a, 0xfc,
0x84, 0xac, 0x99, 0x3b, 0xed, 0xe2, 0x8b, 0x55, 0xcd, 0x8e, 0xd3, 0x9c, 0x4b, 0x9b, 0x57, 0x36,
0xe7, 0x7a, 0x2e, 0x9a, 0x81, 0x61, 0x8a, 0xc5, 0xd3, 0xb0, 0x39, 0xc2, 0x4d, 0xf3, 0x7c, 0x05,
0x00, 0x00, 0xff, 0xff, 0xd2, 0x01, 0xe8, 0x5f, 0xa2, 0x01, 0x00, 0x00,
}