2011-12-21 28 views
6

En mi código, necesito archivos hash usando una variedad de algoritmos, incluido CRC32. Como también estoy usando otras funciones hash criptográficas en la familia Digest, pensé que sería bueno mantener una interfaz coherente para todas ellas.Resumen :: CRC32 con Zlib

Para el registro, encontré digest-crc, una joya que hace exactamente lo que quiero. El caso es que Zlib es parte de la biblioteca estándar y tiene una implementación activa de CRC32 que me gustaría volver a utilizar. Además, está escrito en C por lo que debe ofrecer un rendimiento superior en relación con digest-crc, que es una implementación de rubí puro.

La implementación de Digest::CRC32 en realidad parecía bastante sencillo en un primer momento:

%w(digest zlib).each { |f| require f } 

class Digest::CRC32 < Digest::Class 
    include Digest::Instance 

    def update(str) 
    @crc32 = Zlib.crc32(str, @crc32) 
    end 

    def initialize; reset; end 
    def reset; @crc32 = 0; end 
    def finish; @crc32.to_s; end 
end 

Todo se ve bien:

crc32 = File.open('Rakefile') { |f| Zlib.crc32 f.read } 
digest = Digest::CRC32.file('Rakefile').digest!.to_i 
crc32 == digest 
=> true 

Por desgracia, no todo funciona:

Digest::CRC32.file('Rakefile').hexdigest! 
=> "313635393830353832" 

# What I actually expected was: 
Digest::CRC32.file('Rakefile').digest!.to_i.to_s(16) 
=> "9e4a9a6" 

hexdigest vuelve básicamente Digest.hexencode(digest), which works with the value of the digest at the byte level. No estoy seguro de cómo funciona esa función, así que me preguntaba si es posible lograr esto con solo el número entero devuelto desde Zlib.crc32.

+0

Qué plataforma de rubí estás trabajando? – 2potatocakes

+0

@ 2potatocakes, C Ruby 1.9.3. –

Respuesta

6

Digest espera que el resumen devuelva los bytes sin formato que conforman la suma de comprobación, es decir, en el caso de un crc32 los 4 bytes que componen ese entero de 32 bits. Sin embargo, en su lugar está devolviendo una cadena que contiene la representación de base 10 de ese entero.

quieres algo como

[@crc32].pack('V') 

convertir esa entero en los bytes que representan eso.Vaya y lea en el paquete y sus diversos especificadores de formato: hay muchas maneras de empaquetar un entero dependiendo de si los bytes se deben presentar en endian-ness nativo, big-endian, little-endian etc. por lo que debe averiguar qué uno se adapte a sus necesidades

+1

Usé [@ crc32] .pack ('N') para obtener mi versión de Digest :: CRC32.file (nombre de archivo) para que funcione como se esperaba. –

3

Lo sentimos, este no responde realmente a su pregunta, pero que podría ayudar ..

En primer lugar, cuando se lee en un archivo, asegúrese de que pasa el parámetro "rb". Veo que no estás en Windows, pero si por casualidad su código no terminan recibiendo corrió en una máquina Windows su código no funcionará de la misma, especialmente cuando la lectura de archivos de rubí en el ejemplo:.

crc32 = File.open('test.rb') { |f| Zlib.crc32 f.read } 
#=> 189072290 
digest = Digest::CRC32.file('test.rb').digest!.to_i 
#=> 314435800 
crc32 == digest 
#=> false 

crc32 = File.open('test.rb', "rb") { |f| Zlib.crc32 f.read } 
#=> 314435800 
digest = Digest::CRC32.file('test.rb').digest!.to_i 
#=> 314435800 
crc32 == digest 
#=> true 

El arriba funcionará en todas las plataformas y todos los rubíes ... que yo sepa ... Pero eso no es lo que preguntaste ...

Estoy bastante seguro de que los métodos hexdigest y digest en tu ejemplo anterior funcionan como deberían, ..

dig_file = Digest::CRC32.file('test.rb') 

test1 = dig_file.hexdigest 
#=> "333134343335383030" 

test2 = dig_file.digest 
#=> "314435800" 

def hexdigest_to_digest(h) 
    h.unpack('a2'*(h.size/2)).collect {|i| i.hex.chr }.join 
end 

test3 = hexdigest_to_digest(test1) 
#=> "314435800" 

Así que estoy adivinando ei ¿El .to_i.to_s (16) arroja el resultado esperado o es posible que el resultado esperado sea incorrecto? No estoy seguro, pero todo lo mejor

+0

Estás en algo allí; Creo que la respuesta es lo opuesto a eso: digerir a hexdigest. Intenté algo con 'unpack' antes de intentar forzar la base 16, pero realmente no tenía idea de lo que estaba haciendo. Todavía no lo entiendo –

+0

'digest' genera la suma de comprobación" correcta "porque simplemente devuelve lo que devuelve el método' finish'. En realidad, debería devolver una cadena binaria adecuada para 'Digest.hexencode', que debería codificar los bytes en hexadecimal. Así que sí, parece que mis dos métodos están rotos. :) –

3

funciona muy bien, asegúrese de utilizar siempre el orden de bytes de red, como este:

def finish; [@crc32].pack('N'); end