decode.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. package toml
  2. import (
  3. "fmt"
  4. "math"
  5. "strconv"
  6. "time"
  7. "github.com/pelletier/go-toml/v2/unstable"
  8. )
  9. func parseInteger(b []byte) (int64, error) {
  10. if len(b) > 2 && b[0] == '0' {
  11. switch b[1] {
  12. case 'x':
  13. return parseIntHex(b)
  14. case 'b':
  15. return parseIntBin(b)
  16. case 'o':
  17. return parseIntOct(b)
  18. default:
  19. panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1]))
  20. }
  21. }
  22. return parseIntDec(b)
  23. }
  24. func parseLocalDate(b []byte) (LocalDate, error) {
  25. // full-date = date-fullyear "-" date-month "-" date-mday
  26. // date-fullyear = 4DIGIT
  27. // date-month = 2DIGIT ; 01-12
  28. // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
  29. var date LocalDate
  30. if len(b) != 10 || b[4] != '-' || b[7] != '-' {
  31. return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD")
  32. }
  33. var err error
  34. date.Year, err = parseDecimalDigits(b[0:4])
  35. if err != nil {
  36. return LocalDate{}, err
  37. }
  38. date.Month, err = parseDecimalDigits(b[5:7])
  39. if err != nil {
  40. return LocalDate{}, err
  41. }
  42. date.Day, err = parseDecimalDigits(b[8:10])
  43. if err != nil {
  44. return LocalDate{}, err
  45. }
  46. if !isValidDate(date.Year, date.Month, date.Day) {
  47. return LocalDate{}, unstable.NewParserError(b, "impossible date")
  48. }
  49. return date, nil
  50. }
  51. func parseDecimalDigits(b []byte) (int, error) {
  52. v := 0
  53. for i, c := range b {
  54. if c < '0' || c > '9' {
  55. return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)")
  56. }
  57. v *= 10
  58. v += int(c - '0')
  59. }
  60. return v, nil
  61. }
  62. func parseDateTime(b []byte) (time.Time, error) {
  63. // offset-date-time = full-date time-delim full-time
  64. // full-time = partial-time time-offset
  65. // time-offset = "Z" / time-numoffset
  66. // time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
  67. dt, b, err := parseLocalDateTime(b)
  68. if err != nil {
  69. return time.Time{}, err
  70. }
  71. var zone *time.Location
  72. if len(b) == 0 {
  73. // parser should have checked that when assigning the date time node
  74. panic("date time should have a timezone")
  75. }
  76. if b[0] == 'Z' || b[0] == 'z' {
  77. b = b[1:]
  78. zone = time.UTC
  79. } else {
  80. const dateTimeByteLen = 6
  81. if len(b) != dateTimeByteLen {
  82. return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone")
  83. }
  84. var direction int
  85. switch b[0] {
  86. case '-':
  87. direction = -1
  88. case '+':
  89. direction = +1
  90. default:
  91. return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character")
  92. }
  93. if b[3] != ':' {
  94. return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator")
  95. }
  96. hours, err := parseDecimalDigits(b[1:3])
  97. if err != nil {
  98. return time.Time{}, err
  99. }
  100. if hours > 23 {
  101. return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours")
  102. }
  103. minutes, err := parseDecimalDigits(b[4:6])
  104. if err != nil {
  105. return time.Time{}, err
  106. }
  107. if minutes > 59 {
  108. return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes")
  109. }
  110. seconds := direction * (hours*3600 + minutes*60)
  111. if seconds == 0 {
  112. zone = time.UTC
  113. } else {
  114. zone = time.FixedZone("", seconds)
  115. }
  116. b = b[dateTimeByteLen:]
  117. }
  118. if len(b) > 0 {
  119. return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone")
  120. }
  121. t := time.Date(
  122. dt.Year,
  123. time.Month(dt.Month),
  124. dt.Day,
  125. dt.Hour,
  126. dt.Minute,
  127. dt.Second,
  128. dt.Nanosecond,
  129. zone)
  130. return t, nil
  131. }
  132. func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) {
  133. var dt LocalDateTime
  134. const localDateTimeByteMinLen = 11
  135. if len(b) < localDateTimeByteMinLen {
  136. return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]")
  137. }
  138. date, err := parseLocalDate(b[:10])
  139. if err != nil {
  140. return dt, nil, err
  141. }
  142. dt.LocalDate = date
  143. sep := b[10]
  144. if sep != 'T' && sep != ' ' && sep != 't' {
  145. return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space")
  146. }
  147. t, rest, err := parseLocalTime(b[11:])
  148. if err != nil {
  149. return dt, nil, err
  150. }
  151. dt.LocalTime = t
  152. return dt, rest, nil
  153. }
  154. // parseLocalTime is a bit different because it also returns the remaining
  155. // []byte that is didn't need. This is to allow parseDateTime to parse those
  156. // remaining bytes as a timezone.
  157. func parseLocalTime(b []byte) (LocalTime, []byte, error) {
  158. var (
  159. nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0}
  160. t LocalTime
  161. )
  162. // check if b matches to have expected format HH:MM:SS[.NNNNNN]
  163. const localTimeByteLen = 8
  164. if len(b) < localTimeByteLen {
  165. return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]")
  166. }
  167. var err error
  168. t.Hour, err = parseDecimalDigits(b[0:2])
  169. if err != nil {
  170. return t, nil, err
  171. }
  172. if t.Hour > 23 {
  173. return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23")
  174. }
  175. if b[2] != ':' {
  176. return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes")
  177. }
  178. t.Minute, err = parseDecimalDigits(b[3:5])
  179. if err != nil {
  180. return t, nil, err
  181. }
  182. if t.Minute > 59 {
  183. return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59")
  184. }
  185. if b[5] != ':' {
  186. return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds")
  187. }
  188. t.Second, err = parseDecimalDigits(b[6:8])
  189. if err != nil {
  190. return t, nil, err
  191. }
  192. if t.Second > 60 {
  193. return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60")
  194. }
  195. b = b[8:]
  196. if len(b) >= 1 && b[0] == '.' {
  197. frac := 0
  198. precision := 0
  199. digits := 0
  200. for i, c := range b[1:] {
  201. if !isDigit(c) {
  202. if i == 0 {
  203. return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point")
  204. }
  205. break
  206. }
  207. digits++
  208. const maxFracPrecision = 9
  209. if i >= maxFracPrecision {
  210. // go-toml allows decoding fractional seconds
  211. // beyond the supported precision of 9
  212. // digits. It truncates the fractional component
  213. // to the supported precision and ignores the
  214. // remaining digits.
  215. //
  216. // https://github.com/pelletier/go-toml/discussions/707
  217. continue
  218. }
  219. frac *= 10
  220. frac += int(c - '0')
  221. precision++
  222. }
  223. if precision == 0 {
  224. return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit")
  225. }
  226. t.Nanosecond = frac * nspow[precision]
  227. t.Precision = precision
  228. return t, b[1+digits:], nil
  229. }
  230. return t, b, nil
  231. }
  232. //nolint:cyclop
  233. func parseFloat(b []byte) (float64, error) {
  234. if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' {
  235. return math.NaN(), nil
  236. }
  237. cleaned, err := checkAndRemoveUnderscoresFloats(b)
  238. if err != nil {
  239. return 0, err
  240. }
  241. if cleaned[0] == '.' {
  242. return 0, unstable.NewParserError(b, "float cannot start with a dot")
  243. }
  244. if cleaned[len(cleaned)-1] == '.' {
  245. return 0, unstable.NewParserError(b, "float cannot end with a dot")
  246. }
  247. dotAlreadySeen := false
  248. for i, c := range cleaned {
  249. if c == '.' {
  250. if dotAlreadySeen {
  251. return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point")
  252. }
  253. if !isDigit(cleaned[i-1]) {
  254. return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit")
  255. }
  256. if !isDigit(cleaned[i+1]) {
  257. return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit")
  258. }
  259. dotAlreadySeen = true
  260. }
  261. }
  262. start := 0
  263. if cleaned[0] == '+' || cleaned[0] == '-' {
  264. start = 1
  265. }
  266. if cleaned[start] == '0' && len(cleaned) > start+1 && isDigit(cleaned[start+1]) {
  267. return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes")
  268. }
  269. f, err := strconv.ParseFloat(string(cleaned), 64)
  270. if err != nil {
  271. return 0, unstable.NewParserError(b, "unable to parse float: %w", err)
  272. }
  273. return f, nil
  274. }
  275. func parseIntHex(b []byte) (int64, error) {
  276. cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
  277. if err != nil {
  278. return 0, err
  279. }
  280. i, err := strconv.ParseInt(string(cleaned), 16, 64)
  281. if err != nil {
  282. return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err)
  283. }
  284. return i, nil
  285. }
  286. func parseIntOct(b []byte) (int64, error) {
  287. cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
  288. if err != nil {
  289. return 0, err
  290. }
  291. i, err := strconv.ParseInt(string(cleaned), 8, 64)
  292. if err != nil {
  293. return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err)
  294. }
  295. return i, nil
  296. }
  297. func parseIntBin(b []byte) (int64, error) {
  298. cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
  299. if err != nil {
  300. return 0, err
  301. }
  302. i, err := strconv.ParseInt(string(cleaned), 2, 64)
  303. if err != nil {
  304. return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err)
  305. }
  306. return i, nil
  307. }
  308. func isSign(b byte) bool {
  309. return b == '+' || b == '-'
  310. }
  311. func parseIntDec(b []byte) (int64, error) {
  312. cleaned, err := checkAndRemoveUnderscoresIntegers(b)
  313. if err != nil {
  314. return 0, err
  315. }
  316. startIdx := 0
  317. if isSign(cleaned[0]) {
  318. startIdx++
  319. }
  320. if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' {
  321. return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number")
  322. }
  323. i, err := strconv.ParseInt(string(cleaned), 10, 64)
  324. if err != nil {
  325. return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err)
  326. }
  327. return i, nil
  328. }
  329. func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) {
  330. start := 0
  331. if b[start] == '+' || b[start] == '-' {
  332. start++
  333. }
  334. if len(b) == start {
  335. return b, nil
  336. }
  337. if b[start] == '_' {
  338. return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore")
  339. }
  340. if b[len(b)-1] == '_' {
  341. return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
  342. }
  343. // fast path
  344. i := 0
  345. for ; i < len(b); i++ {
  346. if b[i] == '_' {
  347. break
  348. }
  349. }
  350. if i == len(b) {
  351. return b, nil
  352. }
  353. before := false
  354. cleaned := make([]byte, i, len(b))
  355. copy(cleaned, b)
  356. for i++; i < len(b); i++ {
  357. c := b[i]
  358. if c == '_' {
  359. if !before {
  360. return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
  361. }
  362. before = false
  363. } else {
  364. before = true
  365. cleaned = append(cleaned, c)
  366. }
  367. }
  368. return cleaned, nil
  369. }
  370. func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) {
  371. if b[0] == '_' {
  372. return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore")
  373. }
  374. if b[len(b)-1] == '_' {
  375. return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
  376. }
  377. // fast path
  378. i := 0
  379. for ; i < len(b); i++ {
  380. if b[i] == '_' {
  381. break
  382. }
  383. }
  384. if i == len(b) {
  385. return b, nil
  386. }
  387. before := false
  388. cleaned := make([]byte, 0, len(b))
  389. for i := 0; i < len(b); i++ {
  390. c := b[i]
  391. switch c {
  392. case '_':
  393. if !before {
  394. return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
  395. }
  396. if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') {
  397. return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent")
  398. }
  399. before = false
  400. case '+', '-':
  401. // signed exponents
  402. cleaned = append(cleaned, c)
  403. before = false
  404. case 'e', 'E':
  405. if i < len(b)-1 && b[i+1] == '_' {
  406. return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent")
  407. }
  408. cleaned = append(cleaned, c)
  409. case '.':
  410. if i < len(b)-1 && b[i+1] == '_' {
  411. return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point")
  412. }
  413. if i > 0 && b[i-1] == '_' {
  414. return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point")
  415. }
  416. cleaned = append(cleaned, c)
  417. default:
  418. before = true
  419. cleaned = append(cleaned, c)
  420. }
  421. }
  422. return cleaned, nil
  423. }
  424. // isValidDate checks if a provided date is a date that exists.
  425. func isValidDate(year int, month int, day int) bool {
  426. return month > 0 && month < 13 && day > 0 && day <= daysIn(month, year)
  427. }
  428. // daysBefore[m] counts the number of days in a non-leap year
  429. // before month m begins. There is an entry for m=12, counting
  430. // the number of days before January of next year (365).
  431. var daysBefore = [...]int32{
  432. 0,
  433. 31,
  434. 31 + 28,
  435. 31 + 28 + 31,
  436. 31 + 28 + 31 + 30,
  437. 31 + 28 + 31 + 30 + 31,
  438. 31 + 28 + 31 + 30 + 31 + 30,
  439. 31 + 28 + 31 + 30 + 31 + 30 + 31,
  440. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
  441. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
  442. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
  443. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
  444. 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
  445. }
  446. func daysIn(m int, year int) int {
  447. if m == 2 && isLeap(year) {
  448. return 29
  449. }
  450. return int(daysBefore[m] - daysBefore[m-1])
  451. }
  452. func isLeap(year int) bool {
  453. return year%4 == 0 && (year%100 != 0 || year%400 == 0)
  454. }
  455. func isDigit(r byte) bool {
  456. return r >= '0' && r <= '9'
  457. }