github.com/artpar/rclone@v1.67.3/backend/onedrive/quickxorhash/quickxorhash.go (about) 1 // Package quickxorhash provides the quickXorHash algorithm which is a 2 // quick, simple non-cryptographic hash algorithm that works by XORing 3 // the bytes in a circular-shifting fashion. 4 // 5 // It is used by Microsoft Onedrive for Business to hash data. 6 // 7 // See: https://docs.microsoft.com/en-us/onedrive/developer/code-snippets/quickxorhash 8 package quickxorhash 9 10 // This code was ported from a fast C-implementation from 11 // https://github.com/namazso/QuickXorHash 12 // which has licenced as BSD Zero Clause License 13 // 14 // BSD Zero Clause License 15 // 16 // Copyright (c) 2022 namazso <admin@namazso.eu> 17 // 18 // Permission to use, copy, modify, and/or distribute this software for any 19 // purpose with or without fee is hereby granted. 20 // 21 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 22 // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 23 // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 24 // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 25 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 26 // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 27 // PERFORMANCE OF THIS SOFTWARE. 28 29 import "hash" 30 31 const ( 32 // BlockSize is the preferred size for hashing 33 BlockSize = 64 34 // Size of the output checksum 35 Size = 20 36 shift = 11 37 widthInBits = 8 * Size 38 dataSize = shift * widthInBits 39 ) 40 41 type quickXorHash struct { 42 data [dataSize]byte 43 size uint64 44 } 45 46 // New returns a new hash.Hash computing the quickXorHash checksum. 47 func New() hash.Hash { 48 return &quickXorHash{} 49 } 50 51 // Write (via the embedded io.Writer interface) adds more data to the running hash. 52 // It never returns an error. 53 // 54 // Write writes len(p) bytes from p to the underlying data stream. It returns 55 // the number of bytes written from p (0 <= n <= len(p)) and any error 56 // encountered that caused the write to stop early. Write must return a non-nil 57 // error if it returns n < len(p). Write must not modify the slice data, even 58 // temporarily. 59 // 60 // Implementations must not retain p. 61 func (q *quickXorHash) Write(p []byte) (n int, err error) { 62 var i int 63 // fill last remain 64 lastRemain := q.size % dataSize 65 if lastRemain != 0 { 66 i += xorBytes(q.data[lastRemain:], p) 67 } 68 69 if i != len(p) { 70 for len(p)-i >= dataSize { 71 i += xorBytes(q.data[:], p[i:]) 72 } 73 xorBytes(q.data[:], p[i:]) 74 } 75 q.size += uint64(len(p)) 76 return len(p), nil 77 } 78 79 // Calculate the current checksum 80 func (q *quickXorHash) checkSum() (h [Size + 1]byte) { 81 for i := 0; i < dataSize; i++ { 82 shift := (i * 11) % 160 83 shiftBytes := shift / 8 84 shiftBits := shift % 8 85 shifted := int(q.data[i]) << shiftBits 86 h[shiftBytes] ^= byte(shifted) 87 h[shiftBytes+1] ^= byte(shifted >> 8) 88 } 89 h[0] ^= h[20] 90 91 // XOR the file length with the least significant bits in little endian format 92 d := q.size 93 h[Size-8] ^= byte(d >> (8 * 0)) 94 h[Size-7] ^= byte(d >> (8 * 1)) 95 h[Size-6] ^= byte(d >> (8 * 2)) 96 h[Size-5] ^= byte(d >> (8 * 3)) 97 h[Size-4] ^= byte(d >> (8 * 4)) 98 h[Size-3] ^= byte(d >> (8 * 5)) 99 h[Size-2] ^= byte(d >> (8 * 6)) 100 h[Size-1] ^= byte(d >> (8 * 7)) 101 102 return h 103 } 104 105 // Sum appends the current hash to b and returns the resulting slice. 106 // It does not change the underlying hash state. 107 func (q *quickXorHash) Sum(b []byte) []byte { 108 hash := q.checkSum() 109 return append(b, hash[:Size]...) 110 } 111 112 // Reset resets the Hash to its initial state. 113 func (q *quickXorHash) Reset() { 114 *q = quickXorHash{} 115 } 116 117 // Size returns the number of bytes Sum will return. 118 func (q *quickXorHash) Size() int { 119 return Size 120 } 121 122 // BlockSize returns the hash's underlying block size. 123 // The Write method must be able to accept any amount 124 // of data, but it may operate more efficiently if all writes 125 // are a multiple of the block size. 126 func (q *quickXorHash) BlockSize() int { 127 return BlockSize 128 } 129 130 // Sum returns the quickXorHash checksum of the data. 131 func Sum(data []byte) (h [Size]byte) { 132 var d quickXorHash 133 _, _ = d.Write(data) 134 s := d.checkSum() 135 copy(h[:], s[:]) 136 return h 137 }