divine 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +4 -14
  2. data/README.md +25 -0
  3. data/Rakefile +34 -19
  4. data/divine.gemspec +1 -0
  5. data/lib/divine.rb +3 -1
  6. data/lib/divine/code_generators/code_generator.rb +51 -1
  7. data/lib/divine/code_generators/csharp.rb +898 -0
  8. data/lib/divine/code_generators/java.rb +125 -13
  9. data/lib/divine/code_generators/javascript.rb +111 -4
  10. data/lib/divine/code_generators/ruby.rb +103 -9
  11. data/lib/divine/dsl.rb +95 -6
  12. data/lib/divine/graph_generator/graph_generator.rb +81 -0
  13. data/lib/divine/version.rb +2 -1
  14. data/test/basic_complex_test/basic_complex_test.rb +5 -0
  15. data/test/basic_complex_test/graph.jpg +0 -0
  16. data/test/basic_complex_test/java_test/JavaTest.java +1 -1
  17. data/test/basic_complex_test/ruby_test/ruby_test.rb +17 -4
  18. data/test/binaryTree_test/binaryTree_test.rb +5 -0
  19. data/test/binaryTree_test/csharp_test/csharp_test.cs +99 -0
  20. data/test/binaryTree_test/graph.png +0 -0
  21. data/test/binaryTree_test/java_test/JavaTest.java +1 -1
  22. data/test/binaryTree_test/ruby_test/ruby_test.rb +26 -3
  23. data/test/complex_test/complex_test.rb +5 -0
  24. data/test/complex_test/csharp_test/csharp_test.cs +109 -0
  25. data/test/complex_test/graph.png +0 -0
  26. data/test/complex_test/java_test/JavaTest.java +1 -1
  27. data/test/complex_test/ruby_test/ruby_test.rb +24 -1
  28. data/test/dynamic_int_test/csharp_test/csharp_test.cs +76 -0
  29. data/test/dynamic_int_test/dynamic_int_test.rb +20 -0
  30. data/test/dynamic_int_test/graph.jpg +0 -0
  31. data/test/dynamic_int_test/java_test/JavaTest.java +72 -0
  32. data/test/dynamic_int_test/js_test/js_test.js +54 -0
  33. data/test/dynamic_int_test/ruby_test/ruby_test.rb +55 -0
  34. data/test/ipv6_test/csharp_test/csharp_test.cs +73 -0
  35. data/test/ipv6_test/graph.jpg +0 -0
  36. data/test/ipv6_test/ipv6_test.rb +5 -0
  37. data/test/ipv6_test/java_test/JavaTest.java +1 -1
  38. data/test/ipv6_test/ruby_test/ruby_test.rb +24 -4
  39. data/test/lib/csharp/nunit.framework.dll +0 -0
  40. data/test/{java_lib → lib/java}/junit.jar +0 -0
  41. data/test/signed_int_test/csharp_test/csharp_test.cs +86 -0
  42. data/test/signed_int_test/graph.jpg +0 -0
  43. data/test/signed_int_test/java_test/JavaTest.java +1 -1
  44. data/test/signed_int_test/ruby_test/ruby_test.rb +21 -1
  45. data/test/signed_int_test/signed_int_test.rb +6 -0
  46. data/test/unify_test/unify_test.rb +17 -4
  47. metadata +54 -8
  48. data/test/signed_float_test/ruby_test/ruby_test.rb +0 -36
  49. data/test/signed_float_test/signed_float_test.rb +0 -14
data/.gitignore CHANGED
@@ -16,25 +16,11 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  *.class
19
- test/basic_complex_test/java_test/bin.babel
20
- test/basic_complex_test/js_test/bin.babel.js
21
- test/basic_complex_test/ruby_test/bin.babel.rb
22
- test/binaryTree_test/java_test/bin.babel
23
- test/binaryTree_test/js_test/bin.babel.js
24
- test/binaryTree_test/ruby_test/bin.babel.rb
25
- test/complex_test/java_test/bin.babel
26
- test/complex_test/js_test/bin.babel.js
27
- test/complex_test/ruby_test/bin.babel.rb
28
- test/ipv6_test/java_test/bin.babel
29
- test/ipv6_test/js_test/bin.babel.js
30
- test/ipv6_test/ruby_test/bin.babel.rb
31
19
  test/basic_complex_test/ruby_test/test_babel.rb
32
20
  test/binaryTree_test/ruby_test/test_binaryTree.rb
33
21
  test/complex_test/ruby_test/test_complex.rb
34
22
  test/ipv6_test/ruby_test/test_ipv6.rb
35
23
  test/signed_int_test/ruby_test/test_signed_int.rb
36
- test/signed_int_test/js_test/bin.babel.js
37
- test/signed_int_test/ruby_test/bin.babel.rb
38
24
  test/basic_complex_test/java_test/test_babel.java
39
25
  test/binaryTree_test/java_test/test_binaryTree.java
40
26
  test/complex_test/java_test/test_complex.java
@@ -45,3 +31,7 @@ test/binaryTree_test/js_test/test_binaryTree.js
45
31
  test/complex_test/js_test/test_complex.js
46
32
  test/ipv6_test/js_test/test_ipv6.js
47
33
  test/signed_int_test/js_test/test_signed_int.js
