Class: FilterIO

Inherits:
Object
  • Object
show all
Defined in:
lib/filter_io.rb,
lib/filter_io/version.rb

Defined Under Namespace

Classes: BlockState, NeedMoreData

Constant Summary collapse

DEFAULT_BLOCK_SIZE =
1024
VERSION =
'0.2.8'

Instance Method Summary collapse

Constructor Details

#initialize(io, options = nil, &block) ⇒ FilterIO

Returns a new instance of FilterIO.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/filter_io.rb', line 17

def initialize(io, options = nil, &block)
  @io = io
  @options = options || {}
  @block = block
  @source_pos = 0
  @pos = 0
  @buffer = empty_string
  @buffer_raw = empty_string_raw

  invalid_options = @options.keys - [:block_size]
  unless invalid_options.empty?
    raise ArgumentError, "Invalid options: #{invalid_options.join ', '}"
  end
end

Instance Method Details

#bof?Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/filter_io.rb', line 36

def bof?
  @pos == 0
end

#closeObject



48
49
50
# File 'lib/filter_io.rb', line 48

def close
  @io.close
end

#closed?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/filter_io.rb', line 52

def closed?
  @io.closed?
end

#default_encodingObject



56
57
58
59
60
61
62
63
# File 'lib/filter_io.rb', line 56

def default_encoding
  unless @default_encoding
    c = @io.getc
    @io.ungetc c
    @default_encoding = c.encoding
  end
  @default_encoding
end

#each_line(sep_string = $/) ⇒ Object Also known as: each, lines



242
243
244
245
246
247
248
249
250
# File 'lib/filter_io.rb', line 242

def each_line(sep_string = $/)
  unless block_given?
    return to_enum(:each_line, sep_string)
  end
  while line = gets(sep_string)
    yield line
  end
  self
end

#eof?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/filter_io.rb', line 40

def eof?
  @buffer.empty? && source_eof?
end

#external_encodingObject



73
74
75
76
77
78
79
# File 'lib/filter_io.rb', line 73

def external_encoding
  if @io.respond_to?(:external_encoding)
    @io.external_encoding
  else
    default_encoding
  end
end

#getcObject



95
96
97
98
99
# File 'lib/filter_io.rb', line 95

def getc
  readchar
rescue EOFError
  nil
end

#gets(*args) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/filter_io.rb', line 175

def gets(*args)
  sep_string, limit = case args.size
  when 0
    [$/, nil]
  when 1
    if args.first.is_a?(Integer)
      [$/, args.first]
    else
      [args.first, nil]
    end
  when 2
    args
  else
    raise ArgumentError
  end

  return nil if eof?
  return read if sep_string.nil?

  paragraph_mode = sep_string == ''
  sep_string = "\n\n" if paragraph_mode
  sep_string = sep_string.to_s unless sep_string.is_a? String

  if paragraph_mode
    # consume any leading newlines
    char = getc
    char = getc while char && char.ord == 10
    if char
      ungetc char # push the first non-newline back onto the buffer
    else
      return nil # nothing left except newlines, bail out
    end
  end

  # fill the buffer until it contains the separator sequence
  until source_eof? || find_bytes(sep_string) || (limit && @buffer.bytesize >= limit)
    buffer_data @options[:block_size]
  end

  # calculate how much of the buffer to return
  length = if idx = find_bytes(sep_string)
    # we found the separator, include it in our output
    length = idx + sep_string.bytesize
  else
    # no separator found (must be EOF). return everything we've got
    length = @buffer.bytesize
  end
  if limit && length > limit
    length = limit
  end

  # extract the requested number of byte from the buffer
  data = pop_bytes(length).force_encoding(@buffer.encoding)
  # continue retreiving more bytes until we have complete characters
  while limit && !data.valid_encoding? && (@buffer.bytesize > 0 || !source_eof?)
    data += pop_bytes(1).force_encoding(@buffer.encoding)
  end
  # increment the position
  @pos += data.bytesize

  data
end

#internal_encodingObject



65
66
67
68
69
70
71
# File 'lib/filter_io.rb', line 65

def internal_encoding
  if @io.respond_to?(:internal_encoding)
    @io.internal_encoding
  else
    default_encoding
  end
end

#posObject



32
33
34
# File 'lib/filter_io.rb', line 32

def pos
  @pos
end

#read(length = nil, buffer = nil) ⇒ Object

Raises:

  • (ArgumentError)


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/filter_io.rb', line 101

def read(length = nil, buffer = nil)
  raise ArgumentError if length && length < 0
  return '' if length == 0

  # fill the buffer up to the fill level (or whole input if length is nil)
  while (!source_eof? || !@did_first_read) && (length.nil? || length > @buffer.bytesize)
    @did_first_read = true
    buffer_data @options[:block_size] || length
  end

  # we now have all the data in the buffer that we need (or can get if EOF)
  case
  when @buffer.bytesize > 0
    # limit length to the buffer size if we were asked for it all or have ran out (EOF)
    read_length = if length.nil? or length > @buffer.bytesize
      @buffer.bytesize
    else
      length
    end
    data = pop_bytes read_length, buffer
    @pos += data.bytesize
    if length.nil?
      data.force_encoding external_encoding if external_encoding
      data.encode! internal_encoding if internal_encoding
    end
    data
  when source_eof?
    # end of file, nothing in the buffer to return
    buffer.replace empty_string if buffer
    length.nil? ? empty_string : nil
  else
    raise IOError, 'Read error'
  end
end

#readcharObject

Raises:

  • (EOFError)


81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/filter_io.rb', line 81

def readchar
  raise EOFError, 'end of file reached' if eof?
  data = empty_string_raw
  begin
    byte = read(1)
    if internal_encoding || external_encoding
      byte.force_encoding internal_encoding || external_encoding
    end
    data << byte
  end until data.valid_encoding? or source_eof?
  data.encode! internal_encoding if internal_encoding
  data
end

#readline(sep_string = $/) ⇒ Object



238
239
240
# File 'lib/filter_io.rb', line 238

def readline(sep_string = $/)
  gets(sep_string) or raise EOFError, 'end of file reached'
end

#readlines(sep_string = $/) ⇒ Object



254
255
256
257
258
# File 'lib/filter_io.rb', line 254

def readlines(sep_string = $/)
  lines = []
  each_line(sep_string) { |line| lines << line }
  lines
end

#rewindObject



136
137
138
# File 'lib/filter_io.rb', line 136

def rewind
  seek 0, IO::SEEK_SET
end

#seek(offset, whence = IO::SEEK_SET) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/filter_io.rb', line 140

def seek(offset, whence = IO::SEEK_SET)
  new_pos = case whence
  when IO::SEEK_SET
    offset
  when IO::SEEK_CUR
    pos + offset
  when IO::SEEK_END
    raise Errno::EINVAL, 'SEEK_END not supported'
  else
    raise Errno::EINVAL
  end

  case new_pos
  when pos
    # noop
  when 0
    @io.rewind
    @source_pos = 0
    @pos = 0
    @buffer = empty_string
    @buffer_raw = empty_string_raw
  else
    raise Errno::EINVAL, 'Random seek not supported'
  end

  0
end

#source_eof?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/filter_io.rb', line 44

def source_eof?
  @buffer_raw.empty? && @io.eof?
end

#ungetc(char) ⇒ Object



168
169
170
171
172
173
# File 'lib/filter_io.rb', line 168

def ungetc(char)
  char = char.chr
  @pos -= char.bytesize
  @pos = 0 if @pos < 0
  @buffer = char + @buffer
end