Class: Flexr::Runtime::Buffer

Inherits:
Object
  • Object
show all
Defined in:
lib/flexr/runtime/buffer.rb

Constant Summary collapse

DEFAULT_CHUNK_SIZE =
64 * 1024

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, chunk_size: DEFAULT_CHUNK_SIZE, max_buffer_size: 64 * 1024 * 1024, retain_input: true, filename: nil) ⇒ Buffer

Returns a new instance of Buffer.

Raises:

  • (ArgumentError)


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/flexr/runtime/buffer.rb', line 10

def initialize(input, chunk_size: DEFAULT_CHUNK_SIZE, max_buffer_size: 64 * 1024 * 1024,
               retain_input: true, filename: nil)
  raise ArgumentError, "chunk_size must be positive" unless chunk_size.to_i.positive?
  raise ArgumentError, "max_buffer_size must be non-negative" if max_buffer_size.to_i.negative?

  @chunk_size = chunk_size.to_i
  @max_buffer_size = max_buffer_size.to_i
  @retain_input = retain_input
  @filename = filename
  @base_offset = 0
  @window_start = 0
  @io = input.is_a?(String) ? nil : input
  raise ArgumentError, "input must be a String or IO" if @io && !@io.respond_to?(:read)

  @source = input.is_a?(String) ? input : String.new(encoding: Encoding::BINARY)
  @eof = @io.nil?
  ensure_buffer_size!(@source.bytesize)
end

Instance Attribute Details

#base_offsetObject (readonly)

Returns the value of attribute base_offset.



8
9
10
# File 'lib/flexr/runtime/buffer.rb', line 8

def base_offset
  @base_offset
end

#max_buffer_sizeObject (readonly)

Returns the value of attribute max_buffer_size.



8
9
10
# File 'lib/flexr/runtime/buffer.rb', line 8

def max_buffer_size
  @max_buffer_size
end

Instance Method Details

#bytesizeObject



29
30
31
32
33
# File 'lib/flexr/runtime/buffer.rb', line 29

def bytesize
  return @source.bytesize if @window_start.zero? && base_offset.zero?

  base_offset + retained_bytesize
end

#byteslice(range, length = nil) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/flexr/runtime/buffer.rb', line 56

def byteslice(range, length = nil)
  ensure_range(range, length)
  if @window_start.zero? && base_offset.zero?
    return length ? @source.byteslice(range, length) : @source.byteslice(range)
  end

  if length
    @source.byteslice(storage_position(range), length)
  else
    @source.byteslice(storage_range(range))
  end
end

#discard_before(position) ⇒ Object



141
142
143
144
145
146
147
148
149
# File 'lib/flexr/runtime/buffer.rb', line 141

def discard_before(position)
  return if @retain_input || position <= base_offset

  ending = [position, bytesize].min
  removed = ending - base_offset
  @window_start += removed
  @base_offset = ending
  compact_storage!
end

#ensure_available?(end_position) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/flexr/runtime/buffer.rb', line 69

def ensure_available?(end_position)
  return true if end_position <= bytesize
  return false if @eof

  while bytesize < end_position && !@eof
    chunk = read_chunk
    if chunk.nil? || chunk.empty?
      @eof = true
      break
    end

    @source.force_encoding(chunk.encoding) if @source.empty?
    @source << chunk
  end
  bytesize >= end_position
end

#eof?(position) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
# File 'lib/flexr/runtime/buffer.rb', line 95

def eof?(position)
  !ensure_available?(position + 1)
end

#eof_loaded?Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/flexr/runtime/buffer.rb', line 91

def eof_loaded?
  @eof
end

#getbyte(position) ⇒ Object



49
50
51
52
53
54
# File 'lib/flexr/runtime/buffer.rb', line 49

def getbyte(position)
  return @source.getbyte(position) if @window_start.zero? && base_offset.zero? && position < @source.bytesize

  ensure_available?(position + 1)
  @source.getbyte(storage_position(position))
end

#read_to_endObject



86
87
88
89
# File 'lib/flexr/runtime/buffer.rb', line 86

def read_to_end
  ensure_available?(Float::INFINITY)
  source
end

#retained_bytesizeObject



35
36
37
# File 'lib/flexr/runtime/buffer.rb', line 35

def retained_bytesize
  @source.bytesize - @window_start
end

#sourceObject



39
40
41
42
43
# File 'lib/flexr/runtime/buffer.rb', line 39

def source
  return @source if @window_start.zero?

  @source.byteslice(@window_start..).to_s
end

#stable_sourceObject



45
46
47
# File 'lib/flexr/runtime/buffer.rb', line 45

def stable_source
  @source if @retain_input && @window_start.zero? && base_offset.zero?
end

#utf8_boundary?(position) ⇒ Boolean

Returns:

  • (Boolean)


99
100
101
102
103
104
105
106
# File 'lib/flexr/runtime/buffer.rb', line 99

def utf8_boundary?(position)
  return false if position < base_offset
  return true if position == base_offset
  return true unless ensure_available?(position)

  following = getbyte(position)
  following.nil? || (following & 0xc0) != 0x80
end

#utf8_character_length(position) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/flexr/runtime/buffer.rb', line 126

def utf8_character_length(position)
  first = getbyte(position)
  return 0 unless first
  return 1 if first <= 0x7f

  length = if first <= 0xdf
    2
  elsif first <= 0xef
    3
  else
    4
  end
  valid_utf8_at?(position) ? length : 1
end

#valid_utf8_at?(position) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/flexr/runtime/buffer.rb', line 108

def valid_utf8_at?(position)
  first = getbyte(position)
  return false unless first
  return true if first <= 0x7f

  length = if first.between?(0xc2, 0xdf)
    2
  elsif first.between?(0xe0, 0xef)
    3
  elsif first.between?(0xf0, 0xf4)
    4
  end
  return false unless length
  return false unless ensure_available?(position + length)

  byteslice(position, length).dup.force_encoding(Encoding::UTF_8).valid_encoding?
end