mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Added support for integer suffixes, e.g. 1k, 8mb
This commit is contained in:
80
conf/lex.go
80
conf/lex.go
@@ -17,6 +17,7 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -182,7 +183,7 @@ func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
|||||||
// lexTop consumes elements at the top level of data structure.
|
// lexTop consumes elements at the top level of data structure.
|
||||||
func lexTop(lx *lexer) stateFn {
|
func lexTop(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
if isWhitespace(r) || isNL(r) {
|
if unicode.IsSpace(r) {
|
||||||
return lexSkip(lx, lexTop)
|
return lexSkip(lx, lexTop)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +249,7 @@ func lexKeyStart(lx *lexer) stateFn {
|
|||||||
switch {
|
switch {
|
||||||
case isKeySeparator(r):
|
case isKeySeparator(r):
|
||||||
return lx.errorf("Unexpected key separator '%v'", r)
|
return lx.errorf("Unexpected key separator '%v'", r)
|
||||||
case isWhitespace(r) || isNL(r):
|
case unicode.IsSpace(r):
|
||||||
lx.next()
|
lx.next()
|
||||||
return lexSkip(lx, lexKeyStart)
|
return lexSkip(lx, lexKeyStart)
|
||||||
case r == dqStringStart:
|
case r == dqStringStart:
|
||||||
@@ -291,7 +292,7 @@ func lexQuotedKey(lx *lexer) stateFn {
|
|||||||
// is not whitespace) has already been consumed.
|
// is not whitespace) has already been consumed.
|
||||||
func lexKey(lx *lexer) stateFn {
|
func lexKey(lx *lexer) stateFn {
|
||||||
r := lx.peek()
|
r := lx.peek()
|
||||||
if isWhitespace(r) || isNL(r) || isKeySeparator(r) || r == eof {
|
if unicode.IsSpace(r) || isKeySeparator(r) || r == eof {
|
||||||
lx.emit(itemKey)
|
lx.emit(itemKey)
|
||||||
return lexKeyEnd
|
return lexKeyEnd
|
||||||
}
|
}
|
||||||
@@ -305,7 +306,7 @@ func lexKey(lx *lexer) stateFn {
|
|||||||
func lexKeyEnd(lx *lexer) stateFn {
|
func lexKeyEnd(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
case isWhitespace(r) || isNL(r):
|
case unicode.IsSpace(r):
|
||||||
return lexSkip(lx, lexKeyEnd)
|
return lexSkip(lx, lexKeyEnd)
|
||||||
case isKeySeparator(r):
|
case isKeySeparator(r):
|
||||||
return lexSkip(lx, lexValue)
|
return lexSkip(lx, lexValue)
|
||||||
@@ -345,11 +346,11 @@ func lexValue(lx *lexer) stateFn {
|
|||||||
lx.ignore() // ignore the " or '
|
lx.ignore() // ignore the " or '
|
||||||
return lexDubQuotedString
|
return lexDubQuotedString
|
||||||
case r == '-':
|
case r == '-':
|
||||||
return lexNumberStart
|
return lexNegNumberStart
|
||||||
case r == blockStart:
|
case r == blockStart:
|
||||||
lx.ignore()
|
lx.ignore()
|
||||||
return lexBlock
|
return lexBlock
|
||||||
case isDigit(r):
|
case unicode.IsDigit(r):
|
||||||
lx.backup() // avoid an extra state and use the same as above
|
lx.backup() // avoid an extra state and use the same as above
|
||||||
return lexNumberOrDateOrIPStart
|
return lexNumberOrDateOrIPStart
|
||||||
case r == '.': // special error case, be kind to users
|
case r == '.': // special error case, be kind to users
|
||||||
@@ -366,7 +367,7 @@ func lexValue(lx *lexer) stateFn {
|
|||||||
func lexArrayValue(lx *lexer) stateFn {
|
func lexArrayValue(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
case isWhitespace(r) || isNL(r):
|
case unicode.IsSpace(r):
|
||||||
return lexSkip(lx, lexArrayValue)
|
return lexSkip(lx, lexArrayValue)
|
||||||
case r == commentHashStart:
|
case r == commentHashStart:
|
||||||
lx.push(lexArrayValue)
|
lx.push(lexArrayValue)
|
||||||
@@ -433,7 +434,7 @@ func lexMapKeyStart(lx *lexer) stateFn {
|
|||||||
switch {
|
switch {
|
||||||
case isKeySeparator(r):
|
case isKeySeparator(r):
|
||||||
return lx.errorf("Unexpected key separator '%v'.", r)
|
return lx.errorf("Unexpected key separator '%v'.", r)
|
||||||
case isWhitespace(r) || isNL(r):
|
case unicode.IsSpace(r):
|
||||||
lx.next()
|
lx.next()
|
||||||
return lexSkip(lx, lexMapKeyStart)
|
return lexSkip(lx, lexMapKeyStart)
|
||||||
case r == mapEnd:
|
case r == mapEnd:
|
||||||
@@ -491,7 +492,7 @@ func lexMapDubQuotedKey(lx *lexer) stateFn {
|
|||||||
// is not whitespace) has already been consumed.
|
// is not whitespace) has already been consumed.
|
||||||
func lexMapKey(lx *lexer) stateFn {
|
func lexMapKey(lx *lexer) stateFn {
|
||||||
r := lx.peek()
|
r := lx.peek()
|
||||||
if isWhitespace(r) || isNL(r) || isKeySeparator(r) {
|
if unicode.IsSpace(r) || isKeySeparator(r) {
|
||||||
lx.emit(itemKey)
|
lx.emit(itemKey)
|
||||||
return lexMapKeyEnd
|
return lexMapKeyEnd
|
||||||
}
|
}
|
||||||
@@ -505,7 +506,7 @@ func lexMapKey(lx *lexer) stateFn {
|
|||||||
func lexMapKeyEnd(lx *lexer) stateFn {
|
func lexMapKeyEnd(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
case isWhitespace(r) || isNL(r):
|
case unicode.IsSpace(r):
|
||||||
return lexSkip(lx, lexMapKeyEnd)
|
return lexSkip(lx, lexMapKeyEnd)
|
||||||
case isKeySeparator(r):
|
case isKeySeparator(r):
|
||||||
return lexSkip(lx, lexMapValue)
|
return lexSkip(lx, lexMapValue)
|
||||||
@@ -521,7 +522,7 @@ func lexMapKeyEnd(lx *lexer) stateFn {
|
|||||||
func lexMapValue(lx *lexer) stateFn {
|
func lexMapValue(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
case isWhitespace(r) || isNL(r):
|
case unicode.IsSpace(r):
|
||||||
return lexSkip(lx, lexMapValue)
|
return lexSkip(lx, lexMapValue)
|
||||||
case r == mapValTerm:
|
case r == mapValTerm:
|
||||||
return lx.errorf("Unexpected map value terminator %q.", mapValTerm)
|
return lx.errorf("Unexpected map value terminator %q.", mapValTerm)
|
||||||
@@ -722,7 +723,7 @@ func lexStringBinary(lx *lexer) stateFn {
|
|||||||
// It assumes that NO negative sign has been consumed, that is triggered above.
|
// It assumes that NO negative sign has been consumed, that is triggered above.
|
||||||
func lexNumberOrDateOrIPStart(lx *lexer) stateFn {
|
func lexNumberOrDateOrIPStart(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
if !isDigit(r) {
|
if !unicode.IsDigit(r) {
|
||||||
if r == '.' {
|
if r == '.' {
|
||||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||||
}
|
}
|
||||||
@@ -740,10 +741,12 @@ func lexNumberOrDateOrIP(lx *lexer) stateFn {
|
|||||||
return lx.errorf("All ISO8601 dates must be in full Zulu form.")
|
return lx.errorf("All ISO8601 dates must be in full Zulu form.")
|
||||||
}
|
}
|
||||||
return lexDateAfterYear
|
return lexDateAfterYear
|
||||||
case isDigit(r):
|
case unicode.IsDigit(r):
|
||||||
return lexNumberOrDateOrIP
|
return lexNumberOrDateOrIP
|
||||||
case r == '.':
|
case r == '.':
|
||||||
return lexFloatStart
|
return lexFloatStart // Assume float at first, but could be IP
|
||||||
|
case isNumberSuffix(r):
|
||||||
|
return lexConvenientNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
lx.backup()
|
lx.backup()
|
||||||
@@ -751,6 +754,18 @@ func lexNumberOrDateOrIP(lx *lexer) stateFn {
|
|||||||
return lx.pop()
|
return lx.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lexConvenientNumber is when we have a suffix, e.g. 1k or 1Mb
|
||||||
|
func lexConvenientNumber(lx *lexer) stateFn {
|
||||||
|
r := lx.next()
|
||||||
|
switch {
|
||||||
|
case r == 'b' || r == 'B':
|
||||||
|
return lexConvenientNumber
|
||||||
|
}
|
||||||
|
lx.backup()
|
||||||
|
lx.emit(itemInteger)
|
||||||
|
return lx.pop()
|
||||||
|
}
|
||||||
|
|
||||||
// lexDateAfterYear consumes a full Zulu Datetime in ISO8601 format.
|
// lexDateAfterYear consumes a full Zulu Datetime in ISO8601 format.
|
||||||
// It assumes that "YYYY-" has already been consumed.
|
// It assumes that "YYYY-" has already been consumed.
|
||||||
func lexDateAfterYear(lx *lexer) stateFn {
|
func lexDateAfterYear(lx *lexer) stateFn {
|
||||||
@@ -765,7 +780,7 @@ func lexDateAfterYear(lx *lexer) stateFn {
|
|||||||
for _, f := range formats {
|
for _, f := range formats {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
if f == '0' {
|
if f == '0' {
|
||||||
if !isDigit(r) {
|
if !unicode.IsDigit(r) {
|
||||||
return lx.errorf("Expected digit in ISO8601 datetime, "+
|
return lx.errorf("Expected digit in ISO8601 datetime, "+
|
||||||
"but found '%v' instead.", r)
|
"but found '%v' instead.", r)
|
||||||
}
|
}
|
||||||
@@ -778,29 +793,31 @@ func lexDateAfterYear(lx *lexer) stateFn {
|
|||||||
return lx.pop()
|
return lx.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexNumberStart consumes either an integer or a float. It assumes that a
|
// lexNegNumberStart consumes either an integer or a float. It assumes that a
|
||||||
// negative sign has already been read, but that *no* digits have been consumed.
|
// negative sign has already been read, but that *no* digits have been consumed.
|
||||||
// lexNumberStart will move to the appropriate integer or float states.
|
// lexNegNumberStart will move to the appropriate integer or float states.
|
||||||
func lexNumberStart(lx *lexer) stateFn {
|
func lexNegNumberStart(lx *lexer) stateFn {
|
||||||
// we MUST see a digit. Even floats have to start with a digit.
|
// we MUST see a digit. Even floats have to start with a digit.
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
if !isDigit(r) {
|
if !unicode.IsDigit(r) {
|
||||||
if r == '.' {
|
if r == '.' {
|
||||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||||
}
|
}
|
||||||
return lx.errorf("Expected a digit but got '%v'.", r)
|
return lx.errorf("Expected a digit but got '%v'.", r)
|
||||||
}
|
}
|
||||||
return lexNumber
|
return lexNegNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexNumber consumes an integer or a float after seeing the first digit.
|
// lexNumber consumes a negative integer or a float after seeing the first digit.
|
||||||
func lexNumber(lx *lexer) stateFn {
|
func lexNegNumber(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
switch {
|
switch {
|
||||||
case isDigit(r):
|
case unicode.IsDigit(r):
|
||||||
return lexNumber
|
return lexNegNumber
|
||||||
case r == '.':
|
case r == '.':
|
||||||
return lexFloatStart
|
return lexFloatStart
|
||||||
|
case isNumberSuffix(r):
|
||||||
|
return lexConvenientNumber
|
||||||
}
|
}
|
||||||
lx.backup()
|
lx.backup()
|
||||||
lx.emit(itemInteger)
|
lx.emit(itemInteger)
|
||||||
@@ -811,7 +828,7 @@ func lexNumber(lx *lexer) stateFn {
|
|||||||
// Namely, at least one digit is required.
|
// Namely, at least one digit is required.
|
||||||
func lexFloatStart(lx *lexer) stateFn {
|
func lexFloatStart(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
if !isDigit(r) {
|
if !unicode.IsDigit(r) {
|
||||||
return lx.errorf("Floats must have a digit after the '.', but got "+
|
return lx.errorf("Floats must have a digit after the '.', but got "+
|
||||||
"'%v' instead.", r)
|
"'%v' instead.", r)
|
||||||
}
|
}
|
||||||
@@ -822,7 +839,7 @@ func lexFloatStart(lx *lexer) stateFn {
|
|||||||
// Assumes that one digit has been consumed after a '.' already.
|
// Assumes that one digit has been consumed after a '.' already.
|
||||||
func lexFloat(lx *lexer) stateFn {
|
func lexFloat(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
if isDigit(r) {
|
if unicode.IsDigit(r) {
|
||||||
return lexFloat
|
return lexFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,7 +856,7 @@ func lexFloat(lx *lexer) stateFn {
|
|||||||
// lexIPAddr consumes IP addrs, like 127.0.0.1:4222
|
// lexIPAddr consumes IP addrs, like 127.0.0.1:4222
|
||||||
func lexIPAddr(lx *lexer) stateFn {
|
func lexIPAddr(lx *lexer) stateFn {
|
||||||
r := lx.next()
|
r := lx.next()
|
||||||
if isDigit(r) || r == '.' || r == ':' {
|
if unicode.IsDigit(r) || r == '.' || r == ':' {
|
||||||
return lexIPAddr
|
return lexIPAddr
|
||||||
}
|
}
|
||||||
lx.backup()
|
lx.backup()
|
||||||
@@ -876,6 +893,11 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests to see if we have a number suffix
|
||||||
|
func isNumberSuffix(r rune) bool {
|
||||||
|
return r == 'k' || r == 'K' || r == 'm' || r == 'M' || r == 'g' || r == 'G'
|
||||||
|
}
|
||||||
|
|
||||||
// Tests for both key separators
|
// Tests for both key separators
|
||||||
func isKeySeparator(r rune) bool {
|
func isKeySeparator(r rune) bool {
|
||||||
return r == keySepEqual || r == keySepColon
|
return r == keySepEqual || r == keySepColon
|
||||||
@@ -891,10 +913,6 @@ func isNL(r rune) bool {
|
|||||||
return r == '\n' || r == '\r'
|
return r == '\n' || r == '\r'
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDigit(r rune) bool {
|
|
||||||
return r >= '0' && r <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHexadecimal(r rune) bool {
|
func isHexadecimal(r rune) bool {
|
||||||
return (r >= '0' && r <= '9') ||
|
return (r >= '0' && r <= '9') ||
|
||||||
(r >= 'a' && r <= 'f') ||
|
(r >= 'a' && r <= 'f') ||
|
||||||
|
|||||||
@@ -100,6 +100,89 @@ func TestSimpleKeyNegativeIntegerValues(t *testing.T) {
|
|||||||
expect(t, lx, expectedItems)
|
expect(t, lx, expectedItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvenientIntegerValues(t *testing.T) {
|
||||||
|
expectedItems := []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1k", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx := lex("foo = 1k")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1K", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = 1K")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1m", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = 1m")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1M", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = 1M")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1g", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = 1g")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1G", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = 1G")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1MB", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = 1MB")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "1Gb", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = 1Gb")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
// Negative versions
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "-1m", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = -1m")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
|
||||||
|
expectedItems = []item{
|
||||||
|
{itemKey, "foo", 1},
|
||||||
|
{itemInteger, "-1GB", 1},
|
||||||
|
{itemEOF, "", 1},
|
||||||
|
}
|
||||||
|
lx = lex("foo = -1GB ")
|
||||||
|
expect(t, lx, expectedItems)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSimpleKeyFloatValues(t *testing.T) {
|
func TestSimpleKeyFloatValues(t *testing.T) {
|
||||||
expectedItems := []item{
|
expectedItems := []item{
|
||||||
{itemKey, "foo", 1},
|
{itemKey, "foo", 1},
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
@@ -117,7 +118,15 @@ func (p *parser) processItem(it item) error {
|
|||||||
case itemString:
|
case itemString:
|
||||||
p.setValue(it.val) // FIXME(dlc) sanitize string?
|
p.setValue(it.val) // FIXME(dlc) sanitize string?
|
||||||
case itemInteger:
|
case itemInteger:
|
||||||
num, err := strconv.ParseInt(it.val, 10, 64)
|
lastDigit := 0
|
||||||
|
for _, r := range it.val {
|
||||||
|
if !unicode.IsDigit(r) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lastDigit++
|
||||||
|
}
|
||||||
|
numStr := it.val[:lastDigit]
|
||||||
|
num, err := strconv.ParseInt(numStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if e, ok := err.(*strconv.NumError); ok &&
|
if e, ok := err.(*strconv.NumError); ok &&
|
||||||
e.Err == strconv.ErrRange {
|
e.Err == strconv.ErrRange {
|
||||||
@@ -125,7 +134,25 @@ func (p *parser) processItem(it item) error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("Expected integer, but got '%s'.", it.val)
|
return fmt.Errorf("Expected integer, but got '%s'.", it.val)
|
||||||
}
|
}
|
||||||
p.setValue(num)
|
// Process a suffix
|
||||||
|
suffix := strings.ToLower(strings.TrimSpace(it.val[lastDigit:]))
|
||||||
|
switch suffix {
|
||||||
|
case "":
|
||||||
|
p.setValue(num)
|
||||||
|
case "k":
|
||||||
|
p.setValue(num * 1000)
|
||||||
|
case "kb":
|
||||||
|
p.setValue(num * 1024)
|
||||||
|
case "m":
|
||||||
|
p.setValue(num * 1000 * 1000)
|
||||||
|
case "mb":
|
||||||
|
p.setValue(num * 1024 * 1024)
|
||||||
|
case "g":
|
||||||
|
p.setValue(num * 1000 * 1000 * 1000)
|
||||||
|
case "gb":
|
||||||
|
p.setValue(num * 1024 * 1024 * 1024)
|
||||||
|
}
|
||||||
|
|
||||||
case itemFloat:
|
case itemFloat:
|
||||||
num, err := strconv.ParseFloat(it.val, 64)
|
num, err := strconv.ParseFloat(it.val, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -96,6 +96,27 @@ func TestBcryptVariable(t *testing.T) {
|
|||||||
test(t, "password: $2a$11$ooo", ex)
|
test(t, "password: $2a$11$ooo", ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var easynum = `
|
||||||
|
k = 8k
|
||||||
|
kb = 4kb
|
||||||
|
m = 1m
|
||||||
|
mb = 2MB
|
||||||
|
g = 2g
|
||||||
|
gb = 22GB
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestConvenientNumbers(t *testing.T) {
|
||||||
|
ex := map[string]interface{}{
|
||||||
|
"k": int64(8 * 1000),
|
||||||
|
"kb": int64(4 * 1024),
|
||||||
|
"m": int64(1000 * 1000),
|
||||||
|
"mb": int64(2 * 1024 * 1024),
|
||||||
|
"g": int64(2 * 1000 * 1000 * 1000),
|
||||||
|
"gb": int64(22 * 1024 * 1024 * 1024),
|
||||||
|
}
|
||||||
|
test(t, easynum, ex)
|
||||||
|
}
|
||||||
|
|
||||||
var sample1 = `
|
var sample1 = `
|
||||||
foo {
|
foo {
|
||||||
host {
|
host {
|
||||||
|
|||||||
Reference in New Issue
Block a user