package inetcksum import ( `io` ) /* Aligned returns true if the current underlying buffer in an InetChecksum is aligned to the algorithm's requirement for an even number of bytes. Note that if Aligned returns false, a single null pad byte will be applied to the underlying data buffer at time of a Sum* call, but will not be written to the persistent underlying storage. If aligned's underlying buffer/storage is empty or nil, aligned will be true. Aligned will also force-set the internal state's aligned status. */ func (i *InetChecksum) Aligned() (aligned bool) { i.alignLock.Lock() defer i.alignLock.Unlock() i.bufLock.RLock() aligned = i.buf.Len()&2 == 0 i.bufLock.RUnlock() i.aligned = aligned return } // BlockSize returns the number of bytes at a time that InetChecksum operates on. (It will always return 1.) func (i *InetChecksum) BlockSize() (blockSize int) { blockSize = 1 return } /* Bytes returns teh bytes currently in the internal storage. curBuf will be nil if the internal storage has not yet been initialized. */ func (i *InetChecksum) Bytes() (curBuf []byte) { i.bufLock.RLock() defer i.bufLock.RUnlock() if i.buf.Len() != 0 { curBuf = i.buf.Bytes() } return } // Clear empties the internal buffer (but does not affect the checksum state). func (i *InetChecksum) Clear() { i.bufLock.Lock() defer i.bufLock.Unlock() i.buf.Reset() } /* DisablePersist disables the internal persistence of an InetChecksum. This is recommended for integrations that desire the concurrency safety of an InetChecksum but want a smaller memory footprint and do not need a copy of data that was hashed. Any data existing in the buffer will NOT be cleared out if DisablePersist is called. You must call [InetChecksum.Clear] to do that. Persistence CANNOT be reenabled once disabled. [InetChecksum.Reset] must be called to re-enable persistence. */ func (i *InetChecksum) DisablePersist() { i.bufLock.Lock() defer i.bufLock.Unlock() i.disabledBuf = true } // Len returns the current amount of bytes stored in this InetChecksum's internal buffer. func (i *InetChecksum) Len() (l int) { i.bufLock.RLock() defer i.bufLock.RUnlock() l = i.buf.Len() return } /* Reset resets the internal buffer/storage to an empty state. If persistence was disabled ([InetChecksum.DisablePersist]), this method will re-enable it with an empty buffer. If you wish the buffer to be disabled, you must invoke [InetChecksum.DisablePersist] again. If you only wish to clear the buffer without losing the checksum state, use [InetChecksum.Clear]. */ func (i *InetChecksum) Reset() { i.alignLock.Lock() i.bufLock.Lock() i.sumLock.Lock() i.lastLock.Lock() i.aligned = false i.alignLock.Unlock() i.buf.Reset() i.disabledBuf = false i.bufLock.Unlock() i.last = 0x00 i.lastLock.Unlock() i.sum = 0 i.sumLock.Unlock() } // Size returns how many bytes a checksum is. (It will always return 2.) func (i *InetChecksum) Size() (bufSize int) { bufSize = 2 return } // Sum computes the checksum cksum of the current buffer and appends it as big-endian bytes to b. func (i *InetChecksum) Sum(b []byte) (cksumAppended []byte) { var sum16 []byte = i.Sum16Bytes() cksumAppended = append(b, sum16...) return } /* Sum16 computes the checksum of the current buffer and returns it as a uint16. This is the native number used in the IPv4 header. All other Sum* methods wrap this method. If the underlying buffer is empty or nil, cksum will be 0xffff (65535) in line with common implementations. */ func (i *InetChecksum) Sum16() (cksum uint16) { var thisSum uint32 i.alignLock.RLock() i.lastLock.RLock() i.sumLock.RLock() thisSum = i.sum i.sumLock.RUnlock() if !i.aligned { /* "Pad" at the end of the additive ops - a bitshift is used on the sum integer itself instead of a binary.Append() or append() or such to avoid additional memory allocation. */ thisSum += uint32(i.last) << padShift } i.lastLock.RUnlock() i.alignLock.RUnlock() // Fold the "carried ones". for thisSum > cksumMask { thisSum = (thisSum & cksumMask) + (thisSum >> cksumShift) } cksum = ^uint16(thisSum) return } /* Sum16Bytes is a convenience wrapper around [InetChecksum.Sum16] which returns a slice of the uint16 as a 2-byte-long slice instead. */ func (i *InetChecksum) Sum16Bytes() (cksum []byte) { var sum16 uint16 = i.Sum16() cksum = make([]byte, 2) ord.PutUint16(cksum, sum16) return } /* Write writes data to the underlying InetChecksum buffer. It conforms to [io.Writer]. If this operation returns an error, you MUST call [InetChecksum.Reset] as the instance being used can no longer be considered to be in a consistent state. p may be nil or empty; no error will be returned and n will be 0 if so. Write is concurrency safe; a copy of p is made first and all hashing/internal storage writing is performed on/which that copy. */ func (i *InetChecksum) Write(p []byte) (n int, err error) { var idx int var bufLen int var buf []byte var iter int var origLast byte var origAligned bool var origSum uint32 if p == nil || len(p) == 0 { return } // The TL;DR here is the checksum boils down to: // cksum = cksum + ((high << 8) | low) bufLen = len(p) buf = make([]byte, bufLen) copy(buf, p) i.alignLock.Lock() defer i.alignLock.Unlock() i.bufLock.Lock() defer i.bufLock.Unlock() i.sumLock.Lock() defer i.sumLock.Unlock() i.lastLock.Lock() defer i.lastLock.Unlock() origLast = i.last origAligned = i.aligned origSum = i.sum if !i.aligned { // Last write was unaligned, so pair i.last in. i.sum += (uint32(i.last) << padShift) | uint32(buf[0]) i.aligned = true idx = 1 } // Operate on bytepairs. // Note that idx is set to either 0 or 1 depending on if // buf[0] has already been summed in. for iter = idx; iter < bufLen; iter += 2 { if iter+1 < bufLen { // Technically could use "i.sum += uint32(ord.Uint16(buf[iter:iter+2))" here instead. i.sum += (uint32(buf[iter]) << padShift) | uint32(buf[iter+1]) } else { i.last = buf[iter] i.aligned = false break } } if !i.disabledBuf { if n, err = i.buf.Write(buf); err != nil { i.sum = origSum i.aligned = origAligned i.last = origLast return } } return } // WriteByte writes a single byte to the underlying storage. It conforms to [io.ByteWriter]. func (i *InetChecksum) WriteByte(c byte) (err error) { var origLast byte var origAligned bool var origSum uint32 i.alignLock.Lock() defer i.alignLock.Unlock() i.bufLock.Lock() defer i.bufLock.Unlock() i.sumLock.Lock() defer i.sumLock.Unlock() i.lastLock.Lock() defer i.lastLock.Unlock() origLast = i.last origAligned = i.aligned origSum = i.sum if i.aligned { // Since it's a single byte, we just set i.last and unalign. i.last = c i.aligned = false } else { // It's unaligned, so join with i.last and align. i.sum += (uint32(i.last) << padShift) | uint32(c) i.aligned = true } if !i.disabledBuf { if err = i.WriteByte(c); err != nil { i.sum = origSum i.aligned = origAligned i.last = origLast return } } return } // WriteString writes a string to the underlying storage. It conforms to [io.StringWriter]. func (i *InetChecksum) WriteString(s string) (n int, err error) { if n, err = i.Write([]byte(s)); err != nil { return } return } // WriteTo writes the current contents of the underlying buffer to w. The contents are not drained. Noop if persistence is disabled. func (i *InetChecksum) WriteTo(w io.Writer) (n int64, err error) { var wrtn int if i.disabledBuf { return } i.bufLock.RLock() defer i.bufLock.RUnlock() if wrtn, err = w.Write(i.buf.Bytes()); err != nil { n = int64(wrtn) return } n = int64(wrtn) return }