2011-12-21 10 views
6

Nel mio codice, ho bisogno di file hash che utilizzano una varietà di algoritmi, incluso CRC32. Poiché sto anche utilizzando altre funzioni hash crittografiche nella famiglia Digest, ho pensato che sarebbe stato utile mantenere un'interfaccia coerente per tutti.Digest :: CRC32 with Zlib

Per la cronaca, ho trovato digest-crc, una gemma che fa esattamente quello che voglio. Il fatto è che Zlib fa parte della libreria standard e ha un'implementazione funzionante di CRC32 che vorrei riutilizzare. Inoltre, è scritto in C, quindi dovrebbe offrire prestazioni superiori in relazione a digest-crc, che è un'implementazione di puro rubino.

Implementazione Digest::CRC32 in realtà sembrava abbastanza semplice in un primo 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 

Tutto sembra giusto:

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

Purtroppo, non tutto funziona:

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

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

hexdigest restituisce fondamentalmente Digest.hexencode(digest), 012.330.. Non sono sicuro di come funzioni quella funzione, quindi mi chiedevo se è possibile ottenere ciò con il numero intero restituito da Zlib.crc32.

+0

Quale piattaforma rubino stai lavorando? – 2potatocakes

+0

@ 2potatocakes, C Ruby 1.9.3. –

risposta

6

Digest si aspetta che il digest restituisca i byte grezzi che costituiscono il checksum, vale a dire nel caso di un crc32 i 4 byte che compongono tale numero intero a 32 bit. Comunque stai invece restituendo una stringa che contiene la rappresentazione di base 10 di quel intero.

si desidera qualcosa di simile

[@crc32].pack('V') 

trasformare questo intero in byte che rappresentano questo.Andate a leggere su pack e sui suoi vari specificatori di formato - ci sono molti modi per impacchettare un intero a seconda che i byte debbano essere presentati in endianità nativa, big-endian, little-endian ecc, quindi dovreste capire quale una adatta alle vostre esigenze

+1

Ho usato [@ crc32] .pack ('N') per ottenere la mia versione di Digest :: CRC32.file (nome file) per funzionare come previsto. –

3

Spiacente, questo in realtà non rispondere alla tua domanda, ma potrebbe aiutare ..

In primo luogo, durante la lettura di un file, assicurarsi che si passa il parametro "rb". Vedo che non sei su Windows, ma se per caso il vostro codice non finire per ottenere corse su una macchina Windows il codice non funziona lo stesso, soprattutto quando la lettura dei file Ruby in Esempio:.

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 

Il sopra funzionerà su tutte le piattaforme e tutti i rubini .. che io conosca .. Ma non è quello che hai chiesto ..

Sono quasi sicuro che i metodi esadecimali e di digitazione nell'esempio precedente funzionano come dovrebbero ..

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" 

Quindi sto indovinando ei Quindi il .to_i.to_s (16) sta eliminando il risultato atteso o il risultato atteso potrebbe essere sbagliato? Non è sicuro, ma tutto il meglio

+0

Sei su qualcosa lì; Penso che la risposta sia l'opposto: digerire a esadecimale. Ho provato qualcosa con 'unpack' prima di provare a forzare la base 16 ma in realtà non avevo idea di cosa stavo facendo. Ancora non lo capisco. –

+0

'digest' restituisce il checksum" corretto "perché restituisce solo ciò che restituisce il metodo' finish'. In realtà, dovrebbe restituire una stringa binaria adatta per 'Digest.hexencode', che dovrebbe codificare i byte in esadecimale. Quindi sì, sembra che entrambi i miei metodi siano infranti. :) –

3

funziona bene, assicurarsi di utilizzare sempre l'ordine di byte di rete, in questo modo:

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