puma 4.3.10 → 5.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +58 -31
  3. data/LICENSE +23 -20
  4. data/README.md +17 -11
  5. data/docs/deployment.md +3 -1
  6. data/docs/fork_worker.md +31 -0
  7. data/docs/jungle/README.md +13 -0
  8. data/{tools → docs}/jungle/rc.d/README.md +0 -0
  9. data/{tools → docs}/jungle/rc.d/puma +0 -0
  10. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  11. data/{tools → docs}/jungle/upstart/README.md +0 -0
  12. data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
  13. data/{tools → docs}/jungle/upstart/puma.conf +0 -0
  14. data/docs/signals.md +1 -0
  15. data/docs/systemd.md +1 -63
  16. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  17. data/ext/puma_http11/extconf.rb +3 -2
  18. data/ext/puma_http11/http11_parser.c +11 -26
  19. data/ext/puma_http11/http11_parser.rl +1 -3
  20. data/ext/puma_http11/http11_parser_common.rl +1 -1
  21. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  22. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +46 -48
  23. data/ext/puma_http11/puma_http11.c +2 -38
  24. data/lib/puma/app/status.rb +16 -5
  25. data/lib/puma/binder.rb +62 -60
  26. data/lib/puma/cli.rb +7 -15
  27. data/lib/puma/client.rb +35 -32
  28. data/lib/puma/cluster.rb +179 -74
  29. data/lib/puma/configuration.rb +30 -42
  30. data/lib/puma/const.rb +2 -3
  31. data/lib/puma/control_cli.rb +27 -17
  32. data/lib/puma/detect.rb +8 -0
  33. data/lib/puma/dsl.rb +70 -34
  34. data/lib/puma/io_buffer.rb +9 -2
  35. data/lib/puma/jruby_restart.rb +0 -58
  36. data/lib/puma/launcher.rb +41 -29
  37. data/lib/puma/minissl.rb +13 -8
  38. data/lib/puma/null_io.rb +1 -1
  39. data/lib/puma/plugin.rb +1 -10
  40. data/lib/puma/rack/builder.rb +0 -4
  41. data/lib/puma/reactor.rb +6 -1
  42. data/lib/puma/runner.rb +5 -34
  43. data/lib/puma/server.rb +70 -190
  44. data/lib/puma/single.rb +7 -64
  45. data/lib/puma/state_file.rb +5 -2
  46. data/lib/puma/thread_pool.rb +85 -47
  47. data/lib/puma.rb +4 -0
  48. data/lib/rack/handler/puma.rb +1 -3
  49. data/tools/{docker/Dockerfile → Dockerfile} +0 -0
  50. metadata +22 -26
  51. data/docs/tcp_mode.md +0 -96
  52. data/ext/puma_http11/io_buffer.c +0 -155
  53. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  54. data/lib/puma/tcp_logger.rb +0 -41
  55. data/tools/jungle/README.md +0 -19
  56. data/tools/jungle/init.d/README.md +0 -61
  57. data/tools/jungle/init.d/puma +0 -421
  58. data/tools/jungle/init.d/run-puma +0 -18
@@ -47,6 +47,7 @@ module Puma
47
47
  @shutdown = false
48
48
 
49
49
  @trim_requested = 0
50
+ @out_of_band_pending = false
50
51
 
51
52
  @workers = []
52
53
 
@@ -54,7 +55,10 @@ module Puma
54
55
  @reaper = nil
55
56
 
56
57
  @mutex.synchronize do
57
- @min.times { spawn_thread }
58
+ @min.times do
59
+ spawn_thread
60
+ @not_full.wait(@mutex)
61
+ end
58
62
  end
59
63
 
60
64
  @clean_thread_locals = false
@@ -62,6 +66,7 @@ module Puma
62
66
 
63
67
  attr_reader :spawned, :trim_requested, :waiting
64
68
  attr_accessor :clean_thread_locals
69
+ attr_accessor :out_of_band_hook
65
70
 
66
71
  def self.clean_thread_locals
67
72
  Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods
@@ -72,13 +77,17 @@ module Puma
72
77
  # How many objects have yet to be processed by the pool?
73
78
  #
74
79
  def backlog
75
- @mutex.synchronize { @todo.size }
80
+ with_mutex { @todo.size }
76
81
  end
