md5-digest_amd64.go 3.8 KB

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