podman

Форк
0
550 строк · 12.5 Кб
1
package toml
2

3
import (
4
	"fmt"
5
	"math"
6
	"strconv"
7
	"time"
8

9
	"github.com/pelletier/go-toml/v2/unstable"
10
)
11

12
func parseInteger(b []byte) (int64, error) {
13
	if len(b) > 2 && b[0] == '0' {
14
		switch b[1] {
15
		case 'x':
16
			return parseIntHex(b)
17
		case 'b':
18
			return parseIntBin(b)
19
		case 'o':
20
			return parseIntOct(b)
21
		default:
22
			panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1]))
23
		}
24
	}
25

26
	return parseIntDec(b)
27
}
28

29
func parseLocalDate(b []byte) (LocalDate, error) {
30
	// full-date      = date-fullyear "-" date-month "-" date-mday
31
	// date-fullyear  = 4DIGIT
32
	// date-month     = 2DIGIT  ; 01-12
33
	// date-mday      = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year
34
	var date LocalDate
35

36
	if len(b) != 10 || b[4] != '-' || b[7] != '-' {
37
		return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD")
38
	}
39

40
	var err error
41

42
	date.Year, err = parseDecimalDigits(b[0:4])
43
	if err != nil {
44
		return LocalDate{}, err
45
	}
46

47
	date.Month, err = parseDecimalDigits(b[5:7])
48
	if err != nil {
49
		return LocalDate{}, err
50
	}
51

52
	date.Day, err = parseDecimalDigits(b[8:10])
53
	if err != nil {
54
		return LocalDate{}, err
55
	}
56

57
	if !isValidDate(date.Year, date.Month, date.Day) {
58
		return LocalDate{}, unstable.NewParserError(b, "impossible date")
59
	}
60

61
	return date, nil
62
}
63

64
func parseDecimalDigits(b []byte) (int, error) {
65
	v := 0
66

67
	for i, c := range b {
68
		if c < '0' || c > '9' {
69
			return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)")
70
		}
71
		v *= 10
72
		v += int(c - '0')
73
	}
74

75
	return v, nil
76
}
77

78
func parseDateTime(b []byte) (time.Time, error) {
79
	// offset-date-time = full-date time-delim full-time
80
	// full-time      = partial-time time-offset
81
	// time-offset    = "Z" / time-numoffset
82
	// time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
83

84
	dt, b, err := parseLocalDateTime(b)
85
	if err != nil {
86
		return time.Time{}, err
87
	}
88

89
	var zone *time.Location
90

91
	if len(b) == 0 {
92
		// parser should have checked that when assigning the date time node
93
		panic("date time should have a timezone")
94
	}
95

96
	if b[0] == 'Z' || b[0] == 'z' {
97
		b = b[1:]
98
		zone = time.UTC
99
	} else {
100
		const dateTimeByteLen = 6
101
		if len(b) != dateTimeByteLen {
102
			return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone")
103
		}
104
		var direction int
105
		switch b[0] {
106
		case '-':
107
			direction = -1
108
		case '+':
109
			direction = +1
110
		default:
111
			return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character")
112
		}
113

114
		if b[3] != ':' {
115
			return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator")
116
		}
117

118
		hours, err := parseDecimalDigits(b[1:3])
119
		if err != nil {
120
			return time.Time{}, err
121
		}
122
		if hours > 23 {
123
			return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours")
124
		}
125

126
		minutes, err := parseDecimalDigits(b[4:6])
127
		if err != nil {
128
			return time.Time{}, err
129
		}
130
		if minutes > 59 {
131
			return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes")
132
		}
133

134
		seconds := direction * (hours*3600 + minutes*60)
135
		if seconds == 0 {
136
			zone = time.UTC
137
		} else {
138
			zone = time.FixedZone("", seconds)
139
		}
140
		b = b[dateTimeByteLen:]
141
	}
142

143
	if len(b) > 0 {
144
		return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone")
145
	}
146

147
	t := time.Date(
148
		dt.Year,
149
		time.Month(dt.Month),
150
		dt.Day,
151
		dt.Hour,
152
		dt.Minute,
153
		dt.Second,
154
		dt.Nanosecond,
155
		zone)
156

157
	return t, nil
158
}
159

160
func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) {
161
	var dt LocalDateTime
162

163
	const localDateTimeByteMinLen = 11
164
	if len(b) < localDateTimeByteMinLen {
165
		return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]")
166
	}
167

168
	date, err := parseLocalDate(b[:10])
169
	if err != nil {
170
		return dt, nil, err
171
	}
172
	dt.LocalDate = date
173

174
	sep := b[10]
175
	if sep != 'T' && sep != ' ' && sep != 't' {
176
		return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space")
177
	}
