2019-07-07 11:18:12 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'objspace'
|
|
|
|
|
|
|
|
module Gitlab
|
|
|
|
module Utils
|
|
|
|
class DeepSize
|
|
|
|
Error = Class.new(StandardError)
|
|
|
|
TooMuchDataError = Class.new(Error)
|
|
|
|
|
|
|
|
DEFAULT_MAX_SIZE = 1.megabyte
|
|
|
|
DEFAULT_MAX_DEPTH = 100
|
|
|
|
|
|
|
|
def initialize(root, max_size: DEFAULT_MAX_SIZE, max_depth: DEFAULT_MAX_DEPTH)
|
|
|
|
@root = root
|
2020-03-13 15:44:24 +05:30
|
|
|
@max_size = max_size || DEFAULT_MAX_SIZE
|
|
|
|
@max_depth = max_depth || DEFAULT_MAX_DEPTH
|
2019-07-07 11:18:12 +05:30
|
|
|
@size = 0
|
|
|
|
@depth = 0
|
|
|
|
|
|
|
|
evaluate
|
|
|
|
end
|
|
|
|
|
|
|
|
def valid?
|
|
|
|
!too_big? && !too_deep?
|
|
|
|
end
|
|
|
|
|
2019-12-26 22:10:19 +05:30
|
|
|
def self.human_default_max_size
|
|
|
|
ActiveSupport::NumberHelper.number_to_human_size(DEFAULT_MAX_SIZE)
|
|
|
|
end
|
|
|
|
|
2019-07-07 11:18:12 +05:30
|
|
|
private
|
|
|
|
|
|
|
|
def evaluate
|
|
|
|
add_object(@root)
|
|
|
|
rescue Error
|
|
|
|
# NOOP
|
|
|
|
end
|
|
|
|
|
|
|
|
def too_big?
|
|
|
|
@size > @max_size
|
|
|
|
end
|
|
|
|
|
|
|
|
def too_deep?
|
|
|
|
@depth > @max_depth
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_object(object)
|
|
|
|
@size += ObjectSpace.memsize_of(object)
|
|
|
|
raise TooMuchDataError if @size > @max_size
|
|
|
|
|
|
|
|
add_array(object) if object.is_a?(Array)
|
|
|
|
add_hash(object) if object.is_a?(Hash)
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_array(object)
|
|
|
|
with_nesting do
|
|
|
|
object.each do |n|
|
|
|
|
add_object(n)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_hash(object)
|
|
|
|
with_nesting do
|
|
|
|
object.each do |key, value|
|
|
|
|
add_object(key)
|
|
|
|
add_object(value)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def with_nesting
|
|
|
|
@depth += 1
|
|
|
|
raise TooMuchDataError if too_deep?
|
|
|
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
@depth -= 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|