adler32.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Package rollinghash/adler32 implements a rolling version of hash/adler32
  2. package adler32
  3. import (
  4. "hash"
  5. vanilla "hash/adler32"
  6. "github.com/chmduquesne/rollinghash"
  7. )
  8. const (
  9. Mod = 65521
  10. Size = 4
  11. )
  12. type digest struct {
  13. a, b uint32
  14. // window is treated like a circular buffer, where the oldest element
  15. // is indicated by d.oldest
  16. window []byte
  17. oldest int
  18. n uint32
  19. vanilla hash.Hash32
  20. }
  21. // Reset resets the Hash to its initial state.
  22. func (d *digest) Reset() {
  23. d.window = d.window[:0] // Reset the size but don't reallocate
  24. d.a = 1
  25. d.b = 0
  26. d.oldest = 0
  27. }
  28. // New returns a new rollinghash.Hash32 computing the rolling Adler-32
  29. // checksum. The window is copied from the last Write(). This window is
  30. // only used to determine which is the oldest element (leaving the
  31. // window). The calls to Roll() do not recompute the whole checksum.
  32. func New() rollinghash.Hash32 {
  33. return &digest{
  34. a: 1,
  35. b: 0,
  36. window: make([]byte, 0),
  37. oldest: 0,
  38. vanilla: vanilla.New(),
  39. }
  40. }
  41. // Size returns the number of bytes Sum will return.
  42. func (d *digest) Size() int { return Size }
  43. // BlockSize returns the hash's underlying block size.
  44. // The Write method must be able to accept any amount
  45. // of data, but it may operate more efficiently if all
  46. // writes are a multiple of the block size.
  47. func (d *digest) BlockSize() int { return 1 }
  48. // Write (via the embedded io.Writer interface) adds more data to the
  49. // running hash. It never returns an error.
  50. func (d *digest) Write(p []byte) (int, error) {
  51. // Copy the window, avoiding allocations where possible
  52. if len(d.window) != len(p) {
  53. if cap(d.window) >= len(p) {
  54. d.window = d.window[:len(p)]
  55. } else {
  56. d.window = make([]byte, len(p))
  57. }
  58. }
  59. copy(d.window, p)
  60. // Piggy-back on the core implementation
  61. d.vanilla.Reset()
  62. d.vanilla.Write(p)
  63. s := d.vanilla.Sum32()
  64. d.a, d.b = s&0xffff, s>>16
  65. d.n = uint32(len(p)) % Mod
  66. return len(d.window), nil
  67. }
  68. func (d *digest) Sum32() uint32 {
  69. return d.b<<16 | d.a
  70. }
  71. func (d *digest) Sum(b []byte) []byte {
  72. v := d.Sum32()
  73. return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
  74. }
  75. // Roll updates the checksum of the window from the leaving byte and the
  76. // entering byte. See
  77. // http://stackoverflow.com/questions/40985080/why-does-my-rolling-adler32-checksum-not-work-in-go-modulo-arithmetic
  78. func (d *digest) Roll(b byte) {
  79. if len(d.window) == 0 {
  80. d.window = make([]byte, 1)
  81. d.window[0] = b
  82. }
  83. // extract the entering/leaving bytes and update the circular buffer.
  84. enter := uint32(b)
  85. leave := uint32(d.window[d.oldest])
  86. d.window[d.oldest] = b
  87. d.oldest += 1
  88. if d.oldest >= len(d.window) {
  89. d.oldest = 0
  90. }
  91. // compute
  92. d.a = (d.a + Mod + enter - leave) % Mod
  93. d.b = (d.b + (d.n*leave/Mod+1)*Mod + d.a - (d.n * leave) - 1) % Mod
  94. }