Files
bin/bin.go

428 lines
8.6 KiB
Go
Raw Permalink Normal View History

2022-12-24 17:48:08 +02:00
package bin
import (
"errors"
"fmt"
"io"
"math"
)
// ByteOrder - порядок байт
type ByteOrder string
const (
2026-07-19 06:29:35 +03:00
2022-12-24 17:48:08 +02:00
// LH - low to high byte order
LH ByteOrder = "LH"
// HL - high to low byte order
HL ByteOrder = "HL"
2026-07-19 06:29:35 +03:00
// Int32SignBit uint32 = 1 << 31
// Int64SignBit uint64 = 1 << 63
maxReadAttempts = 5
2022-12-24 17:48:08 +02:00
)
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'")
)
// ReadN - безопасно читаем n байт. Обрабатывает все возможные ситуации, в том числе
// баги Reader
func ReadN(r io.Reader, n int) (_ []byte, err error) {
if n < 0 {
2024-10-31 03:55:08 +02:00
err = fmt.Errorf("wrong n=%d", n)
return
2022-12-24 17:48:08 +02:00
}
buf := make([]byte, n)
2024-10-31 03:55:08 +02:00
err = ReadNInto(r, buf)
if err != nil {
return
}
return buf, nil
}
func ReadNInto(r io.Reader, buf []byte) (err error) {
if len(buf) == 0 {
return
}
2022-12-24 17:48:08 +02:00
var q, total, readAttempts int
// Избегаем вечного цикла из-за неправильной реализации ридера
for readAttempts < maxReadAttempts {
2024-10-31 03:55:08 +02:00
bufsize := len(buf) - total
2022-12-24 17:48:08 +02:00
q, err = r.Read(buf[total:])
2024-10-31 03:55:08 +02:00
// Если буфер заполнен - успех, err игнорируем
2022-12-24 17:48:08 +02:00
if q == bufsize {
2024-10-31 03:55:08 +02:00
return nil
2022-12-24 17:48:08 +02:00
}
// Если ошибка - выходим с ошибкой
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
}
2026-07-19 06:29:35 +03:00
// 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()),
// }
// }
// _, err = w.Write(arr)
// return
// }
2022-12-24 17:48:08 +02:00
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,
}
}
/*
// 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
}
*/
2026-07-19 06:29:35 +03:00
// FLAGS
func IsSetFlag(flags byte, flag byte) bool {
return (flags & flag) == flag
}
func SetFlag(flags byte, flag byte) byte {
return flags | flag
}
func UnsetFlag(flags byte, flag byte) byte {
return flags & ^flag
}
2022-12-24 17:48:08 +02:00
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
}