gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }