protomsg 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = protomsg
2
2
 
3
- Protocol Message Buffers for C. This gem generates C socket code for reading and writing messages described in the protomsg DSL.
3
+ Protocol Message Buffers for C. This gem generates C socket code for reading and writing messages described in the protomsg DSL. The code is highly optimised and often similar to what you would write by hand manually.
4
4
 
5
5
  == Overview
6
6
  Input for a 'request' message:
@@ -34,7 +34,7 @@ Output is a header file of macros for reading, writing and manipulating request
34
34
  write_request
35
35
  read_request
36
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.
37
+ The macros generate highly optimised copy free socket code (using scatter/gather IO functions), and automatically check message sanity and handle incomplete reads and writes. The generated code is often very similar to what you would write by hand, and in some cases is even faster.
38
38
 
39
39
  == Usage
40
40
  Call the protomsg script with the message types input file.
@@ -58,7 +58,7 @@ All message type and attribute names must be valid C identifiers. Valid attribut
58
58
  raw Raw bytes
59
59
 
60
60
  == Example
61
- Once you have defined your message types, sending and receiving messages is easy. For example, given the following message type:
61
+ Once you have defined your message types, sending and receiving messages is easy. For example, given the following message type definitions:
62
62
 
63
63
  # ping_message.protomsg
64
64
  ping {
@@ -113,7 +113,7 @@ And the corresponding server could be:
113
113
 
114
114
  // start a server and wait for a ping message
115
115
  create_server_socket(9000, 1, server, error);
116
- accept_client(server, client);
116
+ accept_client(server, client, error);
117
117
  read_ping(p, client, error);
118
118
  printf("Received ping: %llu, %s\n", get_ping_number(p), get_ping_message(p));
119
119
 
@@ -123,7 +123,7 @@ And the corresponding server could be:
123
123
  write_response(r, client, error);
124
124
  }
125
125
 
126
- It's not necessary to use the included socket helper macros - you can use protomsg's over any socket - but for simple client/server applications like this they help reduce the amount of code required to get something running quickly.
126
+ It's not necessary to use the included socket helper macros - you can use proto messages over any socket - but for simple client/server applications like this they help reduce the amount of code required to get something running quickly.
127
127
 
128
128
  == Known Issues
129
- All clients are servers are assumed to be on machines of the same endianess.
129
+ All clients are servers are assumed to be on machines of the same endianess. The simple socket creation macros can't create IP6 sockets as yet. Writev is used to send messages; an error is currently raised when an incomplete write completes, rather than restart the write where it was left off.
data/lib/templates/io.h CHANGED
@@ -1,23 +1,32 @@
1
1
  #define write_<%= @name %>(msg, sock, error) {\
2
2
  error = 0;\
3
- ssize_t written = writev(sock, (const struct iovec *)msg, <%= variable_attributes.size + 1 %>);\
4
- if(written != <%= @name %>_length(msg)){\
3
+ ssize_t _written = writev(sock, (const struct iovec *)msg, <%= variable_attributes.size + 1 %>);\
4
+ if(_written != <%= @name %>_length(msg)){\
5
5
  error = errno;\
6
6
  }\
7
7
  }
8
8
 
9
9
  #define read_<%= @name %>(msg, sock, error) {\
10
- msg = (<%= @name %> *) malloc(sizeof(<%= @name %>));\
11
- msg->header_length = sizeof(<%= @name %>_header);\
12
- msg->header = (<%= @name %>_header *)malloc(sizeof(<%= @name %>_header));\
13
- read_struct(sock, msg->header, sizeof(<%= @name %>_header), error);\
10
+ error = 0;\
11
+ if(!msg) {\
12
+ msg = (<%= @name %> *) malloc(sizeof(<%= @name %>));\
13
+ msg->header_length = sizeof(<%= @name %>_header);\
14
+ msg->header = (<%= @name %>_header *)malloc(sizeof(<%= @name %>_header));\
15
+ }\
16
+ safe_read(sock, msg->header, sizeof(<%= @name %>_header), error);\
14
17
  if(!error) {\
15
- if(msg->header->_version == PROTO_MESSAGE_VERSION && msg->header->_type == <%= @name.upcase %>_MESSAGE_TYPE) {\
16
- <% variable_attributes.each do |attr| %>msg-><%= attr.name %>_length = msg->header-><%= attr.name %>_length;\
17
- msg-><%= attr.name %> = malloc(msg->header-><%= attr.name %>_length);\
18
- <% end %>readv(sock, (const struct iovec *)msg + 1, <%= variable_attributes.size %>);\
19
- } else {\
20
- error = INVALID_PROTOMSG_HEADER;\
18
+ if(msg->header->_version != PROTO_MESSAGE_VERSION) {\
19
+ error = INVALID_PROTOMSG_VERSION;\
20
+ } else if(msg->header->_type != <%= @name.upcase %>_MESSAGE_TYPE) {\
21
+ error = INVALID_MESSAGE_TYPE;\
22
+ } else {\<% if variable_attributes.size > 0 %>
23
+ if(msg-><%= variable_attributes.first.name %>)\
24
+ free(msg-><%= variable_attributes.first.name %>);\
25
+ msg-><%= variable_attributes.first.name %> = malloc(<%= @name %>_variable_length(msg));\
26
+ <% variable_attributes.each_with_index do |attr, index| %>msg-><%= attr.name %>_length = msg->header-><%= attr.name %>_length;\<% if index > 0 %>
27
+ msg-><%= attr.name %> = msg-><%= variable_attributes.first.name %> + <%= variable_attributes[0..index-1].collect {|attr| "msg->header->#{attr.name}_length" }.join(" + ") %>;\<% end %>
28
+ <% end %>safe_read(sock, msg-><%= variable_attributes.first.name %>, <%= @name %>_variable_length(msg), error);\<% else %>
29
+ ;\<% end %>
21
30
  }\
22
31
  }\
23
32
  }
