rev 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -145,7 +145,7 @@ module Rev
145
145
  @parser_nbytes = 0
146
146
 
147
147
  @state = :response_header
148
- @data = Rev::Buffer.new
148
+ @data = ::IO::Buffer.new
149
149
 
150
150
  @response_header = HttpResponseHeader.new
151
151
  @chunk_header = HttpChunkHeader.new
@@ -21,7 +21,7 @@ module Rev
21
21
 
22
22
  def initialize(io)
23
23
  @_io = io
24
- @_write_buffer ||= Rev::Buffer.new
24
+ @_write_buffer ||= ::IO::Buffer.new
25
25
  @_read_watcher = Watcher.new(io, self, :r)
26
26
  @_write_watcher = Watcher.new(io, self, :w)
27
27
  end
@@ -15,7 +15,12 @@ module Rev
15
15
  @listen_socket = listen_socket
16
16
  super(@listen_socket)
17
17
  end
18
-
18
+
19
+ # Returns an integer representing the underlying numeric file descriptor
20
+ def fileno
21
+ @listen_socket.fileno
22
+ end
23
+
19
24
  # Close the listener
20
25
  def close
21
26
  detach if attached?
@@ -49,11 +54,19 @@ module Rev
49
54
  # :backlog - Max size of the pending connection queue (default 1024)
50
55
  # :reverse_lookup - Retain BasicSocket's reverse DNS functionality (default false)
51
56
  #
52
- def initialize(addr, port, options = {})
57
+ # If the specified address is an TCPServer object, it will ignore
58
+ # the port and :backlog option and create a new Rev::TCPListener out
59
+ # of the existing TCPServer object.
60
+ def initialize(addr, port = nil, options = {})
53
61
  BasicSocket.do_not_reverse_lookup = true unless options[:reverse_lookup]
54
62
  options[:backlog] ||= DEFAULT_BACKLOG
55
63
 
56
- listen_socket = ::TCPServer.new(addr, port)
64
+ listen_socket = if ::TCPServer === addr
65
+ addr
66
+ else
67
+ raise ArgumentError, "port must be an integer" if nil == port
68
+ ::TCPServer.new(addr, port)
69
+ end
57
70
  listen_socket.instance_eval { listen(options[:backlog]) }
58
71
  super(listen_socket)
59
72
  end
@@ -63,8 +76,10 @@ module Rev
63
76
  # Create a new Rev::UNIXListener
64
77
  #
65
78
  # Accepts the same arguments as UNIXServer.new
79
+ # Optionally, it can also take anyn existing UNIXServer object
80
+ # and create a Rev::UNIXListener out of it.
66
81
  def initialize(*args)
67
- super(::UNIXServer.new(*args))
82
+ super(::UNIXServer === args.first ? args.first : ::UNIXServer.new(*args))
68
83
  end
69
84
  end
70
85
  end
@@ -27,6 +27,11 @@ module Rev
27
27
  super(listen_socket)
28
28
  end
29
29
 
30
+ # Returns an integer representing the underlying numeric file descriptor
31
+ def fileno
32
+ @listen_socket.fileno
33
+ end
34
+
30
35
  #########
31
36
  protected
32
37
  #########
@@ -41,9 +46,16 @@ module Rev
41
46
  # TCP server class. Listens on the specified host and port and creates
42
47
  # new connection objects of the given class. This is the most common server class.
43
48
  # Note that the new connection objects will be bound by default to the same event loop that the server is attached to.
49
+ # Optionally, it can also take any existing core TCPServer object as
50
+ # +host+ and create a Rev::TCPServer out of it.
44
51
  class TCPServer < Server
45
- def initialize(host, port, klass = TCPSocket, *args, &block)
46
- listen_socket = ::TCPServer.new(host, port)
52
+ def initialize(host, port = nil, klass = TCPSocket, *args, &block)
53
+ listen_socket = if ::TCPServer === host
54
+ host
55
+ else
56
+ raise ArgumentError, "port must be an integer" if nil == port
57
+ ::TCPServer.new(host, port)
58
+ end
47
59
  listen_socket.instance_eval { listen(1024) } # Change listen backlog to 1024
48
60
  super(listen_socket, klass, *args, &block)
49
61
  end
@@ -51,9 +63,12 @@ module Rev
51
63
 
52
64
  # UNIX server class. Listens on the specified UNIX domain socket and
