Files
Go_I2p/lib/common/data/integer.go
2024-11-11 20:11:58 -05:00

131 lines
3.9 KiB
Go

package data
import (
"encoding/binary"
"errors"
"math"
)
// MAX_INTEGER_SIZE is the maximum length of an I2P integer in bytes.
const MAX_INTEGER_SIZE = 8
/*
[I2P Integer]
Accurate for version 0.9.49
Description
Represents a non-negative integer.
Contents
1 to 8 bytes in network byte order (big endian) representing an unsigned integer.
*/
var (
// ErrInvalidSize indicates the requested integer size is invalid (<=0 or >MAX_INTEGER_SIZE)
ErrInvalidSize = errors.New("invalid integer size")
// ErrInsufficientData indicates there isn't enough data to read the requested size
ErrInsufficientData = errors.New("insufficient data")
// ErrNegativeValue indicates an attempt to create an Integer from a negative value
ErrNegativeValue = errors.New("negative values not allowed")
// ErrIntegerOverflow indicates the value exceeds the maximum allowed size
ErrIntegerOverflow = errors.New("integer overflow")
)
// Integer is the representation of an I2P Integer.
// It contains 1 to 8 bytes in network byte order (big endian)
// representing an unsigned integer value.
type Integer []byte
// Bytes returns the raw []byte content of an Integer.
// This represents the big-endian encoded form of the integer.
func (i Integer) Bytes() []byte {
return i
}
// Int returns the Integer as a Go integer.
// Returns an error if the value would overflow on the current platform
// or if the encoding is invalid.
func (i Integer) Int() int {
val, _ := intFromBytes(i)
return val
}
// ReadInteger returns an Integer from a []byte of specified length.
// The remaining bytes after the specified length are also returned.
// Returns an error if size is invalid or there isn't enough data.
func ReadInteger(bytes []byte, size int) (Integer, []byte, error) {
if size <= 0 {
return nil, bytes, ErrInvalidSize
}
if size > len(bytes) {
return nil, bytes, ErrInsufficientData
}
return Integer(bytes[:size]), bytes[size:], nil
}
// NewInteger creates a new Integer from []byte using ReadInteger.
// Limits the length of the created Integer to MAX_INTEGER_SIZE.
// Returns a pointer to Integer and the remaining bytes.
// Returns an error if size is invalid or there isn't enough data.
func NewInteger(bytes []byte, size int) (*Integer, []byte, error) {
if size <= 0 || size > MAX_INTEGER_SIZE {
return nil, bytes, ErrInvalidSize
}
if len(bytes) < size {
return nil, bytes, ErrInsufficientData
}
integer, remainder, err := ReadInteger(bytes, size)
if err != nil {
return nil, bytes, err
}
return &integer, remainder, nil
}
// NewIntegerFromInt creates a new Integer from a Go integer of a specified []byte length.
// The value must be non-negative and fit within the specified number of bytes.
// Returns an error if the size is invalid or the value cannot be represented.
func NewIntegerFromInt(value int, size int) (*Integer, error) {
if size <= 0 || size > MAX_INTEGER_SIZE {
return nil, ErrInvalidSize
}
if value < 0 {
return nil, ErrNegativeValue
}
// Check if value fits in specified size
maxVal := int(math.Pow(2, float64(size*8))) - 1
if value > maxVal {
return nil, ErrIntegerOverflow
}
buf := make([]byte, MAX_INTEGER_SIZE)
binary.BigEndian.PutUint64(buf, uint64(value))
data := buf[MAX_INTEGER_SIZE-size:]
integer := Integer(data)
return &integer, nil
}
// intFromBytes interprets a slice of bytes from length 0 to length 8 as a big-endian
// integer and returns an int representation.
// Returns an error if the value would overflow on the current platform
// or if the input is invalid.
func intFromBytes(number []byte) (int, error) {
if len(number) == 0 {
return 0, nil
}
if len(number) > MAX_INTEGER_SIZE {
return 0, ErrInvalidSize
}
padded := make([]byte, MAX_INTEGER_SIZE)
copy(padded[MAX_INTEGER_SIZE-len(number):], number)
val := int64(binary.BigEndian.Uint64(padded))
if val > math.MaxInt32 || val < math.MinInt32 {
return 0, ErrIntegerOverflow
}
return int(val), nil
}