/lib/arachni/database/hash.rb

https://bitbucket.org/moon2l/arachni-scanner · Ruby · 384 lines · 165 code · 33 blank · 186 comment · 19 complexity · fd4dde8b7d86d911be582bbd3f6fd1b0 MD5 · raw file

  1. =begin
  2. Arachni
  3. Copyright (c) 2010-2012 Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
  4. This is free software; you can copy and distribute and modify
  5. this program under the term of the GPL v2.0 License
  6. (See LICENSE file for details)
  7. =end
  8. require 'digest/sha1'
  9. module Arachni
  10. module Database
  11. #
  12. # Flat-file Hash implementation
  13. #
  14. # Behaves pretty much like a Ruby Hash however it transparently serializes and
  15. # saves its values to the file-system under the OS's temp directory.
  16. #
  17. # It's not interchangeable with Ruby's Hash as it lacks a lot of the
  18. # stdlib methods.
  19. #
  20. # @author: Tasos "Zapotek" Laskos
  21. # <tasos.laskos@gmail.com>
  22. # <zapotek@segfault.gr>
  23. # @version: 0.1
  24. #
  25. class Hash < Base
  26. #
  27. # @see Arachni::Database::Base#initialize
  28. #
  29. def initialize( *args )
  30. super( *args )
  31. # holds the internal representation of the Hash
  32. # same keys as self but the values are actually pointing to filepaths
  33. # where the real values are being stores
  34. @h = ::Hash.new
  35. # holds a key-value pair of self with digests as values
  36. # in order to allow comparisons without requiring to load
  37. # the actual values from their files.
  38. @eql_h = ::Hash.new
  39. end
  40. #
  41. # Associates the given value with the given key.
  42. #
  43. # @param [Object] k key
  44. # @param [Object] v value
  45. #
  46. def []=( k, v )
  47. @h[k] = dump( v ) {
  48. |serialized|
  49. @eql_h[k] = eql_hash( serialized )
  50. }
  51. end
  52. alias :store :[]=
  53. #
  54. # Retrieves the value object corresponding to the key object,
  55. # nil otherwise.
  56. #
  57. # @param [Obj] k key
  58. #
  59. # @return [Object]
  60. #
  61. def []( k )
  62. load( @h[k] ) if @h[k]
  63. end
  64. #
  65. # Returns an array containing the given key and its value.
  66. #
  67. # @param [Object] k key
  68. #
  69. # @return [Array]
  70. #
  71. def assoc( k )
  72. return if !@h[k]
  73. [ k, self[k] ]
  74. end
  75. #
  76. # Returns an array containing the key for the given value and that value.
  77. #
  78. # @param [Object] v value
  79. #
  80. # @return [Array]
  81. #
  82. def rassoc( v )
  83. return if !value?( v )
  84. [ key( v ), v ]
  85. end
  86. #
  87. # Removes an entry by key and returns its value.
  88. #
  89. # If the key doesn't exist and a block has been provided it's passed
  90. # the key and the method returns the result of that block.
  91. #
  92. # @param [Object] k key
  93. #
  94. # @return [Object]
  95. #
  96. def delete( k, &block )
  97. if @h[k]
  98. obj = load_and_delete_file( @h[k] )
  99. @h.delete( k )
  100. @eql_h.delete( k )
  101. return obj
  102. else
  103. block.call( k ) if block_given?
  104. end
  105. end
  106. #
  107. # Removes the first key-value pair from the hash and returns it as a array,
  108. #
  109. # @return [Array]
  110. #
  111. def shift
  112. k, v = @h.shift
  113. [ k, load_and_delete_file( v ) ]
  114. end
  115. #
  116. # Calls block with each key-value pair.
  117. #
  118. # If a block has been given it retuns self.<br/>
  119. # If no block has been given it returns an enumerator.
  120. #
  121. # @param [Proc] block
  122. #
  123. def each( &block )
  124. if block_given?
  125. @h.each {
  126. |k, v|
  127. block.call( [ k, self[k] ] )
  128. }
  129. self
  130. else
  131. enum_for( :each )
  132. end
  133. end
  134. alias :each_pair :each
  135. #
  136. # Calls block with each key.
  137. #
  138. # If a block has been given it returns self.<br/>
  139. # If no block has been given it returns an enumerator.
  140. #
  141. # @param [Proc] block
  142. #
  143. def each_key( &block )
  144. if block_given?
  145. @h.each_key( &block )
  146. self
  147. else
  148. enum_for( :each_key )
  149. end
  150. end
  151. #
  152. # Calls block with each value.
  153. #
  154. # If a block has been given it retuns self.<br/>
  155. # If no block has been given it returns an enumerator.
  156. #
  157. # @param [Proc] block
  158. #
  159. def each_value( &block )
  160. if block_given?
  161. @h.keys.each {
  162. |k|
  163. block.call( self[k] )
  164. }
  165. self
  166. else
  167. enum_for( :each_value )
  168. end
  169. end
  170. #
  171. # Returns all keys as an array.
  172. #
  173. # @return [Array] keys
  174. #
  175. def keys
  176. @h.keys
  177. end
  178. #
  179. # Returns the key for the given value.
  180. #
  181. # @param [Object] val
  182. #
  183. # @return [Object] key
  184. #
  185. def key( val )
  186. return if !value?( val )
  187. each {
  188. |k, v|
  189. return k if val == self[val]
  190. }
  191. end
  192. #
  193. # Returns all values as an array.
  194. #
  195. # @return [Array] values
  196. #
  197. def values
  198. each_value.to_a
  199. end
  200. #
  201. # Returns true if the given key exists in the hash, false otherwise.
  202. #
  203. # @return [Bool]
  204. #
  205. def include?( k )
  206. @h.include?( k )
  207. end
  208. alias :member? :include?
  209. alias :key? :include?
  210. alias :has_key? :include?
  211. #
  212. # Returns true if the given value exists in the hash, false otherwise.
  213. #
  214. # @return [Bool]
  215. #
  216. def value?( v )
  217. each_value {
  218. |val|
  219. return true if val == v
  220. }
  221. return false
  222. end
  223. #
  224. # Merges the contents of self with the contents of the given hash and
  225. # returns them in a new object.
  226. #
  227. # @param [Hash] h
  228. #
  229. # @return [Arachni::Database::Hash]
  230. #
  231. def merge( h )
  232. self.class.new( serializer ).merge!( self ).merge!( h )
  233. end
  234. #
  235. # Merges self with the contents of the given hash and returns self.
  236. #
  237. # If the given Hash is of the same type as self then the values will
  238. # not be loaded during the merge in order to keep memory usage down.
  239. #
  240. # If the given Hash is any other kind of object it will be coerced
  241. # to a Hash by calling 'to_hash' on it and the merging it with self.
  242. #
  243. # @param [Hash] h
  244. #
  245. def merge!( h )
  246. if !h.is_a?( self.class )
  247. h.to_hash.each {
  248. |k, v|
  249. delete( k ) if @h.include?( k )
  250. self[k] = v
  251. }
  252. else
  253. h._internal.each {
  254. |k, v|
  255. delete( k ) if @h.include?( k )
  256. @h[k] = v
  257. }
  258. @eql_h.merge!( h._eql_h )
  259. end
  260. self
  261. end
  262. alias :update :merge!
  263. #
  264. # Converts self to a Ruby Hash
  265. #
  266. # @return [Hash]
  267. #
  268. def to_hash
  269. h = {}
  270. each { |k, v| h[k] = v }
  271. return h
  272. end
  273. alias :to_h :to_hash
  274. #
  275. # Converts self to a Ruby Array
  276. #
  277. # @return [Array]
  278. #
  279. def to_a
  280. to_hash.to_a
  281. end
  282. #
  283. # Size of the Queue, the number of objects it currently holds.
  284. #
  285. # @return [Integer]
  286. #
  287. def size
  288. @h.size
  289. end
  290. alias :length :size
  291. #
  292. # True if the Queue if empty, false otherwise.
  293. #
  294. # @return [Bool]
  295. #
  296. def empty?
  297. @h.empty?
  298. end
  299. #
  300. # Removes all objects from the Queue.
  301. #
  302. def clear
  303. @h.values.each { |filepath| delete_file( filepath ) }
  304. @h.clear
  305. end
  306. #
  307. # Returns true if self and the given hash contain the same key-pair
  308. # values.
  309. #
  310. # If the given hash is not of the same type as self it will be coerced
  311. # to a Ruby Hash by calling 'to_hash' on it.
  312. #
  313. def ==( h )
  314. if !h.is_a?( self.class )
  315. eql = {}
  316. h.to_hash.each {
  317. |k, v|
  318. eql[k] = eql_hash( v.to_yaml )
  319. }
  320. @eql_h == eql
  321. else
  322. @eql_h == h._eql_h
  323. end
  324. end
  325. alias :eql? :==
  326. #
  327. # Returns the internal representation of self.
  328. #
  329. # It will return a Ruby Hash with the same values as self but
  330. # with filepaths as values (pointing to the files that store them).
  331. #
  332. # This is used for efficient merging, i.e. without requiring to load
  333. # the actual values when merging 2 objects.
  334. #
  335. # @return [Hash]
  336. #
  337. def _internal
  338. @h.dup
  339. end
  340. def _eql_h
  341. @eql_h.dup
  342. end
  343. private
  344. def eql_hash( str )
  345. Digest::SHA1.hexdigest( str )
  346. end
  347. end
  348. end
  349. end