From 57c4f8e7b91cad07703db458981cc5ded3749938 Mon Sep 17 00:00:00 2001 From: dima <1.e4.kc6@gmail.com> Date: Sat, 24 Dec 2022 17:48:08 +0200 Subject: [PATCH] first commit --- arr.go | 198 ++++++++ bin.go | 1470 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bin.sgo | 108 +++++ bin_test.go | 663 +++++++++++++++++++++++++ buffer.go | 47 ++ buffer_test.go | 27 ++ 6 files changed, 2513 insertions(+) create mode 100644 arr.go create mode 100755 bin.go create mode 100755 bin.sgo create mode 100755 bin_test.go create mode 100644 buffer.go create mode 100644 buffer_test.go diff --git a/arr.go b/arr.go new file mode 100644 index 0000000..2380bc4 --- /dev/null +++ b/arr.go @@ -0,0 +1,198 @@ +package bin + +func DeleteArrElem(arr []byte, qty int, elemSize int, idx int) { + dstIdx := elemSize * idx + srcIdx := dstIdx + elemSize + + end := qty * elemSize + + for ; srcIdx < end; srcIdx++ { + arr[dstIdx] = arr[srcIdx] + dstIdx++ + } + + for i := elemSize * (qty - 1); i < end; i++ { + arr[i] = 0 + } +} + +func InsertArrElem(arr []byte, qty int, elem []byte, idx int) { + elemSize := len(elem) + + srcIdx := qty*elemSize - 1 // last byte + dstIdx := srcIdx + elemSize + + end := elemSize * idx + + for ; srcIdx >= end; srcIdx-- { + arr[dstIdx] = arr[srcIdx] + dstIdx-- + } + + // Вставляем элемент + for _, b := range elem { + arr[end] = b + end++ + } +} + +// Для массивов, которые начинаются с конца arr и движутся к началу +func DeleteReverseArrElem(arr []byte, qty int, elemSize int, idx int) { + dstIdx := len(arr) - idx*elemSize - 1 + srcIdx := dstIdx - elemSize + + end := len(arr) - qty*elemSize + + for ; srcIdx >= end; srcIdx-- { + arr[dstIdx] = arr[srcIdx] + dstIdx-- + } + + for i := end; i < end+elemSize; i++ { + arr[i] = 0 + } +} + +// qty = 2 +// 0 0 3 3 1 1 +// 3 3 3 3 1 1 +func InsertReverseArrElem(arr []byte, qty int, elem []byte, idx int) { + elemSize := len(elem) + + srcIdx := len(arr) - qty*elemSize + dstIdx := srcIdx - elemSize + + end := len(arr) - elemSize*idx + + for ; srcIdx < end; srcIdx++ { + arr[dstIdx] = arr[srcIdx] + dstIdx++ + } + + // Вставляем элемент + i := end - elemSize + for _, b := range elem { + arr[i] = b + i++ + } +} + +// Предполагается что значения отсортированы ASC +func FindArrElem(arr []byte, qty int, elem []byte, byteOrder ByteOrder) (elemIdx int, isFound bool) { + if qty == 0 { + return + } + + // Границы (индексы элементов массива) + a := 0 + b := qty - 1 + + for { + elemIdx = (b-a)/2 + a + + code := compareToArrElem(arr, elem, elemIdx, byteOrder) + + if code == 1 { + a = elemIdx + 1 + if a > b { + return elemIdx + 1, false + } + } else if code == -1 { + b = elemIdx - 1 + if b < a { + return elemIdx, false + } + } else { + return elemIdx, true + } + } +} + +// -1, меньше +// 1, больше +// 0, равны +func compareToArrElem(arr []byte, elem []byte, elemIdx int, byteOrder ByteOrder) int { + if byteOrder == HL { + // индекс первого байта + idx := elemIdx * len(elem) + + for _, b := range elem { + if b > arr[idx] { + return 1 + } else if b < arr[idx] { + return -1 + } + idx++ + } + } else { + // индекс последнего байта + idx := (elemIdx+1)*len(elem) - 1 + + for bIdx := len(elem) - 1; bIdx >= 0; bIdx-- { + b := elem[bIdx] + if b > arr[idx] { + return 1 + } else if b < arr[idx] { + return -1 + } + idx-- + } + } + return 0 +} + +// func find(arr []int, num int) (int, bool) { +// var a, b, i int +// b = len(arr) - 1 +// for { +// i = (b-a)/2 + a +// if num > arr[i] { +// a = i + 1 +// if a > b { +// return i + 1, false +// } +// } else if num < arr[i] { +// b = i - 1 +// if b < a { +// return i, false +// } +// } else { +// return i, true +// } +// } +// } + +type KeyComparator interface { + CompareTo(int) int +} + +// Предполагается что значения отсортированы ASC +func BinarySearch(qty int, keyComparator KeyComparator) (elemIdx int, isFound bool) { + if qty == 0 { + return + } + + // Границы (индексы элементов массива) + a := 0 + b := qty - 1 + + for { + elemIdx = (b-a)/2 + a + + code := keyComparator.CompareTo(elemIdx) + + if code == 1 { + a = elemIdx + 1 + if a > b { + return elemIdx + 1, false + } + } else if code == -1 { + b = elemIdx - 1 + if b < a { + return elemIdx, false + } + } else { + return elemIdx, true + } + } +} diff --git a/bin.go b/bin.go new file mode 100755 index 0000000..83990fb --- /dev/null +++ b/bin.go @@ -0,0 +1,1470 @@ +package bin + +// ВАЖНО! +// Игры с битами для int* лучше всего делать в аналогичном uint* типе, а затем в конце +// приводить, например int32(uint32). +// Битовое представление отрицательного числа - все 0 заменяются на единицы. + +import ( + "bytes" + "errors" + "fmt" + "io" + "log" + "math" + "time" +) + +// ByteOrder - порядок байт +type ByteOrder string + +const ( + maxReadAttempts = 5 + // LH - low to high byte order + LH ByteOrder = "LH" + // HL - high to low byte order + HL ByteOrder = "HL" + + Int32SignBit uint32 = 1 << 31 + Int64SignBit uint64 = 1 << 63 +) + +var ( + // ErrReadOverflow shows 100% bug in the source reader + ErrReadOverflow = errors.New("bin: reader returned 'n' > bufsize") + // ErrNegativeReadCount shows 100% bug in the source reader + ErrNegativeReadCount = errors.New("bin: reader returned negative 'n'") + + unknownByteOrder = "bin: unknown byteOrder %q" + wrongArraySize = "bin: array size %d is not enough" + + offsets24HL = []uint{24, 16, 8} + offsets24LH = []uint{0, 8, 16} + + offsets32HL = []uint{24, 16, 8, 0} + offsets32LH = []uint{0, 8, 16, 24} + + offsets48HL = []uint{56, 48, 40, 32, 24, 16} + offsets48LH = []uint{0, 8, 16, 24, 32, 40} + + offsets64HL = []uint{56, 48, 40, 32, 24, 16, 8, 0} + offsets64LH = []uint{0, 8, 16, 24, 32, 40, 48, 56} +) + +// ReadN - безопасно читаем n байт. Обрабатывает все возможные ситуации, в том числе +// баги Reader +func ReadN(r io.Reader, n int) (_ []byte, err error) { + if n == 0 { + return + } + if n < 0 { + err = fmt.Errorf("Wrong n=%d", n) + } + buf := make([]byte, n) + + var q, total, readAttempts int + + // Избегаем вечного цикла из-за неправильной реализации ридера + for readAttempts < maxReadAttempts { + bufsize := n - total + q, err = r.Read(buf[total:]) + // Если буфер заполнен - успех, возвращаем буфер, err игнорируем + if q == bufsize { + return buf, nil + } + // Если ошибка - выходим с ошибкой + if err != nil { + return + } + // Если прочитали больше чем размер буфера. Баг в Reader + if q > bufsize { + err = ErrReadOverflow + return + } + // Если прочитали < 0. Баг в Reader + if q < 0 { + err = ErrNegativeReadCount + return + } + // Если ошибки нет, а прочитали 0 байт - чтобы не войти в бесконечный цикл + // увеличиваем readAttempts + if q == 0 { + readAttempts++ + } else { + // Что-то прочитали, но меньше чем нужно - увеличиваем счетчик + // суммарно прочитанного + total += q + } + } + err = io.ErrNoProgress + return +} + +func ReadString(r io.Reader, n int) (_ string, err error) { + buf, err := ReadN(r, n) + if err != nil { + return + } + return string(buf), nil +} + +// GetInt16 - читает из массива arr 2 байта и декодирует их в int16. +func GetInt16(arr []byte, byteOrder ByteOrder) (num int16, err error) { + if len(arr) < 2 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + num = int16(arr[0]) | (int16(arr[1]) << 8) + case HL: + num = int16(arr[1]) | (int16(arr[0]) << 8) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// GetInt32 - читает из массива arr 4 байта и декодирует их в int32. +func GetInt32(arr []byte, byteOrder ByteOrder) (num int32, err error) { + if len(arr) < 4 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + num = int32(arr[0]) | (int32(arr[1]) << 8) | + (int32(arr[2]) << 16) | (int32(arr[3]) << 24) + case HL: + num = int32(arr[3]) | (int32(arr[2]) << 8) | + (int32(arr[1]) << 16) | (int32(arr[0]) << 24) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// GetInt64 - читает из массива arr 8 байт и декодирует их в int64. +func GetInt64(arr []byte, byteOrder ByteOrder) (num int64, err error) { + if len(arr) < 8 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + num = int64(arr[0]) | (int64(arr[1]) << 8) | + (int64(arr[2]) << 16) | (int64(arr[3]) << 24) | + (int64(arr[4]) << 32) | (int64(arr[5]) << 40) | + (int64(arr[6]) << 48) | (int64(arr[7]) << 56) + case HL: + num = int64(arr[7]) | (int64(arr[6]) << 8) | + (int64(arr[5]) << 16) | (int64(arr[4]) << 24) | + (int64(arr[3]) << 32) | (int64(arr[2]) << 40) | + (int64(arr[1]) << 48) | (int64(arr[0]) << 56) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// GetUint16 - читает из массива arr 2 байта и декодирует их в uint16. +func GetUint16(arr []byte, byteOrder ByteOrder) (num uint16, err error) { + if len(arr) < 2 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + num = uint16(arr[0]) | (uint16(arr[1]) << 8) + case HL: + num = uint16(arr[1]) | (uint16(arr[0]) << 8) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// GetUint32 - читает из массива arr 4 байта и декодирует их в uint32. +func GetUint32(arr []byte, byteOrder ByteOrder) (num uint32, err error) { + if len(arr) < 4 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + num = uint32(arr[0]) | (uint32(arr[1]) << 8) | + (uint32(arr[2]) << 16) | (uint32(arr[3]) << 24) + case HL: + num = uint32(arr[3]) | (uint32(arr[2]) << 8) | + (uint32(arr[1]) << 16) | (uint32(arr[0]) << 24) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// GetUint64 - читает из массива arr 8 байт и декодирует их в uint64. +func GetUint64(arr []byte, byteOrder ByteOrder) (num uint64, err error) { + if len(arr) < 8 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + num = uint64(arr[0]) | (uint64(arr[1]) << 8) | + (uint64(arr[2]) << 16) | (uint64(arr[3]) << 24) | + (uint64(arr[4]) << 32) | (uint64(arr[5]) << 40) | + (uint64(arr[6]) << 48) | (uint64(arr[7]) << 56) + case HL: + num = uint64(arr[7]) | (uint64(arr[6]) << 8) | + (uint64(arr[5]) << 16) | (uint64(arr[4]) << 24) | + (uint64(arr[3]) << 32) | (uint64(arr[2]) << 40) | + (uint64(arr[1]) << 48) | (uint64(arr[0]) << 56) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// GetFloat32 - записывает 4 байта в массив arr +func GetFloat32(arr []byte, byteOrder ByteOrder) (_ float32, err error) { + tmp, err := GetUint32(arr, byteOrder) + if err != nil { + return + } + return math.Float32frombits(tmp), nil +} + +// GetFloat64 - записывает 4 байта в массив arr +func GetFloat64(arr []byte, byteOrder ByteOrder) (_ float64, err error) { + tmp, err := GetUint64(arr, byteOrder) + if err != nil { + return + } + return math.Float64frombits(tmp), nil +} + +// PutInt16 - записывает 2 байта в массив arr +func PutInt16(arr []byte, num int16, byteOrder ByteOrder) (err error) { + if len(arr) < 2 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + arr[0] = byte(num) + arr[1] = byte(num >> 8) + case HL: + arr[1] = byte(num) + arr[0] = byte(num >> 8) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// PutInt32 - записывает 4 байта в массив arr +func PutInt32(arr []byte, num int32, byteOrder ByteOrder) (err error) { + if len(arr) < 4 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + arr[0] = byte(num) + arr[1] = byte(num >> 8) + arr[2] = byte(num >> 16) + arr[3] = byte(num >> 24) + case HL: + arr[3] = byte(num) + arr[2] = byte(num >> 8) + arr[1] = byte(num >> 16) + arr[0] = byte(num >> 24) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// PutInt64 - записывает 8 байт в массив arr +func PutInt64(arr []byte, num int64, byteOrder ByteOrder) (err error) { + if len(arr) < 8 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + arr[0] = byte(num) + arr[1] = byte(num >> 8) + arr[2] = byte(num >> 16) + arr[3] = byte(num >> 24) + arr[4] = byte(num >> 32) + arr[5] = byte(num >> 40) + arr[6] = byte(num >> 48) + arr[7] = byte(num >> 56) + case HL: + arr[7] = byte(num) + arr[6] = byte(num >> 8) + arr[5] = byte(num >> 16) + arr[4] = byte(num >> 24) + arr[3] = byte(num >> 32) + arr[2] = byte(num >> 40) + arr[1] = byte(num >> 48) + arr[0] = byte(num >> 56) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// PutUint16 - записывает 2 байта в массив arr +func PutUint16(arr []byte, num uint16, byteOrder ByteOrder) (err error) { + if len(arr) < 2 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + arr[0] = byte(num) + arr[1] = byte(num >> 8) + case HL: + arr[1] = byte(num) + arr[0] = byte(num >> 8) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// PutUint32 - записывает 4 байта в массив arr +func PutUint32(arr []byte, num uint32, byteOrder ByteOrder) (err error) { + if len(arr) < 4 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + arr[0] = byte(num) + arr[1] = byte(num >> 8) + arr[2] = byte(num >> 16) + arr[3] = byte(num >> 24) + case HL: + arr[3] = byte(num) + arr[2] = byte(num >> 8) + arr[1] = byte(num >> 16) + arr[0] = byte(num >> 24) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// PutUint64 - записывает 8 байт в массив arr +func PutUint64(arr []byte, num uint64, byteOrder ByteOrder) (err error) { + if len(arr) < 8 { + err = fmt.Errorf(wrongArraySize, len(arr)) + return + } + switch byteOrder { + case LH: + arr[0] = byte(num) + arr[1] = byte(num >> 8) + arr[2] = byte(num >> 16) + arr[3] = byte(num >> 24) + arr[4] = byte(num >> 32) + arr[5] = byte(num >> 40) + arr[6] = byte(num >> 48) + arr[7] = byte(num >> 56) + case HL: + arr[7] = byte(num) + arr[6] = byte(num >> 8) + arr[5] = byte(num >> 16) + arr[4] = byte(num >> 24) + arr[3] = byte(num >> 32) + arr[2] = byte(num >> 40) + arr[1] = byte(num >> 48) + arr[0] = byte(num >> 56) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// PutFloat32 - записывает 4 байта в массив arr +func PutFloat32(arr []byte, num float32, byteOrder ByteOrder) (err error) { + return PutUint32(arr, math.Float32bits(num), byteOrder) +} + +// PutFloat64 - записывает 4 байта в массив arr +func PutFloat64(arr []byte, num float64, byteOrder ByteOrder) (err error) { + return PutUint64(arr, math.Float64bits(num), byteOrder) +} + +/* +PutVarUint64 - записывает в Writer от 1 до 9 байт, возвращает количество +записанных байт. Порядок байт - от младшего к старшему. В 9м байте все биты +значащие. +*/ +func PutVarUint64(arr []byte, num uint64) (_ int, err error) { + for i := 0; i < 8; i++ { + arr[i] = byte(num & 127) + num >>= 7 + if num == 0 { + arr[i] |= 128 + return i + 1, nil + } + } + // в последнем 9м байте все биты значащие + arr[8] = byte(num) + return 9, nil +} + +// WriteInt16 - записывает в Writer 2 байта +func WriteInt16(w io.Writer, num int16, byteOrder ByteOrder) (err error) { + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + }) + case HL: + _, err = w.Write([]byte{ + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteInt24 - записывает в Writer 3 байта +func WriteInt24(w io.Writer, num int32, byteOrder ByteOrder) (err error) { + var sign byte + if num < 0 { + sign = 128 + } + + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + byte((num>>16)&127) | sign, + }) + case HL: + _, err = w.Write([]byte{ + byte((num>>16)&127) | sign, + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteInt32 - записывает в Writer 4 байта +func WriteInt32(w io.Writer, num int32, byteOrder ByteOrder) (err error) { + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + }) + case HL: + _, err = w.Write([]byte{ + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteInt48 - записывает в Writer 6 байт +func WriteInt48(w io.Writer, num int64, byteOrder ByteOrder) (err error) { + var sign byte + if num < 0 { + sign = 128 + } + + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + byte(num >> 32), + byte((num>>40)&127) | sign, + }) + case HL: + _, err = w.Write([]byte{ + byte((num>>40)&127) | sign, + byte(num >> 32), + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteInt64 - записывает в Writer 8 байт +func WriteInt64(w io.Writer, num int64, byteOrder ByteOrder) (err error) { + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + byte(num >> 32), + byte(num >> 40), + byte(num >> 48), + byte(num >> 56), + }) + case HL: + _, err = w.Write([]byte{ + byte(num >> 56), + byte(num >> 48), + byte(num >> 40), + byte(num >> 32), + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteUint16 - записывает в Writer 2 байта +func WriteUint16(w io.Writer, num uint16, byteOrder ByteOrder) (err error) { + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + }) + case HL: + _, err = w.Write([]byte{ + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteUint32 - записывает в Writer 4 байта +func WriteUint32(w io.Writer, num uint32, byteOrder ByteOrder) (err error) { + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + }) + case HL: + _, err = w.Write([]byte{ + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteUint64 - записывает в Writer 8 байт +func WriteUint64(w io.Writer, num uint64, byteOrder ByteOrder) (err error) { + switch byteOrder { + case LH: + _, err = w.Write([]byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + byte(num >> 32), + byte(num >> 40), + byte(num >> 48), + byte(num >> 56), + }) + case HL: + _, err = w.Write([]byte{ + byte(num >> 56), + byte(num >> 48), + byte(num >> 40), + byte(num >> 32), + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + }) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// WriteFloat32 - записывает в Writer 4 байта +func WriteFloat32(w io.Writer, num float32, byteOrder ByteOrder) (err error) { + return WriteUint32(w, math.Float32bits(num), byteOrder) +} + +// WriteFloat64 - записывает в Writer 8 байт +func WriteFloat64(w io.Writer, num float64, byteOrder ByteOrder) (err error) { + return WriteUint64(w, math.Float64bits(num), byteOrder) +} + +// ReadInt16 - читает из ByteReader arr 2 байта и декодирует их в int16. +func ReadInt16(src io.ByteReader, byteOrder ByteOrder) (num int16, err error) { + b1, err := src.ReadByte() + if err != nil { + return + } + b2, err := src.ReadByte() + if err != nil { + return + } + + switch byteOrder { + case LH: + num = int16(b1) | (int16(b2) << 8) + case HL: + num = int16(b2) | (int16(b1) << 8) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// ReadInt24 - читает из ByteReader arr 3 байта и декодирует их в int32. +// Нужно для протокола mbus. +func ReadInt24(src io.ByteReader, byteOrder ByteOrder) (i int32, err error) { + var ( + num uint32 + b, high byte + ) + + if byteOrder == LH { + for _, offset := range []uint{0, 8} { + b, err = src.ReadByte() + if err != nil { + return + } + num |= uint32(b) << offset + } + + high, err = src.ReadByte() + if err != nil { + return + } + } else { + high, err = src.ReadByte() + if err != nil { + return + } + + for _, offset := range []uint{8, 0} { + b, err = src.ReadByte() + if err != nil { + return + } + num |= uint32(b) << offset + } + } + + num |= uint32(high&127) << 16 + + if high > 127 { + // заглушка + num |= 255 << 23 + num |= Int32SignBit + } + + return int32(num), nil +} + +// ReadInt32 - читает из ByteReader arr 4 байта и декодирует их в int32. +func ReadInt32(src io.ByteReader, byteOrder ByteOrder) (num int32, err error) { + var ( + b byte + offsets []uint + ) + switch byteOrder { + case LH: + offsets = offsets32LH + case HL: + offsets = offsets32HL + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + return + } + for _, off := range offsets { + b, err = src.ReadByte() + if err != nil { + return + } + num |= int32(b) << off + } + return +} + +// ReadInt48 - читает из ByteReader arr 6 байт и декодирует их в int64. +// Нужно для протокола mbus. +func ReadInt48(src io.ByteReader, byteOrder ByteOrder) (i int64, err error) { + var ( + num uint64 + b, high byte + ) + + if byteOrder == LH { + for _, offset := range []uint{0, 8, 16, 24, 32} { + b, err = src.ReadByte() + if err != nil { + return + } + num |= uint64(b) << offset + } + + high, err = src.ReadByte() + if err != nil { + return + } + } else { + high, err = src.ReadByte() + if err != nil { + return + } + + for _, offset := range []uint{32, 24, 16, 8, 0} { + b, err = src.ReadByte() + if err != nil { + return + } + num |= uint64(b) << offset + } + + } + + num |= uint64(high&127) << 40 + + if high > 127 { + // 2 заглушки + num |= 255 << 47 + num |= 255 << 55 + num |= Int64SignBit + } + + return int64(num), nil +} + +// ReadInt64 - читает из ByteReader arr 8 байт и декодирует их в int64. +func ReadInt64(src io.ByteReader, byteOrder ByteOrder) (num int64, err error) { + var ( + b byte + offsets []uint + ) + switch byteOrder { + case LH: + offsets = offsets64LH + case HL: + offsets = offsets64HL + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + return + } + for _, off := range offsets { + b, err = src.ReadByte() + if err != nil { + return + } + num |= int64(b) << off + } + return +} + +// ReadUint16 - читает из ByteReader arr 2 байта и декодирует их в uint16. +func ReadUint16(src io.ByteReader, byteOrder ByteOrder) (num uint16, err error) { + b1, err := src.ReadByte() + if err != nil { + return + } + b2, err := src.ReadByte() + if err != nil { + return + } + + switch byteOrder { + case LH: + num = uint16(b1) | (uint16(b2) << 8) + case HL: + num = uint16(b2) | (uint16(b1) << 8) + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + } + return +} + +// ReadUint32 - читает из ByteReader arr 4 байта и декодирует их в uint32. +func ReadUint32(src io.ByteReader, byteOrder ByteOrder) (num uint32, err error) { + var ( + b byte + offsets []uint + ) + switch byteOrder { + case LH: + offsets = offsets32LH + case HL: + offsets = offsets32HL + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + return + } + for _, off := range offsets { + b, err = src.ReadByte() + if err != nil { + return + } + num |= uint32(b) << off + } + return +} + +// ReadUint64 - читает из ByteReader arr 8 байт и декодирует их в uint64. +func ReadUint64(src io.ByteReader, byteOrder ByteOrder) (num uint64, err error) { + var ( + b byte + offsets []uint + ) + switch byteOrder { + case LH: + offsets = offsets64LH + case HL: + offsets = offsets64HL + default: + err = fmt.Errorf(unknownByteOrder, byteOrder) + return + } + for _, off := range offsets { + b, err = src.ReadByte() + if err != nil { + return + } + num |= uint64(b) << off + } + return +} + +// ReadFloat32 - читает из ByteReader arr 4 байта и декодирует их в float32. +func ReadFloat32(src io.ByteReader, byteOrder ByteOrder) (_ float32, err error) { + tmp, err := ReadUint32(src, byteOrder) + if err != nil { + return + } + return math.Float32frombits(tmp), nil +} + +// ReadFloat64 - читает из ByteReader arr 8 байт и декодирует их в float64. +func ReadFloat64(src io.ByteReader, byteOrder ByteOrder) (_ float64, err error) { + tmp, err := ReadUint64(src, byteOrder) + if err != nil { + return + } + return math.Float64frombits(tmp), nil +} + +/////////////////////////////////// VAR + +/* +WriteVarUint32 - записывает в Writer от 1 до 5 байт, возвращает количество +записанных байт. Порядок байт - от младшего к старшему. Значение последнего +байта всегда >= 128, остальных < 128. +*/ +func WriteVarUint32(w io.Writer, num uint32) (i int) { + arr := make([]byte, 5) + for ; i < 5; i++ { + arr[i] = byte(num & 127) + num >>= 7 + if num == 0 { + break + } + } + arr[i] |= 128 + i++ + w.Write(arr[:i]) + return +} + +/* +ReadVarUint32 - читает из ByteReader от 1 до 5 байт, и декодирует их в uint32. +*/ +func ReadVarUint32(src io.ByteReader) (num uint32, err error) { + var b byte + for i := 0; i < 5; i++ { + b, err = src.ReadByte() + if err != nil { + return + } + if b >= 128 { + num |= uint32(b&127) << uint(i*7) + return + } + num |= uint32(b) << uint(i*7) + } + err = fmt.Errorf("VarUint32 encoded value not found\n") + return +} + +/* +WriteVarUint64 - записывает в Writer от 1 до 9 байт, возвращает количество +записанных байт. Порядок байт - от младшего к старшему. В 9м байте все биты +значащие. +*/ +func WriteVarUint64(w io.Writer, num uint64) (_ int, err error) { + arr := make([]byte, 9) + for i := 0; i < 8; i++ { + arr[i] = byte(num & 127) + num >>= 7 + if num == 0 { + arr[i] |= 128 + i++ + return w.Write(arr[:i]) + } + } + // в последнем 9м байте все биты значащие + arr[8] = byte(num) + return w.Write(arr) +} + +/* +ReadVarUint64 - читает из ByteReader от 1 до 9 байт, и декодирует их в uint64. +*/ +func ReadVarUint64(src io.ByteReader) (num uint64, err error) { + var b byte + for i := 0; i < 8; i++ { + b, err = src.ReadByte() + if err != nil { + return + } + if b >= 128 { + num |= uint64(b&127) << uint(i*7) + return + } + num |= uint64(b) << uint(i*7) + } + b, err = src.ReadByte() + if err != nil { + return + } + // в последнем 9м байте все биты значащие + num |= uint64(b) << 56 + return +} + +///////////////// НУЖНЫ ЛИ ? +/* +WriteMaxUint64 - кодирует число минимально возможным количеством байт (от 1 до 8). +Возвращает количество записанных байт. Порядок байт - от младшего к старшему. +*/ +func WriteMaxUint64(w io.Writer, num uint64) (i int) { + arr := make([]byte, 8) + for ; i < 8; i++ { + arr[i] = byte(num) + num >>= 8 + if num == 0 { + break + } + } + i++ + w.Write(arr[:i]) + return +} + +/* +ReadMaxUint64 - читает из ByteReader число, закодированное методом MaxUint64. +*/ +func ReadMaxUint64(src io.ByteReader, n int) (num uint64, err error) { + if n < 1 || n > 8 { + log.Fatalf("n=%d, allowed values 1-8\n", n) + } + var b byte + for i := 0; i < n; i++ { + b, err = src.ReadByte() + if err != nil { + return + } + num |= uint64(b) << uint(i*8) + } + return +} + +/* + +func PutVar2b(w io.Writer, num int) int { + if num < 128 { + w.Write([]byte{byte(num)}) + return 1 + } + w.Write([]byte{ + byte(num&127) | 128, + byte(num >> 7), + }) + return 2 +} + +func getVar2b(w io.Writer, num int) int { + if num < 128 { + w.Write([]byte{byte(num)}) + return 1 + } + w.Write([]byte{ + byte(num&127) | 128, + byte(num >> 7), + }) + return 2 +} + + +func PutVarInt32(w io.Writer, num int32) (int, error) { + //if num > 2147483647 { + // return 0, fmt.Errorf("num %d overflows int32", num) + //} + arr := make([]byte, 5) + for i := 0; i < 4; i++ { + arr[i] = byte(num & 127) + num >>= 7 + if num == 0 { + arr[i] |= 128 + return w.Write(arr[:i+1]) + } + } + // в последнем 9м байте все биты значащие + arr[5] = byte(num) + return w.Write(arr) +} + +func VarInt32(src io.ByteReader) (num int, err error) { + var ( + b byte + off uint + ) + for ; off < 28; off += 7 { + b, err = src.ReadByte() + if err != nil { + return + } + if b < 128 { + num |= int(b) << off + } else { + num |= int(b&127) << off + return + } + } + + b, err = src.ReadByte() + if err != nil { + return + } + // в последнем 9м байте все биты значащие + num |= int(b&7) << 28 + return +} +*/ + +/* +func PackVarUint32(num int) (err error) { + idx := 0 + for num > 127 { + s.vint[idx] = byte(num & 127) + num >>= 7 + idx++ + } + // Даже в последнем байте отсекаем старший бит (знак) + s.vint[idx] = byte(num&127) | 128 + + _, err = s.dst.Write(s.vint[:idx+1]) + return +} +*/ + +var zeroUnixTime = []byte{0, 0, 0, 0, 0, 0} + +func Read6bDateTime(r io.ByteReader) (_ time.Time, err error) { + var arr []byte + + for i := 0; i < 6; i++ { + var b byte + b, err = r.ReadByte() + if err != nil { + return + } + arr = append(arr, b) + } + + if bytes.Equal(arr, zeroUnixTime) { + return time.Unix(0, 0), nil + } + + tm := time.Date( + int(arr[0])+2000, + time.Month(arr[1]), + int(arr[2]), + int(arr[3]), + int(arr[4]), + int(arr[5]), + 0, + time.UTC, + ) + return tm, nil +} + +func Write6bDateTime(w io.Writer, tm time.Time) (err error) { + var arr []byte + + // ВАЖНО! + tm = tm.In(time.UTC) + + if tm.Unix() == 0 { + arr = zeroUnixTime + } else { + arr = []byte{ + byte(tm.Year() - 2000), + byte(tm.Month()), + byte(tm.Day()), + byte(tm.Hour()), + byte(tm.Minute()), + byte(tm.Second()), + } + } + + //fmt.Printf("TIME: %v / %v\n", tm.Unix(), arr) + + _, err = w.Write(arr) + return +} + +const ( + MaxUint8 uint64 = 1<<8 - 1 + MaxUint16 uint64 = 1<<16 - 1 + MaxUint24 uint64 = 1<<24 - 1 + MaxUint32 uint64 = 1<<32 - 1 + MaxUint40 uint64 = 1<<40 - 1 + MaxUint48 uint64 = 1<<48 - 1 + MaxUint56 uint64 = 1<<56 - 1 + MaxUint64 uint64 = 18446744073709551615 + + // MaxSafeInt - максимальное целое, которое можно сохранить в переменной + // типа float64 без потери точности + MinSafeInt int64 = -(1 << 53) + MaxSafeInt int64 = 1<<53 - 1 +) + +// WriteMaxSafeUint - кодирует 2**53-1 // LH +func WriteMaxSafeUint(w io.Writer, num uint64, byteOrder ByteOrder) (n int, err error) { + var arr []byte + + if num <= MaxUint8 { + arr = []byte{ + byte(num), + } + } else if num <= MaxUint16 { + if byteOrder == LH { + arr = []byte{ + byte(num), + byte(num >> 8), + } + } else { + arr = []byte{ + byte(num >> 8), + byte(num), + } + } + } else if num <= MaxUint24 { + if byteOrder == LH { + arr = []byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + } + } else { + arr = []byte{ + byte(num >> 16), + byte(num >> 8), + byte(num), + } + } + } else if num <= MaxUint32 { + if byteOrder == LH { + arr = []byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + } + } else { + arr = []byte{ + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + } + } + } else if num <= MaxUint40 { + if byteOrder == LH { + arr = []byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + byte(num >> 32), + } + } else { + arr = []byte{ + byte(num >> 32), + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + } + } + } else if num <= MaxUint48 { + if byteOrder == LH { + arr = []byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + byte(num >> 32), + byte(num >> 40), + } + } else { + arr = []byte{ + byte(num >> 40), + byte(num >> 32), + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + } + } + } else if num <= uint64(MaxSafeInt) { + if byteOrder == LH { + arr = []byte{ + byte(num), + byte(num >> 8), + byte(num >> 16), + byte(num >> 24), + byte(num >> 32), + byte(num >> 40), + byte(num >> 48), + } + } else { + arr = []byte{ + byte(num >> 48), + byte(num >> 40), + byte(num >> 32), + byte(num >> 24), + byte(num >> 16), + byte(num >> 8), + byte(num), + } + } + } else { + err = fmt.Errorf("num %d > MaxSafeInt %d", num, MaxSafeInt) + return + } + + return w.Write(arr) +} + +type SplittedFloat struct { + Int int64 // bin.MaxSafeInt + Frac int64 // до 7 разрядов +} + +func (s SplittedFloat) AsFloat64(fracDigits int) float64 { + //return float64(s.Int) + float64(s.Frac)/ fracMultipliers[fracDigits] + return float64(s.Int) + float64(s.Frac)/math.Pow10(fracDigits) +} + +/* +var fracMultipliers = []float64{ + 0, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, +} +*/ + +var prec = []float64{ + 0.999999999, // 0 + 0.99999999, // 1 + 0.9999999, // 2 + 0.999999, // 3 + 0.99999, // 4 + 0.9999, // 5 + 0.999, // 6 + 0.99, // 7 + //0.99, // 8 +} + +func SplitFloat(num float64, fracDigits int) SplittedFloat { + var isNegative bool + if num < 0 { + isNegative = true + } + + num = math.Abs(num) + //if fracDigits >= len(fracMultipliers) { + // panic(fmt.Sprintf("max fracDigits is %d, not %d\n", + // len(fracMultipliers), fracDigits)) + //} + i, f := math.Modf(num) + + poweredFrac := f * math.Pow10(fracDigits) + // В этот момент может произойти потеря точности, когда 0.55 превратится + // в 0.5499999999 (в меньшую сторону) + fracAsInt := int64(poweredFrac) + + // + dif := poweredFrac - float64(fracAsInt) + //fmt.Printf("DIF %.10f\n\n", dif) + + var eps float64 + + if fracDigits >= len(prec) { + eps = 0.99 + } else { + eps = prec[fracDigits] + } + + if dif > eps { + fracAsInt++ + } + + //fmt.Printf("\nSPLIT: %.10f, int = %d, frac = %v, %v, %d\n\n", num, int(i), f, f*fracMultipliers[fracDigits], int(f*fracMultipliers[fracDigits])) + if isNegative { + i *= -1 + } + + return SplittedFloat{ + Int: int64(i), + Frac: fracAsInt, + } +} + +/* + if byteOrder == LH { + + } else { + + } +*/ + +/* +// Round a float to a specific decimal place or precision +func Round(input float64, places int) (rounded float64, err error) { + + // If the float is not a number + if math.IsNaN(input) { + return math.NaN(), fmt.Errorf("fuck") + } + + // Find out the actual sign and correct the input for later + sign := 1.0 + if input < 0 { + sign = -1 + input *= -1 + } + + // Use the places arg to get the amount of precision wanted + precision := math.Pow(10, float64(places)) + + // Find the decimal place we are looking to round + digit := input * precision + + // Get the actual decimal number as a fraction to be compared + _, decimal := math.Modf(digit) + + // If the decimal is less than .5 we round down otherwise up + if decimal >= 0.5 { + rounded = math.Ceil(digit) + } else { + rounded = math.Floor(digit) + } + + // Finally we do the math to actually create a rounded number + return rounded / precision * sign, nil +} + +*/ + +func UnsetBit(b byte, idx byte) byte { + if ((b >> idx) & 1) == 1 { + // сбрасываем + var mask byte = ^(1 << idx) + return b & mask + } + return b +} + +func SetBit(b byte, idx byte) byte { + if ((b >> idx) & 1) == 0 { + // устанавливаем + return b | (1 << idx) + } + return b +} + +func Float64AsUint64(f float64) uint64 { + arr := make([]byte, 8) + PutFloat64(arr, f, LH) + u, _ := GetUint64(arr, LH) + return u +} + +func Uint64AsFloat64(u uint64) float64 { + arr := make([]byte, 8) + PutUint64(arr, u, LH) + f, _ := GetFloat64(arr, LH) + return f +} + +func Int64AsUint64(i int64) uint64 { + arr := make([]byte, 8) + PutInt64(arr, i, LH) + u, _ := GetUint64(arr, LH) + return u +} + +func Uint64AsInt64(u uint64) int64 { + arr := make([]byte, 8) + PutUint64(arr, u, LH) + i, _ := GetInt64(arr, LH) + return i +} diff --git a/bin.sgo b/bin.sgo new file mode 100755 index 0000000..494cd25 --- /dev/null +++ b/bin.sgo @@ -0,0 +1,108 @@ +package bin + +import ( + "fmt" + "io" +) + +func PutVarInt32(w io.Writer, num int) (int, error) { + if num > 2147483647 { + return 0, fmt.Errorf("num %d overflows int32", num) + } + arr := make([]byte, 5) + for i := 0; i < 4; i++ { + arr[i] = byte(num & 127) + num >>= 7 + if num == 0 { + arr[i] |= 128 + return w.Write(arr[:i+1]) + } + } + // в последнем 9м байте все биты значащие + arr[5] = byte(num) + return w.Write(arr) +} + +func VarInt32(src io.ByteReader) (num int, err error) { + var ( + b byte + off uint + ) + for ; off < 28; off += 7 { + b, err = src.ReadByte() + if err != nil { + return + } + if b < 128 { + num |= int(b) << off + } else { + num |= int(b&127) << off + return + } + } + + b, err = src.ReadByte() + if err != nil { + return + } + // в последнем 9м байте все биты значащие + num |= int(b&7) << 28 + return +} + +func PutVarUint(arr []byte, num uint64) int { + for i := 0; i < 8; i++ { + arr[i] = byte(num & 127) + num >>= 7 + if num == 0 { + arr[i] |= 128 + return i + 1 + } + } + // в последнем 9м байте все биты значащие + arr[8] = byte(num) + return 9 +} + +func VarUint(src io.ByteReader) (num uint64, err error) { + var ( + b byte + off uint + ) + for ; off < 56; off += 7 { + b, err = src.ReadByte() + if err != nil { + return + } + if b < 128 { + num |= uint64(b) << off + } else { + num |= uint64(b&127) << off + return + } + } + + b, err = src.ReadByte() + if err != nil { + return + } + // в последнем 9м байте все биты значащие + num |= uint64(b) << 56 + return +} + +/* +func PackVarUint32(num int) (err error) { + idx := 0 + for num > 127 { + s.vint[idx] = byte(num & 127) + num >>= 7 + idx++ + } + // Даже в последнем байте отсекаем старший бит (знак) + s.vint[idx] = byte(num&127) | 128 + + _, err = s.dst.Write(s.vint[:idx+1]) + return +} +*/ diff --git a/bin_test.go b/bin_test.go new file mode 100755 index 0000000..0d6ee0c --- /dev/null +++ b/bin_test.go @@ -0,0 +1,663 @@ +package bin + +import ( + "bytes" + "fmt" + "math" + "strconv" + "testing" + "time" +) + +const ( + max7b uint64 = 127 + max14b uint64 = 16383 + max21b uint64 = 2097151 + max28b uint64 = 268435455 + max35b uint64 = 34359738367 + max42b uint64 = 4398046511103 + max49b uint64 = 562949953421311 + max56b uint64 = 72057594037927935 + + max24b uint64 = 16777215 + max40b uint64 = 1099511627775 + max48b uint64 = 281474976710655 +) + +type UintAndSize struct { + Value uint64 + Size int +} + +/* +func TestVarUint64(t *testing.T) { + // числа между 0, 1 и MaxUint64 - это 2 в степени кратной семи + // плюс предыдущее число. Например: 2**7-1, 2**7 + var nums = []UintAndSize{ + {0, 1}, + {1, 1}, + {max7b, 1}, + {max7b + 1, 2}, + {max14b, 2}, + {max14b + 1, 3}, + {max21b, 3}, + {max21b + 1, 4}, + {max28b, 4}, + {max28b + 1, 5}, + {max35b, 5}, + {max35b + 1, 6}, + {max42b, 6}, + {max42b + 1, 7}, + {max49b, 7}, + {max49b + 1, 8}, + {max56b, 8}, + {max56b + 1, 9}, + {math.MaxUint64, 9}, + } + + buf := bytes.NewBuffer(nil) + + for _, num := range nums { + buf.Reset() + n := PutVarUint64(buf, num.Value) + if n != num.Size { + t.Fatalf("Encoded size %d != estimated size %d for num %d\n", n, num.Size, num.Value) + } + encoded := buf.Bytes() + x, err := GetVarUint64(bytes.NewBuffer(encoded)) + if err != nil { + t.Fatalf("GetVarUint64 error: %s\n", err) + } + if x != num.Value { + t.Fatalf("Origin num %d != decoded num %d. Buffer: %v\n", num.Value, x, encoded) + } + } +} + +func TestVarUint32(t *testing.T) { + // числа между 0, 1 и MaxUint64 - это 2 в степени кратной семи + // плюс предыдущее число. Например: 2**7-1, 2**7 + var nums = []UintAndSize{ + {0, 1}, + {1, 1}, + {max7b, 1}, + {max7b + 1, 2}, + {max14b, 2}, + {max14b + 1, 3}, + {max21b, 3}, + {max21b + 1, 4}, + {max28b, 4}, + {max28b + 1, 5}, + {math.MaxUint32, 5}, + } + + buf := bytes.NewBuffer(nil) + + for _, num := range nums { + buf.Reset() + origin := uint32(num.Value) + n := PutVarUint32(buf, origin) + if n != num.Size { + t.Fatalf("Encoded size %d != estimated size %d for num %d\n", n, num.Size, origin) + } + encoded := buf.Bytes() + x, err := GetVarUint32(bytes.NewBuffer(encoded)) + if err != nil { + t.Fatalf("GetVarUint32 error: %s\n", err) + } + if x != origin { + t.Fatalf("Origin num %d != decoded num %d. Buffer: %v\n", origin, x, encoded) + } + } +} +*/ +/* +func TestMaxUint64(t *testing.T) { + // числа между 0, 1 и MaxUint64 - это 2 в степени кратной семи + // плюс предыдущее число. Например: 2**7-1, 2**7 + var nums = []UintAndSize{ + {0, 1}, + {1, 1}, + {math.MaxUint8, 1}, + {math.MaxUint8 + 1, 2}, + {math.MaxUint16, 2}, + {math.MaxUint16 + 1, 3}, + {max24b, 3}, + {max24b + 1, 4}, + {math.MaxUint32, 4}, + {math.MaxUint32 + 1, 5}, + {max40b, 5}, + {max40b + 1, 6}, + {max48b, 6}, + {max48b + 1, 7}, + {max56b, 7}, + {max56b + 1, 8}, + {math.MaxUint64, 8}, + } + + buf := bytes.NewBuffer(nil) + + for _, num := range nums { + buf.Reset() + n := PutMaxUint64(buf, num.Value) + if n != num.Size { + t.Fatalf("Encoded size %d != estimated size %d for num %d\n", n, num.Size, num.Value) + } + encoded := buf.Bytes() + x, err := GetMaxUint64(bytes.NewBuffer(encoded), n) + if err != nil { + t.Fatalf("GetMaxUint64 error: %s\n", err) + } + if x != num.Value { + t.Fatalf("Origin num %d != decoded num %d. Buffer: %v\n", num.Value, x, encoded) + } + } +} +*/ + +func TestFloat32(t *testing.T) { + buf := []byte{234, 171, 234, 171, 234, 171, 234, 171, 234, 171} + + r := bytes.NewBuffer(buf) + + num, err := ReadFloat32(r, HL) + if err != nil { + t.Fatal(err) + } + + fmt.Printf("float32: %f\n", num) + + x := float64(num) + + fmt.Printf("float64: %f\n", x*4) + fmt.Printf("int64: %d\n", int64(x)+10) + + mid := make([]byte, 4) + + err = PutFloat32(mid, num, HL) + if err != nil { + t.Fatal(err) + } + + fmt.Printf("%d\n", mid) +} + +func Test6bDateTime(t *testing.T) { + // + now := time.Now() + + now = now.In(time.UTC) + + w := bytes.NewBuffer(nil) + + Write6bDateTime(w, now) + + arr := w.Bytes() + + fmt.Printf("%s: %v, %d\n", now, arr, now.Unix()) + + r := bytes.NewBuffer(arr) + + tm, _ := Read6bDateTime(r) + + fmt.Printf("%s: %d\n", tm, tm.Unix()) +} + +func TestSplitFloat(t *testing.T) { + f := 650179.6985999999 // округляет вниз .6985 + total := 8.2291999999 // округляет вверх 8.2292 + + n := SplitFloat(f, 4) + x := SplitFloat(total, 4) + + fmt.Printf("int: %d, frac: %d\n", n.Int, n.Frac) + fmt.Printf("total int: %d, frac: %d\n", x.Int, x.Frac) +} + +func TestSplitFloat2(t *testing.T) { + //prev := 65032252.8724999875 // округляет вниз .6985 + //total := 9.2449999973 // округляет вверх 8.2292 + + fracDigits := 4 + + floatPrev := 65000005.499980002 + + floatTotal := 3.5000800019 + + prev := SplitFloat(floatPrev, fracDigits) + total := SplitFloat(floatTotal, fracDigits) + + fmt.Printf("prev: i=%d, f=%d\n", prev.Int, prev.Frac) + fmt.Printf("total i=%d, f=%d\n", total.Int, total.Frac) + + value := SplitFloat(floatPrev+floatTotal, fracDigits) + + fmt.Printf("real i=%d, f=%d\n", value.Int, value.Frac) + + calcInt := prev.Int + total.Int + calcFrac := prev.Frac + total.Frac + + if calcFrac >= 10000 { + calcFrac -= 10000 + calcInt += 1 + } + + fmt.Printf("calc i=%d, f=%d\n", calcInt, calcFrac) + + fmt.Printf("\n%.9f\n", 10000000.999999999) + +} + +func TestBitOps(t *testing.T) { + var b byte = 255 + + var bitNo byte = 7 + + fmt.Printf("%08b\n", UnsetBit(b, bitNo)) + fmt.Printf("%08b\n", SetBit(UnsetBit(b, bitNo), bitNo)) +} + +func TestDecodeFloat(t *testing.T) { + var buf = []byte{0xCE, 0x6E, 0x6B, 0x28} + + f1, err := GetFloat32(buf, LH) + if err != nil { + panic(err) + } + + f2, err := GetFloat32(buf, HL) + if err != nil { + panic(err) + } + + fmt.Printf("LH: %v\nHL: %v\nBIN: %08b\n\n", f1, f2, buf) + + n, err := strconv.ParseFloat("-1e9", 32) + if err != nil { + panic(err) + } + + f := float32(n) + + var arr = []byte{0, 0, 0, 0} + + PutFloat32(arr, f, HL) + + fmt.Printf("%v\nBIN: %08b\n", f, arr) +} + +// Тесты массивов (для написания СУБД). Когда в байтовом виде записан массив из N элементов, +// фиксированного размера (например 4-байтовый uint32 или 2-байтовый uint16). Из массива +// нужно удалять элементы по индексу, либо в массив вставлять по индексу (чтобы другие +// элементы "подвинулись"). + +type _DeleteArrElemTestCase struct { + Arr []byte + Qty int + ElemSize int + Idx int + Result []byte +} + +type _InsertArrElemTestCase struct { + Arr []byte + Qty int + Elem []byte + Idx int + Result []byte +} + +func TestDeleteArrElem(t *testing.T) { + originArr := []byte{1, 1, 2, 2, 3, 3, 255, 0, 0, 0} + + testCases := []_DeleteArrElemTestCase{ + { + Arr: originArr, + Qty: 3, + ElemSize: 2, + Idx: 0, + Result: []byte{2, 2, 3, 3, 0, 0, 255, 0, 0, 0}, + }, + { + Arr: originArr, + Qty: 3, + ElemSize: 2, + Idx: 1, + Result: []byte{1, 1, 3, 3, 0, 0, 255, 0, 0, 0}, + }, + { + Arr: originArr, + Qty: 3, + ElemSize: 2, + Idx: 2, + Result: []byte{1, 1, 2, 2, 0, 0, 255, 0, 0, 0}, + }, + } + + for _, testCase := range testCases { + arrcopy := make([]byte, len(testCase.Arr)) + + copy(arrcopy, testCase.Arr) + + DeleteArrElem(arrcopy, testCase.Qty, testCase.ElemSize, testCase.Idx) + + if !bytes.Equal(arrcopy, testCase.Result) { + t.Fatalf(` +Source: %v +Estimate: %v +Real: %v`, + testCase.Arr, testCase.Result, arrcopy) + } + } +} + +func TestInsertArrElem(t *testing.T) { + originArr := []byte{1, 1, 2, 2, 0, 0, 255, 0} + elem := []byte{7, 7} + + testCases := []_InsertArrElemTestCase{ + { + Arr: originArr, + Qty: 2, + Elem: elem, + Idx: 0, + Result: []byte{7, 7, 1, 1, 2, 2, 255, 0}, + }, + { + Arr: originArr, + Qty: 2, + Elem: elem, + Idx: 1, + Result: []byte{1, 1, 7, 7, 2, 2, 255, 0}, + }, + { + Arr: originArr, + Qty: 2, + Elem: elem, + Idx: 2, + Result: []byte{1, 1, 2, 2, 7, 7, 255, 0}, + }, + } + + for _, testCase := range testCases { + arrcopy := make([]byte, len(testCase.Arr)) + + copy(arrcopy, testCase.Arr) + + InsertArrElem(arrcopy, testCase.Qty, testCase.Elem, testCase.Idx) + + if !bytes.Equal(arrcopy, testCase.Result) { + t.Fatalf(` +Source: %v +Estimate: %v +Real: %v`, + testCase.Arr, testCase.Result, arrcopy) + } + } +} + +func TestDeleteReverseArrElem(t *testing.T) { + originArr := []byte{0, 0, 255, 3, 3, 2, 2, 1, 1} + + testCases := []_DeleteArrElemTestCase{ + { + Arr: originArr, + Qty: 3, + ElemSize: 2, + Idx: 0, + Result: []byte{0, 0, 255, 0, 0, 3, 3, 2, 2}, + }, + { + Arr: originArr, + Qty: 3, + ElemSize: 2, + Idx: 1, + Result: []byte{0, 0, 255, 0, 0, 3, 3, 1, 1}, + }, + { + Arr: originArr, + Qty: 3, + ElemSize: 2, + Idx: 2, + Result: []byte{0, 0, 255, 0, 0, 2, 2, 1, 1}, + }, + } + + for _, testCase := range testCases { + arrcopy := make([]byte, len(testCase.Arr)) + + copy(arrcopy, testCase.Arr) + + DeleteReverseArrElem(arrcopy, testCase.Qty, testCase.ElemSize, testCase.Idx) + + if !bytes.Equal(arrcopy, testCase.Result) { + t.Fatalf(` +Source: %v +Estimate: %v +Real: %v`, + testCase.Arr, testCase.Result, arrcopy) + } + } +} + +func TestInsertReverseArrElem(t *testing.T) { + originArr := []byte{0, 255, 0, 0, 2, 2, 1, 1} + elem := []byte{7, 7} + + testCases := []_InsertArrElemTestCase{ + { + Arr: originArr, + Qty: 2, + Elem: elem, + Idx: 0, + Result: []byte{0, 255, 2, 2, 1, 1, 7, 7}, + }, + { + Arr: originArr, + Qty: 2, + Elem: elem, + Idx: 1, + Result: []byte{0, 255, 2, 2, 7, 7, 1, 1}, + }, + { + Arr: originArr, + Qty: 2, + Elem: elem, + Idx: 2, + Result: []byte{0, 255, 7, 7, 2, 2, 1, 1}, + }, + } + + for _, testCase := range testCases { + arrcopy := make([]byte, len(testCase.Arr)) + + copy(arrcopy, testCase.Arr) + + InsertReverseArrElem(arrcopy, testCase.Qty, testCase.Elem, testCase.Idx) + + if !bytes.Equal(arrcopy, testCase.Result) { + t.Fatalf(` +Source: %v +Estimate: %v +Real: %v`, + testCase.Arr, testCase.Result, arrcopy) + } + } +} + +func TestCompareToArrElem(t *testing.T) { + arr := []byte{1, 2, 3, 4} + elem := []byte{5, 2} + + code := compareToArrElem(arr, elem, 1, HL) + + fmt.Printf("code: %d\n", code) +} + +type _FindArrElemTestCase struct { + Elem []byte // элемент, который будем искать в массиве + // Результат поиска - индекс и флаг + Idx int + Found bool +} + +func TestFindArrElem(t *testing.T) { + arr := []byte{2, 2, 5, 5, 7, 7, 0, 0} + + testCases := []_FindArrElemTestCase{ + { + Elem: []byte{1, 1}, + Idx: 0, + Found: false, + }, + { + Elem: []byte{2, 2}, + Idx: 0, + Found: true, + }, + { + Elem: []byte{3, 3}, + Idx: 1, + Found: false, + }, + { + Elem: []byte{5, 5}, + Idx: 1, + Found: true, + }, + { + Elem: []byte{6, 6}, + Idx: 2, + Found: false, + }, + { + Elem: []byte{7, 7}, + Idx: 2, + Found: true, + }, + { + Elem: []byte{8, 8}, + Idx: 3, + Found: false, + }, + } + + errorPattern := `Case: + byteOrder: %s + elem: %v + idx: %d + found: %t + +Result: + idx: %d + found: %t +` + + var ( + idx int + found bool + ) + + for _, testCase := range testCases { + idx, found = FindArrElem(arr, 3, testCase.Elem, LH) + if idx != testCase.Idx || found != testCase.Found { + t.Fatalf(errorPattern, + LH, testCase.Elem, testCase.Idx, testCase.Found, idx, found) + } + + // Тоже самое, но с другим порядком байт + idx, found = FindArrElem(arr, 3, testCase.Elem, HL) + if idx != testCase.Idx || found != testCase.Found { + t.Fatalf(errorPattern, + HL, testCase.Elem, testCase.Idx, testCase.Found, idx, found) + } + } + + //fmt.Printf("idx: %d, found: %t\n", idx, found) +} + +func TestFloatToUint(t *testing.T) { + //f = +} + +func TestIntToUint(t *testing.T) { + var i int64 = 20 //-math.MaxInt64 - 1 + + u := Int64AsUint64(i) + + fmt.Printf("%v\n", u) + + x := Uint64AsInt64(u) + + fmt.Printf("%v\n", x) +} + +var bytesOrders = []ByteOrder{LH, HL} + +func TestInt24(t *testing.T) { + var nums = []int32{-1, 0, 1, -4915195, 4915195} + + for _, num := range nums { + for _, byteOrder := range bytesOrders { + buf := bytes.NewBuffer(nil) + + err := WriteInt24(buf, num, byteOrder) + if err != nil { + t.Fatalf("%s; num=%d; byteOrder=%s\n", err, num, byteOrder) + } + + unpacked, err := ReadInt24(buf, byteOrder) + if err != nil { + t.Fatal(err) + } + + if unpacked != num { + t.Fatalf("num %d != unpacked %d (byteOrder=%s)\n", num, unpacked, byteOrder) + } + } + } +} + +func TestInt48(t *testing.T) { + var nums = []int64{-1, 0, 1, -4915195, 4915195} + + for _, num := range nums { + for _, byteOrder := range bytesOrders { + buf := bytes.NewBuffer(nil) + + err := WriteInt48(buf, num, byteOrder) + if err != nil { + t.Fatalf("%s; num=%d; byteOrder=%s\n", err, num, byteOrder) + } + + unpacked, err := ReadInt48(buf, byteOrder) + if err != nil { + t.Fatal(err) + } + + if unpacked != num { + t.Fatalf("num %d != unpacked %d (byteOrder=%s)\n", num, unpacked, byteOrder) + } + } + } +} + +func TestInt32(t *testing.T) { + buf := bytes.NewBuffer(nil) + + byteOrder := HL + + err := WriteInt32(buf, ^math.MaxInt32, byteOrder) + if err != nil { + t.Fatal(err) + } + + fmt.Printf("%08b\n", buf.Bytes()) + + num, err := ReadInt32(buf, byteOrder) + if err != nil { + t.Fatal(err) + } + + fmt.Println(num) +} diff --git a/buffer.go b/buffer.go new file mode 100644 index 0000000..7b6592d --- /dev/null +++ b/buffer.go @@ -0,0 +1,47 @@ +package bin + +type Buffer struct { + arr []byte +} + +func NewBuffer(arr []byte) *Buffer { + s := new(Buffer) + s.arr = arr + return s +} + +func (s *Buffer) WriteByte(b byte) (err error) { + s.arr = append(s.arr, b) + return nil +} + +func (s *Buffer) Write(data []byte) (int, error) { + s.arr = append(s.arr, data...) + return len(data), nil +} + +func (s *Buffer) ByteAt(idx int) byte { + return s.arr[idx] +} + +func (s *Buffer) WriteByteAt(b byte, idx int) { + s.arr[idx] = b +} + +func (s *Buffer) Len() int { + return len(s.arr) +} + +func (s *Buffer) Bytes() []byte { + return s.arr[:] +} + +func (s *Buffer) CopyBytes() []byte { + dst := make([]byte, len(s.arr)) + copy(dst, s.arr) + return dst +} + +func (s *Buffer) Reset() { + s.arr = s.arr[:0] +} diff --git a/buffer_test.go b/buffer_test.go new file mode 100644 index 0000000..6b010b7 --- /dev/null +++ b/buffer_test.go @@ -0,0 +1,27 @@ +package bin + +import ( + "fmt" + "testing" +) + +func TestBuffer(t *testing.T) { + arr := make([]byte, 0) + + buf := NewBuffer(arr) + buf.WriteByte(100) + + fmt.Println(buf.ByteAt(0)) + + buf.Write([]byte{1, 2, 3}) + buf.WriteByteAt(200, 0) + + fmt.Println(buf.ByteAt(0)) + + fmt.Println(buf.Bytes()) + + buf.Reset() + + fmt.Println(buf.Bytes()) + fmt.Println(buf.Len()) +}