Class: HashMan::Hasher

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

Constant Summary collapse

MIN_ALPHABET_LENGTH =

VERSION = "1.0.2"

16
SEP_DIV =
3.5
GUARD_DIV =
12.0
DEFAULT_SEPS =
"cfhistuCFHISTU"
DEFAULT_ALPHABET =
"abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"1234567890"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(salt = "", min_hash_length = 0, alphabet = DEFAULT_ALPHABET) ⇒ Hasher

Returns a new instance of Hasher.



17
18
19
20
21
22
23
# File 'lib/hashman/hasher.rb', line 17

def initialize(salt = "", min_hash_length = 0, alphabet = DEFAULT_ALPHABET)
  @salt             = salt
  @min_hash_length  = min_hash_length
  @alphabet         = alphabet

  setup_alphabet
end

Instance Attribute Details

#alphabetObject (readonly)

Returns the value of attribute alphabet.



15
16
17
# File 'lib/hashman/hasher.rb', line 15

def alphabet
  @alphabet
end

#guardsObject (readonly)

Returns the value of attribute guards.



15
16
17
# File 'lib/hashman/hasher.rb', line 15

def guards
  @guards
end

#min_hash_lengthObject (readonly)

Returns the value of attribute min_hash_length.



15
16
17
# File 'lib/hashman/hasher.rb', line 15

def min_hash_length
  @min_hash_length
end

#saltObject (readonly)

Returns the value of attribute salt.



15
16
17
# File 'lib/hashman/hasher.rb', line 15

def salt
  @salt
end

#sepsObject (readonly)

Returns the value of attribute seps.



15
16
17
# File 'lib/hashman/hasher.rb', line 15

def seps
  @seps
end

Instance Method Details

#consistent_shuffle(alphabet, salt) ⇒ Object (protected)



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/hashman/hasher.rb', line 142

def consistent_shuffle(alphabet, salt)
  return alphabet if salt.nil? || salt.empty?

  v = 0
  p = 0

  (alphabet.length-1).downto(1) do |i|
    v  = v % salt.length
    p += n = salt[v].ord
    j  = (n + v + p) % i

    tmp_char = alphabet[j]

    alphabet = alphabet[0, j] + alphabet[i] + alphabet[j + 1..-1]
    alphabet = alphabet[0, i] + tmp_char    + alphabet[i + 1..-1]

    v += 1
  end

  alphabet
end

#decode(hash) ⇒ Object



45
46
47
48
49
# File 'lib/hashman/hasher.rb', line 45

def decode(hash)
  return [] if hash.nil? || hash.empty?

  internal_decode(hash, @alphabet)
end

#decode_hex(hash) ⇒ Object



51
52
53
54
55
56
57
58
59
60
# File 'lib/hashman/hasher.rb', line 51

def decode_hex(hash)
  ret = ""
  numbers = decode(hash)

  numbers.length.times do |i|
    ret += numbers[i].to_s(16)[1 .. -1]
  end

  ret.upcase
end

#encode(*numbers) ⇒ Object



25
26
27
28
29
30
31
32
33
# File 'lib/hashman/hasher.rb', line 25

def encode(*numbers)
  numbers.flatten! if numbers.length == 1

  if numbers.empty? || numbers.reject { |n| Integer(n) && n >= 0 }.any?
    ""
  else
    internal_encode(numbers)
  end
end

#encode_hex(str) ⇒ Object



35
36
37
38
39
40
41
42
43
# File 'lib/hashman/hasher.rb', line 35

def encode_hex(str)
  return "" unless hex_string?(str)

  numbers = str.scan(/[\w\W]{1,12}/).map do |num|
    "1#{num}".to_i(16)
  end

  encode(numbers)
end

#hash(input, alphabet) ⇒ Object (protected)



164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/hashman/hasher.rb', line 164

def hash(input, alphabet)
  num = input.to_i
  len = alphabet.length
  res   = ""

  begin
    res = "#{alphabet[num % len]}#{res}"
    num = num.div(alphabet.length)
  end while num > 0

  res
end

#internal_decode(hash, alphabet) ⇒ Object (protected)



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
# File 'lib/hashman/hasher.rb', line 113

def internal_decode(hash, alphabet)
  ret = []

  breakdown = hash.gsub(/[#{@guards}]/, " ")
  array     = breakdown.split(" ")

  i = [3,2].include?(array.length) ? 1 : 0

  if breakdown = array[i]
    lottery   = breakdown[0]
    breakdown = breakdown[1 .. -1].gsub(/[#{@seps}]/, " ")
    array     = breakdown.split(" ")

    array.length.times do |i|
      sub_hash = array[i]
      buffer   = lottery + salt + alphabet
      alphabet = consistent_shuffle(alphabet, buffer[0, alphabet.length])

      ret.push unhash(sub_hash, alphabet)
    end

    if encode(ret) != hash
      ret = []
    end
  end

  ret
end

#internal_encode(numbers) ⇒ Object (protected)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/hashman/hasher.rb', line 64

def internal_encode(numbers)
  ret = ""

  alphabet = @alphabet
  length   = numbers.length
  hash_int = 0

  length.times do |i|
    hash_int += (numbers[i] % (i + 100))
  end

  lottery = ret = alphabet[hash_int % alphabet.length]

  length.times do |i|
    num = numbers[i]
    buf = lottery + salt + alphabet

    alphabet = consistent_shuffle(alphabet, buf[0, alphabet.length])
    last     = hash(num, alphabet)

    ret += last

    if (i + 1) < length
      num %= (last.ord + i)
      ret += seps[num % seps.length]
    end
  end

  if ret.length < min_hash_length
    ret = guards[(hash_int + ret[0].ord) % guards.length] + ret

    if ret.length < min_hash_length
      ret += guards[(hash_int + ret[2].ord) % guards.length]
    end
  end

  half_length = alphabet.length.div(2)

  while(ret.length < min_hash_length)
    alphabet = consistent_shuffle(alphabet, alphabet)
    ret = alphabet[half_length .. -1] + ret + alphabet[0, half_length]

    excess = ret.length - min_hash_length
    ret = ret[excess / 2, min_hash_length] if excess > 0
  end

  ret
end

#unhash(input, alphabet) ⇒ Object (protected)



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/hashman/hasher.rb', line 177

def unhash(input, alphabet)
  num = 0

  input.length.times do |i|
    pos = alphabet.index(input[i])

    raise InputError, "unable to unhash" unless pos

    num += pos * alphabet.length ** (input.length - i - 1)
  end

  num
end