77
82
 
78
83
  def pool_capacity
79
84
  waiting + (@max - spawned)
80
85
  end
81
86
 
87
+ def busy_threads
88
+ with_mutex { @spawned - @waiting + @todo.size }
89
+ end
90
+
82
91
  # :nodoc:
83
92
  #
84
93
  # Must be called with @mutex held!
@@ -99,48 +108,37 @@ module Puma
99
108
  while true
100
109
  work = nil
101
110
 
102
- continue = true
103
-
104
111
  mutex.synchronize do
105
112
  while todo.empty?
106
113
  if @trim_requested > 0
107
114
  @trim_requested -= 1
108
- continue = false
109
- not_full.signal
110
- break
111
- end
112
-
113
- if @shutdown
114
- continue = false
115
- break
115
+ @spawned -= 1
116
+ @workers.delete th
117
+ Thread.exit
116
118
  end
117
119
 
118
120
  @waiting += 1
121
+ if @out_of_band_pending && trigger_out_of_band_hook
122
+ @out_of_band_pending = false
123
+ end
119
124
  not_full.signal
120
125
  not_empty.wait mutex
121
126
  @waiting -= 1
122
127
  end
123
128
 
124
- work = todo.shift if continue
129
+ work = todo.shift
125
130
  end
126
131
 
127
- break unless continue
128
-
129
132
  if @clean_thread_locals
130
133
  ThreadPool.clean_thread_locals
131
134
  end
132
135
 
133
136
  begin
134
- block.call(work, *extra)
137
+ @out_of_band_pending = true if block.call(work, *extra)
135
138
  rescue Exception => e
136
139
  STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
137
140
  end
138
141
  end
139
-
140
- mutex.synchronize do
141
- @spawned -= 1
142
- @workers.delete th
143
- end
144
142
  end
145
143
 
146
144
  @workers << th
@@ -150,9 +148,30 @@ module Puma
150
148
 
151
149
  private :spawn_thread
152
150
 
151
+ def trigger_out_of_band_hook
152
+ return false unless out_of_band_hook && out_of_band_hook.any?
153
+
154
+ # we execute on idle hook when all threads are free
155
+ return false unless @spawned == @waiting
156
+
157
+ out_of_band_hook.each(&:call)
158
+ true
159
+ rescue Exception => e
160
+ STDERR.puts "Exception calling out_of_band_hook: #{e.message} (#{e.class})"
161
+ true
162
+ end
163
+
164
+ private :trigger_out_of_band_hook
165
+
166
+ def with_mutex(&block)
167
+ @mutex.owned? ?
168
+ yield :
169
+ @mutex.synchronize(&block)
170
+ end
171
+
153
172
  # Add +work+ to the todo list for a Thread to pickup and process.
154
173
  def <<(work)
155
- @mutex.synchronize do
174
+ with_mutex do
156
175
  if @shutdown
157
176
  raise "Unable to add work while shutting down"
158
177
  end
@@ -193,11 +212,8 @@ module Puma
193
212
  # method would not block and another request would be added into the reactor
194
213
  # by the server. This would continue until a fully bufferend request
195
214
  # makes it through the reactor and can then be processed by the thread pool.
196
- #
197
- # Returns the current number of busy threads, or +nil+ if shutting down.
198
- #
199
215
  def wait_until_not_full
200
- @mutex.synchronize do
216
+ with_mutex do
201
217
  while true
202
218
  return if @shutdown
203
219
 
@@ -205,21 +221,40 @@ module Puma
205
221
  # is work queued that cannot be handled by waiting
206
222
  # threads, then accept more work until we would
207
223
  # spin up the max number of threads.
208
- busy_threads = @spawned - @waiting + @todo.size
209
- return busy_threads if @max > busy_threads
224
+ return if busy_threads < @max
210
225
 
211
226
  @not_full.wait @mutex
212
227
  end
213
228
  end
214
229
  end
215
230
 