53
65
  # creates new connection objects of the given class.
66
+ # Optionally, it can also take any existing core UNIXServer object as
67
+ # +path+ and create a Rev::UNIXServer out of it.
54
68
  class UNIXServer < Server
55
69
  def initialize(path, klass = UNIXSocket, *args, &block)
56
- super(::UNIXServer.new(*args), klass, *args, &block)
70
+ s = ::UNIXServer === path ? path : ::UNIXServer.new(path)
71
+ super(s, klass, *args, &block)
57
72
  end
58
73
  end
59
74
  end
@@ -123,7 +123,7 @@ module Rev
123
123
 
124
124
  # Called by precreate during asyncronous DNS resolution
125
125
  def preinitialize(addr, port, *args)
126
- @_write_buffer = Rev::Buffer.new # allow for writing BEFORE the DNS has resolved
126
+ @_write_buffer = ::IO::Buffer.new # allow for writing BEFORE the DNS has resolved
127
127
  @remote_host, @remote_addr, @remote_port = addr, addr, port
128
128
  @_resolver = TCPConnectResolver.new(self, addr, port, *args)
129
129
  end
@@ -123,7 +123,12 @@ module Rev
123
123
  end
124
124
  end
125
125
 
126
- # A socket class for SSL connections
126
+ # A socket class for SSL connections. Please note that this class
127
+ # internally uses the on_connect callback for doing SSL setup. If
128
+ # you would like a callback when the SSL connection is completed,
129
+ # please use the on_ssl_connect callback instead. If you really need
130
+ # a callback which fires before SSL setup begins, use on_connect but
131
+ # be sure to call super.
127
132
  class SSLSocket < TCPSocket
128
133
  # Perform a non-blocking connect to the given host and port
129
134
  def self.connect(addr, port, *args)
@@ -1,9 +1,24 @@
1
1
  #--
2
- # Copyright (C)2007 Tony Arcieri, Roger Pack
2
+ # Copyright (C)2007 Tony Arcieri
3
3
  # You can redistribute this under the terms of the Ruby license
4
4
  # See file LICENSE for details
5
5
  #++
6
6
 
7
+
8
+ # This file creates a an EventMachine class
9
+ # that is actually using rev as its underlying socket layer, but uses the EM API.
10
+ # require it instead of eventmachine
11
+ # i.e.
12
+ # require 'revem'
13
+ # instead of
14
+ # require 'eventmachine'
15
+ # Note: you may want to do both a require 'eventmachine' THEN a require 'revem'
16
+ # so that eventmachine is loaded once, then rev overrides it, then there's no require confusion
17
+ # (if simple servers hang, requiring 'eventmachine' after 'revem' could be the cause of it).
18
+ # drawbacks: slightly slower than EM.
19
+ # benefits: timers are more accurate using libev than using EM. Also rev is sometimes more compatible than EM (ex: 1.9 windows)
20
+ # TODO: some things like connection timeouts aren't implemented yet
21
+ # DONE: timers and normal socket functions are implemented.
7
22
  require File.dirname(__FILE__) + '/rev'
8
23
 
9
24
  module EventMachine
@@ -2,16 +2,19 @@ require 'rubygems'
2
2
 
3
3
  GEMSPEC = Gem::Specification.new do |s|
4
4
  s.name = "rev"
5
- s.version = "0.2.4"
5
+ s.version = "0.3.0"
6
6
  s.authors = "Tony Arcieri"
7
7
  s.email = "tony@medioh.com"
8
- s.date = "2009-02-01"
8
+ s.date = "2009-08-26"
9
9
  s.summary = "Rev is a Ruby binding to the libev high performance event library"
10
10
  s.platform = Gem::Platform::RUBY
11
11
  s.required_ruby_version = '>= 1.8.6'
12
12
 
13
13
  # Gem contents
14
14
  s.files = Dir.glob("{lib,ext,examples}/**/*") + ['Rakefile', 'rev.gemspec']
15
+
16
+ # Dependencies
17
+ s.add_dependency "iobuffer", ">= 0.1.0"
15
18
 
16
19
  # RubyForge info
17
20
  s.homepage = "http://rev.rubyforge.org"
@@ -19,8 +22,8 @@ GEMSPEC = Gem::Specification.new do |s|
19
22
 
20
23
  # RDoc settings
21
24
  s.has_rdoc = true