178

179
	t, rest, err := parseLocalTime(b[11:])
180
	if err != nil {
181
		return dt, nil, err
182
	}
183
	dt.LocalTime = t
184

185
	return dt, rest, nil
186
}
187

188
// parseLocalTime is a bit different because it also returns the remaining
189
// []byte that is didn't need. This is to allow parseDateTime to parse those
190
// remaining bytes as a timezone.
191
func parseLocalTime(b []byte) (LocalTime, []byte, error) {
192
	var (
193
		nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0}
194
		t     LocalTime
195
	)
196

197
	// check if b matches to have expected format HH:MM:SS[.NNNNNN]
198
	const localTimeByteLen = 8
199
	if len(b) < localTimeByteLen {
200
		return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]")
201
	}
202

203
	var err error
204

205
	t.Hour, err = parseDecimalDigits(b[0:2])
206
	if err != nil {
207
		return t, nil, err
208
	}
209

210
	if t.Hour > 23 {
211
		return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23")
212
	}
213
	if b[2] != ':' {
214
		return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes")
215
	}
216

217
	t.Minute, err = parseDecimalDigits(b[3:5])
218
	if err != nil {
219
		return t, nil, err
220
	}
221
	if t.Minute > 59 {
222
		return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59")
223
	}
224
	if b[5] != ':' {
225
		return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds")
226
	}
227

228
	t.Second, err = parseDecimalDigits(b[6:8])
229
	if err != nil {
230
		return t, nil, err
231
	}
232

233
	if t.Second > 60 {
234
		return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60")
235
	}
236

237
	b = b[8:]
238

239
	if len(b) >= 1 && b[0] == '.' {
240
		frac := 0
241
		precision := 0
242
		digits := 0
243

244
		for i, c := range b[1:] {
245
			if !isDigit(c) {
246
				if i == 0 {
247
					return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point")
248
				}
249
				break
250
			}
251
			digits++
252

253
			const maxFracPrecision = 9
254
			if i >= maxFracPrecision {
255
				// go-toml allows decoding fractional seconds
256
				// beyond the supported precision of 9
257
				// digits. It truncates the fractional component
258
				// to the supported precision and ignores the
259
				// remaining digits.
260
				//
261
				// https://github.com/pelletier/go-toml/discussions/707
262
				continue
263
			}
264

265
			frac *= 10
266
			frac += int(c - '0')
267
			precision++
268
		}
269

270
		if precision == 0 {
271
			return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit")
272
		}
273

274
		t.Nanosecond = frac * nspow[precision]
275
		t.Precision = precision
276

277
		return t, b[1+digits:], nil
278
	}
279
	return t, b, nil
280
}
281

282
//nolint:cyclop
283
func parseFloat(b []byte) (float64, error) {
284
	if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' {
285
		return math.NaN(), nil
286
	}
287

288
	cleaned, err := checkAndRemoveUnderscoresFloats(b)
289
	if err != nil {
290
		return 0, err
291
	}
292

293
	if cleaned[0] == '.' {
294
		return 0, unstable.NewParserError(b, "float cannot start with a dot")
295
	}
296

297
	if cleaned[len(cleaned)-1] == '.' {
298
		return 0, unstable.NewParserError(b, "float cannot end with a dot")
299
	}
300

301
	dotAlreadySeen := false
302
	for i, c := range cleaned {
303
		if c == '.' {
304
			if dotAlreadySeen {
305
				return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point")
306
			}
307
			if !isDigit(cleaned[i-1]) {
308
				return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit")
309
			}
310
			if !isDigit(cleaned[i+1]) {
311
				return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit")
312
			}
313
			dotAlreadySeen = true
314
		}
315
	}
316

317
	start := 0
318
	if cleaned[0] == '+' || cleaned[0] == '-' {
319
		start = 1
320
	}
321
	if cleaned[start] == '0' && isDigit(cleaned[start+1]) {
322
		return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes")
323
	}
324

325
	f, err := strconv.ParseFloat(string(cleaned), 64)
326
	if err != nil {
327
		return 0, unstable.NewParserError(b, "unable to parse float: %w", err)
328
	}
329

330
	return f, nil
331
}
332

333
func parseIntHex(b []byte) (int64, error) {
334
	cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
335
	if err != nil {
336
		return 0, err
337
	}
338

339
	i, err := strconv.ParseInt(string(cleaned), 16, 64)
340
	if err != nil {
341
		return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err)
342
	}
343

344
	return i, nil
345
}
346

347
func parseIntOct(b []byte) (int64, error) {
348
	cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
349
	if err != nil {
350
		return 0, err
351
	}
352

353
	i, err := strconv.ParseInt(string(cleaned), 8, 64)
354
	if err != nil {
355
		return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err)
356
	}
