Class: Digest::ED2k

Inherits:
Class
  • Object
show all
Defined in:
lib/digest/ed2k.rb

Overview

Note:

Due to ed2k’s use of 9500KB chunks, this object can be up to around 10MB large in memory, not counting OpenSSL::Digest::MD4’s state.

Digest subclass that calculates an ed2k hash.

The implemented algorithm is as described on wiki.anidb.net/w/Ed2k, using the “red code” method (appending null to hashlist).

Uses OpenSSL::Digest::MD4 internally for the MD4 hashing.

Constant Summary collapse

VERSION =

The version

'1.0.0'
CHUNK_SIZE =

Chunk size of the ed2k hash, 9500KB.

9728000

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(initial_chunk = nil) ⇒ ED2k

Creates a reset object.

Parameters:

  • initial_chunk (defaults to: nil)

    optionally appends data to the new object



26
27
28
29
30
# File 'lib/digest/ed2k.rb', line 26

def initialize (initial_chunk = nil)
    @md4 = OpenSSL::Digest::MD4.new
    self.reset
    self << initial_chunk if initial_chunk
end

Class Method Details

.digest(data) ⇒ Object

Calculates and returns the #digest on data.



175
176
177
# File 'lib/digest/ed2k.rb', line 175

def self.digest (data)
    return new(data).digest
end

.file(path) ⇒ Object

The same as calling #file(path) on a new object.



190
191
192
# File 'lib/digest/ed2k.rb', line 190

def self.file(path)
    return new.file path
end

.hexdigest(data) ⇒ Object

Calculates and returns the #hexdigest on data.



180
181
182
# File 'lib/digest/ed2k.rb', line 180

def self.hexdigest (data)
    return new(data).hexdigest
end

.io(io) ⇒ Object

The same as calling #io(io) on a new object.



185
186
187
# File 'lib/digest/ed2k.rb', line 185

def self.io(io)
    return new.io io
end

Instance Method Details

#digest(str = nil) ⇒ String

Note:

Due to how ed2k hashes work, once the digest has been obtained, the object must be #reset before being reused. The #digest! family of methods automatically resets the object after being called.

#finalizes the digest and returns it.

Parameters:

  • str (defaults to: nil)

    if present and non-nil, will cause digest to reset self, update it with ‘str` and return the new digest.

Returns:

  • (String)

    the raw digest



92
93
94
95
96
97
98
99
100
101
# File 'lib/digest/ed2k.rb', line 92

def digest(str = nil)
    unless str
        self.finalize
        @md4.digest
    else
        reset
        self << str
        digest
    end
end

#digest!String

Note:

Due to how ed2k hashes work, once the digest has been obtained, the object must be #reset before being reused. The #digest! family of methods automatically resets the object after being called.

Note:

calls #reset on self after obtaining digest

#finalizes the digest and returns it.

Parameters:

  • str

    if present and non-nil, will cause digest to reset self, update it with ‘str` and return the new digest.

Returns:

  • (String)

    the raw digest



105
106
107
108
109
# File 'lib/digest/ed2k.rb', line 105

def digest!
    ret = digest
    reset
    return ret
end

#file(path) ⇒ Object

Opens the file pointed by path and calls #io with it.

Parameters:

  • path (String)

    path to a file that will be read

Returns:

  • self for convenient chaining.



50
51
52
53
54
# File 'lib/digest/ed2k.rb', line 50

def file (path)
    File.open(path) do |f|
        return self.io f
    end
end

#finalizeObject

Finalizes the digest and prevents any new data from being added to it. This is automatically called by the #digest family of methods. The only way to unlock the object is to #reset it.

Returns:

  • self for convenient chaining.



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/digest/ed2k.rb', line 141

def finalize
    unless @finalized
        if @rounds > 0
            @md4 << OpenSSL::Digest::MD4.digest(@buf)
        else
            @md4.reset
            @md4 << @buf
        end
        @buf = nil
        @finalized = true
    end
    return self
end

#hexdigest(str = nil) ⇒ String

Note:

Due to how ed2k hashes work, once the digest has been obtained, the object must be #reset before being reused. The #digest! family of methods automatically resets the object after being called.

#finalizes the digest and returns it.

Parameters:

  • str (defaults to: nil)

    if present and non-nil, will cause digest to reset self, update it with ‘str` and return the new digest.

Returns:

  • (String)

    the digest as a hexadecimal string



115
116
117
118
119
120
121
122
123
124
# File 'lib/digest/ed2k.rb', line 115

def hexdigest(str = nil)
    unless str
        self.finalize
        @md4.hexdigest
    else
        reset
        self << str
        hexdigest
    end
end

#hexdigest!String

Note:

Due to how ed2k hashes work, once the digest has been obtained, the object must be #reset before being reused. The #digest! family of methods automatically resets the object after being called.

Note:

calls #reset on self after obtaining digest

#finalizes the digest and returns it.

Parameters:

  • str

    if present and non-nil, will cause digest to reset self, update it with ‘str` and return the new digest.

Returns:

  • (String)

    the digest as a hexadecimal string



130
131
132
133
134
# File 'lib/digest/ed2k.rb', line 130

def hexdigest!
    ret = hexdigest
    reset
    return ret
end

#inspectObject

Override for Object’s inspect



156
157
158
159
# File 'lib/digest/ed2k.rb', line 156

def inspect
    return "#<ed2k unfinalized>" unless @finalized
    return "#<ed2k hash=\"#{digest}\">"
end

#io(io) ⇒ Object

Reads the contents of ‘io`, 9500KB at a time, until EOF, and add to the digest.

Parameters:

  • io (IO)

    the IO to be read

Returns:

  • self for convenient chaining.



37
38
39
40
41
42
43
44
# File 'lib/digest/ed2k.rb', line 37

def io (io)
    # why do I have to use a while instead of an each_chunk or sth
    buf = ""
    while io.read CHUNK_SIZE, buf
        self << buf
    end
    return self
end

#resetObject

Resets the state; effectively the same as constructing a new object.

Returns:

  • self for convenient chaining.



58
59
60
61
62
63
64
# File 'lib/digest/ed2k.rb', line 58

def reset
    @md4.reset
    @buf = ""
    @rounds = 0
    @finalized = false
    return self
end

#update(data) ⇒ Object Also known as: <<

Append ‘data` to the digest. Will raise an ArgumentError if the object has been #finalized before.

Every 9500KB of accumulated data will be hashed as per the ed2k algorithm.

Parameters:

  • data (String)

    the string to be appended to the digest

Returns:

  • self for convenient chaining.



73
74
75
76
77
78
79
80
# File 'lib/digest/ed2k.rb', line 73

def update (data)
    if @finalized
        raise ArgumentError.new("Can't add to an ed2k hash after finalizing. Call reset if you want to calculate a new hash.")
    end
    @buf += data
    _sync
    return self
end