Files
Go_I2p/lib/common/data/string.go

192 lines
5.5 KiB
Go
Raw Normal View History

package data
2016-02-04 00:54:51 -08:00
import (
"github.com/samber/oops"
2024-10-18 12:08:27 -04:00
"github.com/sirupsen/logrus"
2016-02-15 00:34:29 -08:00
)
2022-09-12 08:31:02 +00:00
// STRING_MAX_SIZE is the maximum number of bytes that can be stored in an I2P string
const STRING_MAX_SIZE = 255
2016-02-04 00:54:51 -08:00
2022-09-12 08:31:02 +00:00
/*
[I2P String]
Accurate for version 0.9.49
2016-02-04 00:54:51 -08:00
2022-09-12 08:31:02 +00:00
Description
Represents a UTF-8 encoded string.
Contents
1 or more bytes where the first byte is the number of bytes (not characters!) in the string
and the remaining 0-255 bytes are the non-null terminated UTF-8 encoded character array.
Length limit is 255 bytes (not characters). Length may be 0.
*/
// I2PString is the represenation of an I2P String.
2016-02-06 01:42:47 -08:00
//
2022-09-12 08:31:02 +00:00
// https://geti2p.net/spec/common-structures#string
type I2PString []byte
// Length returns the length specified in the first byte.
// Returns error if the specified does not match the actual length or the string is otherwise invalid.
2022-04-27 10:48:59 -04:00
func (str I2PString) Length() (length int, err error) {
if len(str) == 0 {
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
2022-04-27 10:48:59 -04:00
"at": "(I2PString) Length",
2016-02-15 00:34:29 -08:00
"reason": "no data",
}).Error("error parsing string")
2024-11-17 11:26:02 -05:00
err = ErrZeroLength
2016-02-15 00:34:29 -08:00
return
2016-02-04 00:54:51 -08:00
}
l, _, err := NewInteger(str[:], 1)
if err != nil {
2024-10-18 12:08:27 -04:00
log.WithError(err).Error("Failed to create Integer from I2PString")
return l.Int(), err
}
2022-04-27 10:48:59 -04:00
length = l.Int()
str_len := len(str)
if length > (str_len - 1) {
log.WithFields(logrus.Fields{
"at": "(I2PString) Length",
"string_bytes_length": str_len,
"string_length_field": length,
"reason": "data less than specified by length",
}).Warn("string format warning")
2024-11-17 11:26:02 -05:00
err = ErrDataTooShort
}
if (str_len - 1) > length {
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
"at": "(I2PString) Length",
"string_bytes_length": str_len,
"string_length_field": length,
"reason": "data contains extra bytes beyond specified length",
2024-10-18 12:08:27 -04:00
}).Warn("string format warning")
2024-11-17 11:26:02 -05:00
err = ErrDataTooLong
}
2016-02-15 00:34:29 -08:00
return
}
2022-09-12 08:31:02 +00:00
// Data returns the I2PString content as a string trimmed to the specified length and not including the length byte.
// Returns error encountered by Length.
2022-04-27 10:48:59 -04:00
func (str I2PString) Data() (data string, err error) {
length, err := str.Length()
if err != nil {
2024-11-17 11:26:02 -05:00
switch err {
case ErrZeroLength:
2024-10-18 12:08:27 -04:00
log.WithError(err).Warn("Zero length I2PString")
return "", err
2024-11-17 11:26:02 -05:00
case ErrDataTooShort:
2024-10-18 12:08:27 -04:00
log.WithError(err).Warn("I2PString data shorter than specified length")
/*
if is, e := ToI2PString(string(str[:])); e != nil {
log.WithError(e).Error("Failed to convert short I2PString")
return "", e
} else {
return is.Data()
}
*/ //Recovery attempt
return "", err
2024-11-17 11:26:02 -05:00
case ErrDataTooLong:
2024-10-18 12:08:27 -04:00
log.WithError(err).Warn("I2PString contains data beyond specified length")
data = string(str[1:])
2024-11-26 19:47:17 -05:00
// data = string(str[1 : length+1]) // Should we recover and trim?
2016-02-15 00:34:29 -08:00
return
2024-11-17 11:26:02 -05:00
default:
log.WithError(err).Error("Unknown error encountered in I2PString.Data()")
return "", err
}
}
if length == 0 {
2024-10-18 12:08:27 -04:00
log.Debug("I2PString is empty")
return "", nil
}
data = string(str[1 : length+1])
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
"data_length": len(data),
}).Debug("Retrieved I2PString data")
return data, nil
}
2022-09-12 08:31:02 +00:00
// ToI2PString converts a Go string to an I2PString.
// Returns error if the string exceeds STRING_MAX_SIZE.
2022-04-27 10:48:59 -04:00
func ToI2PString(data string) (str I2PString, err error) {
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Converting string to I2PString")
data_len := len(data)
if data_len > STRING_MAX_SIZE {
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
2022-04-27 10:48:59 -04:00
"at": "ToI2PI2PString",
2016-02-15 00:34:29 -08:00
"string_len": data_len,
"max_len": STRING_MAX_SIZE,
2016-02-15 00:34:29 -08:00
"reason": "too much data",
}).Error("cannot create I2P string")
err = oops.Errorf("cannot store that much data in I2P string")
2016-02-15 00:34:29 -08:00
return
}
2016-02-05 22:35:37 -08:00
i2p_string := []byte{byte(data_len)}
i2p_string = append(i2p_string, []byte(data)...)
2022-04-27 10:48:59 -04:00
str = I2PString(i2p_string)
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
"i2pstring_length": len(str),
}).Debug("Successfully converted string to I2PString")
2016-02-15 00:34:29 -08:00
return
}
2016-02-04 00:54:51 -08:00
2016-02-06 01:42:47 -08:00
//
2016-02-15 00:34:29 -08:00
// Read a string from a slice of bytes, returning any extra data on the end
2022-04-27 10:48:59 -04:00
// of the slice and any errors encountered parsing the I2PString.
2016-02-06 01:42:47 -08:00
//
2022-09-12 08:31:02 +00:00
// 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.
2022-04-27 10:48:59 -04:00
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error) {
2024-11-15 11:01:06 -05:00
if len(data) == 0 {
err = ErrZeroLength
2024-11-15 11:01:06 -05:00
log.WithError(err).Error("Passed data with len == 0")
return
}
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
"input_length": len(data),
}).Debug("Reading I2PString from bytes")
length, _, err := NewInteger(data, 1)
if err != nil {
2024-10-18 12:08:27 -04:00
log.WithError(err).Error("Failed to read I2PString length")
return
}
data_len := length.Int() + 1
2024-11-15 11:31:10 -05:00
if data_len > len(data) {
log.Errorf("I2PString length %d exceeds available data %d", data_len-1, len(data)-1)
err = ErrDataTooShort
2024-11-15 11:31:10 -05:00
log.WithError(err).Error("Failed to read I2PString")
return
}
str = data[:data_len]
remainder = data[data_len:]
l, err := str.Length()
if l != data_len-1 {
2024-11-17 11:26:02 -05:00
err = ErrLengthMismatch
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
"expected_length": data_len - 1,
"actual_length": l,
}).Error("I2PString length mismatch")
return
2016-02-04 00:54:51 -08:00
}
2024-10-18 12:08:27 -04:00
log.WithFields(logrus.Fields{
"string_length": l,
"remainder_length": len(remainder),
}).Debug("Successfully read I2PString from bytes")
2016-02-15 00:34:29 -08:00
return
2016-02-04 00:54:51 -08:00
}
2022-09-12 08:31:02 +00:00
// NewI2PString creates a new *I2PString from []byte using ReadI2PString.
// Returns a pointer to I2PString unlike ReadI2PString.
/*func NewI2PString(data []byte) (str *I2PString, remainder []byte, err error) {
objstr, remainder, err := ReadI2PString(data)
str = &objstr
return
}*/