357

358
	return i, nil
359
}
360

361
func parseIntBin(b []byte) (int64, error) {
362
	cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
363
	if err != nil {
364
		return 0, err
365
	}
366

367
	i, err := strconv.ParseInt(string(cleaned), 2, 64)
368
	if err != nil {
369
		return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err)
370
	}
371

372
	return i, nil
373
}
374

375
func isSign(b byte) bool {
376
	return b == '+' || b == '-'
377
}
378

379
func parseIntDec(b []byte) (int64, error) {
380
	cleaned, err := checkAndRemoveUnderscoresIntegers(b)
381
	if err != nil {
382
		return 0, err
383
	}
384

385
	startIdx := 0
386

387
	if isSign(cleaned[0]) {
388
		startIdx++
389
	}
390

391
	if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' {
392
		return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number")
393
	}
394

395
	i, err := strconv.ParseInt(string(cleaned), 10, 64)
396
	if err != nil {
397
		return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err)
398
	}
399

400
	return i, nil
401
}
402

403
func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) {
404
	start := 0
405
	if b[start] == '+' || b[start] == '-' {
406
		start++
407
	}
408

409
	if len(b) == start {
410
		return b, nil
411
	}
412

413
	if b[start] == '_' {
414
		return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore")
415
	}
416

417
	if b[len(b)-1] == '_' {
418
		return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
419
	}
420

421
	// fast path
422
	i := 0
423
	for ; i < len(b); i++ {
424
		if b[i] == '_' {
425
			break
426
		}
427
	}
428
	if i == len(b) {
429
		return b, nil
430
	}
431

432
	before := false
433
	cleaned := make([]byte, i, len(b))
434
	copy(cleaned, b)
435

436
	for i++; i < len(b); i++ {
437
		c := b[i]
438
		if c == '_' {
439
			if !before {
440
				return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
441
			}
442
			before = false
443
		} else {
444
			before = true
445
			cleaned = append(cleaned, c)
446
		}
447
	}
448

449
	return cleaned, nil
450
}
451

452
func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) {
453
	if b[0] == '_' {
454
		return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore")
455
	}
456

457
	if b[len(b)-1] == '_' {
458
		return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore")
459
	}
460

461
	// fast path
462
	i := 0
463
	for ; i < len(b); i++ {
464
		if b[i] == '_' {
465
			break
466
		}
467
	}
468
	if i == len(b) {
469
		return b, nil
470
	}
471

472
	before := false
473
	cleaned := make([]byte, 0, len(b))
474

475
	for i := 0; i < len(b); i++ {
476
		c := b[i]
477

478
		switch c {
479
		case '_':
480
			if !before {
481
				return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores")
482
			}
483
			if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') {
484
				return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent")
485
			}
486
			before = false
487
		case '+', '-':
488
			// signed exponents
489
			cleaned = append(cleaned, c)
490
			before = false
491
		case 'e', 'E':
492
			if i < len(b)-1 && b[i+1] == '_' {
493
				return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent")
494
			}
495
			cleaned = append(cleaned, c)
496
		case '.':
497
			if i < len(b)-1 && b[i+1] == '_' {
498
				return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point")
499
			}
500
			if i > 0 && b[i-1] == '_' {
501
				return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point")
502
			}
503
			cleaned = append(cleaned, c)
504
		default:
505
			before = true
506
			cleaned = append(cleaned, c)
507
		}
508
	}
509

510
	return cleaned, nil
511
}
512

513
// isValidDate checks if a provided date is a date that exists.
514
func isValidDate(year int, month int, day int) bool {
515
	return month > 0 && month < 13 && day > 0 && day <= daysIn(month, year)
516
}
517

518
// daysBefore[m] counts the number of days in a non-leap year
519
// before month m begins. There is an entry for m=12, counting
520
// the number of days before January of next year (365).
521
var daysBefore = [...]int32{
522
	0,
523
	31,
524
	31 + 28,
525
	31 + 28 + 31,
526
	31 + 28 + 31 + 30,
527
	31 + 28 + 31 + 30 + 31,
528
	31 + 28 + 31 + 30 + 31 + 30,
529
	31 + 28 + 31 + 30 + 31 + 30 + 31,
530
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
531
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
532
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
533
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
534
	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
535
}
536

537
func daysIn(m int, year int) int {
538
	if m == 2 && isLeap(year) {
539
		return 29
540
	}
541
	return int(daysBefore[m] - daysBefore[m-1])
542
}
543

544
func isLeap(year int) bool {
545
	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
546
}
547

548
func isDigit(r byte) bool {
549
	return r >= '0' && r <= '9'
550
}
551

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.