eventmachine 0.12.2 → 0.12.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 (125) hide show
  1. data/Rakefile +191 -0
  2. data/{COPYING → docs/COPYING} +0 -0
  3. data/docs/ChangeLog +183 -0
  4. data/{DEFERRABLES → docs/DEFERRABLES} +1 -1
  5. data/{EPOLL → docs/EPOLL} +0 -0
  6. data/{GNU → docs/GNU} +0 -0
  7. data/docs/INSTALL +15 -0
  8. data/{KEYBOARD → docs/KEYBOARD} +0 -0
  9. data/{LEGAL → docs/LEGAL} +0 -0
  10. data/{LIGHTWEIGHT_CONCURRENCY → docs/LIGHTWEIGHT_CONCURRENCY} +1 -1
  11. data/{PURE_RUBY → docs/PURE_RUBY} +1 -1
  12. data/{README → docs/README} +1 -1
  13. data/{RELEASE_NOTES → docs/RELEASE_NOTES} +1 -1
  14. data/{SMTP → docs/SMTP} +1 -1
  15. data/{SPAWNED_PROCESSES → docs/SPAWNED_PROCESSES} +1 -1
  16. data/{TODO → docs/TODO} +1 -1
  17. data/ext/binder.cpp +1 -1
  18. data/ext/binder.h +1 -1
  19. data/ext/cmain.cpp +65 -74
  20. data/ext/cplusplus.cpp +1 -1
  21. data/ext/ed.cpp +41 -25
  22. data/ext/ed.h +10 -3
  23. data/ext/em.cpp +39 -12
  24. data/ext/em.h +13 -1
  25. data/ext/emwin.cpp +1 -1
  26. data/ext/emwin.h +1 -1
  27. data/ext/epoll.cpp +1 -1
  28. data/ext/epoll.h +1 -1
  29. data/ext/eventmachine.h +1 -1
  30. data/ext/eventmachine_cpp.h +1 -1
  31. data/ext/extconf.rb +86 -139
  32. data/ext/fastfilereader/extconf.rb +161 -0
  33. data/ext/fastfilereader/mapper.cpp +202 -0
  34. data/ext/fastfilereader/mapper.h +59 -0
  35. data/ext/fastfilereader/rubymain.cpp +127 -0
  36. data/ext/files.cpp +1 -1
  37. data/ext/files.h +1 -1
  38. data/ext/kb.cpp +1 -1
  39. data/ext/page.cpp +1 -1
  40. data/ext/page.h +1 -1
  41. data/ext/pipe.cpp +14 -7
  42. data/ext/project.h +1 -1
  43. data/ext/rubymain.cpp +56 -2
  44. data/ext/sigs.cpp +1 -1
  45. data/ext/sigs.h +1 -1
  46. data/ext/ssl.cpp +1 -1
  47. data/ext/ssl.h +1 -1
  48. data/java/src/com/rubyeventmachine/Application.java +196 -0
  49. data/java/src/com/rubyeventmachine/Connection.java +74 -0
  50. data/java/src/com/rubyeventmachine/ConnectionFactory.java +37 -0
  51. data/java/src/com/rubyeventmachine/DefaultConnectionFactory.java +46 -0
  52. data/java/src/com/rubyeventmachine/EmReactor.java +408 -0
  53. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  54. data/java/src/com/rubyeventmachine/EventableChannel.java +57 -0
  55. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +171 -0
  56. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +244 -0
  57. data/java/src/com/rubyeventmachine/PeriodicTimer.java +38 -0
  58. data/java/src/com/rubyeventmachine/Timer.java +54 -0
  59. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +108 -0
  60. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +124 -0
  61. data/java/src/com/rubyeventmachine/tests/EMTest.java +80 -0
  62. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +53 -0
  63. data/java/src/com/rubyeventmachine/tests/TestServers.java +74 -0
  64. data/java/src/com/rubyeventmachine/tests/TestTimers.java +89 -0
  65. data/lib/em/deferrable.rb +1 -1
  66. data/lib/em/eventable.rb +1 -1
  67. data/lib/em/future.rb +1 -1
  68. data/lib/em/messages.rb +1 -1
  69. data/lib/em/processes.rb +1 -1
  70. data/lib/em/spawnable.rb +1 -1
  71. data/lib/em/streamer.rb +1 -1
  72. data/lib/eventmachine.rb +138 -116
  73. data/lib/eventmachine_version.rb +2 -2
  74. data/lib/evma.rb +1 -1
  75. data/lib/evma/callback.rb +1 -1
  76. data/lib/evma/container.rb +1 -1
  77. data/lib/evma/factory.rb +1 -1
  78. data/lib/evma/protocol.rb +1 -1
  79. data/lib/evma/reactor.rb +1 -1
  80. data/lib/jeventmachine.rb +7 -2
  81. data/lib/pr_eventmachine.rb +2 -2
  82. data/lib/protocols/buftok.rb +1 -1
  83. data/lib/protocols/header_and_content.rb +1 -1
  84. data/lib/protocols/httpcli2.rb +16 -6
  85. data/lib/protocols/httpclient.rb +7 -1
  86. data/lib/protocols/line_and_text.rb +6 -2
  87. data/lib/protocols/linetext2.rb +12 -14
  88. data/lib/protocols/postgres.rb +1 -1
  89. data/lib/protocols/saslauth.rb +1 -1
  90. data/lib/protocols/smtpclient.rb +1 -1
  91. data/lib/protocols/smtpserver.rb +36 -23
  92. data/lib/protocols/stomp.rb +24 -1
  93. data/lib/protocols/tcptest.rb +1 -1
  94. data/tasks/cpp.rake +77 -0
  95. data/tasks/project.rake +78 -0
  96. data/tasks/tests.rake +192 -0
  97. data/tests/test_attach.rb +1 -1
  98. data/tests/test_basic.rb +65 -12
  99. data/tests/test_defer.rb +13 -29
  100. data/tests/test_epoll.rb +16 -21
  101. data/tests/test_errors.rb +1 -1
  102. data/tests/test_eventables.rb +4 -5
  103. data/tests/test_exc.rb +1 -1
  104. data/tests/test_futures.rb +1 -1
  105. data/tests/test_hc.rb +185 -236
  106. data/tests/test_httpclient.rb +1 -1
  107. data/tests/test_httpclient2.rb +28 -6
  108. data/tests/test_kb.rb +2 -2
  109. data/tests/test_ltp.rb +139 -141
  110. data/tests/test_ltp2.rb +1 -1
  111. data/tests/test_next_tick.rb +1 -1
  112. data/tests/test_processes.rb +1 -1
  113. data/tests/test_pure.rb +3 -2
  114. data/tests/test_running.rb +1 -1
  115. data/tests/test_sasl.rb +1 -1
  116. data/tests/test_send_file.rb +56 -51
  117. data/tests/test_servers.rb +26 -36
  118. data/tests/test_smtpclient.rb +46 -44
  119. data/tests/test_smtpserver.rb +1 -1
  120. data/tests/test_spawn.rb +1 -1
  121. data/tests/test_ssl_args.rb +68 -0
  122. data/tests/test_timers.rb +15 -9
  123. data/tests/test_ud.rb +1 -1
  124. data/tests/testem.rb +30 -4
  125. metadata +69 -34
