mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-24 22:08:58 -05:00
144 lines
3.0 KiB
Go
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)
|
|
}
|