Class: FormatParser::ReadLimiter

Inherits:
Object
  • Object
show all
Defined in:
lib/read_limiter.rb

Overview

Is used to limit the number of reads/seeks parsers can perform

Defined Under Namespace

Classes: BudgetExceeded

Constant Summary collapse

NO_LIMIT =
nil

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io, max_bytes: NO_LIMIT, max_reads: NO_LIMIT, max_seeks: NO_LIMIT) ⇒ ReadLimiter

Creates a ReadLimiter wrapper around the given IO object and sets the limits on the number of reads/writes

Parameters:

  • io (#seek, #pos, #size, #read)

    the IO object to wrap

  • max_bytes (Integer, nil) (defaults to: NO_LIMIT)

    how many bytes can we read from this before an exception is raised

  • max_reads (Integer, nil) (defaults to: NO_LIMIT)

    how many read() calls can we perform on this before an exception is raised

  • max_seeks (Integer, nil) (defaults to: NO_LIMIT)

    how many seek() calls can we perform on this before an exception is raised



17
18
19
20
21
22
23
24
25
26
# File 'lib/read_limiter.rb', line 17

def initialize(io, max_bytes: NO_LIMIT, max_reads: NO_LIMIT, max_seeks: NO_LIMIT)
  @max_bytes = max_bytes
  @max_reads = max_reads
  @max_seeks = max_seeks

  @io = io
  @seeks = 0
  @reads = 0
  @bytes = 0
end

Instance Attribute Details

#bytesObject (readonly)

Returns the value of attribute bytes.



5
6
7
# File 'lib/read_limiter.rb', line 5

def bytes
  @bytes
end

#readsObject (readonly)

Returns the value of attribute reads.



5
6
7
# File 'lib/read_limiter.rb', line 5

def reads
  @reads
end

#seeksObject (readonly)

Returns the value of attribute seeks.



5
6
7
# File 'lib/read_limiter.rb', line 5

def seeks
  @seeks
end

Instance Method Details

#posObject

Returns the current position/offset within the IO

Returns:

  • Integer



38
39
40
# File 'lib/read_limiter.rb', line 38

def pos
  @io.pos
end

#read(n_bytes) ⇒ String?

Returns at most ‘n_bytes` of data from the IO or less if less data was available before the EOF was hit

Parameters:

  • n_bytes (Integer)

Returns:

  • (String, nil)

    the content read from the IO or ‘nil` if no data was available



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/read_limiter.rb', line 59

def read(n_bytes)
  @bytes += n_bytes
  @reads += 1

  if @max_bytes && @bytes > @max_bytes
    raise BudgetExceeded, 'Read bytes budget (%d) exceeded' % @max_bytes
  end

  if @max_reads && @reads > @max_reads
    raise BudgetExceeded, 'Number of read() calls exceeded (%d max)' % @max_reads
  end

  @io.read(n_bytes)
end

#reset_limits!Object

Resets all the recorded call counters so that the object can be reused for the next parser, which will have it’s own limits

Returns:

  • void



88
89
90
91
92
# File 'lib/read_limiter.rb', line 88

def reset_limits!
  @seeks = 0
  @reads = 0
  @bytes = 0
end

#seek(to) ⇒ Object

Seeks the IO to the given absolute offset from the start of the file/resource

Parameters:

  • to (Integer)

    offset in the IO

Returns:

  • Integer



46
47
48
49
50
51
52
# File 'lib/read_limiter.rb', line 46

def seek(to)
  @seeks += 1
  if @max_seeks && @seeks > @max_seeks
    raise BudgetExceeded, 'Seek budget exceeded (%d seeks performed)' % @max_seeks
  end
  @io.seek(to)
end

#send_metrics(prefix) ⇒ Object

Sends the metrics about the state of this ReadLimiter to a Measurometer

Parameters:

  • prefix (String)

    the prefix to set. For example, with prefix “TIFF” the metrics will be called ‘format_parser.TIFF.read_limiter.num_seeks` and so forth

Returns:

  • void



79
80
81
82
83
# File 'lib/read_limiter.rb', line 79

def send_metrics(prefix)
  Measurometer.add_distribution_value('format_parser.%s.read_limiter.num_seeks' % prefix, @seeks)
  Measurometer.add_distribution_value('format_parser.%s.read_limiter.num_reads' % prefix, @reads)
  Measurometer.add_distribution_value('format_parser.%s.read_limiter.read_bytes' % prefix, @bytes)
end

#sizeObject

Returns the size of the resource contained in the IO

Returns:

  • Integer



31
32
33
# File 'lib/read_limiter.rb', line 31

def size
  @io.size
end