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.
- checksums.yaml +4 -4
- data/History.md +58 -31
- data/LICENSE +23 -20
- data/README.md +17 -11
- data/docs/deployment.md +3 -1
- data/docs/fork_worker.md +31 -0
- data/docs/jungle/README.md +13 -0
- data/{tools → docs}/jungle/rc.d/README.md +0 -0
- data/{tools → docs}/jungle/rc.d/puma +0 -0
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/{tools → docs}/jungle/upstart/README.md +0 -0
- data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
- data/{tools → docs}/jungle/upstart/puma.conf +0 -0
- data/docs/signals.md +1 -0
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/extconf.rb +3 -2
- data/ext/puma_http11/http11_parser.c +11 -26
- data/ext/puma_http11/http11_parser.rl +1 -3
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +46 -48
- data/ext/puma_http11/puma_http11.c +2 -38
- data/lib/puma/app/status.rb +16 -5
- data/lib/puma/binder.rb +62 -60
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +35 -32
- data/lib/puma/cluster.rb +179 -74
- data/lib/puma/configuration.rb +30 -42
- data/lib/puma/const.rb +2 -3
- data/lib/puma/control_cli.rb +27 -17
- data/lib/puma/detect.rb +8 -0
- data/lib/puma/dsl.rb +70 -34
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +41 -29
- data/lib/puma/minissl.rb +13 -8
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +6 -1
- data/lib/puma/runner.rb +5 -34
- data/lib/puma/server.rb +70 -190
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +5 -2
- data/lib/puma/thread_pool.rb +85 -47
- data/lib/puma.rb +4 -0
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +22 -26
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
data/lib/puma/thread_pool.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
222
|
-
|
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
|
-
|
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 =
|
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
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
t.join
|
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
|
320
|
-
|
321
|
-
|
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
data/lib/rack/handler/puma.rb
CHANGED
@@ -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
|
+
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:
|
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/
|
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
|
-
|
116
|
+
bug_tracker_uri: https://github.com/puma/puma/issues
|
123
117
|
changelog_uri: https://github.com/puma/puma/blob/master/History.md
|
124
|
-
|
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:
|
133
|
+
version: 1.3.1
|
138
134
|
requirements: []
|
139
|
-
rubygems_version: 3.1.
|
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`.
|
data/ext/puma_http11/io_buffer.c
DELETED
@@ -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
|
-
}
|