diagnose.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. // Copyright (c) Faye Amacker. All rights reserved.
  2. // Licensed under the MIT License. See LICENSE in the project root for license information.
  3. package cbor
  4. import (
  5. "bytes"
  6. "encoding/base32"
  7. "encoding/base64"
  8. "encoding/hex"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "math"
  13. "math/big"
  14. "strconv"
  15. "unicode/utf16"
  16. "unicode/utf8"
  17. "github.com/x448/float16"
  18. )
  19. // DiagMode is the main interface for CBOR diagnostic notation.
  20. type DiagMode interface {
  21. // Diagnose returns extended diagnostic notation (EDN) of CBOR data items using this DiagMode.
  22. Diagnose([]byte) (string, error)
  23. // DiagnoseFirst returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
  24. DiagnoseFirst([]byte) (string, []byte, error)
  25. // DiagOptions returns user specified options used to create this DiagMode.
  26. DiagOptions() DiagOptions
  27. }
  28. // ByteStringEncoding specifies the base encoding that byte strings are notated.
  29. type ByteStringEncoding uint8
  30. const (
  31. // ByteStringBase16Encoding encodes byte strings in base16, without padding.
  32. ByteStringBase16Encoding ByteStringEncoding = iota
  33. // ByteStringBase32Encoding encodes byte strings in base32, without padding.
  34. ByteStringBase32Encoding
  35. // ByteStringBase32HexEncoding encodes byte strings in base32hex, without padding.
  36. ByteStringBase32HexEncoding
  37. // ByteStringBase64Encoding encodes byte strings in base64url, without padding.
  38. ByteStringBase64Encoding
  39. maxByteStringEncoding
  40. )
  41. func (bse ByteStringEncoding) valid() error {
  42. if bse >= maxByteStringEncoding {
  43. return errors.New("cbor: invalid ByteStringEncoding " + strconv.Itoa(int(bse)))
  44. }
  45. return nil
  46. }
  47. // DiagOptions specifies Diag options.
  48. type DiagOptions struct {
  49. // ByteStringEncoding specifies the base encoding that byte strings are notated.
  50. // Default is ByteStringBase16Encoding.
  51. ByteStringEncoding ByteStringEncoding
  52. // ByteStringHexWhitespace specifies notating with whitespace in byte string
  53. // when ByteStringEncoding is ByteStringBase16Encoding.
  54. ByteStringHexWhitespace bool
  55. // ByteStringText specifies notating with text in byte string
  56. // if it is a valid UTF-8 text.
  57. ByteStringText bool
  58. // ByteStringEmbeddedCBOR specifies notating embedded CBOR in byte string
  59. // if it is a valid CBOR bytes.
  60. ByteStringEmbeddedCBOR bool
  61. // CBORSequence specifies notating CBOR sequences.
  62. // otherwise, it returns an error if there are more bytes after the first CBOR.
  63. CBORSequence bool
  64. // FloatPrecisionIndicator specifies appending a suffix to indicate float precision.
  65. // Refer to https://www.rfc-editor.org/rfc/rfc8949.html#name-encoding-indicators.
  66. FloatPrecisionIndicator bool
  67. // MaxNestedLevels specifies the max nested levels allowed for any combination of CBOR array, maps, and tags.
  68. // Default is 32 levels and it can be set to [4, 65535]. Note that higher maximum levels of nesting can
  69. // require larger amounts of stack to deserialize. Don't increase this higher than you require.
  70. MaxNestedLevels int
  71. // MaxArrayElements specifies the max number of elements for CBOR arrays.
  72. // Default is 128*1024=131072 and it can be set to [16, 2147483647]
  73. MaxArrayElements int
  74. // MaxMapPairs specifies the max number of key-value pairs for CBOR maps.
  75. // Default is 128*1024=131072 and it can be set to [16, 2147483647]
  76. MaxMapPairs int
  77. }
  78. // DiagMode returns a DiagMode with immutable options.
  79. func (opts DiagOptions) DiagMode() (DiagMode, error) {
  80. return opts.diagMode()
  81. }
  82. func (opts DiagOptions) diagMode() (*diagMode, error) {
  83. if err := opts.ByteStringEncoding.valid(); err != nil {
  84. return nil, err
  85. }
  86. decMode, err := DecOptions{
  87. MaxNestedLevels: opts.MaxNestedLevels,
  88. MaxArrayElements: opts.MaxArrayElements,
  89. MaxMapPairs: opts.MaxMapPairs,
  90. }.decMode()
  91. if err != nil {
  92. return nil, err
  93. }
  94. return &diagMode{
  95. byteStringEncoding: opts.ByteStringEncoding,
  96. byteStringHexWhitespace: opts.ByteStringHexWhitespace,
  97. byteStringText: opts.ByteStringText,
  98. byteStringEmbeddedCBOR: opts.ByteStringEmbeddedCBOR,
  99. cborSequence: opts.CBORSequence,
  100. floatPrecisionIndicator: opts.FloatPrecisionIndicator,
  101. decMode: decMode,
  102. }, nil
  103. }
  104. type diagMode struct {
  105. byteStringEncoding ByteStringEncoding
  106. byteStringHexWhitespace bool
  107. byteStringText bool
  108. byteStringEmbeddedCBOR bool
  109. cborSequence bool
  110. floatPrecisionIndicator bool
  111. decMode *decMode
  112. }
  113. // DiagOptions returns user specified options used to create this DiagMode.
  114. func (dm *diagMode) DiagOptions() DiagOptions {
  115. return DiagOptions{
  116. ByteStringEncoding: dm.byteStringEncoding,
  117. ByteStringHexWhitespace: dm.byteStringHexWhitespace,
  118. ByteStringText: dm.byteStringText,
  119. ByteStringEmbeddedCBOR: dm.byteStringEmbeddedCBOR,
  120. CBORSequence: dm.cborSequence,
  121. FloatPrecisionIndicator: dm.floatPrecisionIndicator,
  122. MaxNestedLevels: dm.decMode.maxNestedLevels,
  123. MaxArrayElements: dm.decMode.maxArrayElements,
  124. MaxMapPairs: dm.decMode.maxMapPairs,
  125. }
  126. }
  127. // Diagnose returns extended diagnostic notation (EDN) of CBOR data items using the DiagMode.
  128. func (dm *diagMode) Diagnose(data []byte) (string, error) {
  129. return newDiagnose(data, dm.decMode, dm).diag(dm.cborSequence)
  130. }
  131. // DiagnoseFirst returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
  132. func (dm *diagMode) DiagnoseFirst(data []byte) (diagNotation string, rest []byte, err error) {
  133. return newDiagnose(data, dm.decMode, dm).diagFirst()
  134. }
  135. var defaultDiagMode, _ = DiagOptions{}.diagMode()
  136. // Diagnose returns extended diagnostic notation (EDN) of CBOR data items
  137. // using the default diagnostic mode.
  138. //
  139. // Refer to https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation.
  140. func Diagnose(data []byte) (string, error) {
  141. return defaultDiagMode.Diagnose(data)
  142. }
  143. // Diagnose returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
  144. func DiagnoseFirst(data []byte) (diagNotation string, rest []byte, err error) {
  145. return defaultDiagMode.DiagnoseFirst(data)
  146. }
  147. type diagnose struct {
  148. dm *diagMode
  149. d *decoder
  150. w *bytes.Buffer
  151. }
  152. func newDiagnose(data []byte, decm *decMode, diagm *diagMode) *diagnose {
  153. return &diagnose{
  154. dm: diagm,
  155. d: &decoder{data: data, dm: decm},
  156. w: &bytes.Buffer{},
  157. }
  158. }
  159. func (di *diagnose) diag(cborSequence bool) (string, error) {
  160. // CBOR Sequence
  161. firstItem := true
  162. for {
  163. switch err := di.wellformed(cborSequence); err {
  164. case nil:
  165. if !firstItem {
  166. di.w.WriteString(", ")
  167. }
  168. firstItem = false
  169. if itemErr := di.item(); itemErr != nil {
  170. return di.w.String(), itemErr
  171. }
  172. case io.EOF:
  173. if firstItem {
  174. return di.w.String(), err
  175. }
  176. return di.w.String(), nil
  177. default:
  178. return di.w.String(), err
  179. }
  180. }
  181. }
  182. func (di *diagnose) diagFirst() (diagNotation string, rest []byte, err error) {
  183. err = di.wellformed(true)
  184. if err == nil {
  185. err = di.item()
  186. }
  187. if err == nil {
  188. // Return EDN and the rest of the data slice (which might be len 0)
  189. return di.w.String(), di.d.data[di.d.off:], nil
  190. }
  191. return di.w.String(), nil, err
  192. }
  193. func (di *diagnose) wellformed(allowExtraData bool) error {
  194. off := di.d.off
  195. err := di.d.wellformed(allowExtraData, false)
  196. di.d.off = off
  197. return err
  198. }
  199. func (di *diagnose) item() error { //nolint:gocyclo
  200. initialByte := di.d.data[di.d.off]
  201. switch initialByte {
  202. case cborByteStringWithIndefiniteLengthHead,
  203. cborTextStringWithIndefiniteLengthHead: // indefinite-length byte/text string
  204. di.d.off++
  205. if isBreakFlag(di.d.data[di.d.off]) {
  206. di.d.off++
  207. switch initialByte {
  208. case cborByteStringWithIndefiniteLengthHead:
  209. // indefinite-length bytes with no chunks.
  210. di.w.WriteString(`''_`)
  211. return nil
  212. case cborTextStringWithIndefiniteLengthHead:
  213. // indefinite-length text with no chunks.
  214. di.w.WriteString(`""_`)
  215. return nil
  216. }
  217. }
  218. di.w.WriteString("(_ ")
  219. i := 0
  220. for !di.d.foundBreak() {
  221. if i > 0 {
  222. di.w.WriteString(", ")
  223. }
  224. i++
  225. // wellformedIndefiniteString() already checked that the next item is a byte/text string.
  226. if err := di.item(); err != nil {
  227. return err
  228. }
  229. }
  230. di.w.WriteByte(')')
  231. return nil
  232. case cborArrayWithIndefiniteLengthHead: // indefinite-length array
  233. di.d.off++
  234. di.w.WriteString("[_ ")
  235. i := 0
  236. for !di.d.foundBreak() {
  237. if i > 0 {
  238. di.w.WriteString(", ")
  239. }
  240. i++
  241. if err := di.item(); err != nil {
  242. return err
  243. }
  244. }
  245. di.w.WriteByte(']')
  246. return nil
  247. case cborMapWithIndefiniteLengthHead: // indefinite-length map
  248. di.d.off++
  249. di.w.WriteString("{_ ")
  250. i := 0
  251. for !di.d.foundBreak() {
  252. if i > 0 {
  253. di.w.WriteString(", ")
  254. }
  255. i++
  256. // key
  257. if err := di.item(); err != nil {
  258. return err
  259. }
  260. di.w.WriteString(": ")
  261. // value
  262. if err := di.item(); err != nil {
  263. return err
  264. }
  265. }
  266. di.w.WriteByte('}')
  267. return nil
  268. }
  269. t := di.d.nextCBORType()
  270. switch t {
  271. case cborTypePositiveInt:
  272. _, _, val := di.d.getHead()
  273. di.w.WriteString(strconv.FormatUint(val, 10))
  274. return nil
  275. case cborTypeNegativeInt:
  276. _, _, val := di.d.getHead()
  277. if val > math.MaxInt64 {
  278. // CBOR negative integer overflows int64, use big.Int to store value.
  279. bi := new(big.Int)
  280. bi.SetUint64(val)
  281. bi.Add(bi, big.NewInt(1))
  282. bi.Neg(bi)
  283. di.w.WriteString(bi.String())
  284. return nil
  285. }
  286. nValue := int64(-1) ^ int64(val)
  287. di.w.WriteString(strconv.FormatInt(nValue, 10))
  288. return nil
  289. case cborTypeByteString:
  290. b, _ := di.d.parseByteString()
  291. return di.encodeByteString(b)
  292. case cborTypeTextString:
  293. b, err := di.d.parseTextString()
  294. if err != nil {
  295. return err
  296. }
  297. return di.encodeTextString(string(b), '"')
  298. case cborTypeArray:
  299. _, _, val := di.d.getHead()
  300. count := int(val)
  301. di.w.WriteByte('[')
  302. for i := 0; i < count; i++ {
  303. if i > 0 {
  304. di.w.WriteString(", ")
  305. }
  306. if err := di.item(); err != nil {
  307. return err
  308. }
  309. }
  310. di.w.WriteByte(']')
  311. return nil
  312. case cborTypeMap:
  313. _, _, val := di.d.getHead()
  314. count := int(val)
  315. di.w.WriteByte('{')
  316. for i := 0; i < count; i++ {
  317. if i > 0 {
  318. di.w.WriteString(", ")
  319. }
  320. // key
  321. if err := di.item(); err != nil {
  322. return err
  323. }
  324. di.w.WriteString(": ")
  325. // value
  326. if err := di.item(); err != nil {
  327. return err
  328. }
  329. }
  330. di.w.WriteByte('}')
  331. return nil
  332. case cborTypeTag:
  333. _, _, tagNum := di.d.getHead()
  334. switch tagNum {
  335. case tagNumUnsignedBignum:
  336. if nt := di.d.nextCBORType(); nt != cborTypeByteString {
  337. return newInadmissibleTagContentTypeError(
  338. tagNumUnsignedBignum,
  339. "byte string",
  340. nt.String())
  341. }
  342. b, _ := di.d.parseByteString()
  343. bi := new(big.Int).SetBytes(b)
  344. di.w.WriteString(bi.String())
  345. return nil
  346. case tagNumNegativeBignum:
  347. if nt := di.d.nextCBORType(); nt != cborTypeByteString {
  348. return newInadmissibleTagContentTypeError(
  349. tagNumNegativeBignum,
  350. "byte string",
  351. nt.String(),
  352. )
  353. }
  354. b, _ := di.d.parseByteString()
  355. bi := new(big.Int).SetBytes(b)
  356. bi.Add(bi, big.NewInt(1))
  357. bi.Neg(bi)
  358. di.w.WriteString(bi.String())
  359. return nil
  360. default:
  361. di.w.WriteString(strconv.FormatUint(tagNum, 10))
  362. di.w.WriteByte('(')
  363. if err := di.item(); err != nil {
  364. return err
  365. }
  366. di.w.WriteByte(')')
  367. return nil
  368. }
  369. case cborTypePrimitives:
  370. _, ai, val := di.d.getHead()
  371. switch ai {
  372. case additionalInformationAsFalse:
  373. di.w.WriteString("false")
  374. return nil
  375. case additionalInformationAsTrue:
  376. di.w.WriteString("true")
  377. return nil
  378. case additionalInformationAsNull:
  379. di.w.WriteString("null")
  380. return nil
  381. case additionalInformationAsUndefined:
  382. di.w.WriteString("undefined")
  383. return nil
  384. case additionalInformationAsFloat16,
  385. additionalInformationAsFloat32,
  386. additionalInformationAsFloat64:
  387. return di.encodeFloat(ai, val)
  388. default:
  389. di.w.WriteString("simple(")
  390. di.w.WriteString(strconv.FormatUint(val, 10))
  391. di.w.WriteByte(')')
  392. return nil
  393. }
  394. }
  395. return nil
  396. }
  397. // writeU16 format a rune as "\uxxxx"
  398. func (di *diagnose) writeU16(val rune) {
  399. di.w.WriteString("\\u")
  400. var in [2]byte
  401. in[0] = byte(val >> 8)
  402. in[1] = byte(val)
  403. sz := hex.EncodedLen(len(in))
  404. di.w.Grow(sz)
  405. dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
  406. hex.Encode(dst, in[:])
  407. di.w.Write(dst)
  408. }
  409. var rawBase32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
  410. var rawBase32HexEncoding = base32.HexEncoding.WithPadding(base32.NoPadding)
  411. func (di *diagnose) encodeByteString(val []byte) error {
  412. if len(val) > 0 {
  413. if di.dm.byteStringText && utf8.Valid(val) {
  414. return di.encodeTextString(string(val), '\'')
  415. }
  416. if di.dm.byteStringEmbeddedCBOR {
  417. di2 := newDiagnose(val, di.dm.decMode, di.dm)
  418. // should always notating embedded CBOR sequence.
  419. if str, err := di2.diag(true); err == nil {
  420. di.w.WriteString("<<")
  421. di.w.WriteString(str)
  422. di.w.WriteString(">>")
  423. return nil
  424. }
  425. }
  426. }
  427. switch di.dm.byteStringEncoding {
  428. case ByteStringBase16Encoding:
  429. di.w.WriteString("h'")
  430. if di.dm.byteStringHexWhitespace {
  431. sz := hex.EncodedLen(len(val))
  432. if len(val) > 0 {
  433. sz += len(val) - 1
  434. }
  435. di.w.Grow(sz)
  436. dst := di.w.Bytes()[di.w.Len():]
  437. for i := range val {
  438. if i > 0 {
  439. dst = append(dst, ' ')
  440. }
  441. hex.Encode(dst[len(dst):len(dst)+2], val[i:i+1])
  442. dst = dst[:len(dst)+2]
  443. }
  444. di.w.Write(dst)
  445. } else {
  446. sz := hex.EncodedLen(len(val))
  447. di.w.Grow(sz)
  448. dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
  449. hex.Encode(dst, val)
  450. di.w.Write(dst)
  451. }
  452. di.w.WriteByte('\'')
  453. return nil
  454. case ByteStringBase32Encoding:
  455. di.w.WriteString("b32'")
  456. sz := rawBase32Encoding.EncodedLen(len(val))
  457. di.w.Grow(sz)
  458. dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
  459. rawBase32Encoding.Encode(dst, val)
  460. di.w.Write(dst)
  461. di.w.WriteByte('\'')
  462. return nil
  463. case ByteStringBase32HexEncoding:
  464. di.w.WriteString("h32'")
  465. sz := rawBase32HexEncoding.EncodedLen(len(val))
  466. di.w.Grow(sz)
  467. dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
  468. rawBase32HexEncoding.Encode(dst, val)
  469. di.w.Write(dst)
  470. di.w.WriteByte('\'')
  471. return nil
  472. case ByteStringBase64Encoding:
  473. di.w.WriteString("b64'")
  474. sz := base64.RawURLEncoding.EncodedLen(len(val))
  475. di.w.Grow(sz)
  476. dst := di.w.Bytes()[di.w.Len() : di.w.Len()+sz]
  477. base64.RawURLEncoding.Encode(dst, val)
  478. di.w.Write(dst)
  479. di.w.WriteByte('\'')
  480. return nil
  481. default:
  482. // It should not be possible for users to construct a *diagMode with an invalid byte
  483. // string encoding.
  484. panic(fmt.Sprintf("diagmode has invalid ByteStringEncoding %v", di.dm.byteStringEncoding))
  485. }
  486. }
  487. const utf16SurrSelf = rune(0x10000)
  488. // quote should be either `'` or `"`
  489. func (di *diagnose) encodeTextString(val string, quote byte) error {
  490. di.w.WriteByte(quote)
  491. for i := 0; i < len(val); {
  492. if b := val[i]; b < utf8.RuneSelf {
  493. switch {
  494. case b == '\t', b == '\n', b == '\r', b == '\\', b == quote:
  495. di.w.WriteByte('\\')
  496. switch b {
  497. case '\t':
  498. b = 't'
  499. case '\n':
  500. b = 'n'
  501. case '\r':
  502. b = 'r'
  503. }
  504. di.w.WriteByte(b)
  505. case b >= ' ' && b <= '~':
  506. di.w.WriteByte(b)
  507. default:
  508. di.writeU16(rune(b))
  509. }
  510. i++
  511. continue
  512. }
  513. c, size := utf8.DecodeRuneInString(val[i:])
  514. switch {
  515. case c == utf8.RuneError:
  516. return &SemanticError{"cbor: invalid UTF-8 string"}
  517. case c < utf16SurrSelf:
  518. di.writeU16(c)
  519. default:
  520. c1, c2 := utf16.EncodeRune(c)
  521. di.writeU16(c1)
  522. di.writeU16(c2)
  523. }
  524. i += size
  525. }
  526. di.w.WriteByte(quote)
  527. return nil
  528. }
  529. func (di *diagnose) encodeFloat(ai byte, val uint64) error {
  530. f64 := float64(0)
  531. switch ai {
  532. case additionalInformationAsFloat16:
  533. f16 := float16.Frombits(uint16(val))
  534. switch {
  535. case f16.IsNaN():
  536. di.w.WriteString("NaN")
  537. return nil
  538. case f16.IsInf(1):
  539. di.w.WriteString("Infinity")
  540. return nil
  541. case f16.IsInf(-1):
  542. di.w.WriteString("-Infinity")
  543. return nil
  544. default:
  545. f64 = float64(f16.Float32())
  546. }
  547. case additionalInformationAsFloat32:
  548. f32 := math.Float32frombits(uint32(val))
  549. switch {
  550. case f32 != f32:
  551. di.w.WriteString("NaN")
  552. return nil
  553. case f32 > math.MaxFloat32:
  554. di.w.WriteString("Infinity")
  555. return nil
  556. case f32 < -math.MaxFloat32:
  557. di.w.WriteString("-Infinity")
  558. return nil
  559. default:
  560. f64 = float64(f32)
  561. }
  562. case additionalInformationAsFloat64:
  563. f64 = math.Float64frombits(val)
  564. switch {
  565. case f64 != f64:
  566. di.w.WriteString("NaN")
  567. return nil
  568. case f64 > math.MaxFloat64:
  569. di.w.WriteString("Infinity")
  570. return nil
  571. case f64 < -math.MaxFloat64:
  572. di.w.WriteString("-Infinity")
  573. return nil
  574. }
  575. }
  576. // Use ES6 number to string conversion which should match most JSON generators.
  577. // Inspired by https://github.com/golang/go/blob/4df10fba1687a6d4f51d7238a403f8f2298f6a16/src/encoding/json/encode.go#L585
  578. const bitSize = 64
  579. b := make([]byte, 0, 32)
  580. if abs := math.Abs(f64); abs != 0 && (abs < 1e-6 || abs >= 1e21) {
  581. b = strconv.AppendFloat(b, f64, 'e', -1, bitSize)
  582. // clean up e-09 to e-9
  583. n := len(b)
  584. if n >= 4 && string(b[n-4:n-1]) == "e-0" {
  585. b = append(b[:n-2], b[n-1])
  586. }
  587. } else {
  588. b = strconv.AppendFloat(b, f64, 'f', -1, bitSize)
  589. }
  590. // add decimal point and trailing zero if needed
  591. if bytes.IndexByte(b, '.') < 0 {
  592. if i := bytes.IndexByte(b, 'e'); i < 0 {
  593. b = append(b, '.', '0')
  594. } else {
  595. b = append(b[:i+2], b[i:]...)
  596. b[i] = '.'
  597. b[i+1] = '0'
  598. }
  599. }
  600. di.w.WriteString(string(b))
  601. if di.dm.floatPrecisionIndicator {
  602. switch ai {
  603. case additionalInformationAsFloat16:
  604. di.w.WriteString("_1")
  605. return nil
  606. case additionalInformationAsFloat32:
  607. di.w.WriteString("_2")
  608. return nil
  609. case additionalInformationAsFloat64:
  610. di.w.WriteString("_3")
  611. return nil
  612. }
  613. }
  614. return nil
  615. }