34
+ bin.babel
35
+ bin.babel.csharp
36
+ bin.babel.rb
37
+ bin.babel.js
data/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Divine provides a compact data interchange format for Ruby, Java and Javascript. Divine is similar to [Thrift](http://thrift.apache.org/) and [Protobuf](http://code.google.com/p/protobuf/), but I try to overcome of some of the shortcommings I think the other libraries have.
4
4
 
5
+ This software is still under active development and testing.
6
+
7
+ We support C#, Java, Ruby and Javascript at the moment.
5
8
 
6
9
  ## Example
7
10
 
@@ -152,6 +155,28 @@ Divine::CodeGenerator.new.generate(:ruby, file: 'test_babel.rb')
152
155
  It is now not possible to alter _Foobar_ by accident. If you make a change to the struct, you will also need to provide a correct MD5 sum.
153
156
 
154
157
 
158
+ ### Graphviz
159
+
160
+ To generate a graphviz diagram of your defined structs, add the following line to your definition file
161
+
162
+ ```ruby
163
+ Divine::GraphGenerator.new.draw(".", "graph", "jpg")
164
+ ```
165
+
166
+
167
+ ## Change log
168
+ Version 0.0.4
169
+
170
+ * Added dint63 (Dynamic Int 63)
171
+ * Added graphviz graph generation
172
+
173
+ Version 0.0.3
174
+
175
+ * Added C# code generator
176
+ * Added sint64 (Signed Int 64)
177
+ * Added versioning and freezing
178
+
179
+
155
180
  ## Installation
156
181
 
157
182
  Add this line to your application's Gemfile:
data/Rakefile CHANGED
@@ -1,16 +1,17 @@
1
1
  require "bundler/gem_tasks"
2
-
2
+ require 'fileutils'
3
3
 
4
4
  task :default => ["test:all"]
5
5
 
6
6
 
7
7
  namespace :test do
8
8
  desc "Run all tests"
9
- task :all => [:ruby, :js, :java, :unify] # Make sure that the unify test be the last one in the list.
9
+ task :all => [:ruby, :js, :java, :csharp, :unify] # Make sure that the unify test be the last one in the list.
10
10
 
11
11
  desc "Run Ruby code test suite"
12
12
  task :ruby do
13
13
  generate_source('ruby')
14
+ ruby "test/dynamic_int_test/ruby_test/ruby_test.rb"
14
15
  ruby "test/signed_int_test/ruby_test/ruby_test.rb"
15
16
  ruby "test/ipv6_test/ruby_test/ruby_test.rb"
16
17
  ruby "test/complex_test/ruby_test/ruby_test.rb"
@@ -24,6 +25,7 @@ namespace :test do
24
25
  desc "Run JS code test suite"
25
26
  task :js do
26
27
  generate_source('js')
28
+ system("node test/dynamic_int_test/js_test/js_test.js")
27
29
  system("node test/signed_int_test/js_test/js_test.js")
28
30
  system("node test/ipv6_test/js_test/js_test.js")
29
31
  system("node test/complex_test/js_test/js_test.js")
@@ -37,28 +39,30 @@ namespace :test do
37
39
  desc "Run java code test suite"
38
40
  task :java do
39
41
  generate_source('java')
40
- #system("javac -cp test/java_lib/junit.jar: test/signed_float_test/java_test/*.java")
41
- #system("java -cp test/signed_float_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
42
-
43
- system("javac -cp test/java_lib/junit.jar: test/signed_int_test/java_test/*.java")
44
- system("java -cp test/signed_int_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
45
-
46
- system("javac -cp test/java_lib/junit.jar: test/ipv6_test/java_test/*.java")
47
- system("java -cp test/ipv6_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
48
-
49
- system("javac -cp test/java_lib/junit.jar: test/complex_test/java_test/*.java")
50
- system("java -cp test/complex_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
51
-
52
- system("javac -cp test/java_lib/junit.jar: test/binaryTree_test/java_test/*.java")
53
- system("java -cp test/binaryTree_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
54
-
55
- system("javac -cp test/java_lib/junit.jar: test/basic_complex_test/java_test/*.java")
56
- system("java -cp test/basic_complex_test/java_test/:test/java_lib/junit.jar:. org.junit.runner.JUnitCore JavaTest")
42
+ test_java("dynamic_int")
43
+ test_java("signed_int")
44
+ test_java("ipv6")
45
+ test_java("complex")
46
+ test_java("binaryTree")
47
+ test_java("basic_complex")
57
48
 
58
49
  system("find . -name 'test*.java' | xargs rm") # Remove generated source code files
59
50
  system("find . -name '*.class' | xargs rm") # Remove generated .class files
60
51
  end
61
52
 
53
+ desc "Run cSharp code test suite"
54
+ task :csharp do
55
+ generate_source('csharp')
56
+ test_csharp("dynamic_int")
57
+ test_csharp("signed_int")
58
+ test_csharp("ipv6")
59
+ test_csharp("complex")
60
+ test_csharp("binaryTree")
61
+
62
+ system("find . -name 'test*.cs' | xargs rm") # Remove generated source code files
63
+ system("find . -name '*.exe' | xargs rm") # Remove generated source code files
64
+ end
65
+
62
66
  desc "Unify test to compare the produced binary files. Prerequisite Other tests must be run first to generate bin files to be compared"
63
67
  task :unify do
64
68
  ruby "test/unify_test/unify_test.rb"
@@ -66,9 +70,20 @@ namespace :test do
66
70
  end
67
71
  end
68
72
 
73
+ def test_java(test)
74
+ system("javac -cp test/lib/java/junit.jar: test/#{test}_test/java_test/*.java")
75
+ system("java -cp test/#{test}_test/java_test/:test/lib/java/junit.jar:. org.junit.runner.JUnitCore JavaTest")
76
+ end
77
+
78
+ def test_csharp(test)
79
+ system("gmcs test/#{test}_test/csharp_test/*.cs -r:test/lib/csharp/nunit.framework.dll")
80
+ FileUtils.move "./test/#{test}_test/csharp_test/csharp_test.exe", "./test/lib/csharp/"
81
+ system("mono test/lib/csharp/csharp_test.exe")
82
+ end
69
83
 
70
84
  def generate_source(lang)
71
85
  puts "Divine Version #{Divine::VERSION}"
86
+ ruby "test/dynamic_int_test/dynamic_int_test.rb #{lang}"
72
87
  ruby "test/signed_int_test/signed_int_test.rb #{lang}"
73
88
  ruby "test/ipv6_test/ipv6_test.rb #{lang}"
74
89
  ruby "test/complex_test/complex_test.rb #{lang}"
@@ -19,4 +19,5 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_dependency(%q<docile>, [">= 1.0.0"])
21
21
  gem.add_dependency(%q<erubis>, [">= 2.7.0"])
22
+ gem.add_dependency(%q<ruby-graphviz>, [">= 1.0.8"])
22
23
  end
@@ -7,12 +7,14 @@ require "divine/code_generators/code_generator"
7
7
  require "divine/code_generators/ruby"
8
8
  require "divine/code_generators/java"
9
9
  require "divine/code_generators/javascript"
10
+ require "divine/code_generators/csharp"
11
+ require "divine/graph_generator/graph_generator"
10
12
 
11
13
  module Divine
12
14
  end
13
15
 
14
16
  #
15
- # Toplevel definition of struct'
17
+ # start to build struct
16
18
  #
17
19
  def struct(name, properties=nil, &block)
18
20
  #puts "struct #{name}"
@@ -5,7 +5,13 @@ require 'fileutils'
5
5
  module Divine
6
6
  $language_generators = {}
7
7
 
8
+ #
9
+ # Support methods that help to get fields and fields type from structs
10
+ #
8
11
  class StructHandler
12
+ # name = name of struct
13
+ # latest_version = struct version
14
+ # structs = defined struct
9
15
  attr_reader :name, :latest_version, :structs
10
16
 
11
17
  def initialize(structs)
@@ -15,16 +21,30 @@ module Divine
15
21
  @field_hash = structs.map(&:fields).flatten.group_by(&:name)
16
22
  end
17
23
 
24
+ #
25
+ # Get field of given name
26
+ # * *Args* :
27
+ # - +name+ -> field's name
18
28
  def field(name)
19
29
  @field_hash[name]
20
30
  end
21
31
 
32
+ #
33
+ # Get all fields names
34
+ #
22
35
  def field_names
23
36
  @field_hash.keys.sort
24
37
  end
25
38
  end
26
39
 
40
+ #
41
+ # Support methods needed when generating source code files
42
+ #
27
43
  class BabelHelperMethods
44
+
45
+ #
46
+ # Handle indentation
47
+ #
28
48
  def format_src(first_indent, following_indent, is, spc = " ")
29
49
  indent = "#{spc * first_indent}"
30
50
  is.flatten.compact.map do |i|
@@ -41,16 +61,26 @@ module Divine
41
61
  end.compact.join("\n")
42
62
  end
43
63
 
64
+ #
65
+ # Return new variable name
66
+ #
44
67
  def get_fresh_variable_name
45
68
  @vindex = (@vindex || 0xFF) + 1
46
69
  return "var_#{@vindex.to_s(16)}"
47
70
  end
48
71
 
72
+ #
73
+ # Camelize comming args
74
+ # * *Args* :
75
+ # - +*str+ -> list of arguments needed to be camelized
49
76
  def camelize(*str)
50
77
  ss = str.map(&:to_s).join("_").split(/_/).flatten
51
78
  "#{ss.first.downcase}#{ss[1..-1].map(&:downcase).map(&:capitalize).join}"
52
79
  end
53
80
 
81
+ #
82
+ # Return Header comments for generated files
83
+ #
54
84
  def get_header_comment_text
55
85
  return [
56
86
  "",
@@ -115,6 +145,10 @@ module Divine
115
145
  t1.get_field(field).referenced_types == t2.get_field(field).referenced_types
116
146
  end
117
147
 
148
+ #
149
+ # Check if there is any strcut has changes
150
+ # * *Args* :
151
+ # - +*ss+ -> list of structs
118
152
  def check_freezed_structs(ss)
119
153
  ss.each do |s|
120
154
  if s.freezed?
@@ -126,6 +160,10 @@ module Divine
126
160
  end
127
161
  end
128
162
 
163
+ #
164
+ # Generate signature for given struct
165
+ # * *Args* :
166
+ # - +*s+ -> struct object
129
167
  def calculate_signature(s)
130
168
  str = s.fields.map do |f|
131
169
  "#{f.name}:#{f.referenced_types.inspect}"
@@ -135,8 +173,15 @@ module Divine
135
173
  end
136
174
 
137
175
 
138
-
176
+ #
177
+ # Support basic methods that generate source code file(s) for target language
178
+ #
139
179
  class CodeGenerator
180
+ #
181
+ # generate source code file(s)
182
+ # * *Args* :
183
+ # - +tagret+ -> target language
184
+ # - +opts+ -> Dictionary that contains generation params [file, parent_class, target_dir, ...]
140
185
  def generate(target, opts)
141
186
  gen = $language_generators[target.to_sym]
142
187
  raise "Unknown target language: #{target}" unless gen
@@ -160,6 +205,11 @@ module Divine
160
205
  end
161
206
  end
162
207
 
208
+ #
209
+ # Create file
210
+ # * *Args* :
211
+ # - +path+ -> the path in which the file will be written
212
+ # - +content+ -> the content to be written
163
213
  def writeFile(path, content)
164
214
  File.open(path, 'w+') do |f|
165
215
  puts "... writing #{path}"
@@ -0,0 +1,898 @@
1
+ module Divine
2
+
3
+ ##
4
+ # * +C# Helper+ :
5
+ # Support base function needed to build Divine enviroment and classes corresponding to DSL structs
6
+ #
7
+ class CsharpHelperMethods < BabelHelperMethods
8
+
9
+ #
10
+ # Return the header comment
11
+ #
12
+ def get_header_comment
13
+ get_header_comment_text.map do |s|
14
+ "// #{s}"
15
+ end.join("\n")
16
+ end
17
+
18
+ ##
19
+ # Generate the base Divine Csharp Class
20
+ # that contains the main methods:
21
+ # * serialize
22
+ # * serialize Internal
23
+ # * deserialize
24
+ # * Read Methods
25
+ # * Write Methods
26
+
27
+ def csharp_base_class_template_str
28
+ <<EOS
29
+ namespace divine
30
+ {
31
+ public abstract class Divine
32
+ {
33
+
34
+ public byte[] serialize()
35
+ {
36
+ try
37
+ {
38
+ MemoryStream baos = new MemoryStream();
39
+ serializeInternal(baos);
40
+ baos.Close();
41
+ return baos.ToArray();
42
+ }
43
+ catch (System.IO.IOException ex)
44
+ {
45
+ throw ex;
46
+ }
47
+ }
48
+
49
+ public abstract void serializeInternal(MemoryStream baos);
50
+
51
+ public abstract void deserialize(MemoryStream baos);
52
+
53
+ protected byte readInt8(MemoryStream data)
54
+ {
55
+ return (byte)(data.ReadByte() & 0xff);
56
+ }
57
+
58
+ protected ushort readInt16(MemoryStream data)
59
+ {
60
+ return (ushort)((data.ReadByte() << 8) | readInt8(data));
61
+ }
62
+
63
+ protected int readInt24(MemoryStream data)
64
+ {
65
+ return (data.ReadByte() << 16) | readInt16(data);
66
+ }
67
+
68
+ protected uint readInt32(MemoryStream data)
69
+ {
70
+ uint x = (uint)data.ReadByte() << 24;
71
+ uint y = (uint)readInt24(data);
72
+ return x | y;
73
+ }
74
+
75
+ protected int readSint32(MemoryStream data)
76
+ {
77
+ return (data.ReadByte() << 24) | readInt24(data);
78
+ }
79
+
80
+ protected long readSint64(MemoryStream data)
81
+ {
82
+ return ((long)readInt32(data) << 32) | (readInt32(data) & 0xFFFFFFFF);
83
+ }
84
+
85
+ protected long readDint63(MemoryStream data)
86
+ {
87
+ int b = this.readInt8(data);
88
+ long val = b & 0x7F;
89
+ while ((b >> 7) == 1)
90
+ {
91
+ b = this.readInt8(data);
92
+ val = val << 7;
93
+ val = val | (uint)(b & 0x7F);
94
+ }
95
+ return val;
96
+ }
97
+
98
+ protected bool readBool(MemoryStream data)
99
+ {
100
+ return readInt8(data) == 1;
101
+ }
102
+
103
+ protected String readString(MemoryStream data)
104
+ {
105
+ // Force utf8
106
+ try
107
+ {
108
+ return System.Text.Encoding.UTF8.GetString(readBytes(readInt16(data), data));
109
+ }
110
+ catch (System.IO.IOException ex)
111
+ {
112
+ throw ex;
113
+ }
114
+ }
115
+
116
+ private byte[] readBytes(int size, MemoryStream data)
117
+ {
118
+ try
119
+ {
120
+ byte[] bs = new byte[size];
121
+ data.Read(bs, 0, size);
122
+ return bs;
123
+ }
124
+ catch (System.IO.IOException ex)
125
+ {
126
+ throw ex;
127
+ }
128
+ }
129
+
130
+ protected byte[] readBinary(MemoryStream data)
131
+ {
132
+ try
133
+ {
134
+ long c = readInt32(data);
135
+ if (c > int.MaxValue)
136
+ {
137
+ throw new System.IndexOutOfRangeException("Binary data to big for csharp");
138
+ }
139
+ return readBytes((int)c, data);
140
+ }
141
+ catch (System.IO.IOException ex)
142
+ {
143
+ throw ex;
144
+ }
145
+ }
146
+
147
+ protected byte[] readShortBinary(MemoryStream data)
148
+ {
149
+ try
150
+ {
151
+ return readBytes(readInt8(data), data);
152
+ }
153
+ catch (System.IO.IOException ex)
154
+ {
155
+ throw ex;
156
+ }
157
+ }
158
+
159
+ protected String readIpNumber(MemoryStream data)
160
+ {
161
+ try
162
+ {
163
+ byte[] ips = readShortBinary(data);
164
+ if (ips.Length == 4)
165
+ {
166
+ return readIpv4Number(ips);
167
+ }
168
+ else
169
+ {
170
+ return readIpv6Number(ips);
171
+ }
172
+ }
173
+ catch (System.IO.IOException ex)
174
+ {
175
+ throw ex;
176
+ }
177
+ }
178
+
179
+ protected String readIpv4Number(byte[] ips)
180
+ {
181
+ String ip = "";
182
+ foreach (byte b in ips)
183
+ {
184
+ if (ip.Length > 0)
185
+ {
186
+ ip += ".";
187
+ }
188
+ ip += (b & 0xFF);
189
+ }
190
+ return ip;
191
+ }
192
+
193
+ protected String readIpv6Number(byte[] ips)
194
+ {
195
+ try
196
+ {
197
+ String ip = "";
198
+ int part1, part2;
199
+ for (int i = 0; i < ips.Length; i += 2)
200
+ {
201
+ part1 = ips[i] & 0xFF;
202
+ part2 = ips[i + 1] & 0xFF;
203
+ ip += part1 == 0 ? "" : part1.ToString("X");
204
+ ip += (part1 == 0 && part2 == 0) ? "" : (part2 < 10 && part1 != 0 ? "0" + part2.ToString("X") : part2.ToString("X"));
205
+ if (i < ips.Length - 2)
206
+ {
207
+ ip += ":";
208
+ }
209
+ }
210
+ ip = System.Text.RegularExpressions.Regex.Replace(ip, ":{3,}", "::");
211
+ return ip;
212
+ }
213
+ catch (System.IO.IOException ex)
214
+ {
215
+ throw ex;
216
+ }
217
+ }
218
+
219
+ protected void writeInt8(byte v, MemoryStream output)
220
+ {
221
+ if (v > 0xFF)
222
+ { // Max 255
223
+ raiseError("Too large int8 number: " + v);
224
+ }
225
+ else if (v < 0)
226
+ {
227
+ raiseError("a negative number passed to int8 number: " + v);
228
+ }
229
+ output.WriteByte((byte) v);
230
+ }
231
+
232
+ protected void writeInt16(ushort v, MemoryStream output)
233
+ {
234
+ if (v > 0xFFFF)
235
+ { // Max 65.535
236
+ raiseError("Too large int16 number: " + v);
237
+ }
238
+ else if (v < 0)
239
+ {
240
+ raiseError("a negative number passed to int16 number: " + v);
241
+ }
242
+ writeInt8((byte)(v >> 8 & 0xFF), output);
243
+ writeInt8((byte)(v & 0xFF), output);
244
+ }
245
+
246
+ protected void writeInt24(int v, MemoryStream output)
247
+ {
248
+ if (v > 0xFFFFFF)
249
+ { // Max 16.777.215
250
+ raiseError("Too large int24 number: " + v);
251
+ }
252
+ else if (v < 0)
253
+ { // In Case added to csharp declaration
254
+ raiseError("a negative number passed to int24 number: " + v);
255
+ }
256
+ writeInt8((byte)(v >> 16 & 0xFF), output);
257
+ writeInt16((ushort)(v & 0xFFFF), output);
258
+ }
259
+
260
+ protected void writeInt32(uint v, MemoryStream output)
261
+ {
262
+ if (v > 0xFFFFFFFF)
263
+ { // Max 4.294.967.295
264
+ raiseError("Too large int32 number: " + v);
265
+ }
266
+ else if (v < 0)
267
+ {
268
+ raiseError("a negative number passed to int32 number: " + v);
269
+ }
270
+ writeInt8((byte)((v >> 24) & 0xFF), output);
271
+ writeInt24((int)(v & 0xFFFFFF), output);
272
+ }
273
+
274
+ protected void writeSint32(int v, MemoryStream output)
275
+ {
276
+ if (v > int.MaxValue)
277
+ { // Max 2.147.483.647
278
+ raiseError("Too large sInt32 number: " + v + ", Max = " + int.MaxValue);
279
+ }
280
+ else if (v < int.MinValue)
281
+ { // Min -2.147.483.648
282
+ raiseError("Too small sInt32 number: " + v + ", Min = " + int.MinValue);
283
+ }
284
+ writeInt8((byte)((v >> 24) & 0xFF), output);
285
+ writeInt24((int)(v & 0xFFFFFF), output);
286
+ }
287
+
288
+ protected void writeSint64(long v, MemoryStream output) {
289
+ if (v > long.MaxValue ) { // Max 9,223,372,036,854,775,807
290
+ raiseError("Too large sInt64 number: " + v + ", Max = " + long.MaxValue);
291
+ }else if(v < long.MinValue){ // Min -9,223,372,036,854,775,808
292
+ raiseError("Too small sInt64 number: " + v + ", Min = " + long.MinValue);
293
+ }
294
+ writeInt32((uint)((v >> 32) & 0xFFFFFFFF), output);
295
+ writeInt32((uint)(v & 0xFFFFFFFF), output);
296
+ }
297
+
298
+
299
+
300
+ protected void writeDint63(long v, MemoryStream output)
301
+ {
302
+ if (v > long.MaxValue)
303
+ { // Max 9,223,372,036,854,775,807
304
+ raiseError("Too large Dynamic Int63 number: " + v + ", Max = " + long.MaxValue);
305
+ }
306
+ else if (v < long.MinValue)
307
+ { // Min 0
308
+ raiseError("Too small Dynamic Int63 number: " + v + ", Min = " + 0);
309
+ }
310
+ char[] charArray = Convert.ToString(v, 2).ToCharArray();
311
+ Array.Reverse(charArray);
312
+ MatchCollection matches = Regex.Matches(new String(charArray), ".{1,7}");
313
+ for (int i = matches.Count - 1; i >= 0 ; i--)
314
+ {
315
+ String val = matches[i].Value;
316
+ val += new String(new char[7 - val.Length]).Replace("\\\\0", "0") + Math.Min(i, 1);
317
+ charArray = val.ToCharArray();
318
+ Array.Reverse(charArray);
319
+ String str = new String(charArray);
320
+ int t = Convert.ToInt32(str, 2);
321
+ this.writeInt8((byte)t, output);
322
+ }
323
+
324
+ }
325
+
326
+ protected void writeBool(bool v, MemoryStream output)
327
+ {
328
+ writeInt8((byte)(v ? 1 : 0), output);
329
+ }
330
+
331
+ protected void writeString(String v, MemoryStream output)
332
+ {
333
+ try
334
+ {
335
+ byte[] bs = System.Text.Encoding.UTF8.GetBytes (v);
336
+
337
+ if (bs.Length > 0xFFFF)
338
+ {
339
+ raiseError("Too large string: " + bs.Length + " bytes");
340
+ }
341
+ writeInt16((ushort)bs.Length, output);
342
+ output.Write(bs, 0, bs.Length);
343
+ }
344
+ catch (System.IO.IOException ex)
345
+ {
346
+ throw ex;
347
+ }
348
+ }
349
+
350
+ protected void writeBinary(byte[] v, MemoryStream output)
351
+ {
352
+ try
353
+ {
354
+ if ((uint)v.Length > 0xFFFFFFFF)
355
+ {
356
+ raiseError("Too large binary: " + v.Length + " bytes");
357
+ }
358
+ writeInt32((uint)v.Length, output);
359
+ output.Write(v, 0, v.Length);
360
+ }
361
+ catch (System.IO.IOException ex)
362
+ {
363
+ throw ex;
364
+ }
365
+ }
366
+
367
+ protected void write16Binary(int[] v, MemoryStream output)
368
+ {
369
+ try
370
+ {
371
+ if (v.Length * 2 > 0xFF)
372
+ {
373
+ raiseError("Too large 16_binary: " + (v.Length * 2) + " bytes");
374
+ }
375
+ writeInt8((byte)(v.Length * 2), output);
376
+ for (int i = 0; i < v.Length; i++)
377
+ {
378
+ this.writeInt16((ushort)v[i], output);
379
+ }
380
+ }
381
+ catch (System.IO.IOException ex)
382
+ {
383
+ throw ex;
384
+ }
385
+ }
386
+
387
+ protected void writeShortBinary(byte[] v, MemoryStream output)
388
+ {
389
+ try
390
+ {
391
+ if (v.Length > 0xFF)
392
+ {
393
+ raiseError("Too large short_binary: " + v.Length + " bytes");
394
+ }
395
+ writeInt8((byte)v.Length, output);
396
+ output.Write(v, 0, v.Length);
397
+ }
398
+ catch (System.IO.IOException ex)
399
+ {
400
+ throw ex;
401
+ }
402
+ }
403
+
404
+ protected void writeIpNumber(String v, MemoryStream output)
405
+ {
406
+ try
407
+ {
408
+ if (v.Contains(":"))
409
+ {
410
+ writeIpv6Number(v, output);
411
+ }
412
+ else
413
+ {
414
+ writeIpv4Number(v, output);
415
+ }
416
+ }
417
+ catch (System.IO.IOException ex)
418
+ {
419
+ throw ex;
420
+ }
421
+ }
422
+
423
+ protected void writeIpv4Number(String v, MemoryStream output)
424
+ {
425
+ try
426
+ {
427
+ byte[] ss = new byte[0];
428
+ if (v.Length != 0)
429
+ {
430
+ String[] bs = v.Split('.');
431
+ ss = new byte[bs.Length];
432
+ for (int i = 0; i < bs.Length; i++)
433
+ {
434
+ // TODO: check that each part is in range from 0 to 255
435
+ ss[i] = (byte)(int.Parse(bs[i]) & 0xFF);
436
+ }
437
+ }
438
+ if (ss.Length == 0 || ss.Length == 4)
439
+ {
440
+ writeShortBinary(ss, output);
441
+ }
442
+ else
443
+ {
444
+ raiseError("Unknown IP v4 number " + v); // Only IPv4 for now
445
+ }
446
+ }
447
+ catch (System.IO.IOException ex)
448
+ {
449
+ throw ex;
450
+ }
451
+ }
452
+
453
+ protected void writeIpv6Number(String v, MemoryStream output)
454
+ {
455
+ try
456
+ {
457
+ v = v.Replace(" ", "");
458
+ int[] ss = new int[0];
459
+ bool contains_ipv6_letters = Regex.IsMatch(v.Trim().ToLower(), "[0-9a-f]+");
460
+ bool contains_other_letters = Regex.IsMatch(v.Trim().ToLower(), "[^:0-9a-f]+");
461
+ bool contains_more_seprators = Regex.IsMatch(v.Trim().ToLower(), ":{3,}");
462
+ bool contains_one_shorthand = Regex.Matches(v.Trim().ToLower(), ":{2}").Count <= 1;
463
+ // make sure of v must have only one "::" and no more than two of ":".
464
+ // e.g. 1::1::1 & 1:::1:205
465
+ if (v.Trim().Length != 0 && !contains_more_seprators && contains_one_shorthand && !contains_other_letters
466
+ && contains_ipv6_letters)
467
+ {
468
+ String[] bs = v.Split(':');
469
+ ss = new int[bs.Length];
470
+ for (int i = 0; i < bs.Length; i++)
471
+ {
472
+ String s = bs[i].Trim();
473
+ if (s.Length <= 4)
474
+ { // to avoid such number 0125f
475
+ ss[i] = int.Parse(((s.Length == 0 ? "0" : bs[i].Trim())), System.Globalization.NumberStyles.HexNumber);
476
+ }
477
+ else
478
+ {
479
+ raiseError("Unknown IPv6 Group " + i + " which is " + s);
480
+ }
481
+ }
482
+ }
483
+ // Check for make sure of the size of the IP groups in case "::" is used
484
+ // [> 2 & < 8]or not [must == 8]
485
+ if (!contains_other_letters
486
+ && (!v.Contains("::") && ss.Length == 0 || ss.Length == 8)
487
+ || (v.Contains("::") && ss.Length > 2 && ss.Length < 8))
488
+ {
489
+ write16Binary(ss, output);
490
+ }
491
+ else
492
+ {
493
+ raiseError("Unknown IP v6 number " + v);
494
+ }
495
+ }
496
+ catch (System.IO.IOException ex)
497
+ {
498
+ throw ex;
499
+ }
500
+ }
501
+
502
+ protected void raiseError(String msg)
503
+ {
504
+ throw new System.InvalidOperationException("[" + GetType() + "] " + msg);
505
+ }
506
+ }
507
+ EOS
508
+ end
509
+
510
+ ##
511
+ # Generate C# Class that corresponding to the struct definition
512
+ # * *Args* :
513
+ # - +sh+ -> Struct Name
514
+
515
+ def csharp_class_template(sh)
516
+ code = [
517
+ "public class #{sh.name} : Divine {",
518
+ :indent,
519
+ "",
520
+
521
+ # PROPERTIES
522
+ "public int struct_version = #{sh.latest_version};",
523
+ sh.field_names.map do |fn|
524
+ f = sh.field(fn).last
525
+ "public #{csharp_get_type_declaration(f)} #{fn} = #{csharp_get_empty_declaration(f)};"
526
+ end, "",
527
+
528
+
529
+ # SERiALIZE INTERNAL
530
+ "override",
531
+ "public void serializeInternal(MemoryStream baos) {",
532
+ :indent,
533
+ "try{",
534
+ :indent,
535
+ "writeInt8((byte)this.struct_version, baos);",
536
+ sh.structs.map do |s|
537
+ [
538
+ "if(this.struct_version == #{s.version}) {",
539
+ :indent,
540
+ s.simple_fields.map do |f|
541
+ "#{camelize("write", f.type)}(this.#{f.name}, baos);"
542
+ end,
543
+ s.complex_fields.map do |f|
544
+ [
545
+ "", "// Serialize #{f.type} '#{f.name}'",
546
+ csharp_serialize_internal("this.#{f.name}", f.referenced_types)
547
+ ]
548
+ end,
549
+ "return;",
550
+ :deindent,
551
+ "}", ""
552
+ ]
553
+ end, "",
554
+ "throw new System.InvalidOperationException(\"Unsupported version \" + this.struct_version + \" for type '#{sh.name}'\");",
555
+ :deindent,
556
+ "}catch (System.IO.IOException ex){",
557
+ :indent,
558
+ "throw ex;",
559
+ :deindent,
560
+ "}",
561
+ :deindent,
562
+ "}", "",
563
+
564
+
565
+ # DESERIALIZE
566
+ "override",
567
+ "public void deserialize(MemoryStream bais) {",
568
+ :indent,
569
+ "try{",
570
+ :indent,
571
+ "this.struct_version = readInt8(bais);",
572
+ sh.structs.map do |s|
573
+ [
574
+ "if(this.struct_version == #{s.version}) {",
575
+ :indent,
576
+ s.simple_fields.map do |f|
577
+ "this.#{f.name} = #{camelize("read", f.type)}(bais);"
578
+ end,
579
+ s.complex_fields.map do |f|
580
+ [
581
+ "", "// Read #{f.type} '#{f.name}'",
582
+ csharp_deserialize_internal("this.#{f.name}", f.referenced_types)
583
+ ]
584
+ end,
585
+ "return;",
586
+ :deindent,
587
+ "}"
588
+ ]
589
+ end, "",
590
+ "throw new System.InvalidOperationException(\"Unsupported version \" + this.struct_version + \" for type '#{sh.name}'\");",
591
+ :deindent,
592
+ "}catch (System.IO.IOException ex){",
593
+ :indent,
594
+ "throw ex;",
595
+ :deindent,
596
+ "}",
597
+ :deindent,
598
+ "}", "",
599
+
600
+
601
+ # END OF CLASS
602
+ :deindent,
603
+ "}"
604
+ ]
605
+
606
+ format_src(3, 3, code)
607
+ end
608
+
609
+ ##
610
+ # Generate default C# data types declaration values corresponding to each DSL types:
611
+ # * DSL Type --> Corresponding Default C# Value
612
+ # * dint63 --> 0 range -> [0 - 9,223,372,036,854,775,807]
613
+ # * 1 byte: range -> [0 - 127]
614
+ # * 2 bytes: range -> [0 - 16,383]
615
+ # * 3 bytes: range -> [0 - 2,097,151]
616
+ # * 4 bytes: range -> [0 - 268,435,455]
617
+ # * 5 bytes: range -> [0 - 34,359,738,367]
618
+ # * 6 bytes: range -> [0 - 4,398,046,511,103]
619
+ # * 7 bytes: range -> [0 - 562,949,953,421,311]
620
+ # * 8 bytes: range -> [0 - 72,057,594,037,927,935]
621
+ # * 9 bytes: range -> [0 - 9,223,372,036,854,775,807]
622
+ # * int8 --> 0 Range -> [0 - 255]
623
+ # * int16 --> 0 Range -> [0 - 65535]
624
+ # * int32 --> 0 Range -> [0 - 4.294.967.295]
625
+ # * sint32 --> 0 Range -> [-2.147.483.648 - 2.147.483.647]
626
+ # * sint64 --> 0 Range -> [-9.223.372.036.854.775.808, 9.223.372.036.854.775.807]
627
+ # * string --> ""
628
+ # * ip_number--> ""
629
+ # * binary --> new byte[0]
630
+ # * short_binary --> new byte[0]
631
+ # * list --> new List<type>()
632
+ # * map --> new Dictionary<keyType, valueType>()
633
+
634
+ def csharp_get_empty_declaration(types, is_reference_type = false)
635
+ if types.respond_to? :referenced_types
636
+ csharp_get_empty_declaration(types.referenced_types)
637
+
638
+ elsif types.respond_to?(:first) && types.size > 1
639
+ case types.first
640
+ when :list
641
+ "new #{csharp_get_type_declaration(types, true)}()"
642
+ when :map
643
+ "new #{csharp_get_type_declaration(types, true)}()"
644
+ else
645
+ raise "Missing empty declaration for #{types}"
646
+ end
647
+
648
+ elsif types.respond_to?(:first) && types.size == 1
649
+ csharp_get_empty_declaration(types.first, is_reference_type)
650
+
651
+ else
652
+ case types
653
+ when :binary, :short_binary
654
+ "new byte[0]"
655
+ when :int8, :int16, :int32, :sint32, :sint64, :dint63
656
+ "0"
657
+ when :string, :ip_number
658
+ "\"\""
659
+ else
660
+ if $all_structs[types]
661
+ types
662
+ else
663
+ raise "Unkown field type #{types}"
664
+ end
665
+ end
666
+ end
667
+ end
668
+
669
+ ##
670
+ # Generate C# data types declaration corresponding to each DSL type
671
+ # * DSL Type --> Corresponding C# Type
672
+ # * int8 --> byte
673
+ # * int16 --> ushort
674
+ # * int32 --> uint
675
+ # * sint32 --> int
676
+ # * sint64 --> long
677
+ # * string --> string
678
+ # * ip_number--> string
679
+ # * binary --> byte[]
680
+ # * short_binary --> byte[]
681
+ # * list --> List<type>
682
+ # * map --> Dictionary<keyType, valueType>
683
+
684
+ def csharp_get_type_declaration(types, is_reference_type = false)
685
+ if types.respond_to? :referenced_types
686
+ csharp_get_type_declaration(types.referenced_types, is_reference_type)
687
+
688
+ elsif types.respond_to?(:first) && types.size > 1
689
+ case types.first
690
+ when :list
691
+ subtypes = csharp_get_type_declaration(types[1], true)
692
+ return "List<#{subtypes}>"
693
+ when :map
694
+ key_type = csharp_get_type_declaration(types[1], true)
695
+ value_type = csharp_get_type_declaration(types[2], true)
696
+ return "Dictionary<#{key_type}, #{value_type}>"
697
+ else
698
+ raise "Missing serialization for #{types}"
699
+ end
700
+
701
+ elsif types.respond_to?(:first) && types.size == 1
702
+ csharp_get_type_declaration(types.first, is_reference_type)
703
+
704
+ else
705
+ case types
706
+ when :binary, :short_binary
707
+ "byte[]"
708
+ when :int8
709
+ "byte"
710
+ when :int16
711
+ "ushort"
712
+ when :int32
713
+ "uint"
714
+ when :sint32
715
+ "int"
716
+ when :sint64, :dint63
717
+ "long"
718
+ when :string, :ip_number
719
+ "string"
720
+ else
721
+ if $all_structs[types]
722
+ types
723
+ else
724
+ raise "Unkown field type #{types}"
725
+ end
726
+ end
727
+ end
728
+ end
729
+
730
+ ##
731
+ # Generate the way of serializing different DSL types
732
+ # * *Args* :
733
+ # - +var+ -> variable name
734
+ # - +types+ -> variable type
735
+ def csharp_serialize_internal(var, types)
736
+ if types.respond_to? :first
737
+ case types.first
738
+ when :list
739
+ nv = get_fresh_variable_name
740
+ idx = get_fresh_variable_name
741
+ return [
742
+ "writeInt32((uint)#{var}.Count, baos);",
743
+ "for(int #{idx}=0; #{idx}<#{var}.Count; #{idx}++) {",
744
+ :indent,
745
+ "#{csharp_get_type_declaration types[1]} #{nv} = #{var}[#{idx}];",
746
+ csharp_serialize_internal(nv, types[1]),
747
+ :deindent,
748
+ "}"
749
+ ]
750
+ when :map
751
+ nv1 = get_fresh_variable_name
752
+ nv2 = get_fresh_variable_name
753
+ return [
754
+ "writeInt32((uint)#{var}.Count, baos);",
755
+ "foreach (#{csharp_get_type_declaration types[1]} #{nv1} in #{var}.Keys) {",
756
+ :indent,
757
+ "#{csharp_get_type_declaration types[2]} #{nv2} = #{var}[#{nv1}];",
758
+ csharp_serialize_internal(nv1, types[1]),
759
+ csharp_serialize_internal(nv2, types[2]),
760
+ :deindent,
761
+ "}"
762
+ ]
763
+ else
764
+ raise "Missing serialization for #{var}"
765
+ end
766
+ else
767
+ if $all_structs[types]
768
+ "#{var}.serializeInternal(baos);"
769
+
770
+ elsif $available_types[types] && $available_types[types].ancestors.include?(SimpleDefinition)
771
+ "#{self.camelize "write", types}(#{var}, baos);"
772
+
773
+ else
774
+ raise "Missing code generation case #{types}"
775
+ end
776
+ end
777
+ end
778
+
779
+ ##
780
+ # Generate the way of deserializing different DSL types
781
+ # * *Args* :
782
+ # - +var+ -> variable name
783
+ # - +types+ -> variable type
784
+
785
+ def csharp_deserialize_internal(var, types)
786
+ if types.respond_to? :first
787
+ case types.first
788
+ when :list
789
+ count = get_fresh_variable_name
790
+ nv = get_fresh_variable_name
791
+ iter = get_fresh_variable_name
792
+ return [
793
+ "#{"#{csharp_get_type_declaration(types)} " unless var.include? "this."}#{var} = #{csharp_get_empty_declaration(types)};",
794
+ "uint #{count} = this.readInt32(bais);",
795
+ "for(int #{iter}=0; #{iter}<#{count}; #{iter}++) {",
796
+ :indent,
797
+ csharp_deserialize_internal(nv, types[1]),
798
+ "#{var}.Add(#{nv});",
799
+ :deindent,
800
+ "}"
801
+ ]
802
+ when :map
803
+ count = get_fresh_variable_name
804
+ nv1 = get_fresh_variable_name
805
+ nv2 = get_fresh_variable_name
806
+ iter = get_fresh_variable_name
807
+ return ["#{"#{csharp_get_type_declaration(types)} " unless var.include? "this."}#{var} = #{csharp_get_empty_declaration(types)};",
808
+ "uint #{count} = readInt32(bais);",
809
+ "for(int #{iter}=0; #{iter}<#{count}; #{iter}++) {",
810
+ :indent,
811
+ csharp_deserialize_internal(nv1, types[1]),
812
+ csharp_deserialize_internal(nv2, types[2]),
813
+ "#{var}.Add(#{nv1}, #{nv2});",
814
+ :deindent,
815
+ "}"
816
+ ]
817
+ else
818
+ raise "Missing serialization for #{var}"
819
+ end
820
+ else
821
+ # case types
822
+ # when :map
823
+ # "#{var} = #{csharp_get_empty_declaration(types)}"
824
+ # when :list
825
+ # "#{var} = #{csharp_get_empty_declaration(types)}"
826
+ # else
827
+ if $all_structs.key? types
828
+ [
829
+ "#{types} #{var} = new #{types}();",
830
+ "#{var}.deserialize(bais);"
831
+ ]
832
+ else
833
+ "#{"#{csharp_get_type_declaration(types)} " unless var.include? "this."}#{var} = #{self.camelize("read", types)}(bais);"
834
+ end
835
+ # end
836
+ end
837
+ end
838
+ end
839
+
840
+ ##
841
+ # Responsible for generating Divine and structs classes
842
+ #
843
+ class CsharpGenerator < CsharpHelperMethods
844
+
845
+ ##
846
+ # Generate csharp class(es)
847
+ # * *Args* :
848
+ # - +structs+ -> Dictionary of structs
849
+ # - +opts+ -> Dictionary that contains generation params [file, debug, parent_class, target_dir]
850
+
851
+ def generate_code(structs, opts)
852
+ $debug_csharp = true if opts[:debug]
853
+ base_template = Erubis::Eruby.new(csharp_base_class_template_str)
854
+ keys = structs.keys.sort
855
+ src = keys.map do |k|
856
+ ss = structs[k]
857
+ # Check different aspects the the structs
858
+ vss = sanity_check(ss)
859
+ csharp_class_template(StructHandler.new(vss))
860
+ end
861
+
862
+ # User defined super class?
863
+ toplevel = opts[:parent_class] || nil
864
+ toplevel = " : #{toplevel}" if toplevel
865
+
866
+ return [{
867
+ file: opts[:file],
868
+ src: "#{csharp_get_begin_module(opts)}#{base_template.result({ toplevel_class: toplevel })}\n\n#{src.join("\n\n")} }"
869
+ }]
870
+ end
871
+
872
+ ##
873
+ # Build header comments and list of imports
874
+
875
+ def csharp_get_begin_module(opts)
876
+ str = "#{get_header_comment}\n\n"
877
+ str << get_csharp_imports
878
+ str << "\n\n"
879
+ return str
880
+ end
881
+
882
+ ##
883
+ # Generate list of imports needed in generated C# classes
884
+ #
885
+ def get_csharp_imports
886
+ [
887
+ "System",
888
+ "System.Collections.Generic",
889
+ "System.Text.RegularExpressions",
890
+ "System.IO"
891
+ ].map do |i|
892
+ "using #{i};"
893
+ end.join("\n")
894
+ end
895
+ end
896
+
897
+ $language_generators[:csharp] = CsharpGenerator.new
898
+ end