package ber import ( "bytes" "errors" "fmt" "math" "strconv" "strings" ) func encodeFloat(v float64) []byte { switch { case math.IsInf(v, 1): return []byte{0x40} case math.IsInf(v, -1): return []byte{0x41} case math.IsNaN(v): return []byte{0x42} case v == 0.0: if math.Signbit(v) { return []byte{0x43} } return []byte{} default: // we take the easy part ;-) value := []byte(strconv.FormatFloat(v, 'G', -1, 64)) var ret []byte if bytes.Contains(value, []byte{'E'}) { ret = []byte{0x03} } else { ret = []byte{0x02} } ret = append(ret, value...) return ret } } func ParseReal(v []byte) (val float64, err error) { if len(v) == 0 { return 0.0, nil } switch { case v[0]&0x80 == 0x80: val, err = parseBinaryFloat(v) case v[0]&0xC0 == 0x40: val, err = parseSpecialFloat(v) case v[0]&0xC0 == 0x0: val, err = parseDecimalFloat(v) default: return 0.0, fmt.Errorf("invalid info block") } if err != nil { return 0.0, err } if val == 0.0 && !math.Signbit(val) { return 0.0, errors.New("REAL value +0 must be encoded with zero-length value block") } return val, nil } func parseBinaryFloat(v []byte) (float64, error) { var info byte var buf []byte info, v = v[0], v[1:] var base int switch info & 0x30 { case 0x00: base = 2 case 0x10: base = 8 case 0x20: base = 16 case 0x30: return 0.0, errors.New("bits 6 and 5 of information octet for REAL are equal to 11") } scale := uint((info & 0x0c) >> 2) var expLen int switch info & 0x03 { case 0x00: expLen = 1 case 0x01: expLen = 2 case 0x02: expLen = 3 case 0x03: if len(v) < 2 { return 0.0, errors.New("invalid data") } expLen = int(v[0]) if expLen > 8 { return 0.0, errors.New("too big value of exponent") } v = v[1:] } if expLen > len(v) { return 0.0, errors.New("too big value of exponent") } buf, v = v[:expLen], v[expLen:] exponent, err := ParseInt64(buf) if err != nil { return 0.0, err } if len(v) > 8 { return 0.0, errors.New("too big value of mantissa") } mant, err := ParseInt64(v) if err != nil { return 0.0, err } mantissa := mant << scale if info&0x40 == 0x40 { mantissa = -mantissa } return float64(mantissa) * math.Pow(float64(base), float64(exponent)), nil } func parseDecimalFloat(v []byte) (val float64, err error) { switch v[0] & 0x3F { case 0x01: // NR form 1 var iVal int64 iVal, err = strconv.ParseInt(strings.TrimLeft(string(v[1:]), " "), 10, 64) val = float64(iVal) case 0x02, 0x03: // NR form 2, 3 val, err = strconv.ParseFloat(strings.Replace(strings.TrimLeft(string(v[1:]), " "), ",", ".", -1), 64) default: err = errors.New("incorrect NR form") } if err != nil { return 0.0, err } if val == 0.0 && math.Signbit(val) { return 0.0, errors.New("REAL value -0 must be encoded as a special value") } return val, nil } func parseSpecialFloat(v []byte) (float64, error) { if len(v) != 1 { return 0.0, errors.New(`encoding of "special value" must not contain exponent and mantissa`) } switch v[0] { case 0x40: return math.Inf(1), nil case 0x41: return math.Inf(-1), nil case 0x42: return math.NaN(), nil case 0x43: return math.Copysign(0, -1), nil } return 0.0, errors.New(`encoding of "special value" not from ASN.1 standard`) }