Documentation
¶
Overview ¶
Package data implements I2P common data structures according to specification version 0.9.67.
This package provides fundamental data types used throughout the I2P network protocol, including safe constructors and validators to prevent common programming errors.
Overview ¶
The data package contains low-level I2P data structures that are building blocks for higher-level protocol components:
- Integer: Variable-length big-endian integers
- I2PString: Length-prefixed UTF-8 strings (max 255 bytes)
- Date: 64-bit millisecond timestamps (rolls over in 2106)
- Hash: 32-byte SHA-256 hashes
- Mapping: Key-value property maps
- MappingValues: Type-safe key-value pair collections
Safe Constructors ¶
All types provide safe constructors that validate input and return errors rather than panicking or producing invalid data:
// Integer construction with validation
i, err := data.NewIntegerFromBytes(someBytes)
if err != nil {
return err
}
value, err := i.IntSafe() // Returns error instead of defaulting to 0
// String construction with UTF-8 validation
str, err := data.NewI2PString("hello")
if err != nil {
return err
}
content, err := str.DataSafe() // Safe accessor with error handling
// Hash construction from bytes
hash, err := data.NewHashFromSlice(hashBytes)
if err != nil {
return err
}
// Date construction with validation
date, err := data.NewDateFromUnix(timestamp)
if err != nil {
return err
}
Zero-Value Safety ¶
All types provide methods to check for zero/invalid values:
if integer.IsZero() {
// Handle zero integer
}
if hash.IsZero() {
// Handle zero hash
}
if date.IsZero() {
// Handle undefined date
}
Validation ¶
Types provide Validate() and IsValid() methods for checking integrity:
// Validate mapping structure and all key-value pairs
if err := mapping.Validate(); err != nil {
return err
}
// Boolean validation check
if !mapping.IsValid() {
return errors.New("invalid mapping")
}
Encoding and Decoding ¶
The package provides safe integer encoding with overflow checks:
// Encode with size validation
encoded, err := data.EncodeIntN(12345, 4) // validates value fits in 4 bytes
if err != nil {
return err
}
// Decode with length validation
decoded, err := data.DecodeIntN(encoded)
if err != nil {
return err
}
Stream Parsing ¶
Many types support stream-oriented parsing for efficient multi-value reads:
hash1, remaining, err := data.ReadHash(data)
if err != nil {
return err
}
hash2, remaining, err := data.ReadHash(remaining)
// ... continue parsing
Best Practices ¶
- Always use safe constructors (NewXxx) for untrusted input
- Check IsValid() or Validate() after parsing from bytes
- Use safe accessor methods (xxxSafe) when error handling is critical
- Validate sizes before encoding to prevent DoS attacks
Specification ¶
Reference: https://geti2p.net/spec/common-structures
This implementation follows I2P specification version 0.9.67 and provides backward-compatible safe constructors while maintaining the existing API.
Package data implements I2P common data structures.
Index ¶
- Constants
- Variables
- func DecodeInt16(data [2]byte) int16
- func DecodeInt32(data [4]byte) int32
- func DecodeInt64(data [8]byte) int64
- func DecodeIntN(data []byte) (int, error)
- func DecodeUint16(data [2]byte) uint16
- func DecodeUint32(data [4]byte) uint32
- func DecodeUint64(data [8]byte) uint64
- func EncodeInt16(value int16) [2]byte
- func EncodeInt32(value int32) [4]byte
- func EncodeInt64(value int64) [8]byte
- func EncodeIntN(value, size int) ([]byte, error)
- func EncodeUint16(value uint16) [2]byte
- func EncodeUint32(value uint32) [4]byte
- func EncodeUint64(value uint64) [8]byte
- func PrintErrors(errs []error)
- func WrapErrors(errs []error) error
- type Date
- type Hash
- type I2PString
- type Integer
- type Mapping
- func (mapping *Mapping) Data() []byte
- func (mapping *Mapping) DataSafe() ([]byte, error)
- func (mapping *Mapping) HasDuplicateKeys() (bool, error)
- func (mapping *Mapping) IsValid() bool
- func (mapping *Mapping) ToGoMap() (map[string]string, error)
- func (mapping *Mapping) Validate() error
- func (mapping Mapping) Values() MappingValues
- type MappingValues
Constants ¶
const BITS_PER_BYTE = 8
BITS_PER_BYTE is the number of bits in a byte, used for bit shift calculations Cross-Ref: integer.go
const DATE_SIZE = 8
DATE_SIZE is the length in bytes of an I2P Date. Cross-Ref: date.go
const KEY_VAL_INTEGER_LENGTH = 1
KEY_VAL_INTEGER_LENGTH is the length in bytes for encoding key and value lengths in I2P mappings Cross-Ref: mapping.go
const MAPPING_EQUALS_DELIMITER = 0x3d
MAPPING_EQUALS_DELIMITER is the ASCII character '=' (0x3d) used to separate keys from values in I2P mappings Cross-Ref: mapping.go
const MAPPING_MIN_SIZE = 2
MAPPING_MIN_SIZE is the minimum size in bytes for a valid I2P mapping (2-byte size field; size=0 is valid) Cross-Ref: mapping.go
const MAPPING_SEMICOLON_DELIMITER = 0x3b
MAPPING_SEMICOLON_DELIMITER is the ASCII character ';' (0x3b) used to separate key-value pairs in I2P mappings Cross-Ref: mapping.go
const MAPPING_SIZE_FIELD_LENGTH = 2
MAPPING_SIZE_FIELD_LENGTH is the length in bytes of the mapping size field in I2P mappings Cross-Ref: mapping.go
const MAX_INTEGER_SIZE = 8
MAX_INTEGER_SIZE is the maximum length of an I2P integer in bytes. Cross-Ref: integer.go
const MAX_MAPPING_DATA_SIZE = 65535
MAX_MAPPING_DATA_SIZE is the maximum number of bytes in a mapping's data payload. Per the I2P spec: "Total length limit is 65535 bytes, plus the 2 byte size field, or 65537 total." Cross-Ref: mapping_values.go
const MAX_MAPPING_PAIRS = 1000
MAX_MAPPING_PAIRS is the maximum number of key-value pairs allowed in a single mapping. This prevents infinite loops when parsing malformed mappings. Cross-Ref: mapping_values.go
const STRING_MAX_SIZE = 255
STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P string Cross-Ref: string.go
Variables ¶
var ( ErrZeroLength = fmt.Errorf("error parsing string: zero length") ErrDataTooShort = fmt.Errorf("string parsing warning: string data is shorter than specified by length") ErrDataTooLong = fmt.Errorf("string parsing warning: string contains data beyond length") ErrLengthMismatch = fmt.Errorf("error reading I2P string, length does not match data") ErrMappingLengthMismatch = fmt.Errorf("warning parsing mapping: mapping length exceeds provided data") ErrMappingExpectedEquals = fmt.Errorf("mapping format violation, expected =") ErrMappingExpectedSemicolon = fmt.Errorf("mapping format violation, expected ;") )
var ZeroHash = Hash{}
ZeroHash represents an all-zeros hash (not a valid SHA256 of any data).
Functions ¶
func DecodeInt16 ¶ added in v0.0.6
DecodeInt16 decodes a 2-byte big-endian array to an int16 value.
Example:
value := data.DecodeInt16([2]byte{251, 46})
// value = -1234
func DecodeInt32 ¶ added in v0.0.6
DecodeInt32 decodes a 4-byte big-endian array to an int32 value.
Example:
value := data.DecodeInt32([4]byte{255, 254, 29, 192})
// value = -123456
func DecodeInt64 ¶ added in v0.0.6
DecodeInt64 decodes an 8-byte big-endian array to an int64 value.
Example:
value := data.DecodeInt64([8]byte{255, 255, 255, 255, 248, 164, 50, 235})
// value = -123456789
func DecodeIntN ¶ added in v0.0.6
DecodeIntN decodes a variable-length byte slice to an integer.
Parameters:
- data: The byte slice to decode (1-8 bytes, big-endian)
Returns:
- int: The decoded integer value
- error: Error if data is empty or too large
Example:
value, err := data.DecodeIntN([]byte{4, 210})
// value = 1234, err = nil
func DecodeUint16 ¶ added in v0.0.6
DecodeUint16 decodes a 2-byte big-endian array to a uint16 value.
Example:
value := data.DecodeUint16([2]byte{4, 210})
// value = 1234
func DecodeUint32 ¶ added in v0.0.6
DecodeUint32 decodes a 4-byte big-endian array to a uint32 value.
Example:
value := data.DecodeUint32([4]byte{0, 1, 226, 64})
// value = 123456
func DecodeUint64 ¶ added in v0.0.6
DecodeUint64 decodes an 8-byte big-endian array to a uint64 value.
Example:
value := data.DecodeUint64([8]byte{0, 0, 0, 0, 7, 91, 205, 21})
// value = 123456789
func EncodeInt16 ¶ added in v0.0.6
EncodeInt16 encodes an int16 value to a 2-byte big-endian array. This is a Go interop convenience function; I2P uses unsigned integers exclusively. Callers should ensure the value is non-negative when targeting I2P wire formats.
Example:
bytes := data.EncodeInt16(-1234)
func EncodeInt32 ¶ added in v0.0.6
EncodeInt32 encodes an int32 value to a 4-byte big-endian array. This is a Go interop convenience function; I2P uses unsigned integers exclusively. Callers should ensure the value is non-negative when targeting I2P wire formats.
Example:
bytes := data.EncodeInt32(-123456)
func EncodeInt64 ¶ added in v0.0.6
EncodeInt64 encodes an int64 value to an 8-byte big-endian array. This is a Go interop convenience function; I2P uses unsigned integers exclusively. Callers should ensure the value is non-negative when targeting I2P wire formats.
Example:
bytes := data.EncodeInt64(-123456789)
func EncodeIntN ¶ added in v0.0.6
EncodeIntN encodes an integer to a variable-length byte slice. This is for cases where size checking is needed. Use the fixed-size Encode* functions when the size is known at compile time.
Parameters:
- value: The integer value to encode (must be non-negative)
- size: The number of bytes to use (1-8)
Returns:
- []byte: The encoded value as a big-endian byte slice
- error: Error if value is negative, size is invalid, or value doesn't fit
Example:
bytes, err := data.EncodeIntN(1234, 2) // bytes = [4, 210], err = nil
func EncodeUint16 ¶ added in v0.0.6
EncodeUint16 encodes a uint16 value to a 2-byte big-endian array. This is a convenience function for creating fixed-size integers without error handling.
Example:
bytes := data.EncodeUint16(1234) // bytes = [4, 210] (0x04D2 in big endian)
func EncodeUint32 ¶ added in v0.0.6
EncodeUint32 encodes a uint32 value to a 4-byte big-endian array. This is a convenience function for creating fixed-size integers without error handling.
Example:
bytes := data.EncodeUint32(123456) // bytes = [0, 1, 226, 64] (0x0001E240 in big endian)
func EncodeUint64 ¶ added in v0.0.6
EncodeUint64 encodes a uint64 value to an 8-byte big-endian array. This is a convenience function for creating fixed-size integers without error handling.
Example:
bytes := data.EncodeUint64(123456789)
func PrintErrors ¶
func PrintErrors(errs []error)
PrintErrors prints a formatted list of errors to the console. Used in: errors.go
func WrapErrors ¶
WrapErrors compiles a slice of errors and returns them wrapped together as a single error. Used in: errors.go
Types ¶
type Date ¶
type Date [8]byte
Date is the represenation of an I2P Date.
https://geti2p.net/spec/common-structures#date
func DateFromTime ¶
DateFromTime takes a time.Time and returns a data.Date. Returns error if the time is before the Unix epoch (January 1, 1970).
func NewDate ¶
NewDate creates a new Date from []byte using ReadDate. Returns a pointer to Date unlike ReadDate.
func NewDateFromMillis ¶ added in v0.1.0
NewDateFromMillis creates a Date from milliseconds since epoch with validation. Returns error if milliseconds is negative.
func NewDateFromUnix ¶ added in v0.1.0
NewDateFromUnix creates a Date from a Unix timestamp (seconds) with validation. Returns error if timestamp is negative or exceeds maximum safe value.
func ReadDate ¶
ReadDate creates a Date from []byte using the first DATE_SIZE bytes. Any data after DATE_SIZE is returned as a remainder.
func (Date) Int ¶
Int returns the Date as a Go integer. WARNING: For Date values >= 2^63 ms since epoch, this method returns 0 because the unsigned I2P Date value exceeds Go's signed int range. Use Date.Time() for reliable, full-range date handling.
func (Date) IsValid ¶ added in v0.1.5
IsValid returns true if the Date represents a defined (non-zero) time.
func (Date) IsZero ¶ added in v0.1.0
IsZero returns true if the date represents zero time (undefined/null). According to I2P spec, a date value of 0 means undefined or null.
func (Date) Time ¶
Time takes the value stored in date as an 8 byte big-endian integer representing the number of milliseconds since the beginning of unix time and converts it to a Go time.Time struct. Uses unsigned decoding to correctly handle the full range of I2P Date values.
If the unsigned millisecond value exceeds math.MaxInt64 (high bit set), Time returns the zero time.Time{}, since Go's time.UnixMilli cannot represent such large values. Callers should check for zero time if working with dates that may have the high bit set.
type Hash ¶
type Hash [32]byte
Hash is the represenation of an I2P Hash.
https://geti2p.net/spec/common-structures#hash
func HashReader ¶
HashReader returns the SHA256 sum from all data read from an io.Reader. return error if one occurs while reading from reader
func NewHash ¶ added in v0.1.0
NewHash creates a Hash from a 32-byte array. This is the preferred way to construct a Hash from known bytes.
func NewHashFromSlice ¶ added in v0.1.0
NewHashFromSlice creates a Hash from a byte slice with validation. Returns error if the slice is not exactly 32 bytes.
func ReadHash ¶ added in v0.1.0
ReadHash reads a 32-byte hash from data and returns remaining bytes.
func (Hash) IsZero ¶ added in v0.1.0
IsZero returns true if the hash is all zeros. Note: This is not the same as the SHA256 of empty data.
type I2PString ¶
type I2PString []byte
I2PString is the representation of an I2P String.
https://geti2p.net/spec/common-structures#string
func NewI2PString ¶ added in v0.1.0
NewI2PString creates a validated I2PString from a Go string. Returns error if the string exceeds STRING_MAX_SIZE (255 bytes). This is the preferred constructor for creating I2PStrings from Go strings.
func NewI2PStringFromBytes ¶ added in v0.1.0
NewI2PStringFromBytes creates an I2PString from raw bytes with validation. Validates that the length prefix matches the actual data length. This is the preferred constructor for creating I2PStrings from byte slices.
func ReadI2PString ¶
ReadI2PString returns I2PString from a []byte. The remaining bytes after the specified length are also returned. Returns a list of errors that occurred during parsing.
func ToI2PString ¶
ToI2PString converts a Go string to an I2PString. Returns error if the string exceeds STRING_MAX_SIZE. Deprecated: Use NewI2PString instead for better clarity and consistency.
func (I2PString) Data ¶
Data returns the I2PString content as a string trimmed to the specified length and not including the length byte. Returns error encountered by Length.
func (I2PString) DataSafe ¶ added in v0.1.0
DataSafe returns the I2PString content with strict validation. Unlike Data(), this fails fast on any inconsistency. Returns error if the I2PString structure is invalid.
func (I2PString) IsValid ¶ added in v0.1.0
IsValid checks if the I2PString has a valid structure. Returns true if the length byte matches the actual data length.
type Integer ¶
type Integer []byte
Integer is the represenation of an I2P Integer.
https://geti2p.net/spec/common-structures#integer
func NewInteger ¶
NewInteger creates a new Integer from []byte using ReadInteger. Deprecated: Use ReadInteger and take address if pointer needed. This function will be removed in v2.0. Returns a pointer to Integer unlike ReadInteger.
func NewIntegerFromBytes ¶ added in v0.1.0
NewIntegerFromBytes creates a validated Integer from a byte slice. Returns error if bytes is empty or exceeds maximum integer size. This is the recommended safe constructor for creating Integers from raw bytes.
func NewIntegerFromInt ¶
NewIntegerFromInt creates a new Integer from a Go integer of a specified []byte length.
func ReadInteger ¶
ReadInteger returns an Integer from a []byte of specified length. The remaining bytes after the specified length are also returned. Size must be between 1 and MAX_INTEGER_SIZE (8) inclusive.
func (Integer) Int ¶
Int returns the Integer as a Go integer. Returns 0 if conversion fails. WARNING: For 8-byte values >= 2^63, this method returns 0 because the unsigned I2P Integer exceeds Go's signed int range. Use UintSafe() for the full unsigned range.
func (Integer) IntSafe ¶ added in v0.1.0
IntSafe returns the Integer as a Go int with error handling. Unlike Int(), this method returns an error instead of defaulting to 0. Use this method when you need to distinguish between actual zero values and errors. WARNING: For 8-byte values >= 2^63, this returns an error because the unsigned I2P Integer exceeds Go's signed int range. Use UintSafe() for the full unsigned range.
func (Integer) IsValid ¶ added in v0.1.5
IsValid returns true if the Integer has a valid byte length per the I2P spec. Valid Integers are 1–8 bytes (inclusive). Nil or empty Integers are invalid.
func (Integer) IsZero ¶ added in v0.1.0
IsZero returns true if the integer represents a valid zero value. All bytes in the integer must be 0x00 for this to return true. Returns false for nil or empty Integers, since a zero-length Integer is invalid per the I2P spec (Integers must be 1–8 bytes). Use IsValid() to check whether an Integer has valid length before calling IsZero().
func (Integer) UintSafe ¶ added in v0.1.5
UintSafe returns the Integer as a Go uint64 with error handling. This method correctly handles unsigned integers per the I2P spec, which defines Integer as "an unsigned integer." Values with the high bit set are returned correctly as large positive values, unlike Int()/IntSafe() which may wrap negative for 8-byte values >= 2^63.
type Mapping ¶
type Mapping struct {
// contains filtered or unexported fields
}
Mapping is the represenation of an I2P Mapping.
https://geti2p.net/spec/common-structures#mapping
func GoMapToMapping ¶
GoMapToMapping converts a Go map to a Mapping. The Go map iteration order is intentionally non-deterministic; the resulting Mapping is always sorted by Java String.compareTo() order via ValuesToMapping(), so the output IS deterministic and suitable for use in signed structures.
func NewMapping ¶
NewMapping creates a new *Mapping from []byte using ReadMapping. Returns a pointer to Mapping unlike ReadMapping.
func ReadMapping ¶
ReadMapping returns Mapping from a []byte. The remaining bytes after the specified length are also returned. Returns a list of errors that occurred during parsing.
func ValuesToMapping ¶
func ValuesToMapping(values MappingValues) (*Mapping, error)
ValuesToMapping creates a *Mapping using MappingValues. The values are sorted in the order defined in mappingOrder. Returns error if the total mapping data exceeds the maximum size (65535 bytes).
func (*Mapping) Data ¶
Data returns a Mapping in its []byte form. Returns nil if the mapping is not properly initialized or any key-value pair is invalid. Callers should check for nil to detect serialization failures; use Validate() for details. The size field is recalculated from the serialized pairs to ensure consistency. Data returns the wire format bytes for this Mapping. Deprecated: Use DataSafe() for production serialization paths to ensure proper error handling. This method returns nil on error instead of returning an error, violating the project's error handling contract.
func (*Mapping) DataSafe ¶ added in v0.1.6
DataSafe returns the wire format bytes for this Mapping with proper error reporting. This is the recommended method for production code that needs serialization with guaranteed error handling. Returns error if validation fails or serialization cannot proceed. Empty or uninitialized mappings return the wire encoding for an empty mapping (0x00, 0x00).
func (*Mapping) HasDuplicateKeys ¶
HasDuplicateKeys returns true if two keys in a mapping are identical. Returns error if any key in the mapping is invalid.
func (*Mapping) IsValid ¶ added in v0.1.0
IsValid returns true if the Mapping is properly initialized and valid.
func (*Mapping) ToGoMap ¶ added in v0.1.5
ToGoMap converts a Mapping to a Go map[string]string. Returns an error if any key or value cannot be extracted.
func (*Mapping) Validate ¶ added in v0.1.0
Validate checks if the Mapping is properly initialized and all key-value pairs are valid.
func (Mapping) Values ¶
func (mapping Mapping) Values() MappingValues
Values returns the values contained in a Mapping as MappingValues.
type MappingValues ¶
type MappingValues [][2]I2PString
MappingValues represents the parsed key value pairs inside of an I2P Mapping.
func NewMappingValues ¶ added in v0.1.0
func NewMappingValues(capacity int) MappingValues
NewMappingValues creates a new empty MappingValues with optional initial capacity. This is the safe way to construct MappingValues for building mappings programmatically.
Parameters:
- capacity: Optional initial capacity hint (0 for default)
Returns:
- MappingValues: Empty mapping values ready for use
Example:
mv := data.NewMappingValues(10) // Pre-allocate space for 10 pairs
mv, err := mv.Add("key1", "value1")
if err != nil {
return err
}
func ReadMappingValues ¶
func ReadMappingValues(remainder []byte, map_length Integer) (values *MappingValues, remainder_bytes []byte, errs []error)
ReadMappingValues returns *MappingValues from a []byte. The remaining bytes after the specified length are also returned. Returns a list of errors that occurred during parsing.
func (MappingValues) Add ¶ added in v0.1.0
func (mv MappingValues) Add(key, value string) (MappingValues, error)
Add appends a new key-value pair to the MappingValues. Both key and value are validated as I2P strings before adding.
Parameters:
- key: The key string (max 255 bytes)
- value: The value string (max 255 bytes)
Returns:
- MappingValues: Updated mapping values with the new pair
- error: Error if key or value validation fails
Example:
mv := data.NewMappingValues(0)
mv, err := mv.Add("host", "127.0.0.1")
if err != nil {
return err
}
mv, err = mv.Add("port", "7654")
func (MappingValues) Get ¶
func (m MappingValues) Get(key I2PString) I2PString
Get retrieves the value for a given key from MappingValues.
func (MappingValues) IsValid ¶ added in v0.1.0
func (mv MappingValues) IsValid() bool
IsValid returns true if all key-value pairs in MappingValues are valid. This is a convenience wrapper around Validate().
Example:
if !mv.IsValid() {
return errors.New("invalid mapping values")
}
func (MappingValues) Validate ¶ added in v0.1.0
func (mv MappingValues) Validate() error
Validate checks if all key-value pairs in MappingValues are valid. This ensures all I2PStrings are properly formatted.
Returns:
- error: Error if any key or value is invalid, nil otherwise
Example:
if err := mv.Validate(); err != nil {
return fmt.Errorf("invalid mapping values: %w", err)
}