virtfs 0.0.1

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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +154 -0
  8. data/Rakefile +5 -0
  9. data/lib/virtfs-nativefs-thick.rb +1 -0
  10. data/lib/virtfs-nativefs-thin.rb +1 -0
  11. data/lib/virtfs.rb +38 -0
  12. data/lib/virtfs/activation.rb +97 -0
  13. data/lib/virtfs/block_io.rb +140 -0
  14. data/lib/virtfs/byte_range.rb +71 -0
  15. data/lib/virtfs/context.rb +300 -0
  16. data/lib/virtfs/context_manager.rb +175 -0
  17. data/lib/virtfs/context_switch_class_methods.rb +96 -0
  18. data/lib/virtfs/delegate_module.rb +40 -0
  19. data/lib/virtfs/dir_instance_delegate.rb +3 -0
  20. data/lib/virtfs/exception.rb +13 -0
  21. data/lib/virtfs/file_instance_delegate.rb +3 -0
  22. data/lib/virtfs/file_modes_and_options.rb +293 -0
  23. data/lib/virtfs/find_class_methods.rb +106 -0
  24. data/lib/virtfs/io_buffer.rb +133 -0
  25. data/lib/virtfs/io_instance_delegate.rb +3 -0
  26. data/lib/virtfs/kernel.rb +146 -0
  27. data/lib/virtfs/nativefs/thick.rb +30 -0
  28. data/lib/virtfs/nativefs/thick/dir_class_methods.rb +38 -0
  29. data/lib/virtfs/nativefs/thick/file_class_methods.rb +178 -0
  30. data/lib/virtfs/nativefs/thin.rb +32 -0
  31. data/lib/virtfs/nativefs/thin/dir.rb +30 -0
  32. data/lib/virtfs/nativefs/thin/dir_class_methods.rb +41 -0
  33. data/lib/virtfs/nativefs/thin/file.rb +112 -0
  34. data/lib/virtfs/nativefs/thin/file_class_methods.rb +181 -0
  35. data/lib/virtfs/protofs/protofs.rb +7 -0
  36. data/lib/virtfs/protofs/protofs_base.rb +12 -0
  37. data/lib/virtfs/protofs/protofs_dir.rb +13 -0
  38. data/lib/virtfs/protofs/protofs_dir_class.rb +31 -0
  39. data/lib/virtfs/protofs/protofs_file.rb +27 -0
  40. data/lib/virtfs/protofs/protofs_file_class.rb +136 -0
  41. data/lib/virtfs/stat.rb +100 -0
  42. data/lib/virtfs/thin_dir_delegator.rb +79 -0
  43. data/lib/virtfs/thin_file_delegator.rb +77 -0
  44. data/lib/virtfs/thin_io_delegator_methods.rb +301 -0
  45. data/lib/virtfs/thin_io_delegator_methods_bufferio.rb +337 -0
  46. data/lib/virtfs/v_dir.rb +238 -0
  47. data/lib/virtfs/v_file.rb +480 -0
  48. data/lib/virtfs/v_io.rb +243 -0
  49. data/lib/virtfs/v_pathname.rb +128 -0
  50. data/lib/virtfs/version.rb +3 -0
  51. data/spec/activate_spec.rb +202 -0
  52. data/spec/chroot_spec.rb +120 -0
  53. data/spec/context_manager_class_spec.rb +246 -0
  54. data/spec/context_manager_instance_spec.rb +255 -0
  55. data/spec/context_spec.rb +335 -0
  56. data/spec/data/UTF-16LE-data.txt +0 -0
  57. data/spec/data/UTF-8-data.txt +212 -0
  58. data/spec/dir_class_spec.rb +506 -0
  59. data/spec/dir_instance_spec.rb +208 -0
  60. data/spec/file_class_spec.rb +2106 -0
  61. data/spec/file_instance_spec.rb +154 -0
  62. data/spec/file_modes_and_options_spec.rb +1556 -0
  63. data/spec/find_spec.rb +142 -0
  64. data/spec/io_bufferio_size_shared_examples.rb +371 -0
  65. data/spec/io_bufferio_size_spec.rb +861 -0
  66. data/spec/io_bufferio_spec.rb +801 -0
  67. data/spec/io_class_spec.rb +145 -0
  68. data/spec/io_instance_spec.rb +516 -0
  69. data/spec/kernel_spec.rb +285 -0
  70. data/spec/mount_spec.rb +186 -0
  71. data/spec/nativefs_local_root_spec.rb +132 -0
  72. data/spec/path_spec.rb +39 -0
  73. data/spec/spec_helper.rb +126 -0
  74. data/tasks/rspec.rake +3 -0
  75. data/tasks/yard.rake +7 -0
  76. data/test/UTF-8-demo.txt +212 -0
  77. data/test/bench.rb +18 -0
  78. data/test/bio_internal_test.rb +45 -0
  79. data/test/delegate_io.rb +31 -0
  80. data/test/delegate_module.rb +62 -0
  81. data/test/encode_test.rb +42 -0
  82. data/test/enoent_test.rb +30 -0
  83. data/test/namespace_test.rb +42 -0
  84. data/test/read_block_valid_encoding.rb +44 -0
  85. data/test/read_test.rb +78 -0
  86. data/test/stream_readers.rb +46 -0
  87. data/test/utf-16-demo.txt +0 -0
  88. data/test/utf8_to_utf16.rb +77 -0
  89. data/test/wrapper_test.rb +34 -0
  90. data/virtfs.gemspec +29 -0
  91. metadata +230 -0
