Class: Fog::AWS::Glacier::TreeHash

Inherits:
Object
  • Object
show all
Defined in:
lib/fog/aws/glacier.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeTreeHash

Returns a new instance of TreeHash.



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/fog/aws/glacier.rb', line 42

def initialize
  @last_chunk_digest = nil	# Digest OBJECT for last chunk (Digest::SHA256)
  @last_chunk_digest_temp = nil	# Digest VALUE for last chunk
  @last_chunk_length = 0	# Length of last chunk, always smaller than 1MB.
  @digest_stack = []
  # First position on stack corresponds to 1MB, second 2MB, third 4MB, fourt 8MB and so on.
  # In any time, the size of all already added parts is equal to sum of all existing (non-nil)
  # positions multiplied by that number, plus last_chunk_length for the remainder smaller than
  # one megabyte. So, if last_chunk_length is half megabyte, stack[0] is filled, stack[1] and 
  # stack[2] empty and stack[3] filled, the size is 0.5MB + 1x1MB + 0x2MB + 0x4MB + 1x8MB = 9.5MB.
end

Class Method Details

.digest(body) ⇒ Object



38
39
40
# File 'lib/fog/aws/glacier.rb', line 38

def self.digest(body)
  new.add_part(body)
end

Instance Method Details

#add_part(bytes) ⇒ Object



81
82
83
84
# File 'lib/fog/aws/glacier.rb', line 81

def add_part(bytes)
  part = self.digest_for_part(bytes)
  part.unpack('H*').first
end

#digestObject



144
145
146
# File 'lib/fog/aws/glacier.rb', line 144

def digest
  reduce_digest_stack(@last_chunk_digest_temp, @digest_stack)
end

#digest_for_part(body) ⇒ Object



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
135
136
137
138
139
140
141
142
# File 'lib/fog/aws/glacier.rb', line 102

def digest_for_part(body)
  part_stack = []
  part_temp = nil
  body_size = body.bytesize
  prepare_body_for_slice(body) {|body, slice|
    start_offset = 0
    if @last_chunk_length != 0
      start_offset = MEGABYTE - @last_chunk_length
      @last_chunk_hash.update(body.send(slice, 0, start_offset))
      hash = @last_chunk_hash.digest
      @last_chunk_digest_temp = hash
      if body_size > start_offset
        @last_chunk_length = 0
        @last_chunk_hash = nil
        @last_chunk_digest_temp = nil
        update_digest_stack(hash, @digest_stack)
      else
        part_temp = hash
        @last_chunk_digest_temp = hash
        @last_chunk_length += body_size
        next
      end
    end
    whole_chunk_count = (body_size - start_offset) / MEGABYTE
    whole_chunk_count.times.each {|chunk_index|
      hash = Digest::SHA256.digest(body.send(slice, start_offset + chunk_index * MEGABYTE, MEGABYTE))
      update_digest_stack(hash, part_stack)
      update_digest_stack(hash, @digest_stack)
    }
    rest_size = body_size - start_offset - whole_chunk_count * MEGABYTE
    if rest_size > 0 || whole_chunk_count == 0
      @last_chunk_hash = Digest::SHA256.new
      @last_chunk_length = rest_size
      @last_chunk_hash.update(body.send(slice, start_offset + whole_chunk_count * MEGABYTE, rest_size))
      hash = @last_chunk_hash.digest
      @last_chunk_digest_temp = hash
      part_temp = hash
    end
  }
  reduce_digest_stack(part_temp, part_stack)
end

#hexdigestObject



148
149
150
# File 'lib/fog/aws/glacier.rb', line 148

def hexdigest
  digest.unpack('H*').first
end

#prepare_body_for_slice(body) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/fog/aws/glacier.rb', line 86

def prepare_body_for_slice(body)
  if body.respond_to? :byteslice
    r = yield(body, :byteslice)
  else
    if body.respond_to? :encoding
      old_encoding = body.encoding
      body.force_encoding('BINARY')
    end
    r = yield(body, :slice)
    if body.respond_to? :encoding
      body.force_encoding(old_encoding)
    end
  end
  r
end

#reduce_digest_stack(digest, stack) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/fog/aws/glacier.rb', line 68

def reduce_digest_stack(digest, stack)
  stack.each_with_index{|s,i|
    unless digest
      digest = stack[i]
      next
    end
    if stack[i]
      digest = Digest::SHA256.digest(stack[i] + digest)
    end
  }
  digest
end

#update_digest_stack(digest, stack) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/fog/aws/glacier.rb', line 54

def update_digest_stack(digest, stack)
  stack.each_with_index{|s,i|
    if s
      digest = Digest::SHA256.digest(s + digest)
      stack[i] = nil
    else
      stack[i] = digest # Update this position with value obtained in previous run of cycle.
      digest = nil
      break
    end
  }
  stack << digest if digest
end