@@ -1,2 +1,3 @@
1
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.size > 0 ? ("+ " + variable_attributes.collect {|attr| "msg->#{attr.name}_length"}.join(" + ")) : '' %>)
2
+ <% end %>#define <%= @name %>_length(msg) (msg->header_length <%= variable_attributes.size > 0 ? ("+ " + variable_attributes.collect {|attr| "msg->#{attr.name}_length"}.join(" + ")) : '' %>)<% if variable_attributes.size > 0 %>
3
+ #define <%= @name %>_variable_length(msg) (<%= variable_attributes.collect {|attr| "msg->#{attr.name}_length"}.join(" + ")%>)<% end %>
@@ -11,3 +11,8 @@
11
11
  free(msg->header);\
12
12
  free(msg);\
13
13
  }
14
+
15
+ #define free_<%= @name %>_structure(msg) {\
16
+ free(msg->header);\
17
+ free(msg);\
18
+ }
@@ -8,14 +8,15 @@
8
8
  #ifndef __protomsg__
9
9
  #define __protomsg__
10
10
 
11
- #define PROTO_MESSAGE_VERSION 1
12
- #define INVALID_PROTOMSG_HEADER 1
11
+ #define PROTO_MESSAGE_VERSION 1
12
+ #define INVALID_PROTOMSG_VERSION 1
13
+ #define INVALID_MESSAGE_TYPE 2
13
14
 
14
- #define read_struct(sock, structure, size, error) {\
15
+ #define safe_read(sock, buffer, size, error) {\
15
16
  error = 0;\
16
17
  ssize_t _bytes_read = 0, _bytes_total = 0;\
17
18
  while(_bytes_total < size) {\
18
- _bytes_read = read(sock, structure + _bytes_total, size - _bytes_total);\
19
+ _bytes_read = read(sock, buffer + _bytes_total, size - _bytes_total);\
19
20
  if(_bytes_read != -1) {\
20
21
  _bytes_total += _bytes_read;\
21
22
  } else {\
@@ -28,35 +29,47 @@
28
29
  #define create_server_socket(port, queue, sock, error) {\
29
30
  error = 0;\
30
31
  sock = socket(AF_INET, SOCK_STREAM, 0);\
31
- struct sockaddr_in _servaddr;\
32
- memset(&_servaddr, 0, sizeof(_servaddr));\
33
- _servaddr.sin_family = AF_INET;\
34
- _servaddr.sin_addr.s_addr = htonl(INADDR_ANY);\
35
- _servaddr.sin_port = htons(port);\
36
- error = bind(sock, (const struct sockaddr *)&_servaddr, sizeof(_servaddr));\
37
- if(error) {\
32
+ if(sock == -1) {\
38
33
  error = errno;\
39
34
  } else {\
40
- error = listen(sock, queue);\
35
+ struct sockaddr_in _servaddr;\
36
+ memset(&_servaddr, 0, sizeof(_servaddr));\
37
+ _servaddr.sin_family = AF_INET;\
38
+ _servaddr.sin_addr.s_addr = htonl(INADDR_ANY);\
39
+ _servaddr.sin_port = htons(port);\
40
+ error = bind(sock, (const struct sockaddr *)&_servaddr, sizeof(_servaddr));\
41
41
  if(error) {\
42
42
  error = errno;\
43
+ } else {\
44
+ error = listen(sock, queue);\
45
+ if(error) {\
46
+ error = errno;\
47
+ }\
43
48
  }\
44
49
  }\
45
50
  }
46
51
 
47
- #define accept_client(sock, client) {\
52
+ #define accept_client(sock, client, error) {\
48
53
  client = accept(sock, NULL, NULL);\
54
+ if(client == -1)\
55
+ error = errno;\
49
56
  }
50
57
 
51
58
  #define connect_to_server(address, port, sock, error) {\
52
59
  error = 0;\
53
60
  sock = socket(AF_INET, SOCK_STREAM, 0);\
54
- struct sockaddr _servaddr;\
55
- socklen_t _servaddr_length;\
56
- get_socket_addr_from_hostname(address, _servaddr, _servaddr_length, error);\
57
- if(!error) {\
58
- ((struct sockaddr_in *) &_servaddr)->sin_port = htons(port);\
59
- error = connect(sock, (struct sockaddr *) &_servaddr, sizeof(_servaddr));\
61
+ if(sock == -1) {\
62
+ error = errno;\
63
+ } else {\
64
+ struct sockaddr _servaddr;\
65
+ socklen_t _servaddr_length;\
66
+ get_socket_addr_from_hostname(address, _servaddr, _servaddr_length, error);\
67
+ if(!error) {\
68
+ ((struct sockaddr_in *) &_servaddr)->sin_port = htons(port);\
69
+ if(connect(sock, (struct sockaddr *) &_servaddr, sizeof(_servaddr))) {\
70
+ error = errno;\
71
+ }\
72
+ }\
60
73
  }\
61
74
  }
62
75
 
@@ -1,4 +1,4 @@
1
1
  #define set_<%= @message_type.name %>_<%= @name %>(msg, val) {\
2
2
  msg-><%= @name %> = val;\
3
- msg-><%= @name %>_length = msg->header-><%= @name %>_length = (val) ? (strlen(val) + 1) : 0;\
3
+ msg-><%= @name %>_length = msg->header-><%= @name %>_length = ((val) ? (strlen(val) + 1) : 0);\
4
4
  }
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 6
7
+ - 7
8
8
  - 0
9
- version: 0.6.0
9
+ version: 0.7.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Will Cannings