@@ -0,0 +1,301 @@
1
+ require_relative 'byte_range'
2
+ require_relative 'io_buffer'
3
+ require_relative 'thin_io_delegator_methods_bufferio'
4
+
5
+ module VirtFS
6
+ # Dispatches File calls to/from VirtFS and the 'Thin' subsystem
7
+ #
8
+ # IO objects are not instantiated directly, because
9
+ # IO.new delegates to VfsRealIO. These instances methods
10
+ # are only called through File objects.
11
+ #
12
+ module ThinIODelegatorMethods
13
+ include ThinIODelegatorMethodsBufferio
14
+
15
+ # Instance methods
16
+ def initialize(fs_io_obj, parsed_args)
17
+ @fs_io_obj = fs_io_obj
18
+ @size = @fs_io_obj.size
19
+ @start_byte_addr = 0
20
+ @end_byte_addr = @size - 1
21
+
22
+ @parsed_args = parsed_args
23
+ @seek_pos = @parsed_args.append? ? @size : 0 # The current byte position within the file.
24
+
25
+ @binary_encoding = Encoding.find("ASCII-8BIT")
26
+ @autoclose = @parsed_args.autoclose?
27
+
28
+ bio_init
29
+
30
+ enable_finalizer if @autoclose
31
+ end
32
+
33
+ def re_initialize(io_obj)
34
+ close
35
+ io_obj.flush
36
+
37
+ @fs_io_obj = io_obj.instance_variable_get(:@fs_io_obj)
38
+ @parsed_args = io_obj.instance_variable_get(:@parsed_args)
39
+ @seek_pos = io_obj.instance_variable_get(:@seek_pos)
40
+
41
+ @size = @fs_io_obj.size
42
+ @start_byte_addr = 0
43
+ @end_byte_addr = @size - 1
44
+ @autoclose = @parsed_args.autoclose?
45
+
46
+ bio_reinit(io_obj)
47
+
48
+ enable_finalizer if @autoclose
49
+ end
50
+
51
+ def enable_finalizer
52
+ # XXX ObjectSpace.define_finalizer(self, self.class.finalize(fs_file_obj))
53
+ end
54
+
55
+ def disable_finalizer
56
+ # XXX
57
+ end
58
+
59
+ def self.finalize(obj)
60
+ proc { obj.close }
61
+ end
62
+
63
+ def autoclose=(bool)
64
+ file_open
65
+ initial_val = @autoclose
66
+ if (@autoclose = bool ? true : false)
67
+ enable_finalizer if initial_val == false
68
+ else
69
+ disable_finalizer if initial_val == true
70
+ end
71
+ bool
72
+ end
73
+
74
+ def autoclose?
75
+ file_open
76
+ @autoclose
77
+ end
78
+
79
+ def binmode
80
+ file_open
81
+ @parsed_args.binmode
82
+ self
83
+ end
84
+
85
+ def binmode?
86
+ file_open
87
+ @parsed_args.binmode?
88
+ end
89
+
90
+ def close
91
+ file_open
92
+ @io_buffer.flush
93
+ @fs_io_obj.close
94
+ @parsed_args.close
95
+ @autoclose = false
96
+ nil
97
+ end
98
+
99
+ def close_on_exec?
100
+ file_open
101
+ @fs_io_obj.close_on_exec?
102
+ end
103
+
104
+ def close_on_exec=(bool)
105
+ @fs_io_obj.close_on_exec = bool
106
+ end
107
+
108
+ def close_read
109
+ file_open
110
+ raise IOError, "closing non-duplex IO for reading" unless @parsed_args.rdonly?
111
+ @parsed_args.close_read
112
+ @fs_io_obj.close_read
113
+ end
114
+
115
+ def close_write
116
+ file_open
117
+ raise IOError, "closing non-duplex IO for writing" unless @parsed_args.wronly?
118
+ @parsed_args.close_write
119
+ @fs_io_obj.close_write
120
+ end
121
+
122
+ def closed?
123
+ @parsed_args.closed?
124
+ end
125
+
126
+ def eof
127
+ file_open && for_reading
128
+ @seek_pos > @end_byte_addr
129
+ end
130
+ alias_method :eof?, :eof
131
+
132
+ def external_encoding
133
+ file_open
134
+ @parsed_args.external_encoding
135
+ end
136
+
137
+ def fcntl(cms, arg)
138
+ file_open
139
+ @fs_io_obj.fcntl(cms, arg)
140
+ end
141
+
142
+ def fdatasync
143
+ file_open
144
+ @fs_io_obj.fdatasync
145
+ end
146
+
147
+ def fileno
148
+ file_open
149
+ @fs_io_obj.fileno
150
+ end
151
+ alias_method :to_i, :fileno
152
+
153
+ def flush
154
+ file_open
155
+ @io_buffer.flush
156
+ @fs_io_obj.flush
157
+ self
158
+ end
159
+
160
+ def fsync
161
+ file_open
162
+ @fs_io_obj.fsync
163
+ end
164
+
165
+ def internal_encoding
166
+ file_open
167
+ @parsed_args.internal_encoding
168
+ end
169
+
170
+ def ioctl(cmd, arg)
171
+ file_open
172
+ @fs_io_obj.ioctl(cmd, arg)
173
+ end
174
+
175
+ def isatty
176
+ file_open
177
+ @fs_io_obj.isatty
178
+ end
179
+ alias_method :tty?, :isatty
180
+
181
+ def pid
182
+ file_open
183
+ @fs_io_obj.pid
184
+ end
185
+
186
+ def pos
187
+ file_open
188
+ @seek_pos
189
+ end
190
+ alias_method :tell, :pos
191
+
192
+ def pos=(p)
193
+ file_open
194
+ raise SystemCallError.new(p.to_s, Errno::EINVAL::Errno) if p < 0
195
+ @seek_pos = p
196
+ end
197
+
198
+ def readpartial(limit, result = "")
199
+ file_open && for_reading
200
+ @fs_io_obj.readpartial(limit, result)
201
+ end
202
+
203
+ def reopen(*args)
204
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 1..2)" if args.empty? || args.length > 2
205
+ if args[0].respond_to?(:to_str)
206
+ VFile.new(*args).__getobj__
207
+ elsif args[0].respond_to?(:__getobj__)
208
+ args[0].__getobj__.dup
209
+ else
210
+ args[0]
211
+ end
212
+ end
213
+
214
+ def rewind
215
+ file_open
216
+ @seek_pos = 0
217
+ end
218
+
219
+ def seek(offset, whence = IO::SEEK_SET)
220
+ file_open
221
+ sysseek(offset, whence)
222
+ 0
223
+ end
224
+
225
+ def set_encoding(*args)
226
+ file_open
227
+ @parsed_args.set_encoding(*args)
228
+ self
229
+ end
230
+
231
+ def stat
232
+ file_open
233
+ @fs_io_obj.stat # XXX wrap in VirtFS::Stat
234
+ end
235
+
236
+ def sysread(len, buffer = nil)
237
+ file_open && for_reading && not_at_eof
238
+ rv = @fs_io_obj.raw_read(@seek_pos, len)
239
+ @seek_pos += rv.bytesize
240
+ buffer.replace(rv) unless buffer.nil?
241
+ rv
242
+ end
243
+
244
+ def sysseek(offset, whence = IO::SEEK_SET)
245
+ file_open
246
+ new_pos = case whence
247
+ when IO::SEEK_CUR then @seek_pos + offset
248
+ when IO::SEEK_END then @size + offset
249
+ when IO::SEEK_SET then @start_byte_addr + offset
250
+ end
251
+
252
+ raise SystemCallError.new(offset.to_s, Errno::EINVAL::Errno) if new_pos < 0
253
+ @seek_pos = new_pos
254
+ end
255
+
256
+ def syswrite(buf)
257
+ file_open && for_writing
258
+ rv = @fs_io_obj.raw_write(@seek_pos, buf)
259
+ update_write_pos(rv)
260
+ rv
261
+ end
262
+
263
+ def to_io
264
+ self
265
+ end
266
+
267
+ def write_nonblock(buf)
268
+ file_open && for_writing
269
+ @fs_io_obj.write_nonblock(buf)
270
+ end
271
+
272
+ private
273
+
274
+ def file_open
275
+ raise IOError, "closed stream" if closed?
276
+ true
277
+ end
278
+
279
+ def for_reading
280
+ raise IOError, "not opened for reading" unless @parsed_args.read?
281
+ true
282
+ end
283
+
284
+ def for_writing
285
+ raise IOError, "not opened for writing" unless @parsed_args.write?
286
+ true
287
+ end
288
+
289
+ def not_at_eof
290
+ raise EOFError, "end of file reached" if eof?
291
+ true
292
+ end
293
+
294
+ def update_write_pos(len)
295
+ @seek_pos += len
296
+ return if @seek_pos <= @size
297
+ @size = @seek_pos
298
+ @end_byte_addr = @seek_pos - 1
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,337 @@
1
+ module VirtFS
2
+ # Dispatches BufferIO calls to/from VirtFS and the 'Thin' subsystem
3
+ #
4
+ module ThinIODelegatorMethodsBufferio
5
+ attr_reader :min_read_buf_sz, :read_buffer, :end_byte_addr, :fs_io_obj
6
+
7
+ MIN_READ_BUF_SZ = 1024 * 32
8
+ MAX_CHAR_LEN = 8
9
+
10
+ def min_read_buf_sz=(val)
11
+ @min_read_buf_sz = val
12
+ @io_buffer.min_buf_sz = val
13
+ end
14
+ private :min_read_buf_sz=
15
+
16
+ def <<(obj)
17
+ file_open && for_writing
18
+ write(obj.to_s)
19
+ self
20
+ end
21
+
22
+ def bytes # deprecated
23
+ to_enum(:enumerate_til_eof, :getbyte, nil, nil)
24
+ end
25
+
26
+ def chars # deprecated
27
+ to_enum(:enumerate_til_eof, :getc, nil, nil)
28
+ end
29
+
30
+ def each(*args, &block)
31
+ sep, lim = separator_limit_args(args)
32
+ # block = block_given? ? Proc.new : nil
33
+ each_common(sep, lim, block)
34
+ end
35
+ alias_method :each_line, :each
36
+
37
+ def each_common(sep, lim, block)
38
+ return enumerate_return(:read, self, block) if sep.nil?
39
+ return enumerate_return(:read_paragraph, self, lim, block) if sep.empty?
40
+ return enumerate_return(:read_line_with_limit, self, sep, lim, block) if lim > 0
41
+ enumerate_return(:read_line, self, sep, block)
42
+ end
43
+
44
+ def each_byte
45
+ block = block_given? ? Proc.new : nil
46
+ enumerate_return(:getbyte, self, block)
47
+ end
48
+
49
+ def each_char
50
+ block = block_given? ? Proc.new : nil
51
+ enumerate_return(:getc, self, block)
52
+ end
53
+
54
+ def each_codepoint
55
+ block = block_given? ? Proc.new : nil
56
+ enumerate_return(:codepoint, self, block)
57
+ end
58
+ alias_method :codepoints, :each_codepoint
59
+
60
+ def codepoint
61
+ file_open && for_reading
62
+ return nil if eof?
63
+ rv = @io_buffer.get_char(@seek_pos)
64
+ @seek_pos += rv.bytesize
65
+ rv.ord
66
+ end
67
+ private :codepoint
68
+
69
+ def getbyte
70
+ file_open && for_reading
71
+ return nil if eof?
72
+ rv = @io_buffer.get_byte(@seek_pos)
73
+ @seek_pos += 1
74
+ rv
75
+ end
76
+
77
+ def getc
78
+ file_open && for_reading
79
+ return nil if eof?
80
+ rv = @io_buffer.get_char(@seek_pos)
81
+ @seek_pos += rv.bytesize
82
+ rv.encode!(internal_encoding) if internal_encoding
83
+ rv
84
+ end
85
+
86
+ def gets(*args)
87
+ file_open && for_reading
88
+ return nil if eof?
89
+ sep, lim = separator_limit_args(args)
90
+ return read if sep.nil?
91
+ return read_paragraph(lim) if sep.empty?
92
+ return read_line_with_limit(sep, lim) if lim > 0
93
+ read_line(sep)
94
+ end
95
+
96
+ def lineno
97
+ file_open && for_reading
98
+ @lineno
99
+ end
100
+
101
+ def lineno=(ln)
102
+ file_open && for_reading
103
+ raise TypeError, "no implicit conversion from #{ln.class.name} to integer" unless ln.respond_to?(:to_int)
104
+ @lineno = ln
105
+ end
106
+
107
+ def lines(*args) # deprecated
108
+ to_enum(:enumerate_til_eof, :gets, self, *args, nil)
109
+ end
110
+
111
+ def print(*args)
112
+ file_open && for_writing
113
+ write(objects_to_str(args, true, $_, $\, $,))
114
+ nil
115
+ end
116
+
117
+ def printf(format_str, *args)
118
+ file_open && for_writing
119
+ write(format(format_str, *args))
120
+ nil
121
+ end
122
+
123
+ def putc(obj)
124
+ file_open && for_writing
125
+ c = obj.is_a?(Integer) ? obj.chr : obj.to_s[0]
126
+ write(c)
127
+ obj
128
+ end
129
+
130
+ def puts(*args)
131
+ file_open && for_writing
132
+ write(objects_to_str(args, false, $/, $/, $/))
133
+ nil
134
+ end
135
+
136
+ def read(len = nil, buffer = nil)
137
+ file_open && for_reading
138
+ if len.nil?
139
+ return "" if eof?
140
+ len = @size
141
+ encode = true
142
+ else
143
+ return nil if eof?
144
+ encode = false
145
+ end
146
+
147
+ rv = @io_buffer.get_str(@seek_pos, len)
148
+ @seek_pos += rv.bytesize
149
+
150
+ if encode
151
+ rv.force_encoding(external_encoding) if external_encoding
152
+ rv.encode!(internal_encoding) if internal_encoding
153
+ end
154
+ buffer.replace(rv) unless buffer.nil?
155
+ rv
156
+ end
157
+
158
+ def readbyte
159
+ file_open && for_reading && not_at_eof
160
+ getbyte
161
+ end
162
+
163
+ def readchar
164
+ file_open && for_reading && not_at_eof
165
+ getc
166
+ end
167
+
168
+ def readline(*args)
169
+ file_open && for_reading && not_at_eof
170
+ gets(*args)
171
+ end
172
+
173
+ def readlines(*args)
174
+ file_open && for_reading
175
+ each(*args).to_a
176
+ end
177
+
178
+ def sync
179
+ file_open
180
+ @io_buffer.sync
181
+ end
182
+
183
+ def sync=(bool)
184
+ file_open
185
+ @io_buffer.sync = bool
186
+ end
187
+
188
+ def ungetbyte(val)
189
+ file_open && for_reading
190
+ str = val.respond_to?(:to_int) ? val.to_int.chr : val.to_str
191
+ @io_buffer.cover_range(@seek_pos, 1)
192
+ @io_buffer.truncate_left(@seek_pos)
193
+ len = @io_buffer.prepend_bytes(str)
194
+ @seek_pos -= len
195
+ nil
196
+ end
197
+
198
+ def ungetc(string)
199
+ file_open && for_reading
200
+ @io_buffer.cover_range(@seek_pos, 1)
201
+ @io_buffer.truncate_left(@seek_pos)
202
+ len = @io_buffer.prepend_str(string)
203
+ @seek_pos -= len
204
+ nil
205
+ end
206
+
207
+ def write(buf)
208
+ file_open && for_writing
209
+ buf = buf.dup
210
+ buf.encode!(external_encoding) if external_encoding
211
+ buf.force_encoding(@binary_encoding)
212
+ rv = @io_buffer.write_to_buffer(@seek_pos, buf)
213
+ update_write_pos(rv)
214
+ rv
215
+ end
216
+
217
+ private
218
+
219
+ #
220
+ # Called from initialize()
221
+ #
222
+ def bio_init
223
+ @lineno = 0 # The current line number - based on gets calls.
224
+ @min_read_buf_sz = MIN_READ_BUF_SZ
225
+ @io_buffer = IOBuffer.new(self, @min_read_buf_sz)
226
+ end
227
+
228
+ def bio_reinit(io_obj)
229
+ @lineno = io_obj.instance_variable_get(:@lineno)
230
+ @io_buffer = IOBuffer.new(self, @min_read_buf_sz)
231
+ end
232
+
233
+ #
234
+ # Return at most lim bytes, or up to end of line.
235
+ #
236
+ def read_line_with_limit(sep, lim)
237
+ file_open
238
+ @io_buffer.cover_range(@seek_pos, lim)
239
+ offset = @io_buffer.buf_offset(@seek_pos)
240
+ if (eol_pos = @io_buffer.buffer.index(sep, offset))
241
+ eol_len = eol_pos - offset + sep.bytesize - 1
242
+ @lineno += 1
243
+ else
244
+ eol_len = lim + 1
245
+ @lineno += 1 if eof?
246
+ end
247
+ read_from_buf([lim, eol_len, @io_buffer.available_bytes(@seek_pos)].min)
248
+ end
249
+
250
+ #
251
+ # Return up to end of line, or end of file.
252
+ #
253
+ def read_line(sep)
254
+ file_open
255
+ @io_buffer.cover_range(@seek_pos, 1)
256
+ offset = @io_buffer.buf_offset(@seek_pos)
257
+ while (eol_pos = @io_buffer.buffer.index(sep, offset)).nil?
258
+ break if @io_buffer.at_eof?
259
+ @io_buffer.extend_right(@min_read_buf_sz)
260
+ end
261
+
262
+ @lineno += 1
263
+ return read_from_buf(@io_buffer.available_bytes(@seek_pos)) unless eol_pos
264
+ read_from_buf(eol_pos - offset + sep.bytesize) if eol_pos
265
+ end
266
+
267
+ #
268
+ # Return len bytes from the buffer, adjusting the current position.
269
+ #
270
+ def read_from_buf(len)
271
+ rv = @io_buffer.get_str(@seek_pos, len)
272
+ @seek_pos += len
273
+ rv.force_encoding(external_encoding) if external_encoding
274
+ rv.encode!(internal_encoding) if internal_encoding
275
+ rv
276
+ end
277
+
278
+ def enumerate_til_eof(meth, rv, *args, block)
279
+ if block
280
+ block.call(send(meth, *args)) until eof?
281
+ else
282
+ yield(send(meth, *args)) until eof?
283
+ end
284
+ rv
285
+ end
286
+
287
+ def enumerate_return(meth, ret, *args, block)
288
+ return to_enum(:enumerate_til_eof, meth, ret, *args, block) if block.nil?
289
+ enumerate_til_eof(meth, ret, *args, block)
290
+ end
291
+
292
+ #
293
+ # Parse separator and limit args.
294
+ #
295
+ def separator_limit_args(args)
296
+ case args.length
297
+ when 0
298
+ return encode_separator($/), 0
299
+ when 1
300
+ return encode_separator($/), args[0].to_int if args[0].respond_to?(:to_int)
301
+ return encode_separator(args[0]), 0
302
+ when 2
303
+ return encode_separator(args[0]), args[1]
304
+ else
305
+ raise ArgumentError, "wrong number of arguments (3 for 0..2)"
306
+ end
307
+ end
308
+
309
+ def encode_separator(sep)
310
+ return nil if sep.nil?
311
+ sep = sep.encode(external_encoding)
312
+ sep.force_encoding(@binary_encoding)
313
+ end
314
+
315
+ def objects_to_str(args, dup_sep_ok = true, default = nil, ors = nil, ofs = nil)
316
+ ret_str = ""
317
+ if args.empty?
318
+ ret_str << default if default
319
+ else
320
+ first_obj = true
321
+ args.each do |obj|
322
+ add_sep(ret_str, ofs, dup_sep_ok) unless first_obj
323
+ ret_str << obj.to_s
324
+ first_obj = false
325
+ end
326
+ end
327
+ add_sep(ret_str, ors, dup_sep_ok)
328
+ ret_str
329
+ end
330
+
331
+ def add_sep(str, sep, dup_sep_ok)
332
+ return unless sep
333
+ return if !dup_sep_ok && str.end_with?(sep)
334
+ str << sep
335
+ end
336
+ end
337
+ end