22
- s.rdoc_options = %w(--title Rev --main README --line-numbers)
23
- s.extra_rdoc_files = ["LICENSE", "README", "CHANGES"]
25
+ s.rdoc_options = %w(--title Rev --main README.textile --line-numbers)
26
+ s.extra_rdoc_files = ["LICENSE", "README.textile", "CHANGES"]
24
27
 
25
28
  # Extensions
26
29
  s.extensions = FileList["ext/**/extconf.rb"].to_a
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Arcieri
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-01 00:00:00 -07:00
12
+ date: 2009-08-26 00:00:00 -06:00
13
13
  default_executable:
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: iobuffer
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.0
24
+ version:
16
25
  description:
17
26
  email: tony@medioh.com
18
27
  executables: []
@@ -22,10 +31,9 @@ extensions:
22
31
  - ext/rev/extconf.rb
23
32
  extra_rdoc_files:
24
33
  - LICENSE
25
- - README
34
+ - README.textile
26
35
  - CHANGES
27
36
  files:
28
- - lib/rev
29
37
  - lib/rev/async_watcher.rb
30
38
  - lib/rev/dns_resolver.rb
31
39
  - lib/rev/http_client.rb
@@ -40,14 +48,12 @@ files:
40
48
  - lib/rev/timer_watcher.rb
41
49
  - lib/rev.rb
42
50
  - lib/revem.rb
43
- - ext/http11_client
44
51
  - ext/http11_client/ext_help.h
45
52
  - ext/http11_client/extconf.rb
46
53
  - ext/http11_client/http11_client.c
47
54
  - ext/http11_client/http11_parser.c
48
55
  - ext/http11_client/http11_parser.h
49
56
  - ext/http11_client/http11_parser.rl
50
- - ext/libev
51
57
  - ext/libev/Changes
52
58
  - ext/libev/ev.c
53
59
  - ext/libev/ev.h
@@ -64,16 +70,15 @@ files:
64
70
  - ext/libev/README.embed
65
71
  - ext/libev/test_libev_win32.c
66
72
  - ext/libev/update_ev_wrap
67
- - ext/rev
68
73
  - ext/rev/ev_wrap.h
69
74
  - ext/rev/extconf.rb
70
75
  - ext/rev/libev.c
71
76
  - ext/rev/rev.h
72
- - ext/rev/rev_buffer.c
73
77
  - ext/rev/rev_ext.c
74
78
  - ext/rev/rev_io_watcher.c
75
79
  - ext/rev/rev_loop.c
76
80
  - ext/rev/rev_ssl.c
81
+ - ext/rev/rev_stat_watcher.c
77
82
  - ext/rev/rev_timer_watcher.c
78
83
  - ext/rev/rev_utils.c
79
84
  - ext/rev/rev_watcher.c
@@ -85,16 +90,18 @@ files:
85
90
  - Rakefile
86
91
  - rev.gemspec
87
92
  - LICENSE
88
- - README
93
+ - README.textile
89
94
  - CHANGES
90
95
  has_rdoc: true
91
96
  homepage: http://rev.rubyforge.org
97
+ licenses: []
98
+
92
99
  post_install_message:
93
100
  rdoc_options:
94
101
  - --title
95
102
  - Rev
96
103
  - --main
97
- - README
104
+ - README.textile
98
105
  - --line-numbers
99
106
  require_paths:
100
107
  - lib
@@ -113,9 +120,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
120
  requirements: []
114
121
 
115
122
  rubyforge_project: rev
116
- rubygems_version: 1.3.1
123
+ rubygems_version: 1.3.5
117
124
  signing_key:
118
- specification_version: 2
125
+ specification_version: 3
119
126
  summary: Rev is a Ruby binding to the libev high performance event library
120
127
  test_files: []
121
128
 
