md5-digest_amd64.go (4042B)
1 //+build !noasm,!appengine,gc 2 3 // Copyright (c) 2020 MinIO Inc. All rights reserved. 4 // Use of this source code is governed by a license that can be 5 // found in the LICENSE file. 6 7 package md5simd 8 9 import ( 10 "encoding/binary" 11 "errors" 12 "fmt" 13 "sync" 14 "sync/atomic" 15 ) 16 17 // md5Digest - Type for computing MD5 using either AVX2 or AVX512 18 type md5Digest struct { 19 uid uint64 20 blocksCh chan blockInput 21 cycleServer chan uint64 22 x [BlockSize]byte 23 nx int 24 len uint64 25 buffers <-chan []byte 26 } 27 28 // NewHash - initialize instance for Md5 implementation. 29 func (s *md5Server) NewHash() Hasher { 30 uid := atomic.AddUint64(&s.uidCounter, 1) 31 blockCh := make(chan blockInput, buffersPerLane) 32 s.newInput <- newClient{ 33 uid: uid, 34 input: blockCh, 35 } 36 return &md5Digest{ 37 uid: uid, 38 buffers: s.buffers, 39 blocksCh: blockCh, 40 cycleServer: s.cycle, 41 } 42 } 43 44 // Size - Return size of checksum 45 func (d *md5Digest) Size() int { return Size } 46 47 // BlockSize - Return blocksize of checksum 48 func (d md5Digest) BlockSize() int { return BlockSize } 49 50 func (d *md5Digest) Reset() { 51 if d.blocksCh == nil { 52 panic("reset after close") 53 } 54 d.nx = 0 55 d.len = 0 56 d.sendBlock(blockInput{uid: d.uid, reset: true}, false) 57 } 58 59 // write to digest 60 func (d *md5Digest) Write(p []byte) (nn int, err error) { 61 if d.blocksCh == nil { 62 return 0, errors.New("md5Digest closed") 63 } 64 65 // break input into chunks of maximum internalBlockSize size 66 for { 67 l := len(p) 68 if l > internalBlockSize { 69 l = internalBlockSize 70 } 71 nnn, err := d.write(p[:l]) 72 if err != nil { 73 return nn, err 74 } 75 nn += nnn 76 p = p[l:] 77 78 if len(p) == 0 { 79 break 80 } 81 82 } 83 return 84 } 85 86 func (d *md5Digest) write(p []byte) (nn int, err error) { 87 88 nn = len(p) 89 d.len += uint64(nn) 90 if d.nx > 0 { 91 n := copy(d.x[d.nx:], p) 92 d.nx += n 93 if d.nx == BlockSize { 94 // Create a copy of the overflow buffer in order to send it async over the channel 95 // (since we will modify the overflow buffer down below with any access beyond multiples of 64) 96 tmp := <-d.buffers 97 tmp = tmp[:BlockSize] 98 copy(tmp, d.x[:]) 99 d.sendBlock(blockInput{uid: d.uid, msg: tmp}, len(p)-n < BlockSize) 100 d.nx = 0 101 } 102 p = p[n:] 103 } 104 if len(p) >= BlockSize { 105 n := len(p) &^ (BlockSize - 1) 106 buf := <-d.buffers 107 buf = buf[:n] 108 copy(buf, p) 109 d.sendBlock(blockInput{uid: d.uid, msg: buf}, len(p)-n < BlockSize) 110 p = p[n:] 111 } 112 if len(p) > 0 { 113 d.nx = copy(d.x[:], p) 114 } 115 return 116 } 117 118 func (d *md5Digest) Close() { 119 if d.blocksCh != nil { 120 close(d.blocksCh) 121 d.blocksCh = nil 122 } 123 } 124 125 var sumChPool sync.Pool 126 127 func init() { 128 sumChPool.New = func() interface{} { 129 return make(chan sumResult, 1) 130 } 131 } 132 133 // Sum - Return MD5 sum in bytes 134 func (d *md5Digest) Sum(in []byte) (result []byte) { 135 if d.blocksCh == nil { 136 panic("sum after close") 137 } 138 139 trail := <-d.buffers 140 trail = append(trail[:0], d.x[:d.nx]...) 141 142 length := d.len 143 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. 144 var tmp [64]byte 145 tmp[0] = 0x80 146 if length%64 < 56 { 147 trail = append(trail, tmp[0:56-length%64]...) 148 } else { 149 trail = append(trail, tmp[0:64+56-length%64]...) 150 } 151 152 // Length in bits. 153 length <<= 3 154 binary.LittleEndian.PutUint64(tmp[:], length) // append length in bits 155 156 trail = append(trail, tmp[0:8]...) 157 if len(trail)%BlockSize != 0 { 158 panic(fmt.Errorf("internal error: sum block was not aligned. len=%d, nx=%d", len(trail), d.nx)) 159 } 160 sumCh := sumChPool.Get().(chan sumResult) 161 d.sendBlock(blockInput{uid: d.uid, msg: trail, sumCh: sumCh}, true) 162 163 sum := <-sumCh 164 sumChPool.Put(sumCh) 165 166 return append(in, sum.digest[:]...) 167 } 168 169 // sendBlock will send a block for processing. 170 // If cycle is true we will block on cycle, otherwise we will only block 171 // if the block channel is full. 172 func (d *md5Digest) sendBlock(bi blockInput, cycle bool) { 173 if cycle { 174 select { 175 case d.blocksCh <- bi: 176 d.cycleServer <- d.uid 177 } 178 return 179 } 180 // Only block on cycle if we filled the buffer 181 select { 182 case d.blocksCh <- bi: 183 return 184 default: 185 d.cycleServer <- d.uid 186 d.blocksCh <- bi 187 } 188 }