protomsg 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'protomsg'
4
+
5
+ unless ARGV.size == 1
6
+ puts "Usage: protomsg input_file"
7
+ else
8
+ Protomsg.parse_file(ARGV[0])
9
+ end
@@ -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,6 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+
4
+ require 'protomsg/message_type'
5
+ require 'protomsg/attribute'
6
+ require 'protomsg/parser'
@@ -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;
@@ -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,2 @@
1
+ <% variable_attributes.each do |attr| %>#define <%= @name %>_<%= attr.name %>_length(msg) (msg-><%= attr.name %>_length)
2
+ <% end %>#define <%= @name %>_length(msg) (msg->header_length + <%= variable_attributes.collect {|attr| "msg->#{attr.name}_length"}.join(" + ") %>)
@@ -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,4 @@
1
+ #define set_<%= @message_type.name %>_<%= @name %>(msg, val, length) {\
2
+ msg-><%= @name %> = val;\
3
+ msg-><%= @name %>_length = msg->header-><%= @name %>_length = length;\
4
+ }
@@ -0,0 +1,4 @@
1
+ #define set_<%= @message_type.name %>_<%= @name %>(msg, val) {\
2
+ msg-><%= @name %> = val;\
3
+ msg-><%= @name %>_length = msg->header-><%= @name %>_length = (val) ? (strlen(val) + 1) : 0;\
4
+ }
@@ -0,0 +1 @@
1
+ <% @attributes.each do |attr| %><%= attr.setter %><% end %>
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'protomsg'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ class TestProtomsg < Test::Unit::TestCase
4
+ end
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