Files
opencloud/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go
2023-04-19 20:24:34 +02:00

144 lines
3.0 KiB
Go

// Copyright 2016 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package topdown
import (
"fmt"
"math/big"
"strings"
"unicode"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/topdown/builtins"
)
const (
none uint64 = 1 << (10 * iota)
ki
mi
gi
ti
pi
ei
kb uint64 = 1000
mb = kb * 1000
gb = mb * 1000
tb = gb * 1000
pb = tb * 1000
eb = pb * 1000
)
func parseNumBytesError(msg string) error {
return fmt.Errorf("%s: %s", ast.UnitsParseBytes.Name, msg)
}
func errBytesUnitNotRecognized(unit string) error {
return parseNumBytesError(fmt.Sprintf("byte unit %s not recognized", unit))
}
var (
errBytesValueNoAmount = parseNumBytesError("no byte amount provided")
errBytesValueNumConv = parseNumBytesError("could not parse byte amount to a number")
errBytesValueIncludesSpaces = parseNumBytesError("spaces not allowed in resource strings")
)
func builtinNumBytes(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
var m big.Float
raw, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
}
s := formatString(raw)
if strings.Contains(s, " ") {
return errBytesValueIncludesSpaces
}
num, unit := extractNumAndUnit(s)
if num == "" {
return errBytesValueNoAmount
}
switch unit {
case "":
m.SetUint64(none)
case "kb", "k":
m.SetUint64(kb)
case "kib", "ki":
m.SetUint64(ki)
case "mb", "m":
m.SetUint64(mb)
case "mib", "mi":
m.SetUint64(mi)
case "gb", "g":
m.SetUint64(gb)
case "gib", "gi":
m.SetUint64(gi)
case "tb", "t":
m.SetUint64(tb)
case "tib", "ti":
m.SetUint64(ti)
case "pb", "p":
m.SetUint64(pb)
case "pib", "pi":
m.SetUint64(pi)
case "eb", "e":
m.SetUint64(eb)
case "eib", "ei":
m.SetUint64(ei)
default:
return errBytesUnitNotRecognized(unit)
}
numFloat, ok := new(big.Float).SetString(num)
if !ok {
return errBytesValueNumConv
}
var total big.Int
numFloat.Mul(numFloat, &m).Int(&total)
return iter(ast.NewTerm(builtins.IntToNumber(&total)))
}
// Makes the string lower case and removes quotation marks
func formatString(s ast.String) string {
str := string(s)
lower := strings.ToLower(str)
return strings.Replace(lower, "\"", "", -1)
}
// Splits the string into a number string à la "10" or "10.2" and a unit
// string à la "gb" or "MiB" or "foo". Either can be an empty string
// (error handling is provided elsewhere).
func extractNumAndUnit(s string) (string, string) {
isNum := func(r rune) bool {
return unicode.IsDigit(r) || r == '.'
}
firstNonNumIdx := -1
for idx, r := range s {
if !isNum(r) {
firstNonNumIdx = idx
break
}
}
if firstNonNumIdx == -1 { // only digits and '.'
return s, ""
}
if firstNonNumIdx == 0 { // only units (starts with non-digit)
return "", s
}
return s[0:firstNonNumIdx], s[firstNonNumIdx:]
}
func init() {
RegisterBuiltinFunc(ast.UnitsParseBytes.Name, builtinNumBytes)
}