66 lines
1.9 KiB
Go
66 lines
1.9 KiB
Go
package usr
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/zlib"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
MaxInputBytes int64 = 15_000_000
|
|
DefaultMaxXMLBytes int64 = 30_000_000
|
|
)
|
|
|
|
var ErrUnsupportedFormat = errors.New("неподдерживаемый формат USR")
|
|
|
|
// encoded[i] = zlib[i] + 0x21 + 0x32*i (mod 256).
|
|
func Decode(encoded []byte, maxXMLBytes int64) ([]byte, error) {
|
|
if len(encoded) == 0 {
|
|
return nil, errors.New("пустой файл USR")
|
|
}
|
|
if int64(len(encoded)) > MaxInputBytes {
|
|
return nil, fmt.Errorf("входной USR слишком большой: %d > %d байт", len(encoded), MaxInputBytes)
|
|
}
|
|
if maxXMLBytes <= 0 {
|
|
return nil, errors.New("лимит распаковки должен быть положительным")
|
|
}
|
|
|
|
compressed := make([]byte, len(encoded))
|
|
offset := byte(0x21)
|
|
for i, value := range encoded {
|
|
compressed[i] = value - offset
|
|
offset += 0x32
|
|
}
|
|
|
|
if len(compressed) < 2 || compressed[0]&0x0f != 8 ||
|
|
(compressed[0]>>4) > 7 ||
|
|
(int(compressed[0])<<8|int(compressed[1]))%31 != 0 ||
|
|
compressed[1]&0x20 != 0 {
|
|
return nil, ErrUnsupportedFormat
|
|
}
|
|
|
|
input := bytes.NewReader(compressed)
|
|
zr, err := zlib.NewReader(input)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("не удалось открыть поток zlib: %w", err)
|
|
}
|
|
|
|
plain, readErr := io.ReadAll(io.LimitReader(zr, maxXMLBytes+1))
|
|
closeErr := zr.Close()
|
|
if int64(len(plain)) > maxXMLBytes {
|
|
return nil, fmt.Errorf("превышен лимит распаковки %d байт", maxXMLBytes)
|
|
}
|
|
if readErr != nil {
|
|
return nil, fmt.Errorf("ошибка распаковки/контрольной суммы zlib: %w", readErr)
|
|
}
|
|
if closeErr != nil {
|
|
return nil, fmt.Errorf("ошибка закрытия zlib: %w", closeErr)
|
|
}
|
|
if input.Len() != 0 {
|
|
return nil, fmt.Errorf("после потока zlib обнаружены лишние байты (%d)", input.Len())
|
|
}
|
|
return plain, nil
|
|
}
|