428 lines
8.6 KiB
Go
428 lines
8.6 KiB
Go
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
|
||
}
|