-
Notifications
You must be signed in to change notification settings - Fork 4
/
b1t6.go
118 lines (105 loc) · 3.98 KB
/
b1t6.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Package b1t6 implements the b1t6 encoding which uses a group of 6 trits to encode each byte.
// See the IOTA protocol TIP-5 https://iotaledger.github.io/tips/tips/TIP-0005/tip-0005.html for details.
package b1t6
import (
"errors"
"fmt"
"math"
"strings"
"github.com/iotaledger/iota.go/consts"
"github.com/iotaledger/iota.go/trinary"
)
const (
tritsPerByte = 6
trytesPerByte = tritsPerByte / consts.TritsPerTryte
)
// EncodedLen returns the trit-length of an encoding of n source bytes.
func EncodedLen(n int) int { return n * tritsPerByte }
// Encode encodes src into EncodedLen(len(in)) trits of dst. As a convenience, it returns the number of trits written,
// but this value is always EncodedLen(len(src)).
// Encode implements the b1t6 encoding converting a bit string into ternary.
func Encode(dst trinary.Trits, src []byte) int {
j := 0
for i := range src {
t1, t2 := encodeGroup(src[i])
trinary.MustPutTryteTrits(dst[j:], t1)
trinary.MustPutTryteTrits(dst[j+consts.TritsPerTryte:], t2)
j += 6
}
return j
}
// EncodeToTrytes returns the encoding of src converted into trytes.
func EncodeToTrytes(src []byte) trinary.Trytes {
var dst strings.Builder
dst.Grow(EncodedLen(len(src)) / consts.TritsPerTryte)
for i := range src {
t1, t2 := encodeGroup(src[i])
dst.WriteByte(trinary.MustTryteValueToTryte(t1))
dst.WriteByte(trinary.MustTryteValueToTryte(t2))
}
return dst.String()
}
var (
// ErrInvalidLength reports an attempt to decode an input which has a trit-length that is not a multiple of 6.
ErrInvalidLength = errors.New("length must be a multiple of 6 trits")
// ErrInvalidTrits reports an attempt to decode an input that contains an invalid trit sequence.
ErrInvalidTrits = errors.New("invalid trits")
)
// DecodedLen returns the byte-length of a decoding of n source trits.
func DecodedLen(n int) int { return n / tritsPerByte }
// Decode decodes src into DecodedLen(len(in)) bytes of dst and returns the actual number of bytes written.
// Decode expects that src contains a valid b1t6 encoding and that src has a length that is a multiple of 6,
// it returns an error otherwise. If src does not contain trits, the behavior of Decode is undefined.
func Decode(dst []byte, src trinary.Trits) (int, error) {
i := 0
for j := 0; j <= len(src)-tritsPerByte; j += tritsPerByte {
t1 := trinary.MustTritsToTryteValue(src[j:])
t2 := trinary.MustTritsToTryteValue(src[j+consts.TritsPerTryte:])
b, ok := decodeGroup(t1, t2)
if !ok {
return i, fmt.Errorf("%w: %v", ErrInvalidTrits, src[j:j+6])
}
dst[i] = b
i++
}
if len(src)%tritsPerByte != 0 {
return i, ErrInvalidLength
}
return i, nil
}
// DecodeTrytes returns the bytes represented by the t6b1 encoded trytes.
// DecodeTrytes expects that src contains a valid b1t6 encoding and that in has even length,
// it returns an error otherwise. If src does not contain trytes, the behavior of DecodeTrytes is undefined.
func DecodeTrytes(src trinary.Trytes) ([]byte, error) {
dst := make([]byte, DecodedLen(len(src)*consts.TritsPerTryte))
i := 0
for j := 0; j <= len(src)-trytesPerByte; j += trytesPerByte {
t1 := trinary.MustTryteToTryteValue(src[j])
t2 := trinary.MustTryteToTryteValue(src[j+1])
b, ok := decodeGroup(t1, t2)
if !ok {
return nil, fmt.Errorf("%w: %s", ErrInvalidTrits, src[j:j+2])
}
dst[i] = b
i++
}
if len(src)%trytesPerByte != 0 {
return nil, ErrInvalidLength
}
return dst, nil
}
// encodeGroup converts a byte into two tryte values.
func encodeGroup(b byte) (int8, int8) {
// this is equivalent to: IntToTrytes(int8(b), 2)
v := int(int8(b)) + (consts.TryteRadix/2)*consts.TryteRadix + consts.TryteRadix/2 // make un-balanced
quo, rem := v/consts.TryteRadix, v%consts.TryteRadix
return int8(rem + consts.MinTryteValue), int8(quo + consts.MinTryteValue)
}
// decodeGroup converts two tryte values into a byte and a success flag.
func decodeGroup(t1, t2 int8) (byte, bool) {
v := int(t1) + int(t2)*consts.TryteRadix
if v < math.MinInt8 || v > math.MaxInt8 {
return 0, false
}
return byte(v), true
}