216
- # If too many threads are in the pool, tell one to finish go ahead
231
+ def wait_for_less_busy_worker(delay_s)
232
+ # Ruby MRI does GVL, this can result
233
+ # in processing contention when multiple threads
234
+ # (requests) are running concurrently
235
+ return unless Puma.mri?
236
+ return unless delay_s > 0
237
+
238
+ with_mutex do
239
+ return if @shutdown
240
+
241
+ # do not delay, if we are not busy
242
+ return unless busy_threads > 0
243
+
244
+ # this will be signaled once a request finishes,
245
+ # which can happen earlier than delay
246
+ @not_full.wait @mutex, delay_s
247
+ end
248
+ end
249
+
250
+ # If there are any free threads in the pool, tell one to go ahead
217
251
  # and exit. If +force+ is true, then a trim request is requested
218
252
  # even if all threads are being utilized.
219
253
  #
220
254
  def trim(force=false)
221
- @mutex.synchronize do
222
- if (force or @waiting > 0) and @spawned - @trim_requested > @min
255
+ with_mutex do
256
+ free = @waiting - @todo.size
257
+ if (force or free > 0) and @spawned - @trim_requested > @min
223
258
  @trim_requested += 1
224
259
  @not_empty.signal
225
260
  end
@@ -229,7 +264,7 @@ module Puma
229
264
  # If there are dead threads in the pool make them go away while decreasing
230
265
  # spawned counter so that new healthy threads could be created again.
231
266
  def reap
232
- @mutex.synchronize do
267
+ with_mutex do
233
268
  dead_workers = @workers.reject(&:alive?)
234
269
 
235
270
  dead_workers.each do |worker|
@@ -281,10 +316,14 @@ module Puma
281
316
  end
282
317
 
283
318
  # Tell all threads in the pool to exit and wait for them to finish.
319
+ # Wait +timeout+ seconds then raise +ForceShutdown+ in remaining threads.
320
+ # Next, wait an extra +grace+ seconds then force-kill remaining threads.
321
+ # Finally, wait +kill_grace+ seconds for remaining threads to exit.
284
322
  #
285
323
  def shutdown(timeout=-1)
286
- threads = @mutex.synchronize do
324
+ threads = with_mutex do
287
325
  @shutdown = true
326
+ @trim_requested = @spawned
288
327
  @not_empty.broadcast
289
328
  @not_full.broadcast
290
329
 
@@ -298,27 +337,26 @@ module Puma
298
337
  # Wait for threads to finish without force shutdown.
299
338
  threads.each(&:join)
300
339
  else
301
- # Wait for threads to finish after n attempts (+timeout+).
302
- # If threads are still running, it will forcefully kill them.
303
- timeout.times do
304
- threads.delete_if do |t|
305
- t.join 1
306
- end
307
-
308
- if threads.empty?
309
- break
310
- else
311
- sleep 1
340
+ join = ->(inner_timeout) do
341
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
342
+ threads.reject! do |t|
343
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
344
+ t.join inner_timeout - elapsed
312
345
  end
313
346
  end
314
347
 
348
+ # Wait +timeout+ seconds for threads to finish.
349
+ join.call(timeout)
350
+
351
+ # If threads are still running, raise ForceShutdown and wait to finish.
315
352
  threads.each do |t|
316
353
  t.raise ForceShutdown
317
354
  end
355
+ join.call(SHUTDOWN_GRACE_TIME)
318
356
 
319
- threads.each do |t|
320
- t.join SHUTDOWN_GRACE_TIME
321
- end
357
+ # If threads are _still_ running, forcefully kill them and wait to finish.
358
+ threads.each(&:kill)
359
+ join.call(1)
322
360
  end
323
361
 
324
362
  @spawned = 0
data/lib/puma.rb CHANGED
@@ -20,6 +20,10 @@ module Puma
20
20
  end
21
21
 
22
22
  def self.stats
23
+ @get_stats.stats.to_json
24
+ end
25
+
26
+ def self.stats_hash
23
27
  @get_stats.stats
24
28
  end
25
29
 
@@ -30,8 +30,6 @@ module Rack
30
30
  end
31
31
 
32
32
  conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
33
- user_config.quiet
34
-
35
33
  if options.delete(:Verbose)
36
34
  app = Rack::CommonLogger.new(app, STDOUT)
37
35
  end
@@ -61,7 +59,7 @@ module Rack
61
59
  conf
62
60
  end
63
61
 
64
- def self.run(app, options = {})
62
+ def self.run(app, **options)
65
63
  conf = self.config(app, options)
