protomsg 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -0
- data/README.rdoc +61 -0
- data/bin/protomsg +9 -0
- data/lib/protomsg/attribute.rb +44 -0
- data/lib/protomsg/message_type.rb +50 -0
- data/lib/protomsg/parser.rb +25 -0
- data/lib/protomsg.rb +6 -0
- data/lib/templates/finalisers/raw_finaliser.h +1 -0
- data/lib/templates/finalisers/string_finaliser.h +1 -0
- data/lib/templates/getters/float_getter.h +1 -0
- data/lib/templates/getters/int_getter.h +1 -0
- data/lib/templates/getters/raw_getter.h +1 -0
- data/lib/templates/getters/string_getter.h +1 -0
- data/lib/templates/getters.h +1 -0
- data/lib/templates/header.h +12 -0
- data/lib/templates/header_entries/float_header_entry.h +1 -0
- data/lib/templates/header_entries/int_header_entry.h +1 -0
- data/lib/templates/header_entries/raw_header_entry.h +1 -0
- data/lib/templates/header_entries/string_header_entry.h +1 -0
- data/lib/templates/io.h +18 -0
- data/lib/templates/lengths.h +2 -0
- data/lib/templates/memory.h +13 -0
- data/lib/templates/message_type.h +25 -0
- data/lib/templates/protomsg.h +21 -0
- data/lib/templates/setters/float_setter.h +1 -0
- data/lib/templates/setters/int_setter.h +1 -0
- data/lib/templates/setters/raw_setter.h +4 -0
- data/lib/templates/setters/string_setter.h +4 -0
- data/lib/templates/setters.h +1 -0
- data/test/helper.rb +10 -0
- data/test/test_protomsg.rb +4 -0
- metadata +108 -0
data/LICENSE
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Public Domain.
|
data/README.rdoc
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
= protomsg
|
2
|
+
|
3
|
+
Protocol Message Buffers for C. This gem generates C socket code for reading and writing messages described in the protomsg DSL.
|
4
|
+
|
5
|
+
== Example
|
6
|
+
Input for a 'request' message:
|
7
|
+
|
8
|
+
request {
|
9
|
+
int type
|
10
|
+
string key
|
11
|
+
raw data
|
12
|
+
}
|
13
|
+
|
14
|
+
Output is a header file of macros for reading, writing and manipulating request messages:
|
15
|
+
|
16
|
+
// attribute getters
|
17
|
+
get_request_type
|
18
|
+
get_request_key
|
19
|
+
get_request_data
|
20
|
+
|
21
|
+
// attribute setters
|
22
|
+
set_request_type
|
23
|
+
set_request_key
|
24
|
+
set_request_data
|
25
|
+
|
26
|
+
// lengths
|
27
|
+
request_key_length
|
28
|
+
request_data_length
|
29
|
+
request_length
|
30
|
+
|
31
|
+
// memory & IO
|
32
|
+
init_request
|
33
|
+
free_request
|
34
|
+
write_request
|
35
|
+
read_request
|
36
|
+
|
37
|
+
The macros generate highly optimised, copy free socket code (using the scatter/gather IO functions readv and writev), and automatically check message sanity, and handle incomplete reads and writes. Message types with many variable length attributes (strings or raw data segments) will benefit more from the generated code than message types primarily made up of fixed length attributes.
|
38
|
+
|
39
|
+
== Usage
|
40
|
+
Call the protomsg script with the message types input file.
|
41
|
+
|
42
|
+
$ protomsg mymessages.protomsg
|
43
|
+
Created protomsg.h
|
44
|
+
Created request_message.h
|
45
|
+
|
46
|
+
Each file may describe multiple message types. The format for each message type is:
|
47
|
+
|
48
|
+
message_type_name {
|
49
|
+
attribute_type attribute_name
|
50
|
+
...
|
51
|
+
}
|
52
|
+
|
53
|
+
All message type and attribute names must be valid C identifiers. Valid attribute types are:
|
54
|
+
|
55
|
+
int 64 bit signed integer
|
56
|
+
float 64 bit float
|
57
|
+
string C string
|
58
|
+
raw Raw bytes
|
59
|
+
|
60
|
+
== Known Issues
|
61
|
+
All clients are servers are assumed to be on machines of the same endianess.
|
data/bin/protomsg
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Protomsg
|
2
|
+
class Attribute
|
3
|
+
VALID_TYPES = %w{int float string raw}
|
4
|
+
attr_accessor :name, :type
|
5
|
+
|
6
|
+
def initialize(message_type, definition_line)
|
7
|
+
definition_line =~ /(\w+)\s+(\w+)/ or raise "Invalid attribute definition: '#{definition_line}'"
|
8
|
+
VALID_TYPES.include?($1) or raise "Invalid attribute type: #{$1}"
|
9
|
+
@message_type = message_type
|
10
|
+
@type = $1
|
11
|
+
@name = $2
|
12
|
+
end
|
13
|
+
|
14
|
+
def getter
|
15
|
+
render_template('getters', 'getter')
|
16
|
+
end
|
17
|
+
|
18
|
+
def setter
|
19
|
+
render_template('setters', 'setter')
|
20
|
+
end
|
21
|
+
|
22
|
+
def header_entry
|
23
|
+
render_template('header_entries', 'header_entry')
|
24
|
+
end
|
25
|
+
|
26
|
+
def finaliser
|
27
|
+
render_template('finalisers', 'finaliser')
|
28
|
+
end
|
29
|
+
|
30
|
+
def variable_length?
|
31
|
+
%w{string raw}.include?(@type)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fixed_length?
|
35
|
+
!variable_length?
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def render_template(folder, suffix)
|
40
|
+
path = File.join(File.dirname(__FILE__), '..', 'templates', folder, "#{@type}_#{suffix}.h")
|
41
|
+
ERB.new(File.open(path) {|file| file.read}).result(binding)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Protomsg
|
2
|
+
class MessageType
|
3
|
+
attr_accessor :type_number, :attributes, :name
|
4
|
+
def initialize(type_number, name, lines)
|
5
|
+
@name = name
|
6
|
+
@type_number = type_number
|
7
|
+
lines = lines.split("\n").collect(&:strip).reject(&:empty?)
|
8
|
+
@attributes = lines.collect {|line| Attribute.new(self, line)}
|
9
|
+
end
|
10
|
+
|
11
|
+
def variable_attributes
|
12
|
+
@variable_attributes ||= @attributes.select(&:variable_length?)
|
13
|
+
end
|
14
|
+
|
15
|
+
def header
|
16
|
+
render_template('header')
|
17
|
+
end
|
18
|
+
|
19
|
+
def getters
|
20
|
+
render_template('getters')
|
21
|
+
end
|
22
|
+
|
23
|
+
def setters
|
24
|
+
render_template('setters')
|
25
|
+
end
|
26
|
+
|
27
|
+
def lengths
|
28
|
+
render_template('lengths')
|
29
|
+
end
|
30
|
+
|
31
|
+
def memory
|
32
|
+
render_template('memory')
|
33
|
+
end
|
34
|
+
|
35
|
+
def io
|
36
|
+
render_template('io')
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_header_file
|
40
|
+
render_template('message_type')
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def render_template(name)
|
45
|
+
path = File.join(File.dirname(__FILE__), '..', 'templates', "#{name}.h")
|
46
|
+
ERB.new(File.open(path){|file| file.read}).result(binding)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Protomsg
|
2
|
+
def self.parse_file(path)
|
3
|
+
File.open(path) do |file|
|
4
|
+
# write the standard protomsg header
|
5
|
+
FileUtils.cp File.join(File.dirname(__FILE__), '..', 'templates', 'protomsg.h'), 'protomsg.h'
|
6
|
+
puts "Created protomsg.h"
|
7
|
+
|
8
|
+
# extract each message type definition
|
9
|
+
message_types = []
|
10
|
+
data = file.read
|
11
|
+
data.scan(/(\w+)\s*\{([^\}]+)\}/) do |message_type|
|
12
|
+
message_types << MessageType.new(message_types.size + 1, message_type[0], message_type[1])
|
13
|
+
end
|
14
|
+
|
15
|
+
# write out each message type's header file
|
16
|
+
message_types.each do |message_type|
|
17
|
+
header_name = "#{message_type.name}_message.h"
|
18
|
+
File.open(header_name, 'w') do |file|
|
19
|
+
file.write message_type.to_header_file
|
20
|
+
end
|
21
|
+
puts "Created #{header_name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/protomsg.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
if(msg-><%= @name %>){free(msg-><%= @name %>);}\
|
@@ -0,0 +1 @@
|
|
1
|
+
if(msg-><%= @name %>){free(msg-><%= @name %>);}\
|
@@ -0,0 +1 @@
|
|
1
|
+
#define get_<%= @message_type.name %>_<%= @name %>(msg) (msg->header-><%= @name %>)
|
@@ -0,0 +1 @@
|
|
1
|
+
#define get_<%= @message_type.name %>_<%= @name %>(msg) (msg->header-><%= @name %>)
|
@@ -0,0 +1 @@
|
|
1
|
+
#define get_<%= @message_type.name %>_<%= @name %>(msg) (msg-><%= @name %>)
|
@@ -0,0 +1 @@
|
|
1
|
+
#define get_<%= @message_type.name %>_<%= @name %>(msg) (msg-><%= @name %>)
|
@@ -0,0 +1 @@
|
|
1
|
+
<% @attributes.each do |attr|%><%= attr.getter %><% end%>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
typedef struct {
|
2
|
+
char _version;
|
3
|
+
char _type;<% @attributes.each do |attr|%>
|
4
|
+
<%= attr.header_entry %><% end %>
|
5
|
+
} <%= @name %>_header;
|
6
|
+
|
7
|
+
typedef struct {
|
8
|
+
<%= @name %>_header *header;
|
9
|
+
size_t header_length;<% variable_attributes.each do |attr| %>
|
10
|
+
void *<%= attr.name %>;
|
11
|
+
size_t <%= attr.name %>_length;<% end %>
|
12
|
+
} <%= @name %>;
|
@@ -0,0 +1 @@
|
|
1
|
+
double <%= @name %>;
|
@@ -0,0 +1 @@
|
|
1
|
+
long long <%= @name %>;
|
@@ -0,0 +1 @@
|
|
1
|
+
int <%= @name %>_length;
|
@@ -0,0 +1 @@
|
|
1
|
+
int <%= @name %>_length;
|
data/lib/templates/io.h
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#define write_<%= @name %>(msg, sock, error) {\
|
2
|
+
ssize_t written = writev(sock, (const struct iovec *)msg, <%= variable_attributes.size + 1 %>);\
|
3
|
+
if(written != <%= @name %>_length(msg)){\
|
4
|
+
error = errno;\
|
5
|
+
}\
|
6
|
+
}
|
7
|
+
|
8
|
+
#define read_<%= @name %>(msg, sock, error) {\
|
9
|
+
msg = (<%= @name %> *) malloc(sizeof(<%= @name %>));\
|
10
|
+
msg->header_length = sizeof(<%= @name %>_header);\
|
11
|
+
msg->header = (<%= @name %>_header *)malloc(sizeof(<%= @name %>_header));\
|
12
|
+
read_struct(sock, msg->header, sizeof(<%= @name %>_header), error);\
|
13
|
+
if(error == 0) {\
|
14
|
+
<% variable_attributes.each do |attr| %>msg-><%= attr.name %>_length = msg->header-><%= attr.name %>_length;\
|
15
|
+
msg-><%= attr.name %> = malloc(msg->header-><%= attr.name %>_length);\
|
16
|
+
<% end %>readv(sock, (const struct iovec *)msg + 1, <%= variable_attributes.size %>);\
|
17
|
+
}\
|
18
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#define init_<%= @name %>(msg) {\
|
2
|
+
msg = (<%= @name %> *) calloc(1, sizeof(<%= @name %>));\
|
3
|
+
msg->header = (<%= @name %>_header *) calloc(1, sizeof(<%= @name %>_header));\
|
4
|
+
msg->header->_version = PROTO_MESSAGE_VERSION;\
|
5
|
+
msg->header->_type = <%= @name.upcase %>_MESSAGE_TYPE;\
|
6
|
+
msg->header_length = sizeof(<%= @name %>_header);\
|
7
|
+
}
|
8
|
+
|
9
|
+
#define free_<%= @name %>(msg) {\<% variable_attributes.each do |attr| %>
|
10
|
+
<%= attr.finaliser %><% end %>
|
11
|
+
free(msg->header);\
|
12
|
+
free(msg);\
|
13
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <errno.h>
|
5
|
+
#include <sys/socket.h>
|
6
|
+
#include <sys/types.h>
|
7
|
+
#include <arpa/inet.h>
|
8
|
+
#include <sys/uio.h>
|
9
|
+
#include <unistd.h>
|
10
|
+
#include "protomsg.h"
|
11
|
+
|
12
|
+
#define <%= @name.upcase %>_MESSAGE_TYPE <%= @type_number %>
|
13
|
+
|
14
|
+
<%= header %>
|
15
|
+
|
16
|
+
// getters
|
17
|
+
<%= getters %>
|
18
|
+
// setters
|
19
|
+
<%= setters %>
|
20
|
+
// lengths
|
21
|
+
<%= lengths %>
|
22
|
+
// memory
|
23
|
+
<%= memory %>
|
24
|
+
// io
|
25
|
+
<%= io %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
|
3
|
+
#ifndef __protomsg__
|
4
|
+
#define __protomsg__
|
5
|
+
|
6
|
+
#define PROTO_MESSAGE_VERSION 1
|
7
|
+
#define read_struct(sock, structure, size, error) {\
|
8
|
+
ssize_t bytes_read = 0, bytes_total = 0;\
|
9
|
+
error = 0;\
|
10
|
+
while(bytes_total < size) {\
|
11
|
+
bytes_read = read(sock, structure + bytes_total, size - bytes_total);\
|
12
|
+
if(bytes_read != -1) {\
|
13
|
+
bytes_total += bytes_read;\
|
14
|
+
} else {\
|
15
|
+
error = errno;\
|
16
|
+
bytes_total = size;\
|
17
|
+
}\
|
18
|
+
}\
|
19
|
+
}
|
20
|
+
|
21
|
+
#endif
|
@@ -0,0 +1 @@
|
|
1
|
+
#define set_<%= @message_type.name %>_<%= @name %>(msg, val) {msg->header-><%= @name %> = val;}
|
@@ -0,0 +1 @@
|
|
1
|
+
#define set_<%= @message_type.name %>_<%= @name %>(msg, val) {msg->header-><%= @name %> = val;}
|
@@ -0,0 +1 @@
|
|
1
|
+
<% @attributes.each do |attr| %><%= attr.setter %><% end %>
|
data/test/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: protomsg
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 5
|
8
|
+
- 0
|
9
|
+
version: 0.5.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Will Cannings
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-05 00:00:00 +10:00
|
18
|
+
default_executable: protomsg
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: thoughtbot-shoulda
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
description: Protocol Message Buffers for C. This gem generates C socket code for reading and writing messages described in the protomsg DSL.
|
34
|
+
email: me@willcannings.com
|
35
|
+
executables:
|
36
|
+
- protomsg
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files:
|
40
|
+
- LICENSE
|
41
|
+
- README.rdoc
|
42
|
+
files:
|
43
|
+
- bin/protomsg
|
44
|
+
- lib/protomsg.rb
|
45
|
+
- lib/protomsg/attribute.rb
|
46
|
+
- lib/protomsg/message_type.rb
|
47
|
+
- lib/protomsg/parser.rb
|
48
|
+
- lib/templates/finalisers/raw_finaliser.h
|
49
|
+
- lib/templates/finalisers/string_finaliser.h
|
50
|
+
- lib/templates/getters.h
|
51
|
+
- lib/templates/getters/float_getter.h
|
52
|
+
- lib/templates/getters/int_getter.h
|
53
|
+
- lib/templates/getters/raw_getter.h
|
54
|
+
- lib/templates/getters/string_getter.h
|
55
|
+
- lib/templates/header.h
|
56
|
+
- lib/templates/header_entries/float_header_entry.h
|
57
|
+
- lib/templates/header_entries/int_header_entry.h
|
58
|
+
- lib/templates/header_entries/raw_header_entry.h
|
59
|
+
- lib/templates/header_entries/string_header_entry.h
|
60
|
+
- lib/templates/io.h
|
61
|
+
- lib/templates/lengths.h
|
62
|
+
- lib/templates/memory.h
|
63
|
+
- lib/templates/message_type.h
|
64
|
+
- lib/templates/protomsg.h
|
65
|
+
- lib/templates/setters.h
|
66
|
+
- lib/templates/setters/float_setter.h
|
67
|
+
- lib/templates/setters/int_setter.h
|
68
|
+
- lib/templates/setters/raw_setter.h
|
69
|
+
- lib/templates/setters/string_setter.h
|
70
|
+
- LICENSE
|
71
|
+
- README.rdoc
|
72
|
+
- test/helper.rb
|
73
|
+
- test/test_protomsg.rb
|
74
|
+
has_rdoc: true
|
75
|
+
homepage: http://github.com/willcannings/protomsg
|
76
|
+
licenses: []
|
77
|
+
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options:
|
80
|
+
- --charset=UTF-8
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.3.7
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Protocol Message Buffers for C
|
106
|
+
test_files:
|
107
|
+
- test/helper.rb
|
108
|
+
- test/test_protomsg.rb
|