divine 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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