added many var* funcs

This commit is contained in:
2026-06-04 23:56:18 +00:00
parent 12b8905852
commit bff16d774d
2 changed files with 478 additions and 58 deletions

View File

@@ -960,7 +960,7 @@ func ReadVarUint32(src io.Reader) (num uint32, err error) {
// значащие.
func WriteVarUint64(w io.Writer, num uint64) (int, error) {
a := make([]byte, 9)
for i := 0; i < 8; i++ {
for i := range 8 {
a[i] = byte(num & 127)
num >>= 7
if num == 0 {
@@ -977,7 +977,7 @@ func WriteVarUint64(w io.Writer, num uint64) (int, error) {
// ReadVarUint64 - читает из io.Reader от 1 до 9 байт, и декодирует их в uint64.
func ReadVarUint64(src io.Reader) (num uint64, err error) {
var b byte
for i := 0; i < 8; i++ {
for i := range 8 {
b, err = ReadByte(src)
if err != nil {
return
@@ -1024,7 +1024,7 @@ func WriteVarSize(w io.Writer, n int) (int, error) {
num = uint64(n)
a = make([]byte, 9)
)
for i := 0; i < 8; i++ {
for i := range 8 {
a[i] = byte(num & 127)
num >>= 7
if num == 0 {
@@ -1043,7 +1043,7 @@ func ReadVarSize(src io.Reader) (_ int, err error) {
b byte
num uint64
)
for i := 0; i < 8; i++ {
for i := range 8 {
b, err = ReadByte(src)
if err != nil {
return
@@ -1098,6 +1098,46 @@ func CountVarUint64(n uint64) int {
}
return 9
}
var varInt64PlusCutoffs = []int64{
1 << 6,
1 << 13,
1 << 20,
1 << 27,
1 << 34,
1 << 41,
1 << 48,
1 << 55,
}
var varInt64MinusCutoffs = []int64{
-1 << 6,
-1 << 13,
-1 << 20,
-1 << 27,
-1 << 34,
-1 << 41,
-1 << 48,
-1 << 55,
}
func CountVarInt64(n int64) int {
if n >= 0 {
for i, cutoff := range varInt64PlusCutoffs {
if n < cutoff {
return i + 1
}
}
} else {
for i, cutoff := range varInt64MinusCutoffs {
if n >= cutoff {
return i + 1
}
}
}
return 9
}
func CountVarSize(n int) int {
u64 := uint64(n)
for i, cutoff := range varUint64Cutoffs {
@@ -1110,15 +1150,9 @@ func CountVarSize(n int) int {
// PUT/GET VAR SIZE
func PutVarSize(a []byte, n int) (int, error) {
if n < 0 {
return 0, ErrNegativeSize
}
var (
num = uint64(n)
tmp = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0}
)
for i := 0; i < 8; i++ {
func PutVarUint64(a []byte, num uint64) (int, error) {
var tmp [9]byte
for i := range 8 {
if i < len(a) {
if num < 128 {
tmp[i] = byte(num) | 128
@@ -1136,26 +1170,191 @@ func PutVarSize(a []byte, n int) (int, error) {
return 0, ErrNoSpace
}
tmp[8] = byte(num)
copy(a, tmp)
copy(a, tmp[:])
return 9, nil
}
func GetVarSize(a []byte) (int, error) {
func GetVarUint64(a []byte) (uint64, int, error) {
var num uint64
for i := 0; i < 8; i++ {
for i := range 8 {
if i < len(a) {
b := a[i]
num |= uint64(b&127) << uint(i*7)
if b >= 128 {
return int(num), nil
return num, i + 1, nil
}
} else {
return 0, ErrNoSpace
return 0, 0, ErrNoSpace
}
}
if len(a) < 9 {
return 0, ErrNoSpace
return 0, 0, ErrNoSpace
}
num |= uint64(a[8]) << 56
return num, 9, nil
}
func PutVarSize(arr []byte, n int) (int, error) {
if n < 0 {
return 0, ErrNegativeSize
}
return PutVarUint64(arr, uint64(n))
}
func GetVarSize(arr []byte) (int, error) {
num, _, err := GetVarUint64(arr)
if err != nil {
return 0, err
}
return int(num), nil
}
func PutVarInt64(arr []byte, num int64) (int, error) {
return PutVarUint64(arr, EncodeZigZag(num))
}
func GetVarInt64(arr []byte) (int64, int, error) {
u64, n, err := GetVarUint64(arr)
if err != nil {
return 0, 0, err
}
return DecodeZigZag(u64), n, nil
}
// REVERSE
// func reverseCopy(dst []byte, src []byte) error {
// if len(dst) >= len(src) {
// i := len(dst) - 1
// for _, b := range src {
// dst[i] = b
// i--
// }
// return nil
// }
// return ErrNoSpace
// }
func copyReversed(dst []byte, src []byte) error {
if len(dst) >= len(src) {
j := 0
for i := len(src) - 1; i >= 0; i-- {
dst[j] = src[i]
j++
}
return nil
}
return ErrNoSpace
}
// ReversePutVarUint64 - записує закодоване значення як PutVarUint64, але із оберненим
// порядком байт. Прочитати значення можна функцією ReverseGetVarUint64, вказавши правильне
// початкове зміщення справа.
func ReversePutVarUint64(arr []byte, num uint64) (int, error) {
var tmp [9]byte
for i := range 8 {
tmp[i] = byte(num & 127)
num >>= 7
if num == 0 {
tmp[i] |= 128
n := i + 1
err := copyReversed(arr, tmp[:n])
if err != nil {
return 0, err
}
return n, nil
}
}
tmp[8] = byte(num)
err := copyReversed(arr, tmp[:])
if err != nil {
return 0, err
}
return 9, nil
}
// ReverseGetVarUint64 - читає значення, закодоване функцією ReversePutVarUint64.
// Останній байт arr - це молодший байт закодованного значення.
func ReverseGetVarUint64(arr []byte) (uint64, int, error) {
var (
num uint64
j = len(arr) - 1
)
for i := range 8 {
if j >= 0 {
b := arr[j]
num |= uint64(b&127) << uint(i*7)
if b >= 128 {
return num, i + 1, nil
}
j--
} else {
return 0, 0, io.EOF
}
}
if j >= 0 {
num |= uint64(arr[j]) << 56
return num, 9, nil
}
return 0, 0, io.EOF
}
// ReversePutVarInt64 - записує закодоване значення як PutVarInt64, але із оберненим
// порядком байт. Прочитати значення можна функцією ReverseGetVarInt64, вказавши правильне
// початкове зміщення справа.
func ReversePutVarInt64(arr []byte, num int64) (int, error) {
return ReversePutVarUint64(arr, EncodeZigZag(num))
}
// ReverseGetVarInt64 - читає значення, закодоване функцією ReversePutVarInt64.
// Останній байт arr - це молодший байт закодованного значення.
func ReverseGetVarInt64(arr []byte) (int64, int, error) {
u64, n, err := ReverseGetVarUint64(arr)
if err != nil {
return 0, 0, err
}
return DecodeZigZag(u64), n, nil
}
// TailPutVarUint64 - записує закодоване значення в кінці arr.
// Прочитати значення можна функцією GetVarUint64, вказавши правильне
// початкове зміщення зліва.
func TailPutVarUint64(arr []byte, num uint64) (int, error) {
var tmp [9]byte
for i := range 8 {
tmp[i] = byte(num & 127)
num >>= 7
if num == 0 {
tmp[i] |= 128
n := i + 1
if len(arr) < n {
return 0, ErrNoSpace
}
copy(arr[len(arr)-n:], tmp[:n])
return n, nil
}
}
tmp[8] = byte(num)
if len(arr) < 9 {
return 0, ErrNoSpace
}
copy(arr[len(arr)-9:], tmp[:])
return 9, nil
}
// TailPutVarInt64 - записує закодоване значення в кінці arr.
// Прочитати значення можна функцією GetVarInt64, вказавши правильне
// початкове зміщення зліва.
func TailPutVarInt64(arr []byte, num int64) (int, error) {
return TailPutVarUint64(arr, EncodeZigZag(num))
}
// ZigZag encoding: int64 -> uint64
func EncodeZigZag(x int64) uint64 {
return uint64(x<<1) ^ uint64(x>>63)
}
// ZigZag decoding: uint64 -> int64
func DecodeZigZag(u uint64) int64 {
return int64(u>>1) ^ -(int64(u & 1))
}

View File

@@ -703,10 +703,9 @@ func TestWriteReadBool(t *testing.T) {
}
}
// VAR SIZE COUNT
// COUNT VAR SIZE
func TestCountVarUint32(t *testing.T) {
var testCases = []struct {
var varUint32TestCases = []struct {
Num uint32
Count int
}{
@@ -720,7 +719,9 @@ func TestCountVarUint32(t *testing.T) {
{Num: (1 << 28), Count: 5},
{Num: (1 << 32) - 1, Count: 5},
}
for _, testCase := range testCases {
func TestCountVarUint32(t *testing.T) {
for _, testCase := range varUint32TestCases {
count := CountVarUint32(testCase.Num)
if count != testCase.Count {
t.Fatalf("calculated count %d is not equal %d for num %d\n",
@@ -729,11 +730,12 @@ func TestCountVarUint32(t *testing.T) {
}
}
func TestCountVarUint64(t *testing.T) {
var testCases = []struct {
var varUint64TestCases = []struct {
Num uint64
Count int
}{
{Num: 0, Count: 1},
{Num: 1, Count: 1},
{Num: (1 << 7) - 1, Count: 1},
{Num: (1 << 7), Count: 2},
{Num: (1 << 14) - 1, Count: 2},
@@ -752,7 +754,9 @@ func TestCountVarUint64(t *testing.T) {
{Num: (1 << 56), Count: 9},
{Num: (1 << 64) - 1, Count: 9},
}
for _, testCase := range testCases {
func TestCountVarUint64(t *testing.T) {
for _, testCase := range varUint64TestCases {
count := CountVarUint64(testCase.Num)
if count != testCase.Count {
t.Fatalf("calculated count %d is not equal %d for num %d\n",
@@ -761,10 +765,65 @@ func TestCountVarUint64(t *testing.T) {
}
}
var varInt64TestCases = []struct {
Num int64
Count int
}{
{Num: -(1 << 63), Count: 9},
{Num: -(1 << 55) - 1, Count: 9},
{Num: -(1 << 55), Count: 8},
{Num: -(1 << 48) - 1, Count: 8},
{Num: -(1 << 48), Count: 7},
{Num: -(1 << 41) - 1, Count: 7},
{Num: -(1 << 41), Count: 6},
{Num: -(1 << 34) - 1, Count: 6},
{Num: -(1 << 34), Count: 5},
{Num: -(1 << 27) - 1, Count: 5},
{Num: -(1 << 27), Count: 4},
{Num: -(1 << 20) - 1, Count: 4},
{Num: -(1 << 20), Count: 3},
{Num: -(1 << 13) - 1, Count: 3},
{Num: -(1 << 13), Count: 2},
{Num: -(1 << 6) - 1, Count: 2},
{Num: -(1 << 6), Count: 1},
{Num: -1, Count: 1},
{Num: 0, Count: 1},
{Num: 1, Count: 1},
{Num: (1 << 6) - 1, Count: 1},
{Num: (1 << 6), Count: 2},
{Num: (1 << 13) - 1, Count: 2},
{Num: (1 << 13), Count: 3},
{Num: (1 << 20) - 1, Count: 3},
{Num: (1 << 20), Count: 4},
{Num: (1 << 27) - 1, Count: 4},
{Num: (1 << 27), Count: 5},
{Num: (1 << 34) - 1, Count: 5},
{Num: (1 << 34), Count: 6},
{Num: (1 << 41) - 1, Count: 6},
{Num: (1 << 41), Count: 7},
{Num: (1 << 48) - 1, Count: 7},
{Num: (1 << 48), Count: 8},
{Num: (1 << 55) - 1, Count: 8},
{Num: (1 << 55), Count: 9},
{Num: (1 << 63) - 1, Count: 9},
}
func TestCountVarInt64(t *testing.T) {
for _, testCase := range varInt64TestCases {
count := CountVarInt64(testCase.Num)
if count != testCase.Count {
t.Fatalf("calculated count %d is not equal %d for num %d\n",
count, testCase.Count, testCase.Num)
}
}
}
var varSizeTestCases = []struct {
Num int
Count int
}{
{Num: 0, Count: 1},
{Num: 1, Count: 1},
{Num: (1 << 7) - 1, Count: 1},
{Num: (1 << 7), Count: 2},
{Num: (1 << 14) - 1, Count: 2},
@@ -794,6 +853,62 @@ func TestCountVarSize(t *testing.T) {
}
}
// PUT & GET VAR INT64
func TestPutGetVarInt64(t *testing.T) {
for _, testCase := range varInt64TestCases {
a := make([]byte, 9)
putCount, err := PutVarInt64(a, testCase.Num)
if err != nil {
t.Fatalf("put num %d: %s", testCase.Num, err)
}
decoded, getCount, err := GetVarInt64(a)
if err != nil {
t.Fatalf("get num %d: %s", testCase.Num, err)
}
if decoded != testCase.Num {
t.Fatalf("num %d not equal decoded %d", testCase.Num, decoded)
}
if putCount != testCase.Count {
t.Fatalf("count %d not equal put count %d of num %d",
testCase.Count, putCount, testCase.Num)
}
if getCount != testCase.Count {
t.Fatalf("count %d not equal get count %d of num %d",
testCase.Count, getCount, testCase.Num)
}
}
}
// PUT & GET VAR UINT64
func TestPutGetVarUint64(t *testing.T) {
for _, testCase := range varUint64TestCases {
a := make([]byte, 9)
putCount, err := PutVarUint64(a, testCase.Num)
if err != nil {
t.Fatalf("put %d: %s\n", testCase.Num, err)
}
decoded, getCount, err := GetVarUint64(a)
if err != nil {
t.Fatalf("get %d: %s\n", testCase.Num, err)
}
if decoded != testCase.Num {
t.Fatalf("num %d not equal decoded %d", testCase.Num, decoded)
}
if putCount != testCase.Count {
t.Fatalf("count %d not equal put count %d of num %d",
testCase.Count, putCount, testCase.Num)
}
if getCount != testCase.Count {
t.Fatalf("count %d not equal get count %d of num %d",
testCase.Count, getCount, testCase.Num)
}
}
}
// WRITE & READ VAR SIZE
func TestWriteReadVarSize(t *testing.T) {
for _, testCase := range varSizeTestCases {
w := bytes.NewBuffer(nil)
@@ -821,6 +936,8 @@ func TestWriteReadVarSize(t *testing.T) {
}
}
// PUT & GET VAR SIZE
func TestPutGetVarSize(t *testing.T) {
for _, testCase := range varSizeTestCases {
a := make([]byte, 9)
@@ -848,6 +965,110 @@ func TestPutGetVarSize(t *testing.T) {
}
}
// PUT & GET REVERSE VAR UINT64 / INT64
func TestReversePutGetVarUint64(t *testing.T) {
for _, testCase := range varUint64TestCases {
a := make([]byte, 9)
putCount, err := ReversePutVarUint64(a, testCase.Num)
if err != nil {
t.Fatalf("put num %d: %s", testCase.Num, err)
}
decoded, getCount, err := ReverseGetVarUint64(a[:testCase.Count])
if err != nil {
t.Fatalf("get num %d: %s", testCase.Num, err)
}
if decoded != testCase.Num {
t.Fatalf("num %d not equal decoded %d", testCase.Num, decoded)
}
if putCount != testCase.Count {
t.Fatalf("count %d not equal put count %d of num %d",
testCase.Count, putCount, testCase.Num)
}
if getCount != testCase.Count {
t.Fatalf("count %d not equal get count %d of num %d",
testCase.Count, getCount, testCase.Num)
}
}
}
func TestReversePutGetVarInt64(t *testing.T) {
for _, testCase := range varInt64TestCases {
a := make([]byte, 9)
putCount, err := ReversePutVarInt64(a, testCase.Num)
if err != nil {
t.Fatalf("put num %d: %s", testCase.Num, err)
}
decoded, getCount, err := ReverseGetVarInt64(a[:testCase.Count])
if err != nil {
t.Fatalf("get num %d: %s", testCase.Num, err)
}
if decoded != testCase.Num {
t.Fatalf("num %d not equal decoded %d", testCase.Num, decoded)
}
if putCount != testCase.Count {
t.Fatalf("count %d not equal put count %d of num %d",
testCase.Count, putCount, testCase.Num)
}
if getCount != testCase.Count {
t.Fatalf("count %d not equal get count %d of num %d",
testCase.Count, getCount, testCase.Num)
}
}
}
// PUT TAIL VAR UINT64/INT64 & GET VAR UINT64/INT64
func TestTailPutGetVarInt64(t *testing.T) {
for _, testCase := range varInt64TestCases {
a := make([]byte, 9)
putCount, err := TailPutVarInt64(a, testCase.Num)
if err != nil {
t.Fatalf("put num %d: %s", testCase.Num, err)
}
decoded, getCount, err := GetVarInt64(a[9-testCase.Count:])
if err != nil {
t.Fatalf("get num %d: %s", testCase.Num, err)
}
if decoded != testCase.Num {
t.Fatalf("num %d not equal decoded %d", testCase.Num, decoded)
}
if putCount != testCase.Count {
t.Fatalf("count %d not equal put count %d of num %d",
testCase.Count, putCount, testCase.Num)
}
if getCount != testCase.Count {
t.Fatalf("count %d not equal get count %d of num %d",
testCase.Count, getCount, testCase.Num)
}
}
}
func TestTailPutGetVarUint64(t *testing.T) {
for _, testCase := range varUint64TestCases {
a := make([]byte, 9)
putCount, err := TailPutVarUint64(a, testCase.Num)
if err != nil {
t.Fatalf("put num %d: %s", testCase.Num, err)
}
decoded, getCount, err := GetVarUint64(a[9-testCase.Count:])
if err != nil {
t.Fatalf("get num %d: %s", testCase.Num, err)
}
if decoded != testCase.Num {
t.Fatalf("num %d not equal decoded %d", testCase.Num, decoded)
}
if putCount != testCase.Count {
t.Fatalf("count %d not equal put count %d of num %d",
testCase.Count, putCount, testCase.Num)
}
if getCount != testCase.Count {
t.Fatalf("count %d not equal get count %d of num %d",
testCase.Count, getCount, testCase.Num)
}
}
}
// HELPERS
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"