Files
bin/bin.go
2026-07-19 06:29:35 +03:00

428 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package bin
import (
"errors"
"fmt"
"io"
"math"
)
// ByteOrder - порядок байт
type ByteOrder string
const (
// 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
maxReadAttempts = 5
)
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 {
err = fmt.Errorf("wrong n=%d", n)
return
}
buf := make([]byte, n)
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
}
var q, total, readAttempts int
// Избегаем вечного цикла из-за неправильной реализации ридера
for readAttempts < maxReadAttempts {
bufsize := len(buf) - total
q, err = r.Read(buf[total:])
// Если буфер заполнен - успех, err игнорируем
if q == bufsize {
return 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
}
// 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
// }
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
}
*/
// 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
}
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
}