66
64
 
67
65
  events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
File without changes
metadata CHANGED
@@ -1,24 +1,24 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.10
4
+ version: 5.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-12 00:00:00.000000000 Z
11
+ date: 2020-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: nio4r
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
17
  - - "~>"
17
18
  - !ruby/object:Gem::Version
18
19
  version: '2.0'
19
- name: nio4r
20
- prerelease: false
21
20
  type: :runtime
21
+ prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
@@ -45,15 +45,22 @@ files:
45
45
  - bin/pumactl
46
46
  - docs/architecture.md
47
47
  - docs/deployment.md
48
+ - docs/fork_worker.md
48
49
  - docs/images/puma-connection-flow-no-reactor.png
49
50
  - docs/images/puma-connection-flow.png
50
51
  - docs/images/puma-general-arch.png
52
+ - docs/jungle/README.md
53
+ - docs/jungle/rc.d/README.md
54
+ - docs/jungle/rc.d/puma
55
+ - docs/jungle/rc.d/puma.conf
56
+ - docs/jungle/upstart/README.md
57
+ - docs/jungle/upstart/puma-manager.conf
58
+ - docs/jungle/upstart/puma.conf
51
59
  - docs/nginx.md
52
60
  - docs/plugins.md
53
61
  - docs/restart.md
54
62
  - docs/signals.md
55
63
  - docs/systemd.md
56
- - docs/tcp_mode.md
57
64
  - ext/puma_http11/PumaHttp11Service.java
58
65
  - ext/puma_http11/ext_help.h
59
66
  - ext/puma_http11/extconf.rb
@@ -62,11 +69,9 @@ files:
62
69
  - ext/puma_http11/http11_parser.java.rl
63
70
  - ext/puma_http11/http11_parser.rl
64
71
  - ext/puma_http11/http11_parser_common.rl
65
- - ext/puma_http11/io_buffer.c
66
72
  - ext/puma_http11/mini_ssl.c
67
73
  - ext/puma_http11/org/jruby/puma/Http11.java
68
74
  - ext/puma_http11/org/jruby/puma/Http11Parser.java
69
- - ext/puma_http11/org/jruby/puma/IOBuffer.java
70
75
  - ext/puma_http11/org/jruby/puma/MiniSSL.java
71
76
  - ext/puma_http11/puma_http11.c
72
77
  - lib/puma.rb
@@ -99,29 +104,20 @@ files:
99
104
  - lib/puma/server.rb
100
105
  - lib/puma/single.rb
101
106
  - lib/puma/state_file.rb
102
- - lib/puma/tcp_logger.rb
103
107
  - lib/puma/thread_pool.rb
104
108
  - lib/puma/util.rb
105
109
  - lib/rack/handler/puma.rb
106
- - tools/docker/Dockerfile
107
- - tools/jungle/README.md
108
- - tools/jungle/init.d/README.md
109
- - tools/jungle/init.d/puma
110
- - tools/jungle/init.d/run-puma
111
- - tools/jungle/rc.d/README.md
112
- - tools/jungle/rc.d/puma
113
- - tools/jungle/rc.d/puma.conf
114
- - tools/jungle/upstart/README.md
115
- - tools/jungle/upstart/puma-manager.conf
116
- - tools/jungle/upstart/puma.conf
110
+ - tools/Dockerfile
117
111
  - tools/trickletest.rb
118
112
  homepage: http://puma.io
119
113
  licenses:
120
114
  - BSD-3-Clause
121
115
  metadata:
122
- msys2_mingw_dependencies: openssl
116
+ bug_tracker_uri: https://github.com/puma/puma/issues
123
117
  changelog_uri: https://github.com/puma/puma/blob/master/History.md
124
- post_install_message:
118
+ homepage_uri: http://puma.io
119
+ source_code_uri: https://github.com/puma/puma
120
+ post_install_message:
125
121
  rdoc_options: []
126
122
  require_paths:
127
123
  - lib
@@ -132,12 +128,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
128
  version: '2.2'
133
129
  required_rubygems_version: !ruby/object:Gem::Requirement
134
130
  requirements:
135
- - - ">="
131
+ - - ">"
136
132
  - !ruby/object:Gem::Version
