Last active
June 15, 2022 07:41
-
-
Save gzxu/091b0feb3bdde7ffbe1d3d0d66fdfeaf to your computer and use it in GitHub Desktop.
Implementing the QuickXorHash Algorithm used by the Microsoft Graph API on OneDrive for Business
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::env; | |
use std::fs; | |
// extern crate base64; | |
// From the specification | |
// https://docs.microsoft.com/en-us/onedrive/developer/code-snippets/quickxorhash | |
// According to the spec, the block is "big-endian" | |
type Block = [u8; 20]; // Intrinsically with Copy trait | |
fn zero() -> Block { | |
[0; 20] | |
} | |
fn reverse(mut b: Block) -> Block { | |
b.reverse(); | |
b | |
} | |
fn extend8(c: u8) -> Block { | |
let mut b = zero(); | |
b[0] = c; | |
b | |
} | |
fn extend64(i: u64) -> Block { | |
let i = i.to_le_bytes(); | |
let mut b = zero(); | |
for (src, dst) in i.iter().rev().zip(b[..8].iter_mut()) { | |
*dst = *src; | |
} | |
b | |
} | |
// Code from https://github.com/rust-num/num-bigint/blob/master/src/algorithms.rs | |
fn rotate(mut b: Block, n: usize) -> Block { | |
let n = n % (8 * 20); | |
let div = n / 8; | |
let rem = n % 8; | |
b.rotate_right(div); | |
if rem > 0 { | |
let mut carry = 0; | |
for elem in b.iter_mut() { | |
let new_carry = *elem >> (8 - rem); | |
*elem = (*elem << rem) | carry; | |
carry = new_carry; | |
} | |
b[0] |= carry; | |
} | |
b | |
} | |
fn xor(mut b1: Block, b2: Block) -> Block { | |
for (src, dst) in b2.iter().zip(b1.iter_mut()) { | |
*dst ^= *src; | |
} | |
b1 | |
} | |
#[allow(non_snake_case)] // Function names copied from the spec | |
fn XorHash0(rgb: &[u8]) -> Block { | |
let mut ret = zero(); | |
for i in 0..rgb.len() { | |
ret = xor(ret, rotate(extend8(rgb[i]), i * 11)); | |
} | |
reverse(ret) | |
} | |
#[allow(non_snake_case)] | |
fn XorHash(rgb: &[u8]) -> Block { | |
reverse(xor(extend64(rgb.len() as u64), XorHash0(rgb))) | |
} | |
fn main() { | |
let args: Vec<String> = env::args().collect(); | |
if args.len() < 2 { | |
eprintln!("No enough arguments!"); | |
return; | |
} | |
let bytes = match fs::read(&args[1]) { | |
Ok(path) => path, | |
Err(error) => { | |
eprintln!("Error open file: {}", error); | |
return; | |
} | |
}; | |
println!("{}", base64::encode(&XorHash(&bytes))); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Package quickxorhash provides the quickXorHash algorithm which is a | |
// quick, simple non-cryptographic hash algorithm that works by XORing | |
// the bytes in a circular-shifting fashion. | |
// | |
// It is used by Microsoft Onedrive for Business to hash data. | |
// | |
// See: https://docs.microsoft.com/en-us/onedrive/developer/rest-api/resources/hashes | |
// This code was ported from the code | |
// https://github.com/rclone/rclone/blob/master/backend/onedrive/quickxorhash/quickxorhash.go | |
// which was in turn ported from the code snippet linked from | |
// https://docs.microsoft.com/en-us/onedrive/developer/code-snippets/quickxorhash | |
// https://gist.github.com/rgregg/c07a91964300315c6c3e77f7b5b861e4 | |
// Which has the copyright | |
// ------------------------------------------------------------------------------ | |
// Copyright (c) 2016 Microsoft Corporation | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
// THE SOFTWARE. | |
// ------------------------------------------------------------------------------ | |
use std::io::{Write, Result}; | |
use std::cmp::min; | |
const BITS_IN_LAST_CELL: usize = 32; | |
const SHIFT: usize = 11; | |
// const THRESHOLD: usize = 600; | |
const WIDTH_IN_BITS: usize = 160; | |
// BLOCK_SIZE is the preferred size for hashing | |
// const BLOCK_SIZE: usize = 64; | |
// Size of the output checksum | |
const SIZE: usize = (WIDTH_IN_BITS - 1) / 8 + 1; | |
#[derive(Default)] | |
pub struct QuickXorHash { | |
data: [u64; (WIDTH_IN_BITS - 1) / 64 + 1], | |
length_so_far: u64, | |
shift_so_far: usize, | |
} | |
impl Write for QuickXorHash { | |
// Write (via the embedded io.Writer interface) adds more data to the running hash. | |
// It never returns an error. | |
// | |
// Write writes len(p) bytes from p to the underlying data stream. It returns | |
// the number of bytes written from p (0 <= n <= len(p)) and any error | |
// encountered that caused the write to stop early. Write must return a non-nil | |
// error if it returns n < len(p). Write must not modify the slice data, even | |
// temporarily. | |
// | |
// Implementations must not retain p. | |
fn write(&mut self, buf: &[u8]) -> Result<usize> { | |
let current_shift = self.shift_so_far; | |
// The bitvector where we'll start xoring | |
let mut vector_array_index = current_shift / 64; | |
// The position within the bit vector at which we begin xoring | |
let mut vector_offset = current_shift % 64; | |
let iterations = min(buf.len(), WIDTH_IN_BITS); | |
for i in 0..iterations { | |
let is_last_cell = vector_array_index == self.data.len() - 1; | |
let bits_in_vector_cell = if is_last_cell { BITS_IN_LAST_CELL } else { 64 }; | |
// There's at least 2 bitvectors before we reach the end of the buf | |
if vector_offset <= bits_in_vector_cell - 8 { | |
for j in (i..buf.len()).step_by(WIDTH_IN_BITS) { | |
self.data[vector_array_index] ^= (buf[j] as u64) << vector_offset; | |
} | |
} else { | |
let index1 = vector_array_index; | |
let index2 = if is_last_cell { 0 } else { vector_array_index + 1 }; | |
let low = (bits_in_vector_cell - vector_offset) as u8; | |
let mut xored_byte = 0u8; | |
for j in (i..buf.len()).step_by(WIDTH_IN_BITS) { | |
xored_byte ^= buf[j]; | |
} | |
self.data[index1] ^= (xored_byte as u64) << vector_offset; | |
self.data[index2] ^= (xored_byte as u64) >> low; | |
} | |
vector_offset += SHIFT; | |
while vector_offset >= bits_in_vector_cell { | |
vector_array_index = if is_last_cell { 0 } else { vector_array_index + 1 }; | |
vector_offset -= bits_in_vector_cell; | |
} | |
} | |
// Update the starting position in a circular shift pattern | |
self.shift_so_far = (self.shift_so_far + SHIFT * (buf.len() % WIDTH_IN_BITS)) % WIDTH_IN_BITS; | |
self.length_so_far += buf.len() as u64; | |
Ok(buf.len()) | |
} | |
fn flush(&mut self) -> Result<()> { | |
Ok(()) | |
} | |
} | |
impl QuickXorHash { | |
// Calculate the current checksum | |
pub fn check_sum(&self) -> [u8; SIZE] { | |
// Create a byte array big enough to hold all our data | |
let mut rgb = [0u8; SIZE]; | |
// Block copy all our bitvectors to this byte array | |
for i in 0..self.data.len() - 1 { | |
rgb[i * 8..i * 8 + 8].copy_from_slice(&self.data[i].to_le_bytes()); | |
} | |
rgb[(self.data.len() - 1) * 8..].copy_from_slice(&self.data.last().unwrap().to_le_bytes()[..4]); | |
// XOR the file length with the least significant bits | |
// Note that GetBytes is architecture-dependent, so care should | |
// be taken with porting. The expected value is 8-bytes in length in little-endian format | |
let length_bytes: &[u8; 8] = &self.length_so_far.to_le_bytes(); | |
for (dst, src) in rgb[WIDTH_IN_BITS / 8 - length_bytes.len()..].iter_mut().zip(length_bytes) { | |
*dst ^= *src; | |
} | |
rgb | |
} | |
// Sum appends the current hash to b and returns the resulting slice. | |
// It does not change the underlying hash state. | |
// pub fn Sum(&self, b: &[u8]) -> &[u8] { | |
// let hash = self.check_sum(); | |
// return append(b, hash[:]...); | |
// } | |
// Reset resets the Hash to its initial state. | |
// pub fn Reset(&self) { | |
// *self = QuickXorHash {}; | |
// } | |
// Size returns the number of bytes Sum will return. | |
// pub fn size(&self) -> usize { | |
// SIZE | |
// } | |
// BlockSize returns the hash's underlying block size. | |
// The Write method must be able to accept any amount | |
// of data, but it may operate more efficiently if all writes | |
// are a multiple of the block size. | |
// pub fn block_size(&self) -> usize { | |
// BLOCK_SIZE | |
// } | |
} | |
#[cfg(test)] | |
mod tests { | |
extern crate base64; | |
use super::*; | |
static TEST_CASES: &[(&str, &str)] = &[ | |
("", "AAAAAAAAAAAAAAAAAAAAAAAAAAA="), | |
("Sg==", "SgAAAAAAAAAAAAAAAQAAAAAAAAA="), | |
("tbQ=", "taAFAAAAAAAAAAAAAgAAAAAAAAA="), | |
("0pZP", "0rDEEwAAAAAAAAAAAwAAAAAAAAA="), | |
("jRRDVA==", "jaDAEKgAAAAAAAAABAAAAAAAAAA="), | |
("eAV52qE=", "eChAHrQRCgAAAAAABQAAAAAAAAA="), | |
("luBZlaT6", "lgBHFipBCn0AAAAABgAAAAAAAAA="), | |
("qaApEj66lw==", "qQBFCiTgA11cAgAABwAAAAAAAAA="), | |
("/aNzzCFPS/A=", "/RjFHJgRgicsAR4ACAAAAAAAAAA="), | |
("n6Neh7p6fFgm", "nxiFFw6hCz3wAQsmCQAAAAAAAAA="), | |
("J9iPGCbfZSTNyw==", "J8DGIzBggm+UgQTNUgYAAAAAAAA="), | |
("i+UZyUGJKh+ISbk=", "iyhHBpIRhESo4AOIQ0IuAAAAAAA="), | |
("h490d57Pqz5q2rtT", "h3gEHe7giWeswgdq3MYupgAAAAA="), | |
("vPgoDjOfO6fm71RxLw==", "vMAHChwwg0/s4BTmdQcV4vACAAA="), | |
("XoJ1AsoR4fDYJrDqYs4=", "XhBEHQSgjAiEAx7YPgEs1CEGZwA="), | |
("gQaybEqS/4UlDc8e4IJm", "gDCALNigBEn8oxAlZ8AzPAAOQZg="), | |
("2fuxhBJXtpWFe8dOfdGeHw==", "O9tHLAghgSvYohKFyMMxnNCHaHg="), | |
("XBV6YKU9V7yMakZnFIxIkuU=", "HbplHsBQih5cgReMQYMRzkABRiA="), | |
("XJZSOiNO2bmfKnTKD7fztcQX", "/6ZArHQwAidkIxefQgEdlPGAW8w="), | |
("g8VtAh+2Kf4k0kY5tzji2i2zmA==", "wDNrgwHWAVukwB8kg4YRcnALHIg="), | |
("T6LYJIfDh81JrAK309H2JMJTXis=", "zBTHrspn3mEcohlJdIUAbjGNaNg="), | |
("DWAAX5/CIfrmErgZa8ot6ZraeSbu", "LR2Z0PjuRYGKQB/mhQAuMrAGZbQ="), | |
("N9abi3qy/mC1THZuVLHPpx7SgwtLOA==", "1KTYttCBEen8Hwy1doId3ECFWDw="), | |
("LlUe7wHerLqEtbSZLZgZa9u0m7hbiFs=", "TqVZpxs3cN61BnuFvwUtMtECTGQ="), | |
("bU2j/0XYdgfPFD4691jV0AOUEUPR4Z5E", "bnLBiLpVgnxVkXhNsIAPdHAPLFQ="), | |
("lScPwPsyUsH2T1Qsr31wXtP55Wqbe47Uyg==", "VDMSy8eI26nBHCB0e8gVWPCKPsA="), | |
("rJaKh1dLR1k+4hynliTZMGf8Nd4qKKoZiAM=", "r7bjwkl8OYQeNaMcCY8fTmEJEmQ="), | |
("pPsT0CPmHrd3Frsnva1pB/z1ytARLeHEYRCo", "Rdg7rCcDomL59pL0s6GuTvqLVqQ="), | |
("wSRChaqmrsnMrfB2yqI43eRWbro+f9kBvh+01w==", "YTtloIi6frI7HX3vdLvE7I2iUOA="), | |
("apL67KMIRxQeE9k1/RuW09ppPjbF1WeQpTjSWtI=", "CIpedls+ZlSQ654fl+X26+Q7LVU="), | |
("53yx0/QgMTVb7OOzHRHbkS7ghyRc+sIXxi7XHKgT", "zfJtLGFgR9DB3Q64fAFIp+S5iOY="), | |
("PwXNnutoLLmxD8TTog52k8cQkukmT87TTnDipKLHQw==", "PTaGs7yV3FUyBy/SfU6xJRlCJlI="), | |
("NbYXsp5/K6mR+NmHwExjvWeWDJFnXTKWVlzYHoesp2E=", "wjuAuWDiq04qDt1R8hHWDDcwVoQ="), | |
("qQ70RB++JAR5ljNv3lJt1PpqETPsckopfonItu18Cr3E", "FkJaeg/0Z5+euShYlLpE2tJh+Lo="), | |
("RhzSatQTQ9/RFvpHyQa1WLdkr3nIk6MjJUma998YRtp44A==", "SPN2D29reImAqJezlqV2DLbi8tk="), | |
("DND1u1uZ5SqZVpRUk6NxSUdVo7IjjL9zs4A1evDNCDLcXWc=", "S6lBk2hxI2SWBfn7nbEl7D19UUs="), | |
("jEi62utFz69JMYHjg1iXy7oO6ZpZSLcVd2B+pjm6BGsv/CWi", "s0lYU9tr/bp9xsnrrjYgRS5EvV8="), | |
("hfS3DZZnhy0hv7nJdXLv/oJOtIgAuP9SInt/v8KeuO4/IvVh4A==", "CV+HQCdd2A/e/vdi12f2UU55GLA="), | |
("EkPQAC6ymuRrYjIXD/LT/4Vb+7aTjYVZOHzC8GPCEtYDP0+T3Nc=", "kE9H9sEmr3vHBYUiPbvsrcDgSEo="), | |
("vtBOGIENG7yQ/N7xNWPNIgy66Gk/I2Ur/ZhdFNUK9/1FCZuu/KeS", "+Fgp3HBimtCzUAyiinj3pkarYTk="), | |
("YnF4smoy9hox2jBlJ3VUa4qyCRhOZbWcmFGIiszTT4zAdYHsqJazyg==", "arkIn+ELddmE8N34J9ydyFKW+9w="), | |
("0n7nl3YJtipy6yeUbVPWtc2h45WbF9u8hTz5tNwj3dZZwfXWkk+GN3g=", "YJLNK7JR64j9aODWfqDvEe/u6NU="), | |
("FnIIPHayc1pHkY4Lh8+zhWwG8xk6Knk/D3cZU1/fOUmRAoJ6CeztvMOL", "22RPOylMtdk7xO/QEQiMli4ql0k="), | |
("J82VT7ND0Eg1MorSfJMUhn+qocF7PsUpdQAMrDiHJ2JcPZAHZ2nyuwjoKg==", "pOR5eYfwCLRJbJsidpc1rIJYwtM="), | |
("Zbu+78+e35ZIymV5KTDdub5McyI3FEO8fDxs62uWHQ9U3Oh3ZqgaZ30SnmQ=", "DbvbTkgNTgWRqRidA9r1jhtUjro="), | |
("lgybK3Da7LEeY5aeeNrqcdHvv6mD1W4cuQ3/rUj2C/CNcSI0cAMw6vtpVY3y", "700RQByn1lRQSSme9npQB/Ye+bY="), | |
("jStZgKHv4QyJLvF2bYbIUZi/FscHALfKHAssTXkrV1byVR9eACwW9DNZQRHQwg==", "uwN55He8xgE4g93dH9163xPew4U="), | |
("V1PSud3giF5WW72JB/bgtltsWtEB5V+a+wUALOJOGuqztzVXUZYrvoP3XV++gM0=", "U+3ZfUF/6mwOoHJcSHkQkckfTDA="), | |
("VXs4t4tfXGiWAL6dlhEMm0YQF0f2w9rzX0CvIVeuW56o6/ec2auMpKeU2VeteEK5", "sq24lSf7wXLH8eigHl07X+qPTps="), | |
("bLUn3jLH+HFUsG3ptWTHgNvtr3eEv9lfKBf0jm6uhpqhRwtbEQ7Ovj/hYQf42zfdtQ==", "uC8xrnopGiHebGuwgq607WRQyxQ="), | |
("4SVmjtXIL8BB8SfkbR5Cpaljm2jpyUfAhIBf65XmKxHlz9dy5XixgiE/q1lv+esZW/E=", "wxZ0rxkMQEnRNAp8ZgEZLT4RdLM="), | |
("pMljctlXeFUqbG3BppyiNbojQO3ygg6nZPeUZaQcVyJ+Clgiw3Q8ntLe8+02ZSfyCc39", "aZEPmNvOXnTt7z7wt+ewV7QGMlg="), | |
("C16uQlxsHxMWnV2gJhFPuJ2/guZ4N1YgmNvAwL1yrouGQtwieGx8WvZsmYRnX72JnbVtTw==", "QtlSNqXhVij64MMhKJ3EsDFB/z8="), | |
("7ZVDOywvrl3L0GyKjjcNg2CcTI81n2CeUbzdYWcZOSCEnA/xrNHpiK01HOcGh3BbxuS4S6g=", "4NznNJc4nmXeApfiCFTq/H5LbHw="), | |
("JXm2tTVqpYuuz2Cc+ZnPusUb8vccPGrzWK2oVwLLl/FjpFoxO9FxGlhnB08iu8Q/XQSdzHn+", "IwE5+2pKNcK366I2k2BzZYPibSI="), | |
("TiiU1mxzYBSGZuE+TX0l9USWBilQ7dEml5lLrzNPh75xmhjIK8SGqVAkvIMgAmcMB+raXdMPZg==", "yECGHtgR128ScP4XlvF96eLbIBE="), | |
("zz+Q4zi6wh0fCJUFU9yUOqEVxlIA93gybXHOtXIPwQQ44pW4fyh6BRgc1bOneRuSWp85hwlTJl8=", "+3Ef4D6yuoC8J+rbFqU1cegverE="), | |
("sa6SHK9z/G505bysK5KgRO2z2cTksDkLoFc7sv0tWBmf2G2mCiozf2Ce6EIO+W1fRsrrtn/eeOAV", "xZg1CwMNAjN0AIXw2yh4+1N3oos="), | |
("0qx0xdyTHhnKJ22IeTlAjRpWw6y2sOOWFP75XJ7cleGJQiV2kyrmQOST4DGHIL0qqA7sMOdzKyTV\ | |
iw==", "bS0tRYPkP1Gfc+ZsBm9PMzPunG8="), | |
("QuzaF0+5ooig6OLEWeibZUENl8EaiXAQvK9UjBEauMeuFFDCtNcGs25BDtJGGbX90gH4VZvCCDNC\ | |
q4s=", "rggokuJq1OGNOfB6aDp2g4rdPgw="), | |
("+wg2x23GZQmMLkdv9MeAdettIWDmyK6Wr+ba23XD+Pvvq1lIMn9QIQT4Z7QHJE3iC/ZMFgaId9VA\ | |
yY3d", "ahQbTmOdiKUNdhYRHgv5/Ky+Y6k="), | |
("y0ydRgreRQwP95vpNP92ioI+7wFiyldHRbr1SfoPNdbKGFA0lBREaBEGNhf9yixmfE+Azo2AuROx\ | |
b7Yc7g==", "cJKFc0dXfiN4hMg1lcMf5E4gqvo="), | |
("LxlVvGXSQlSubK8r0pGf9zf7s/3RHe75a2WlSXQf3gZFR/BtRnR7fCIcaG//CbGfodBFp06DBx/S\ | |
9hUV8Bk=", "NwuwhhRWX8QZ/vhWKWgQ1+rNomI="), | |
("L+LSB8kmGMnHaWVA5P/+qFnfQliXvgJW7d2JGAgT6+koi5NQujFW1bwQVoXrBVyob/gBxGizUoJM\ | |
gid5gGNo", "ndX/KZBtFoeO3xKeo1ajO/Jy+rY="), | |
("Mb7EGva2rEE5fENDL85P+BsapHEEjv2/siVhKjvAQe02feExVOQSkfmuYzU/kTF1MaKjPmKF/w+c\ | |
bvwfdWL8aQ==", "n1anP5NfvD4XDYWIeRPW3ZkPv1Y="), | |
("jyibxJSzO6ZiZ0O1qe3tG/bvIAYssvukh9suIT5wEy1JBINVgPiqdsTW0cOpP0aUfP7mgqLfADkz\ | |
I/m/GgCuVhr8oFLrOCoTx1/psBOWwhltCbhUx51Icm9aH8tY4Z3ccU+6BKpYQkLCy0B/A9Zc", "hZfLIilSITC6N3e3tQ/iSgEzkto="), | |
("ikwCorI7PKWz17EI50jZCGbV9JU2E8bXVfxNMg5zdmqSZ2NlsQPp0kqYIPjzwTg1MBtfWPg53k0h\ | |
0P2naJNEVgrqpoHTfV2b3pJ4m0zYPTJmUX4Bg/lOxcnCxAYKU29Y5F0U8Quz7ZXFBEweftXxJ7RS\ | |
4r6N7BzJrPsLhY7hgck=", "imAoFvCWlDn4yVw3/oq1PDbbm6U="), | |
("PfxMcUd0vIW6VbHG/uj/Y0W6qEoKmyBD0nYebEKazKaKG+UaDqBEcmQjbfQeVnVLuodMoPp7P7TR\ | |
1htX5n2VnkHh22xDyoJ8C/ZQKiSNqQfXvh83judf4RVr9exJCud8Uvgip6aVZTaPrJHVjQhMCp/d\ | |
EnGvqg0oN5OVkM2qqAXvA0teKUDhgNM71sDBVBCGXxNOR2bpbD1iM4dnuT0ey4L+loXEHTL0fqMe\ | |
UcEi2asgImnlNakwenDzz0x57aBwyq3AspCFGB1ncX4yYCr/OaCcS5OKi/00WH+wNQU3", "QX/YEpG0gDsmhEpCdWhsxDzsfVE="), | |
("qwGf2ESubE5jOUHHyc94ORczFYYbc2OmEzo+hBIyzJiNwAzC8PvJqtTzwkWkSslgHFGWQZR2BV5+\ | |
uYTrYT7HVwRM40vqfj0dBgeDENyTenIOL1LHkjtDKoXEnQ0mXAHoJ8PjbNC93zi5TovVRXTNzfGE\ | |
s5dpWVqxUzb5lc7dwkyvOluBw482mQ4xrzYyIY1t+//OrNi1ObGXuUw2jBQOFfJVj2Y6BOyYmfB1\ | |
y36eBxi3zxeG5d5NYjm2GSh6e08QMAwu3zrINcqIzLOuNIiGXBtl7DjKt7b5wqi4oFiRpZsCyx2s\ | |
mhSrdrtK/CkdU6nDN+34vSR/M8rZpWQdBE7a8g==", "WYT9JY3JIo/pEBp+tIM6Gt2nyTM="), | |
("w0LGhqU1WXFbdavqDE4kAjEzWLGGzmTNikzqnsiXHx2KRReKVTxkv27u3UcEz9+lbMvYl4xFf2Z4\ | |
aE1xRBBNd1Ke5C0zToSaYw5o4B/7X99nKK2/XaUX1byLow2aju2XJl2OpKpJg+tSJ2fmjIJTkfuY\ | |
Uz574dFX6/VXxSxwGH/xQEAKS5TCsBK3CwnuG1p5SAsQq3gGVozDWyjEBcWDMdy8/AIFrj/y03Lf\ | |
c/RNRCQTAfZbnf2QwV7sluw4fH3XJr07UoD0YqN+7XZzidtrwqMY26fpLZnyZjnBEt1FAZWO7RnK\ | |
G5asg8xRk9YaDdedXdQSJAOy6bWEWlABj+tVAigBxavaluUH8LOj+yfCFldJjNLdi90fVHkUD/m4\ | |
Mr5OtmupNMXPwuG3EQlqWUVpQoYpUYKLsk7a5Mvg6UFkiH596y5IbJEVCI1Kb3D1", "e3+wo77iKcILiZegnzyUNcjCdoQ="), | |
]; | |
#[test] | |
fn test_quick_xor() { | |
for (d, h) in TEST_CASES { | |
let d = base64::decode(d).unwrap(); | |
let mut hash = QuickXorHash::default(); | |
hash.write(&d).unwrap(); | |
let got = hash.check_sum(); | |
let want = base64::decode(h).unwrap(); | |
assert!(got.iter().eq(&want)); | |
} | |
} | |
#[test] | |
fn test_quick_xor_by_block() { | |
for block_size in &[1usize, 2, 4, 7, 8, 16, 32, 64, 128, 256, 512] { | |
for (d, h) in TEST_CASES { | |
let d = base64::decode(d).unwrap(); | |
let mut hash = QuickXorHash::default(); | |
for chunk in d.chunks(*block_size) { | |
hash.write(chunk).unwrap(); | |
} | |
let got = hash.check_sum(); | |
let want = base64::decode(h).unwrap(); | |
assert!(got.iter().eq(&want)); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment