decode.go 12 KB

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