80 lines
		
	
	
		
			1.5 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			80 lines
		
	
	
		
			1.5 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # 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
 | |
|         @max_size = max_size || DEFAULT_MAX_SIZE
 | |
|         @max_depth = max_depth || DEFAULT_MAX_DEPTH
 | |
|         @size = 0
 | |
|         @depth = 0
 | |
| 
 | |
|         evaluate
 | |
|       end
 | |
| 
 | |
|       def valid?
 | |
|         !too_big? && !too_deep?
 | |
|       end
 | |
| 
 | |
|       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
 |