Skip to content

Instantly share code, notes, and snippets.

@gzxu
Last active June 15, 2022 07:41
Show Gist options
  • Save gzxu/091b0feb3bdde7ffbe1d3d0d66fdfeaf to your computer and use it in GitHub Desktop.
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
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)));
}
// 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