@@ -1,629 +0,0 @@
1
- /*
2
- * Copyright (C) 2007 Tony Arcieri
3
- * You may redistribute this under the terms of the Ruby license.
4
- * See LICENSE for details
5
- */
6
-
7
- #include "ruby.h"
8
- #include "rubyio.h"
9
-
10
- #include "ev_wrap.h"
11
-
12
- #include "rev.h"
13
-
14
- #include <assert.h>
15
-
16
- #include <string.h>
17
- #include <time.h>
18
- #include <unistd.h>
19
- #include <errno.h>
20
-
21
- /* Default number of bytes in each node's buffer */
22
- #define DEFAULT_NODE_SIZE 16384
23
-
24
- /* Maximum age of a buffer node in a memory pool, in seconds */
25
- #define MAX_AGE 60
26
-
27
- /* How often to scan the pool for old nodes */
28
- #define PURGE_INTERVAL 10
29
-
30
- struct buffer {
31
- time_t last_purged_at;
32
- unsigned size, node_size;
33
- struct buffer_node *head, *tail;
34
- struct buffer_node *pool_head, *pool_tail;
35
-
36
- };
37
-
38
- struct buffer_node {
39
- time_t last_used_at;
40
- unsigned start, end;
41
- struct buffer_node *next;
42
- unsigned char data[0];
43
- };
44
-
45
- static VALUE mRev = Qnil;
46
- static VALUE cRev_Buffer = Qnil;
47
-
48
- static VALUE Rev_Buffer_allocate(VALUE klass);
49
- static void Rev_Buffer_mark(struct buffer *);
50
- static void Rev_Buffer_free(struct buffer *);
51
-
52
- static VALUE Rev_Buffer_initialize(int argc, VALUE *argv, VALUE self);
53
- static VALUE Rev_Buffer_clear(VALUE self);
54
- static VALUE Rev_Buffer_size(VALUE self);
55
- static VALUE Rev_Buffer_empty(VALUE self);
56
- static VALUE Rev_Buffer_append(VALUE self, VALUE data);
57
- static VALUE Rev_Buffer_prepend(VALUE self, VALUE data);
58
- static VALUE Rev_Buffer_read(int argc, VALUE *argv, VALUE self);
59
- static VALUE Rev_Buffer_to_str(VALUE self);
60
- static VALUE Rev_Buffer_read_from(VALUE self, VALUE io);
61
- static VALUE Rev_Buffer_write_to(VALUE self, VALUE io);
62
-
63
- static struct buffer *buffer_new(void);
64
- static void buffer_clear(struct buffer *buf);
65
- static void buffer_free(struct buffer *buf);
66
- static void buffer_gc(struct buffer *buf);
67
- static void buffer_prepend(struct buffer *buf, char *str, unsigned len);
68
- static void buffer_append(struct buffer *buf, char *str, unsigned len);
69
- static void buffer_read(struct buffer *buf, char *str, unsigned len);
70
- static void buffer_copy(struct buffer *buf, char *str, unsigned len);
71
- static int buffer_read_from(struct buffer *buf, int fd);
72
- static int buffer_write_to(struct buffer *buf, int fd);
73
-
74
- /*
75
- * High speed buffering geared towards non-blocking I/O.
76
- *
77
- * Data is stored in a byte queue implemented as a linked list of equal size
78
- * chunks. Since every node in the list is the same size they are easily
79
- * memory pooled. Routines are provided for high speed non-blocking reads
80
- * and writes from Ruby IO objects.
81
- */
82
- void Init_rev_buffer()
83
- {
84
- mRev = rb_define_module("Rev");
85
- cRev_Buffer = rb_define_class_under(mRev, "Buffer", rb_cObject);
86
- rb_define_alloc_func(cRev_Buffer, Rev_Buffer_allocate);
87
-
88
- rb_define_method(cRev_Buffer, "initialize", Rev_Buffer_initialize, -1);
89
- rb_define_method(cRev_Buffer, "clear", Rev_Buffer_clear, 0);
90
- rb_define_method(cRev_Buffer, "size", Rev_Buffer_size, 0);
91
- rb_define_method(cRev_Buffer, "empty?", Rev_Buffer_empty, 0);
92
- rb_define_method(cRev_Buffer, "<<", Rev_Buffer_append, 1);
93
- rb_define_method(cRev_Buffer, "append", Rev_Buffer_append, 1);
94
- rb_define_method(cRev_Buffer, "prepend", Rev_Buffer_prepend, 1);
95
- rb_define_method(cRev_Buffer, "read", Rev_Buffer_read, -1);
96
- rb_define_method(cRev_Buffer, "to_str", Rev_Buffer_to_str, 0);
97
- rb_define_method(cRev_Buffer, "read_from", Rev_Buffer_read_from, 1);
98
- rb_define_method(cRev_Buffer, "write_to", Rev_Buffer_write_to, 1);
99
- }
100
-
101
- static VALUE Rev_Buffer_allocate(VALUE klass)
102
- {
103
- return Data_Wrap_Struct(klass, Rev_Buffer_mark, Rev_Buffer_free, buffer_new());
104
- }
105
-
106
- static void Rev_Buffer_mark(struct buffer *buf)
107
- {
108
- /* Walks the pool of unused chunks and frees any that are beyond a certain age */
109
- buffer_gc(buf);
110
- }
111
-
112
- static void Rev_Buffer_free(struct buffer *buf)
113
- {
114
- buffer_free(buf);
115
- }
116
-
117
- /**
118
- * call-seq:
119
- * Rev::Buffer.new(size = DEFAULT_NODE_SIZE) -> Rev::Buffer
120
- *
121
- * Create a new Rev::Buffer with linked segments of the given size
122
- */
123
- static VALUE Rev_Buffer_initialize(int argc, VALUE *argv, VALUE self)
124
- {
125
- VALUE node_size_obj;
126
- int node_size;
127
- struct buffer *buf;
128
-
129
- if(rb_scan_args(argc, argv, "01", &node_size_obj) == 1) {
130
- node_size = NUM2INT(node_size_obj);
131
-
132
- if(node_size < 1) rb_raise(rb_eArgError, "invalid buffer size");
133
-
134
- Data_Get_Struct(self, struct buffer, buf);
135
-
136
- /* Make sure we're not changing the buffer size after data has been allocated */
137
- assert(!buf->head);
138
- assert(!buf->pool_head);
139
-
140
- buf->node_size = node_size;
141
- }
142
-
143
- return Qnil;
144
- }
145
-
146
- /**
147
- * call-seq:
148
- * Rev::Buffer#clear -> nil
149
- *
150
- * Clear all data from the Rev::Buffer
151
- */
152
- static VALUE Rev_Buffer_clear(VALUE self)
153
- {
154
- struct buffer *buf;
155
- Data_Get_Struct(self, struct buffer, buf);
156
-
157
- buffer_clear(buf);
158
-
159
- return Qnil;
160
- }
161
-
162
- /**
163
- * call-seq:
164
- * Rev::Buffer#size -> Integer
165
- *
166
- * Return the size of the buffer in bytes
167
- */
168
- static VALUE Rev_Buffer_size(VALUE self)
169
- {
170
- struct buffer *buf;
171
- Data_Get_Struct(self, struct buffer, buf);
172
-
173
- return INT2NUM(buf->size);
174
- }
175
-
176
- /**
177
- * call-seq:
178
- * Rev::Buffer#empty? -> Boolean
179
- *
180
- * Is the buffer empty?
181
- */
182
- static VALUE Rev_Buffer_empty(VALUE self)
183
- {
184
- struct buffer *buf;
185
- Data_Get_Struct(self, struct buffer, buf);
186
-
187
- return buf->size > 0 ? Qfalse : Qtrue;
188
- }
189
-
190
- /**
191
- * call-seq:
192
- * Rev::Buffer#append(data) -> String
193
- *
194
- * Append the given data to the end of the buffer
195
- */
196
- static VALUE Rev_Buffer_append(VALUE self, VALUE data)
197
- {
198
- struct buffer *buf;
199
- Data_Get_Struct(self, struct buffer, buf);
200
-
201
- /* Is this needed? Never seen anyone else do it... */
202
- data = rb_convert_type(data, T_STRING, "String", "to_str");
203
- buffer_append(buf, RSTRING_PTR(data), RSTRING_LEN(data));
204
-
205
- return data;
206
- }
207
-
208
- /**
209
- * call-seq:
210
- * Rev::Buffer#prepend(data) -> String
211
- *
212
- * Prepend the given data to the beginning of the buffer
213
- */
214
- static VALUE Rev_Buffer_prepend(VALUE self, VALUE data)
215
- {
216
- struct buffer *buf;
217
- Data_Get_Struct(self, struct buffer, buf);
218
-
219
- data = rb_convert_type(data, T_STRING, "String", "to_str");
220
- buffer_prepend(buf, RSTRING_PTR(data), RSTRING_LEN(data));
221
-
222
- return data;
223
- }
224
-
225
- /**
226
- * call-seq:
227
- * Rev::Buffer#read(length = nil) -> String
228
- *
229
- * Read the specified abount of data from the buffer. If no value
230
- * is given the entire contents of the buffer are returned. Any data
231
- * read from the buffer is cleared.
232
- */
233
- static VALUE Rev_Buffer_read(int argc, VALUE *argv, VALUE self)
234
- {
235
- VALUE length_obj, str;
236
- int length;
237
- struct buffer *buf;
238
-
239
- Data_Get_Struct(self, struct buffer, buf);
240
-
241
- if(rb_scan_args(argc, argv, "01", &length_obj) == 1) {
242
- length = NUM2INT(length_obj);
243
- } else {
244
- if(buf->size == 0)
245
- return rb_str_new2("");
246
-
247
- length = buf->size;
248
- }
249
-
250
- if(length > buf->size)
251
- length = buf->size;
252
-
253
- if(length < 1)
254
- rb_raise(rb_eArgError, "length must be greater than zero");
255
-
256
- str = rb_str_new(0, length);
257
- buffer_read(buf, RSTRING_PTR(str), length);
258
-
259
- return str;
260
- }
261
-
262
- /**
263
- * call-seq:
264
- * Rev::Buffer#to_str -> String
265
- *
266
- * Convert the Buffer to a String. The original buffer is unmodified.
267
- */
268
- static VALUE Rev_Buffer_to_str(VALUE self) {
269
- VALUE str;
270
- struct buffer *buf;
271
-
272
- Data_Get_Struct(self, struct buffer, buf);
273
-
274
- str = rb_str_new(0, buf->size);
275
- buffer_copy(buf, RSTRING_PTR(str), buf->size);
276
-
277
- return str;
278
- }
279
-
280
- /**
281
- * call-seq:
282
- * Rev::Buffer#read_from(io) -> Integer
283
- *
284
- * Perform a nonblocking read of the the given IO object and fill
285
- * the buffer with any data received. The call will read as much
286
- * data as it can until the read would block.
287
- */
288
- static VALUE Rev_Buffer_read_from(VALUE self, VALUE io) {
289
- struct buffer *buf;
290
- #if HAVE_RB_IO_T
291
- rb_io_t *fptr;
292
- #else
293
- OpenFile *fptr;
294
- #endif
295
-
296
- Data_Get_Struct(self, struct buffer, buf);
297
- GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
298
- rb_io_set_nonblock(fptr);
299
-
300
- return INT2NUM(buffer_read_from(buf, FPTR_TO_FD(fptr)));
301
- }
302
-
303
- /**
304
- * call-seq:
305
- * Rev::Buffer#write_to(io) -> Integer
306
- *
307
- * Perform a nonblocking write of the buffer to the given IO object.
308
- * As much data as possible is written until the call would block.
309
- * Any data which is written is removed from the buffer.
310
- */
311
- static VALUE Rev_Buffer_write_to(VALUE self, VALUE io) {
312
- struct buffer *buf;
313
- #if HAVE_RB_IO_T
314
- rb_io_t *fptr;
315
- #else
316
- OpenFile *fptr;
317
- #endif
318
-
319
- Data_Get_Struct(self, struct buffer, buf);
320
- GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
321
- rb_io_set_nonblock(fptr);
322
-
323
- return INT2NUM(buffer_write_to(buf, FPTR_TO_FD(fptr)));
324
- }
325
-
326
- /*
327
- * Ruby bindings end here. Below is the actual implementation of
328
- * the underlying data structures.
329
- */
330
-
331
- /* Create a new buffer */
332
- static struct buffer *buffer_new(void)
333
- {
334
- struct buffer *buf;
335
-
336
- buf = (struct buffer *)xmalloc(sizeof(struct buffer));
337
- buf->head = buf->tail = buf->pool_head = buf->pool_tail = 0;
338
- buf->size = 0;
339
- buf->node_size = DEFAULT_NODE_SIZE;
340
- time(&buf->last_purged_at);
341
-
342
- return buf;
343
- }
344
-
345
- /* Clear all data from a buffer */
346
- static void buffer_clear(struct buffer *buf)
347
- {
348
- struct buffer_node *tmp;
349
-
350
- /* Move everything into the buffer pool */
351
- if(!buf->pool_tail)
352
- buf->pool_head = buf->pool_tail = buf->head;
353
- else
354
- buf->pool_tail->next = buf->head;
355
-
356
- buf->head = buf->tail = 0;
357
- buf->size = 0;
358
- }
359
-
360
- /* Free a buffer */
361
- static void buffer_free(struct buffer *buf)
362
- {
363
- struct buffer_node *tmp;
364
-
365
- buffer_clear(buf);
366
-
367
- while(buf->pool_head) {
368
- tmp = buf->pool_head;
369
- buf->pool_head = tmp->next;
370
- free(tmp);
371
- }
372
-
373
- free(buf);
374
- }
375
-
376
- /* Run through the pool and find elements that haven't been used for awhile */
377
- static void buffer_gc(struct buffer *buf)
378
- {
379
- struct buffer_node *cur, *tmp;
380
- time_t now;
381
- time(&now);
382
-
383
- /* Only purge if we've passed the purge interval */
384
- if(now - buf->last_purged_at < PURGE_INTERVAL)
385
- return;
386
-
387
- buf->last_purged_at = now;
388
-
389
- while(buf->pool_head && now - buf->pool_head->last_used_at >= MAX_AGE) {
390
- tmp = buf->pool_head;
391
- buf->pool_head = buf->pool_head->next;
392
- free(tmp);
393
- }
394
-
395
- if(!buf->pool_head)
396
- buf->pool_tail = 0;
397
- }
398
-
399
- /* Create a new buffer_node (or pull one from the memory pool) */
400
- static struct buffer_node *buffer_node_new(struct buffer *buf)
401
- {
402
- struct buffer_node *node;
403
-
404
- /* Pull from the memory pool if available */
405
- if(buf->pool_head) {
406
- node = buf->pool_head;
407
- buf->pool_head = node->next;
408
-
409
- if(node->next)
410
- node->next = 0;
411
- else
412
- buf->pool_tail = 0;
413
- } else {
414
- node = (struct buffer_node *)xmalloc(sizeof(struct buffer_node) + buf->node_size);
415
- node->next = 0;
416
- }
417
-
418
- node->start = node->end = 0;
419
- return node;
420
- }
421
-
422
- /* Free a buffer node (i.e. return it to the memory pool) */
423
- static void buffer_node_free(struct buffer *buf, struct buffer_node *node)
424
- {
425
- /* Store when the node was freed */
426
- time(&node->last_used_at);
427
-
428
- node->next = buf->pool_head;
429
- buf->pool_head = node;
430
-
431
- if(!buf->pool_tail)
432
- buf->pool_tail = node;
433
- }
434
-
435
- /* Prepend data to the front of the buffer */
436
- static void buffer_prepend(struct buffer *buf, char *str, unsigned len)
437
- {
438
- struct buffer_node *node, *tmp;
439
- buf->size += len;
440
-
441
- /* If it fits in the beginning of the head */
442
- if(buf->head && buf->head->start >= len) {
443
- buf->head->start -= len;
444
- memcpy(buf->head->data + buf->head->start, str, len);
445
- } else {
446
- node = buffer_node_new(buf);
447
- node->next = buf->head;
448
- buf->head = node;
449
- if(!buf->tail) buf->tail = node;
450
-
451
- while(len > buf->node_size) {
452
- memcpy(node->data, str, buf->node_size);
453
- node->end = buf->node_size;
454
-
455
- tmp = buffer_node_new(buf);
456
- tmp->next = node->next;
457
- node->next = tmp;
458
-
459
- if(buf->tail == node) buf->tail = tmp;
460
- node = tmp;
461
-
462
- str += buf->node_size;
463
- len -= buf->node_size;
464
- }
465
-
466
- if(len > 0) {
467
- memcpy(node->data, str, len);
468
- node->end = len;
469
- }
470
- }
471
- }
472
-
473
- /* Append data to the front of the buffer */
474
- static void buffer_append(struct buffer *buf, char *str, unsigned len)
475
- {
476
- unsigned nbytes;
477
- buf->size += len;
478
-
479
- /* If it fits in the remaining space in the tail */
480
- if(buf->tail && len <= buf->node_size - buf->tail->end) {
481
- memcpy(buf->tail->data + buf->tail->end, str, len);
482
- buf->tail->end += len;
483
- return;
484
- }
485
-
486
- /* Empty list needs initialized */
487
- if(!buf->head) {
488
- buf->head = buffer_node_new(buf);
489
- buf->tail = buf->head;
490
- }
491
-
492
- /* Build links out of the data */
493
- while(len > 0) {
494
- nbytes = buf->node_size - buf->tail->end;
495
- if(len < nbytes) nbytes = len;
496
-
497
- memcpy(buf->tail->data + buf->tail->end, str, nbytes);
498
- str += nbytes;
499
- len -= nbytes;
500
-
501
- buf->tail->end += nbytes;
502
-
503
- if(len > 0) {
504
- buf->tail->next = buffer_node_new(buf);
505
- buf->tail = buf->tail->next;
506
- }
507
- }
508
- }
509
-
510
- /* Read data from the buffer (and clear what we've read) */
511
- static void buffer_read(struct buffer *buf, char *str, unsigned len)
512
- {
513
- unsigned nbytes;
514
- struct buffer_node *tmp;
515
-
516
- while(buf->size > 0 && len > 0) {
517
- nbytes = buf->head->end - buf->head->start;
518
- if(len < nbytes) nbytes = len;
519
-
520
- memcpy(str, buf->head->data + buf->head->start, nbytes);
521
- str += nbytes;
522
- len -= nbytes;
523
-
524
- buf->head->start += nbytes;
525
- buf->size -= nbytes;
526
-
527
- if(buf->head->start == buf->head->end) {
528
- tmp = buf->head;
529
- buf->head = tmp->next;
530
- buffer_node_free(buf, tmp);
531
-
532
- if(!buf->head) buf->tail = 0;
533
- }
534
- }
535
- }
536
-
537
- /* Copy data from the buffer without clearing it */
538
- static void buffer_copy(struct buffer *buf, char *str, unsigned len)
539
- {
540
- unsigned nbytes;
541
- struct buffer_node *node;
542
-
543
- node = buf->head;
544
- while(node && len > 0) {
545
- nbytes = node->end - node->start;
546
- if(len < nbytes) nbytes = len;
547
-
548
- memcpy(str, node->data + node->start, nbytes);
549
- str += nbytes;
550
- len -= nbytes;
551
-
552
- if(node->start + nbytes == node->end)
553
- node = node->next;
554
- }
555
- }
556
-
557
- /* Write data from the buffer to a file descriptor */
558
- static int buffer_write_to(struct buffer *buf, int fd)
559
- {
560
- int bytes_written, total_bytes_written = 0;
561
- struct buffer_node *tmp;
562
-
563
- while(buf->head) {
564
- bytes_written = write(fd, buf->head->data + buf->head->start, buf->head->end - buf->head->start);
565
-
566
- /* If the write failed... */
567
- if(bytes_written < 0) {
568
- if(errno != EAGAIN)
569
- rb_sys_fail("write");
570
-
571
- return total_bytes_written;
572
- }
573
-
574
- total_bytes_written += bytes_written;
575
- buf->size -= bytes_written;
576
-
577
- /* If the write blocked... */
578
- if(bytes_written < buf->head->end - buf->head->start) {
579
- buf->head->start += bytes_written;
580
- return total_bytes_written;
581
- }
582
-
583
- /* Otherwise we wrote the whole buffer */
584
- tmp = buf->head;
585
- buf->head = tmp->next;
586
- buffer_node_free(buf, tmp);
587
-
588
- if(!buf->head) buf->tail = 0;
589
- }
590
-
591
- return total_bytes_written;
592
- }
593
-
594
- /* Read data from a file descriptor to a buffer */
595
- /* Append data to the front of the buffer */
596
- static int buffer_read_from(struct buffer *buf, int fd)
597
- {
598
- int bytes_read, total_bytes_read = 0;
599
- unsigned nbytes;
600
-
601
- /* Empty list needs initialized */
602
- if(!buf->head) {
603
- buf->head = buffer_node_new(buf);
604
- buf->tail = buf->head;
605
- }
606
-
607
- do {
608
- nbytes = buf->node_size - buf->tail->end;
609
- bytes_read = read(fd, buf->tail->data + buf->tail->end, nbytes);
610
-
611
- if(bytes_read < 1) {
612
- if(errno != EAGAIN)
613
- rb_sys_fail("read");
614
-
615
- return total_bytes_read;
616
- }
617
-
618
- total_bytes_read += bytes_read;
619
- buf->tail->end += nbytes;
620
- buf->size += nbytes;
621
-
622
- if(buf->tail->end == buf->node_size) {
623
- buf->tail->next = buffer_node_new(buf);
624
- buf->tail = buf->tail->next;
625
- }
626
- } while(bytes_read == nbytes);
627
-
628
- return total_bytes_read;
629
- }