@@ -0,0 +1,78 @@
1
+ require 'rake/gempackagetask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/clean'
4
+
5
+ # monkey bitchin' for windows stuffs...
6
+ module FileUtils
7
+ # If any of these methods ever clobber, try removing them.
8
+ # Hopefully they'll do something semantically similar.
9
+ abort "Err: #{__FILE__}:#{__LINE__} monkey patch windows? clobbers!" unless instance_methods.grep(/windows\?/).empty?
10
+ abort "Err: #{__FILE__}:#{__LINE__} monkey patch sudo clobbers!" unless instance_methods.grep(/sudo/).empty?
11
+ abort "Err: #{__FILE__}:#{__LINE__} monkey patch gem_cmd clobbers!" unless instance_methods.grep(/gem_cmd/).empty?
12
+ def windows?; RUBY_PLATFORM =~ /mswin|mingw/; end
13
+ def sudo(cmd)
14
+ if windows? || (require 'etc'; Etc.getpwuid.uid == 0)
15
+ sh cmd
16
+ else
17
+ sh "sudo #{cmd}"
18
+ end
19
+ end
20
+ def gem_cmd(action, name, *args)
21
+ rb = Gem.ruby rescue nil
22
+ rb ||= (require 'rbconfig'; File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']))
23
+ sudo "#{rb} -r rubygems -e 'require %{rubygems/gem_runner}; Gem::GemRunner.new.run(%w{#{action} #{name} #{args.join(' ')}})'"
24
+ end
25
+ end
26
+
27
+
28
+ # Setup our packaging tasks, we're just jacking the builtins.
29
+ Rake::GemPackageTask.new(Spec) do |pkg|
30
+ pkg.need_tar, pkg.need_tar_gz, pkg.need_zip = true, true, true if Package
31
+ pkg.gem_spec = Spec
32
+ end
33
+ Rake::Task[:clean].enhance [:clobber_package]
34
+
35
+ # Only generate rdoc if the spec says so, again, jack the builtins.
36
+ if Spec.has_rdoc
37
+ Rake::RDocTask.new do |rd|
38
+ rd.title = Spec.name
39
+ rd.rdoc_dir = 'rdoc'
40
+ rd.main = "docs/README" if test ?e, "docs/README"
41
+ rd.rdoc_files.include("lib/**/*.rb", *Spec.extra_rdoc_files)
42
+ end
43
+ Rake::Task[:clean].enhance [:clobber_rdoc]
44
+
45
+ desc 'Generate and open documentation'
46
+ task :docs => :rdoc do
47
+ case RUBY_PLATFORM
48
+ when /darwin/ ; sh 'open rdoc/index.html'
49
+ when /mswin|mingw/ ; sh 'start rdoc\index.html'
50
+ else
51
+ sh 'firefox rdoc/index.html'
52
+ end
53
+ end
54
+ end
55
+
56
+ if Spec.default_executable
57
+ desc "Run #{Spec.default_executable}"
58
+ task :run do ruby File.join(Spec.bindir, Spec.default_executable) end
59
+ end
60
+
61
+ require 'rubygems'
62
+
63
+ desc 'Install gem (and sudo if required)'
64
+ task :install => :package do
65
+ gem_cmd(:install, "pkg/#{Spec.name}-#{Spec.version}.gem")
66
+ end
67
+
68
+ desc 'Uninstall gem (and sudo if required)'
69
+ task :uninstall do
70
+ gem_cmd(:uninstall, "#{Spec.name}", "-v=#{Spec.version}")
71
+ end
72
+
73
+ # Find an scm's store directory, if we do, make a task to commit to it only
74
+ # after running all the tests (successfully).
75
+ if scm = %w(git svn bzr hg).find { |d| File.directory? ".#{d}" }
76
+ desc "Run tests then commit to #{scm}"
77
+ task :commit => :test do sh "#{scm} commit" end
78
+ end
@@ -0,0 +1,192 @@
1
+ # This is used by several rake tasks, that parameterize the
2
+ # behavior so we can use the same tests to test both the
3
+ # extension and non-extension versions.
4
+ def run_tests t, libr = :cascade, test_files="test_*.rb"
5
+ require 'test/unit/testsuite'
6
+ require 'test/unit/ui/console/testrunner'
7
+ require 'tests/testem'
8
+
9
+ base_dir = File.expand_path(File.dirname(__FILE__) + '/../') + '/'
10
+
11
+ runner = Test::Unit::UI::Console::TestRunner
12
+
13
+ $eventmachine_library = libr
14
+ EmTestRunner.run(test_files)
15
+
16
+ suite = Test::Unit::TestSuite.new($name)
17
+
18
+ ObjectSpace.each_object(Class) do |testcase|
19
+ suite << testcase.suite if testcase < Test::Unit::TestCase
20
+ end
21
+
22
+ runner.run(suite)
23
+ end
24
+
25
+ desc "Run tests for #{Spec.name}."
26
+ task :test do |t|
27
+ # run_tests t
28
+ # Rake +/ friends leave threads, etc, less stable test runs.
29
+ ruby "-Ilib -Iext -Iext/fastfilereader -Ijava tests/testem.rb #{'-v' if ENV['VERBOSE']}"
30
+ end
31
+
32
+ namespace :test do
33
+ desc "Run tests for #{Spec.name}."
34
+ task :partial do |t|
35
+ lib = RUBY_PLATFORM =~ /java/ ? :java : :extension
36
+ run_tests t, lib, [
37
+ "test_basic.rb",
38
+ "test_epoll.rb",
39
+ "test_errors.rb",
40
+ "test_eventables.rb",
41
+ "test_exc.rb",
42
+ "test_futures.rb",
43
+ "test_hc.rb",
44
+ "test_httpclient2.rb",
45
+ "test_httpclient.rb",
46
+ "test_kb.rb",
47
+ "test_ltp2.rb",
48
+ "test_ltp.rb",
49
+ "test_next_tick.rb",
50
+ "test_processes.rb",
51
+ "test_pure.rb",
52
+ "test_running.rb",
53
+ "test_sasl.rb",
54
+ "test_send_file.rb",
55
+ "test_servers.rb",
56
+ "test_smtpclient.rb",
57
+ "test_smtpserver.rb",
58
+ "test_spawn.rb",
59
+ "test_timers.rb",
60
+ "test_ud.rb",
61
+ ].map { |tf| "tests/#{tf}" }
62
+ end
63
+
64
+ desc "Run java tests for #$name."
65
+ task :testjava do |t|
66
+ run_tests t, :java
67
+ end
68
+
69
+ desc "Run pure-ruby tests for #$name."
70
+ task :testpr do |t|
71
+ run_tests t, :pure_ruby
72
+ end
73
+
74
+ desc "Run extension tests for #$name."
75
+ task :testext do |t|
76
+ run_tests t, :extension
77
+ end
78
+
79
+ desc "PROVISIONAL: run tests for user-defined events"
80
+ task :ud do |t|
81
+ run_tests t, :extension, "test_ud.rb"
82
+ end
83
+
84
+ desc "PROVISIONAL: run tests for line/text protocol handler"
85
+ task :ltp do |t|
86
+ run_tests t, :extension, "test_ltp*.rb"
87
+ end
88
+
89
+ desc "PROVISIONAL: run tests for header/content protocol handler"
90
+ task :hc do |t|
91
+ run_tests t, :extension, "test_hc.rb"
92
+ end
93
+
94
+ desc "PROVISIONAL: run tests for exceptions"
95
+ task :exc do |t|
96
+ run_tests t, :extension, "test_exc.rb"
97
+ end
98
+
99
+ desc "Test protocol handlers"
100
+ task :protocols => [ :hc, :ltp ]
101
+
102
+
103
+ desc "Test HTTP client"
104
+ task :httpclient do |t|
105
+ run_tests t, :extension, "test_httpclient.rb"
106
+ end
107
+
108
+ desc "Test HTTP client2"
109
+ task :httpclient2 do |t|
110
+ run_tests t, :extension, "test_httpclient2.rb"
111
+ end
112
+
113
+ desc "Test futures"
114
+ task :futures do |t|
115
+ run_tests t, :extension, "test_future*.rb"
116
+ end
117
+
118
+ desc "Test Timers"
119
+ task :timers do |t|
120
+ run_tests t, :extension, "test_timer*.rb"
121
+ end
122
+
123
+ desc "Test Next Tick"
124
+ task :next_tick do |t|
125
+ run_tests t, :extension, "test_next_tick*.rb"
126
+ end
127
+
128
+ desc "Test Epoll"
129
+ task :epoll do |t|
130
+ run_tests t, :extension, "test_epoll*.rb"
131
+ end
132
+
133
+ desc "Test Servers"
134
+ task :servers do |t|
135
+ run_tests t, :extension, "test_servers*.rb"
136
+ end
137
+
138
+ desc "Test Basic"
139
+ task :basic do |t|
140
+ run_tests t, :extension, "test_basic*.rb"
141
+ end
142
+
143
+ desc "Test Send File"
144
+ task :send_file do |t|
145
+ run_tests t, :extension, "test_send_file*.rb"
146
+ end
147
+
148
+ desc "Test Running"
149
+ task :running do |t|
150
+ run_tests t, :extension, "test_running*.rb"
151
+ end
152
+
153
+ desc "Test Keyboard Events"
154
+ task :keyboard do |t|
155
+ run_tests t, :extension, "test_kb*.rb"
156
+ end
157
+
158
+ desc "Test Spawn"
159
+ task :spawn do |t|
160
+ run_tests t, :extension, "test_spawn*.rb"
161
+ end
162
+
163
+ desc "Test SMTP"
164
+ task :smtp do |t|
165
+ run_tests t, :extension, "test_smtp*.rb"
166
+ end
167
+
168
+ desc "Test Errors"
169
+ task :errors do |t|
170
+ run_tests t, :extension, "test_errors*.rb"
171
+ end
172
+
173
+ desc "Test Pure Ruby"
174
+ task :pure do |t|
175
+ run_tests t, :extension, "test_pure*.rb"
176
+ end
177
+
178
+ desc "Test Processes"
179
+ task :processes do |t|
180
+ run_tests t, :extension, "test_process*.rb"
181
+ end
182
+
183
+ desc "Test SASL"
184
+ task :sasl do |t|
185
+ run_tests t, :java, "test_sasl*.rb"
186
+ end
187
+
188
+ desc "Test Attach"
189
+ task :attach do |t|
190
+ run_tests t, :extension, "test_attach*.rb"
191
+ end
192
+ end
@@ -1,4 +1,4 @@
1
- # $Id: test_attach.rb 785 2008-09-15 09:46:23Z francis $
1
+ # $Id$
2
2
  #
3
3
  #----------------------------------------------------------------------------
4
4
  #
@@ -1,4 +1,4 @@
1
- # $Id: test_basic.rb 735 2008-07-05 18:18:49Z francis $
1
+ # $Id$
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -24,23 +24,35 @@
24
24
  #
25
25
  #
26
26
 
27
- $:.unshift "../lib"
27
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
28
28
  require 'eventmachine'
29
29
  require 'test/unit'
30
30
 
31
31
  class TestBasic < Test::Unit::TestCase
32
32
 
33
33
  def setup
34
+ assert(!EM.reactor_running?)
34
35
  end
35
36
 
36
37
  def teardown
38
+ assert(!EM.reactor_running?)
37
39
  end
38
40
 
39
41
  #-------------------------------------
40
42
 
41
43
  def test_libtype
42
44
  lt = EventMachine.library_type
43
- case (ENV["EVENTMACHINE_LIBRARY"] || $eventmachine_library || :xxx).to_sym
45
+ em_lib = (ENV["EVENTMACHINE_LIBRARY"] || $eventmachine_library || :xxx).to_sym
46
+
47
+ # Running from test runner, under jruby.
48
+ if RUBY_PLATFORM == 'java'
49
+ unless em_lib == :pure_ruby
50
+ assert_equal( :java, lt )
51
+ return
52
+ end
53
+ end
54
+
55
+ case em_lib
44
56
  when :pure_ruby
45
57
  assert_equal( :pure_ruby, lt )
46
58
  when :extension
@@ -48,7 +60,12 @@ class TestBasic < Test::Unit::TestCase
48
60
  when :java
49
61
  assert_equal( :java, lt )
50
62
  else
51
- assert_equal( :extension, lt )
63
+ # Running from jruby as a standalone test.
64
+ if RUBY_PLATFORM == 'java'
65
+ assert_equal( :java, lt )
66
+ else
67
+ assert_equal( :extension, lt )
68
+ end
52
69
  end
53
70
  end
54
71
 
@@ -68,7 +85,7 @@ class TestBasic < Test::Unit::TestCase
68
85
  def test_timer
69
86
  n = 0
70
87
  EventMachine.run {
71
- EventMachine.add_periodic_timer(1) {
88
+ EventMachine.add_periodic_timer(0.1) {
72
89
  n += 1
73
90
  EventMachine.stop if n == 2
74
91
  }
@@ -98,9 +115,11 @@ class TestBasic < Test::Unit::TestCase
98
115
  # the loop automatically. Contrast with EventMachine#run, which keeps running the reactor
99
116
  # even after the supplied block completes.
100
117
  def test_run_block
101
- a = nil
102
- EM.run_block { a = "Worked" }
103
- assert a
118
+ assert !EM.reactor_running?
119
+ a = nil
120
+ EM.run_block { a = "Worked" }
121
+ assert a
122
+ assert !EM.reactor_running?
104
123
  end
105
124
 
106
125
 
@@ -137,7 +156,6 @@ class TestBasic < Test::Unit::TestCase
137
156
  }
138
157
  end
139
158
 
140
-
141
159
  #------------------------------------
142
160
  #
143
161
  # TODO. This is an unfinished bug fix.
@@ -163,16 +181,51 @@ class TestBasic < Test::Unit::TestCase
163
181
  aaa bbb # should produce a Ruby exception
164
182
  end
165
183
  end
166
- def test_post_init_error
167
- assert_raise( NameError ) {
184
+ # This test causes issues, the machine becomes unreleasable after
185
+ # release_machine suffers an exception in event_callback.
186
+ def xxx_test_post_init_error
187
+ assert_raise( EventMachine::ConnectionNotBound ) {
168
188
  EM.run {
169
189
  EM::Timer.new(1) {EM.stop}
170
190
  EM.start_server TestHost, TestPort
171
191
  EM.connect TestHost, TestPort, PostInitError
172
192
  }
173
193
  }
194
+ EM.run {
195
+ EM.stop
196
+ }
197
+ assert !EM.reactor_running?
198
+ end
199
+
200
+ module BrsTestSrv
201
+ def receive_data data
202
+ $received << data
203
+ end
204
+ def unbind
205
+ EM.stop
206
+ end
207
+ end
208
+ module BrsTestCli
209
+ def post_init
210
+ send_data $sent
211
+ close_connection_after_writing
212
+ end
213
+ end
214
+
215
+ # From ticket #50
216
+ def test_byte_range_send
217
+ $received = ''
218
+ $sent = (0..255).to_a.pack('C*')
219
+ EM::run {
220
+
221
+ EM::start_server TestHost, TestPort, BrsTestSrv
222
+
223
+ EM::connect TestHost, TestPort, BrsTestCli
224
+
225
+ EM::add_timer(0.5) { assert(false, 'test timed out'); EM.stop; Kernel.warn "test timed out!" }
226
+ }
227
+ assert_equal($sent, $received)
174
228
  end
175
-
176
229
 
177
230
  end
178
231
 
@@ -1,4 +1,4 @@
1
- # $Id: test_defer.rb 668 2008-01-04 23:00:34Z blackhedd $
1
+ # $Id$
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -30,34 +30,18 @@ require 'test/unit'
30
30
 
31
31
  class TestDeferUsage < Test::Unit::TestCase
32
32
 
33
- def setup
34
- end
35
-
36
- def teardown
37
- end
38
-
39
- def run_em_with_defers
40
- n = 0
41
- n_times = 20
42
- EM.run {
43
- n_times.times {
44
- EM.defer proc {
45
- sleep 0.1
46
- }, proc {
47
- n += 1
48
- EM.stop if n == n_times
49
- }
50
- }
51
- }
52
- assert_equal( n, n_times )
53
- end
54
- def test_defers
55
- 10.times {
56
- run_em_with_defers {|n,ntimes|
57
- assert_equal( n, ntimes )
58
- }
59
- }
60
- end
33
+ def test_defers
34
+ n = 0
35
+ n_times = 20
36
+ EM.run {
37
+ n_times.times {
38
+ work_proc = proc { n += 1 }
39
+ callback = proc { EM.stop if n == n_times }
40
+ EM.defer work_proc, callback
41
+ }
42
+ }
43
+ assert_equal( n, n_times )
44
+ end unless RUBY_VERSION >= '1.9.0'
61
45
 
62
46
  end
63
47