137
- version: '0'
133
+ version: 1.3.1
138
134
  requirements: []
139
- rubygems_version: 3.1.6
140
- signing_key:
135
+ rubygems_version: 3.1.2
136
+ signing_key:
141
137
  specification_version: 4
142
138
  summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for
143
139
  Ruby/Rack applications
data/docs/tcp_mode.md DELETED
@@ -1,96 +0,0 @@
1
- # TCP mode
2
-
3
- Puma also could be used as a TCP server to process incoming TCP
4
- connections.
5
-
6
-
7
- ## Configuration
8
-
9
- TCP mode can be enabled with CLI option `--tcp-mode`:
10
-
11
- ```
12
- $ puma --tcp-mode
13
- ```
14
-
15
- Default ip and port to listen to are `0.0.0.0` and `9292`. You can configure
16
- them with `--port` and `--bind` options:
17
-
18
- ```
19
- $ puma --tcp-mode --bind tcp://127.0.0.1:9293
20
- $ puma --tcp-mode --port 9293
21
- ```
22
-
23
- TCP mode could be set with a configuration file as well with `tcp_mode`
24
- and `tcp_mode!` methods:
25
-
26
- ```
27
- # config/puma.rb
28
- tcp_mode
29
- ```
30
-
31
- When Puma starts in the TCP mode it prints the corresponding message:
32
-
33
- ```
34
- puma --tcp-mode
35
- Puma starting in single mode...
36
- ...
37
- * Mode: Lopez Express (tcp)
38
- ```
39
-
40
-
41
- ## How to declare an application
42
-
43
- An application to process TCP connections should be declared as a
44
- callable object which accepts `env` and `socket` arguments.
45
-
46
- `env` argument is a Hash with following structure:
47
-
48
- ```ruby
49
- { "thread" => {}, "REMOTE_ADDR" => "127.0.0.1:51133", "log" => "#<Proc:0x000..." }
50
- ```
51
-
52
- It consists of:
53
- * `thread` - a Hash for each thread in the thread pool that could be
54
- used to store information between requests
55
- * `REMOTE_ADDR` - a client ip address
56
- * `log` - a proc object to write something down
57
-
58
- `log` object could be used this way:
59
-
60
- ```ruby
61
- env['log'].call('message to log')
62
- #> 19/Oct/2019 20:28:53 - 127.0.0.1:51266 - message to log
63
- ```
64
-
65
-
66
- ## Example of an application
67
-
68
- Let's look at an example of a simple application which just echoes
69
- incoming string:
70
-
71
- ```ruby
72
- # config/puma.rb
73
- app do |env, socket|
74
- s = socket.gets
75
- socket.puts "Echo #{s}"
76
- end
77
- ```
78
-
79
- We can easily access the TCP server with `telnet` command and receive an
80
- echo:
81
-
82
- ```shell
83
- telnet 0.0.0.0 9293
84
- Trying 0.0.0.0...
85
- Connected to 0.0.0.0.
86
- Escape character is '^]'.
87
- sssss
88
- Echo sssss
89
- ^CConnection closed by foreign host.
90
- ```
91
-
92
-
93
- ## Socket management
94
-
95
- After the application finishes, Puma closes the socket. In order to
96
- prevent this, the application should set `env['detach'] = true`.
@@ -1,155 +0,0 @@
1
- #define RSTRING_NOT_MODIFIED 1
2
- #include "ruby.h"
3
-
4
- #include <sys/types.h>
5
-
6
- struct buf_int {
7
- uint8_t* top;
8
- uint8_t* cur;
9
-
10
- size_t size;
11
- };
12
-
13
- #define BUF_DEFAULT_SIZE 4096
14
- #define BUF_TOLERANCE 32
15
-
16
- static void buf_free(struct buf_int* internal) {
17
- xfree(internal->top);
18
- xfree(internal);
19
- }
20
-
21
- static VALUE buf_alloc(VALUE self) {
22
- VALUE buf;
23
- struct buf_int* internal;
24
-
25
- buf = Data_Make_Struct(self, struct buf_int, 0, buf_free, internal);
26
-
27
- internal->size = BUF_DEFAULT_SIZE;
28
- internal->top = ALLOC_N(uint8_t, BUF_DEFAULT_SIZE);
29
- internal->cur = internal->top;
30
-
31
- return buf;
32
- }
33
-
34
- static VALUE buf_append(VALUE self, VALUE str) {
35
- struct buf_int* b;
36
- size_t used, str_len, new_size;
37
-
38
- Data_Get_Struct(self, struct buf_int, b);
39
-
40
- used = b->cur - b->top;
41
-
42
- StringValue(str);
43
- str_len = RSTRING_LEN(str);
44
-
45
- new_size = used + str_len;
46
-
47
- if(new_size > b->size) {
48
- size_t n = b->size + (b->size / 2);
49
- uint8_t* top;
50
- uint8_t* old;
51
-
52
- new_size = (n > new_size ? n : new_size + BUF_TOLERANCE);
53
-
54
- top = ALLOC_N(uint8_t, new_size);
55
- old = b->top;
56
- memcpy(top, old, used);
57
- b->top = top;
58
- b->cur = top + used;
59
- b->size = new_size;
60
- xfree(old);
61
- }
62
-
63
- memcpy(b->cur, RSTRING_PTR(str), str_len);
64
- b->cur += str_len;
65
-
66
- return self;
67
- }
68
-
69
- static VALUE buf_append2(int argc, VALUE* argv, VALUE self) {
70
- struct buf_int* b;
71
- size_t used, new_size;
72
- int i;
73
- VALUE str;
74
-
75
- Data_Get_Struct(self, struct buf_int, b);
76
-
77
- used = b->cur - b->top;
78
- new_size = used;
79
-
80
- for(i = 0; i < argc; i++) {
81
- StringValue(argv[i]);
82
-
83
- str = argv[i];
84
-
85
- new_size += RSTRING_LEN(str);
86
- }
87
-
88
- if(new_size > b->size) {
89
- size_t n = b->size + (b->size / 2);
90
- uint8_t* top;
91
- uint8_t* old;
92
-
93
- new_size = (n > new_size ? n : new_size + BUF_TOLERANCE);
94
-
95
- top = ALLOC_N(uint8_t, new_size);
96
- old = b->top;
97
- memcpy(top, old, used);
98
- b->top = top;
99
- b->cur = top + used;
100
- b->size = new_size;
101
- xfree(old);
102
- }
103
-
104
- for(i = 0; i < argc; i++) {
105
- long str_len;
106
- str = argv[i];
107
- str_len = RSTRING_LEN(str);
108
- memcpy(b->cur, RSTRING_PTR(str), str_len);
109
- b->cur += str_len;
110
- }
111
-
112
- return self;
113
- }
114
-
115
- static VALUE buf_to_str(VALUE self) {
116
- struct buf_int* b;
117
- Data_Get_Struct(self, struct buf_int, b);
118
-
119
- return rb_str_new((const char*)(b->top), b->cur - b->top);
120
- }
121
-
122
- static VALUE buf_used(VALUE self) {
123
- struct buf_int* b;
124
- Data_Get_Struct(self, struct buf_int, b);
125
-
126
- return INT2FIX(b->cur - b->top);
127
- }
128
-
129
- static VALUE buf_capa(VALUE self) {
130
- struct buf_int* b;
131
- Data_Get_Struct(self, struct buf_int, b);
132
-
133
- return INT2FIX(b->size);
134
- }
135
-
136
- static VALUE buf_reset(VALUE self) {
137
- struct buf_int* b;
138
- Data_Get_Struct(self, struct buf_int, b);
139
-
140
- b->cur = b->top;
141
- return self;
142
- }
143
-
144
- void Init_io_buffer(VALUE puma) {
145
- VALUE buf = rb_define_class_under(puma, "IOBuffer", rb_cObject);
146
-
147
- rb_define_alloc_func(buf, buf_alloc);
148
- rb_define_method(buf, "<<", buf_append, 1);
149
- rb_define_method(buf, "append", buf_append2, -1);
150
- rb_define_method(buf, "to_str", buf_to_str, 0);
151
- rb_define_method(buf, "to_s", buf_to_str, 0);
152
- rb_define_method(buf, "used", buf_used, 0);
153
- rb_define_method(buf, "capacity", buf_capa, 0);
154
- rb_define_method(buf, "reset", buf_reset, 0);
155
- }