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 }