173 lines
3.9 KiB
Go
173 lines
3.9 KiB
Go
package inetcksum
|
|
|
|
/*
|
|
Aligned returns true if the current checksum for an InetChecksumSimple 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.
|
|
*/
|
|
func (i *InetChecksumSimple) Aligned() (aligned bool) {
|
|
|
|
aligned = i.aligned
|
|
|
|
return
|
|
}
|
|
|
|
// BlockSize returns the number of bytes at a time that InetChecksumSimple operates on. (It will always return 1.)
|
|
func (i *InetChecksumSimple) BlockSize() (blockSize int) {
|
|
|
|
blockSize = 1
|
|
|
|
return
|
|
}
|
|
|
|
// Reset resets the state of an InetChecksumSimple.
|
|
func (i *InetChecksumSimple) Reset() {
|
|
|
|
i.last = 0x00
|
|
i.sum = 0
|
|
i.last = 0x00
|
|
|
|
}
|
|
|
|
// Size returns how many bytes a checksum is. (It will always return 2.)
|
|
func (i *InetChecksumSimple) 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 *InetChecksumSimple) 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 *InetChecksumSimple) Sum16() (cksum uint16) {
|
|
|
|
var thisSum uint32
|
|
|
|
thisSum = i.sum
|
|
|
|
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
|
|
}
|
|
|
|
// Fold the "carried ones".
|
|
for thisSum > cksumMask {
|
|
thisSum = (thisSum & cksumMask) + (thisSum >> cksumShift)
|
|
}
|
|
cksum = ^uint16(thisSum)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Sum16Bytes is a convenience wrapper around [InetChecksumSimple.Sum16]
|
|
which returns a slice of the uint16 as a 2-byte-long slice instead.
|
|
*/
|
|
func (i *InetChecksumSimple) Sum16Bytes() (cksum []byte) {
|
|
|
|
var sum16 uint16 = i.Sum16()
|
|
|
|
cksum = make([]byte, 2)
|
|
|
|
ord.PutUint16(cksum, sum16)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Write writes data to the underlying InetChecksumSimple buffer. It conforms to [io.Writer].
|
|
|
|
p may be nil or empty; no error will be returned and n will be 0 if so.
|
|
|
|
A copy of p is made first and all hashing operations are performed on that copy.
|
|
*/
|
|
func (i *InetChecksumSimple) Write(p []byte) (n int, err error) {
|
|
|
|
var idx int
|
|
var bufLen int
|
|
var buf []byte
|
|
var iter int
|
|
|
|
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)
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// WriteByte checksums a single byte. It conforms to [io.ByteWriter].
|
|
func (i *InetChecksumSimple) WriteByte(c byte) (err error) {
|
|
|
|
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
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// WriteString checksums a string. It conforms to [io.StringWriter].
|
|
func (i *InetChecksumSimple) WriteString(s string) (n int, err error) {
|
|
|
|
if n, err = i.Write([]byte(s)); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|