package rpsl
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"strconv"
"strings"
)
type Range struct {
leaf bool
lo net.IP
hi net.IP
}
var v6max = net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
var v4mask = net.ParseIP("::ffff:ffff")
var v4InV6 = net.ParseIP("::ffff:0.0.0.0")
var asnInV6 = net.ParseIP("2001:0db8:6583:2042:6c6f:636b::")
func NewInetRange(addr string) *Range {
if strings.ContainsRune(addr, '-') {
sp := strings.SplitN(addr, "-", 2)
r := &Range{
lo: net.ParseIP(sp[0]),
hi: net.ParseIP(sp[1]),
}
if r.lo == nil || r.hi == nil {
return nil
}
return r
}
var mask uint = 128
if strings.ContainsRune(addr, '/') {
sp := strings.SplitN(addr, "/", 2)
addr = sp[0]
if m, err := strconv.ParseUint(sp[1], 10, 8); err == nil {
mask = uint(m)
} else {
return nil
}
}
b := net.ParseIP(addr)
if b == nil {
return nil
}
if mask != 128 && bytes.Equal(bytesXor(bytesOr(b, v4mask), v4mask), v4InV6) {
mask += 96
}
if mask == 128 {
return &Range{
lo: b,
hi: b,
}
}
return &Range{
lo: bytesAnd(bytesLsh(v6max, 128-mask), b),
hi: bytesOr(bytesRsh(v6max, mask), b),
}
}
func NewRouteRange(addr string) *Range {
if r := NewInetRange(addr); r != nil {
r.leaf = true
return r
}
return nil
}
func NewASRange(min, max uint32) *Range {
a := &Range{
leaf: min == max,
lo: make([]byte, 16),
hi: make([]byte, 16),
}
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(min))
copy(a.lo, asnInV6)
copy(a.lo[12:], b[4:])
binary.BigEndian.PutUint64(b, uint64(max))
copy(a.hi, asnInV6)
copy(a.hi[12:], b[4:])
return a
}
func NewRange(s string) *Range {
if strings.HasPrefix(s, "AS") {
var one, two string
var min, max uint32
if strings.ContainsRune(s, '-') {
sp := strings.SplitN(s, "-", 2)
one, two = sp[0], sp[1]
} else {
one, two = s, s
}
min, _ = as2int(one)
max, _ = as2int(two)
return NewASRange(min, max)
}
return NewInetRange(s)
}
func (r *Range) MinIP() net.IP {
return r.lo
}
func (r *Range) MaxIP() net.IP {
return r.hi
}
func (r *Range) ExpandedNetwork() string {
lis := make([]string, 8)
for i, w := range splitWords(r.lo) {
lis[i] = fmt.Sprintf("%4x", w)
}
return strings.Join(lis, ":")
}
func (r *Range) ExpandedBroadcast() string {
lis := make([]string, 8)
for i, w := range splitWords(r.hi) {
lis[i] = fmt.Sprintf("%4x", w)
}
return strings.Join(lis, ":")
}
func (r *Range) String() string {
// if bytes.Equal(bytesXor(bytesOr(r.lo, v4mask), v4mask), asnInV6) && bytes.Equal(bytesXor(bytesOr(r.lo, v4mask), v4mask), asnInV6) {
// lo := binary.BigEndian.Uint64(r.lo[8:])
// hi := binary.BigEndian.Uint64(r.hi[8:])
// return fmt.Sprintf("AS%d - AS%d", lo, hi)
// }
return fmt.Sprintf("%x - %x", []byte(r.lo), []byte(r.hi))
}
func (r *Range) Contains(c *Range) bool {
return bytes.Compare(r.lo, c.lo) <= 0 && bytes.Compare(r.hi, c.hi) > 0 || bytes.Compare(r.lo, c.lo) < 0 && bytes.Compare(r.hi, c.hi) >= 0
}
func (r *Range) Equal(c *Range) bool {
return bytes.Equal(r.lo, c.lo) && bytes.Equal(r.hi, c.hi)
}
func splitWords(in net.IP) (out [8][2]byte) {
for i := range out {
copy(out[i][:], in[i*2:])
}
return out
}
func maxLen(a, b []byte) int {
if len(a) >= len(b) {
return len(a)
}
return len(b)
}
func bytesAnd(a, b []byte) []byte {
c := make([]byte, maxLen(a, b))
for i := range a {
if i >= len(b) {
break
}
c[i] = a[i] & b[i]
}
// fmt.Printf("%08b and %08b -> %08b\n", a, b, c)
return c
}
func bytesOr(a, b []byte) []byte {
c := make([]byte, maxLen(a, b))
for i := range a {
if i >= len(b) {
c[i] = a[i]
continue
}
c[i] = a[i] | b[i]
}
// fmt.Printf("%08b or %08b -> %08b\n", a, b, c)
return c
}
func bytesXor(a, b []byte) []byte {
c := make([]byte, maxLen(a, b))
for i := range a {
if i >= len(b) {
c[i] = a[i]
continue
}
c[i] = a[i] ^ b[i]
}
// fmt.Printf("%08b xor %08b -> %08b\n", a, b, c)
return c
}
func BytesLsh(a []byte, n uint) []byte { return bytesLsh(a, n) }
func bytesLsh(a []byte, n uint) []byte {
c := make([]byte, len(a))
m := n % 8
s := int(n / 8)
if s >= len(a) {
// fmt.Printf("lsh(%d) %08b -> %08b\n", n, a, c)
return c
}
if m == 0 {
copy(c, a[s:])
// fmt.Printf("lsh(%d) %08b -> %08b\n", n, a, c)
return c
}
for i, b := range a[s:] {
c[i] |= b << m
if i > 0 {
c[i-1] |= b >> (8 - m)
}
}
// fmt.Printf("lsh(%d) %08b -> %08b\n", n, a, c)
return c
}
func BytesRsh(a []byte, n uint) []byte { return bytesRsh(a, n) }
func bytesRsh(a []byte, n uint) []byte {
c := make([]byte, len(a))
m := n % 8
s := int(n / 8)
if s >= len(a) {
// fmt.Printf("rsh(%d) %08b -> %08b\n", n, a, c)
return c
}
if m == 0 {
copy(c[s:], a[:len(a)-s])
// fmt.Printf("rsh(%d) %08b -> %08b\n", n, a, c)
return c
}
for i, b := range a[:len(a)-s] {
c[s+i] |= b >> m
if s+i+1 < len(c) {
c[s+i+1] |= b << (8 - m)
}
}
// fmt.Printf("rsh(%d) %08b -> %08b\n", n, a, c)
return c
}
package rpsl
import (
"encoding/json"
"strings"
)
// Attribute key-value pairs that make up an Object.
type Attribute struct {
Name string
rows []Value
spec Spec
}
// NewAttribute with name and rows. Comments are parsed from row values.
func NewAttribute(name string, rows ...string) *Attribute {
v := &Attribute{Name: name, rows: make([]Value, len(rows))}
for i, row := range rows {
v.rows[i] = NewValue(row)
}
return v
}
// Raw returns attribute value with comments.
func (attr *Attribute) Raw() string {
lis := make([]string, len(attr.rows))
for i, row := range attr.rows {
lis[i] = row.String()
}
return strings.Join(lis, "\n")
}
// Text returns attribute value without comments.
func (attr *Attribute) Text() string {
lis := attr.Lines()
switch len(lis) {
case 0:
return ""
case 1:
return lis[0]
default:
return strings.Join(lis, "\n")
}
}
// Comment return comments for attribute value.
func (attr *Attribute) Comment() string {
lis := make([]string, 0, len(attr.rows))
for _, v := range attr.rows {
if c := v.Comment; len(c) > 0 {
lis = append(lis, c)
}
}
return strings.Join(lis, "\n")
}
// Default return provided attribute value when nil.
func (attr *Attribute) Default(a *Attribute) *Attribute {
if attr == nil {
return a
}
return attr
}
// DefaultText return provided attribute value when nil.
func (attr *Attribute) DefaultText(text string) string {
if attr == nil {
return text
}
return attr.Text()
}
// Lines split an attribute value on linebreak.
func (attr *Attribute) Lines() []string {
if attr == nil || len(attr.rows) == 0 {
return nil
}
lis := make([]string, len(attr.rows))
for i, row := range attr.rows {
lis[i] = row.Value
}
return lis
}
// Fields value split on whitespace.
func (attr *Attribute) Fields() []string {
return strings.Fields(attr.Text())
}
// StringN pads attribute key-value with at least n spaces.
func (attr *Attribute) StringN(pad int) string {
if attr == nil {
return ""
}
if pad-len(attr.Name) < 0 {
pad -= pad - len(attr.Name)
}
if len(attr.rows) == 0 {
return attr.Name + ":" + strings.Repeat(" ", pad-len(attr.Name))
}
var lis []string
s := attr.Name + ":" + strings.Repeat(" ", pad-len(attr.Name)) + attr.rows[0].String()
lis = append(lis, s)
for _, row := range attr.rows[1:] {
v := row.String()
if v == "" {
lis = append(lis, "+")
continue
}
lis = append(lis, strings.Repeat(" ", pad+1)+v)
}
return strings.Join(lis, "\n")
}
// String formats attribute.
func (attr *Attribute) String() string {
return attr.StringN(PadLength)
}
// Args attribute value parsed with schema spec.
func (attr *Attribute) Args() *Arguments {
fields := attr.Fields()
args := NewArguments()
if attr == nil {
return args
}
spec := append(attr.spec, &SpecRuleText{})
i := 0
for _, s := range spec {
if i >= len(fields) {
break
}
i += s.ApplyArgument(args, fields[i:])
}
return args
}
func (attr *Attribute) MarshalJSON() ([]byte, error) {
lis := make([]interface{}, 2)
lis[0] = attr.Name
if attr.spec != nil {
lis[1] = attr.Args()
} else {
lis[1] = attr.Raw()
}
return json.Marshal(lis)
}
// ListAttribute for aggregate functions.
type ListAttribute []*Attribute
// Text of all attributes joined together.
func (lis ListAttribute) Text() string {
switch len(lis) {
case 0:
return ""
case 1:
return lis[0].Text()
default:
arr := make([]string, len(lis))
for i, a := range lis {
arr[i] = a.Text()
}
return strings.Join(arr, "\n")
}
}
// DefaultText to return when list is nil or empty.
func (lis ListAttribute) DefaultText(text string) string {
if lis == nil || len(lis) == 0 {
return text
}
return lis.Text()
}
// Lines split on whitespace.
func (lis ListAttribute) Lines() []string {
acc := 0
for i := range lis {
acc += len(lis[i].rows)
}
lines := make([]string, 0, acc)
for i := range lis {
lines = append(lines, lis[i].Lines()...)
}
return lines
}
// Fields split on whitespace.
func (lis ListAttribute) Fields() []string {
return strings.Fields(lis.Text())
}
// Value of an object attribute.
type Value struct {
Value string
Comment string
// Lineno location in source file.
Lineno int
}
// NewValue parsed from string with comments.
func NewValue(s string) Value {
sp := strings.SplitN(s, "#", 2)
v := Value{Value: strings.TrimSpace(sp[0])}
if len(sp) > 1 {
v.Comment = strings.TrimSpace(sp[1])
}
return v
}
func (v Value) String() string {
if len(v.Comment) > 0 {
if len(v.Value) == 0 {
return "# " + v.Comment
}
return v.Value + " # " + v.Comment
}
return v.Value
}
package rpsl
import (
"bufio"
"encoding/json"
"io"
"strings"
"unicode/utf8"
)
// Object structured version of RPSL documents
type Object struct {
object ObjectRO
}
func NewObject(o ObjectRO) *Object {
return &Object{o}
}
// ObjectRO is the base Object interface with readonly functions.
type ObjectRO interface {
Keys() []string
Key(string) ([]int, bool)
Attrs() ListAttribute
Attr(int) *Attribute
}
// ObjectRW extends the base Object interface with write functions.
type ObjectRW interface {
ObjectRO
SetN(string, int, ...string)
DeleteN(string, int)
}
// ObjectCLS extends the base Object interface with class schema information.
type ObjectCLS interface {
Schema() string
ObjectRO
ObjectPTR
}
type ObjectPTR interface {
Primary() string
Name() string
}
func FromPtr(o ObjectPTR) (ptr Ptr) {
ptr.name, ptr.primary = o.Name(), o.Primary()
return
}
type baseObject struct {
attributes ListAttribute
keys map[string][]int
}
var _ ObjectRO = (*baseObject)(nil)
var _ ObjectRW = (*baseObject)(nil)
// SetN writes to Nth attribute matching name. Values are parsed with comments.
// if index == -1 or index > len(GetAll(name)) the attribute is appended to end.
func (dom *baseObject) SetN(name string, index int, values ...string) {
rows := stringsToValues(values...)
if key, ok := dom.keys[name]; ok {
if index > -1 && len(key) > index {
dom.attributes[key[index]].rows = rows
return
} else {
dom.keys[name] = append(key, len(dom.attributes))
}
} else {
dom.keys[name] = []int{len(dom.attributes)}
}
dom.attributes = append(dom.attributes, &Attribute{Name: name, rows: rows})
}
// DeleteN the Nth attribute that matches name.
func (dom *baseObject) DeleteN(name string, index int) {
if k, ok := dom.keys[name]; ok {
if len(k) > index {
dom.attributes[k[index]] = nil
k = append(k[:index], k[index+1:]...)
dom.keys[name] = k
}
if len(k) == 0 {
delete(dom.keys, name)
}
}
}
// Keys returns a list of all key names.
func (dom *baseObject) Keys() []string {
lis := make([]string, len(dom.keys))
i := 0
for name := range dom.keys {
lis[i] = name
i++
}
return lis
}
// Key returns a list of attrs for a key name.
func (dom *baseObject) Key(name string) (lis []int, ok bool) {
var ids []int
if ids, ok = dom.keys[name]; ok {
lis = make([]int, len(ids))
copy(lis, ids)
}
return
}
// Attr returns the Nth attribute .
func (dom *baseObject) Attr(index int) (attr *Attribute) {
if index > len(dom.attributes)-1 {
return
}
if a := dom.attributes[index]; a != nil {
attr = &Attribute{Name: a.Name, rows: make([]Value, len(a.rows))}
copy(attr.rows, a.rows)
}
return
}
// Attrs returns all attributes.
func (dom *baseObject) Attrs() ListAttribute {
lis := make([]*Attribute, len(dom.attributes))
for i := range dom.attributes {
lis[i] = dom.Attr(i)
}
return lis
}
func stringsToValues(s ...string) []Value {
var rows []Value
switch len(s) {
case 0:
default:
rows = make([]Value, len(s))
for i, v := range s {
rows[i] = NewValue(v)
}
}
return rows
}
// Class wraps an object with schema information.
type classObject struct {
schema *Schema
ObjectRO
}
func newClass(o ObjectRO, s *Schema) ObjectRO {
return &classObject{s, o}
}
var _ ObjectRO = (*classObject)(nil)
var _ ObjectRW = (*classObject)(nil)
var _ ObjectCLS = (*classObject)(nil)
func (dom *classObject) SetN(name string, index int, values ...string) {
if rw, ok := dom.ObjectRO.(ObjectRW); ok {
rw.SetN(name, index, values...)
}
}
func (dom *classObject) DeleteN(name string, index int) {
if rw, ok := dom.ObjectRO.(ObjectRW); ok {
rw.DeleteN(name, index)
}
}
func (dom *classObject) Attr(index int) (attr *Attribute) {
if attr = dom.ObjectRO.Attr(index); attr != nil {
attr.spec = dom.schema.Spec(attr.Name)
}
return
}
func (dom *classObject) Attrs() ListAttribute {
lis := dom.ObjectRO.Attrs()
for _, attr := range lis {
if attr != nil {
attr.spec = dom.schema.Spec(attr.Name)
}
}
return lis
}
func (dom *classObject) Schema() string {
return dom.schema.Name
}
func (dom *classObject) Primary() string {
return dom.schema.Primary
}
func (dom *classObject) Name() string {
if lis, ok := dom.ObjectRO.Key(dom.Primary()); ok && len(lis) > 0 {
fields := dom.ObjectRO.Attr(lis[0]).Fields()
if len(fields) > 0 {
return fields[0]
}
}
return ""
}
// ParseObject parses an object from string and returns it.
func ParseObject(content string) *Object {
r := strings.NewReader(content)
p := NewParser(r)
p.Scan()
return &Object{p.Current()}
}
// Name returns the name of an object derived from its primary key.
func (dom *Object) Name() string {
fields := dom.Get(dom.Primary()).Fields()
if dom == nil || len(fields) == 0 {
return ""
}
return fields[0]
}
// Schema name for object. Use schema if applied.
func (dom *Object) Schema() string {
if dom == nil {
return ""
}
if s, ok := dom.object.(interface{ Schema() string }); ok {
return s.Schema()
}
if a := dom.object.Attr(0); a != nil {
return a.Name
}
return ""
}
// Primay returns the primary key. Using schema when applied.
func (dom *Object) Primary() string {
if dom == nil {
return ""
}
if s, ok := dom.object.(interface{ Primary() string }); ok {
return s.Primary()
}
return dom.Schema()
}
// String formats object for display or writing to file.
func (dom *Object) String() string {
padLen := PadLength
for _, key := range dom.object.Keys() {
if len(key) > padLen {
padLen = len(key) + 2
}
}
attrs := dom.object.Attrs()
lis := make([]string, 0, len(attrs))
for _, attr := range attrs {
if attr == nil {
continue
}
lis = append(lis, attr.StringN(padLen))
}
return strings.Join(lis, "\n")
}
// Get returns the first attribute matching name.
func (dom *Object) Get(name string) *Attribute {
return dom.GetN(name, 0)
}
// GetN retuns the Nth attribute matching name.
func (dom *Object) GetN(name string, index int) *Attribute {
if dom == nil || dom.object == nil {
return nil
}
if key, ok := dom.object.Key(name); ok {
if len(key) > index {
return dom.object.Attr(key[index])
}
}
return nil
}
// GetAll returns all attributes matching name.
func (dom *Object) GetAll(name string) ListAttribute {
if keys, ok := dom.object.Key(name); ok {
lis := make([]*Attribute, len(keys))
for i, key := range keys {
lis[i] = dom.object.Attr(key)
}
return lis
}
return nil
}
// Set writes to first attribute matching name. Values are parsed with comments.
func (dom *Object) Set(name string, values ...string) {
dom.SetN(name, 0, values...)
}
// Add appends attribute to end of object. Values are parsed with comments.
func (dom *Object) Add(name string, values ...string) {
dom.SetN(name, -1, values...)
}
// SetN writes to Nth attribute matching name. Values are parsed with comments.
// if index == -1 or index > len(GetAll(name)) the attribute is appended to end.
func (dom *Object) SetN(name string, index int, values ...string) {
if rw, ok := dom.object.(ObjectRW); ok {
rw.SetN(name, index, values...)
}
}
// Delete the first attribute that matches name.
func (dom *Object) Delete(name string) {
dom.DeleteN(name, 0)
}
// DeleteN the Nth attribute that matches name.
func (dom *Object) DeleteN(name string, index int) {
if rw, ok := dom.object.(ObjectRW); ok {
rw.DeleteN(name, index)
}
}
// DeleteAll the attributes that match name.
func (dom *Object) DeleteAll(name string) {
if k, ok := dom.object.Key(name); ok {
for range k {
dom.DeleteN(name, 0)
}
}
}
func (dom *Object) Attr(index int) *Attribute {
return dom.object.Attr(index)
}
func (dom *Object) Attrs() ListAttribute {
return dom.object.Attrs()
}
func (dom *Object) Key(name string) ([]int, bool) {
return dom.object.Key(name)
}
func (dom *Object) Keys() []string {
return dom.object.Keys()
}
func (dom *Object) SetObject(o ObjectRO) {
dom.object = o
}
func (dom *Object) GetObject() ObjectRO {
return dom.object
}
func (dom *Object) MarshalJSON() ([]byte, error) {
return json.Marshal(dom.object.Attrs())
}
// ListObject a collection of objects for performing aggragate operations.
type ListObject []*Object
func (lis ListObject) String() string {
if len(lis) == 0 {
return ""
}
s := lis[0].String()
if len(lis) == 1 {
return s
}
var buf strings.Builder
buf.WriteString(s)
for _, dom := range lis[1:] {
buf.WriteRune('\n')
buf.WriteRune('\n')
buf.WriteString(dom.String())
}
return buf.String()
}
// ParseAll objects from reader and return a list of objects.
func ParseAll(in io.Reader) ListObject {
p := NewParser(in)
var lis []*Object
for p.Scan() {
lis = append(lis, &Object{p.Current()})
}
return lis
}
// Parser for objects.
type Parser struct {
scanner *bufio.Scanner
current *baseObject
}
// NewParser reading from io Reader.
func NewParser(in io.Reader) *Parser {
return &Parser{scanner: bufio.NewScanner(in)}
}
// Scan parses a single object and stores it in Current.
func (p *Parser) Scan() bool {
var dom *baseObject
lineno := 0
found := false
for p.scanner.Scan() {
line := p.scanner.Text()
if lineno == 0 && line == "" {
continue
}
if lineno > 0 && line == "" {
break
}
if !found {
found = true
dom = &baseObject{}
dom.keys = make(map[string][]int)
}
lineno += 1
r, _ := utf8.DecodeRuneInString(line)
switch r {
case ' ', '\t', '+':
if len(dom.attributes) == 0 {
continue
}
last := len(dom.attributes) - 1
if r == '+' {
dom.attributes[last].rows = append(
dom.attributes[last].rows, Value{Lineno: lineno})
} else {
sp := strings.SplitN(line, "#", 2)
v := Value{
Lineno: lineno,
Value: strings.TrimSpace(sp[0]),
}
if len(sp) == 2 {
v.Comment = strings.TrimSpace(sp[1])
}
dom.attributes[last].rows = append(
dom.attributes[last].rows, v)
}
default:
sp := strings.SplitN(line, ":", 2)
if len(sp) < 2 {
continue
}
attr := &Attribute{Name: strings.TrimSpace(sp[0])}
sp = strings.SplitN(sp[1], "#", 2)
v := Value{
Lineno: lineno,
Value: strings.TrimSpace(sp[0]),
}
if len(sp) == 2 {
v.Comment = strings.TrimSpace(sp[1])
}
attr.rows = append(attr.rows, v)
if lis, ok := dom.keys[attr.Name]; ok {
dom.keys[attr.Name] = append(lis, len(dom.attributes))
} else {
dom.keys[attr.Name] = []int{len(dom.attributes)}
}
dom.attributes = append(dom.attributes, attr)
}
}
p.current = dom
return found
}
// Current returns last scanned and parsed object.
func (p *Parser) Current() *baseObject {
if p.current == nil {
return &baseObject{keys: map[string][]int{}}
}
return p.current
}
package rpsl
import (
"fmt"
"sort"
"strings"
)
// Schema for objects. Defines scructure and value types.
type Schema struct {
*Object
Name string
Primary string
Links map[string][]string
specTx map[string][]string
spec map[string]Spec
Rules map[string]*Set
}
// Spec for key name.
func (s *Schema) Spec(name string) Spec {
if spec, ok := s.spec[name]; ok {
lis := make([]SpecRule, len(spec))
copy(lis, spec)
return lis
}
if name == s.Primary {
return []SpecRule{&SpecRuleLabel{Type: name}}
}
return nil
}
func (s *Schema) String() string {
var buf strings.Builder
buf.WriteString("schema: ")
buf.WriteString(s.Name)
buf.WriteRune('\n')
buf.WriteString("primary: ")
buf.WriteString(s.Primary)
lis := make([]string, 0, len(s.Rules))
for key := range s.Rules {
lis = append(lis, key)
}
sort.Strings(lis)
for _, key := range lis {
buf.WriteRune('\n')
buf.WriteString(key)
buf.WriteRune(':')
buf.WriteRune(' ')
buf.WriteString(s.Rules[key].String())
}
return buf.String()
}
// Schemas parsed from objects for applying to objects.
type Schemas struct {
m map[string]*Schema
}
// ParseSchemas from list of objects.
func ParseSchemas(lis ListObject) (*Schemas, error) {
p := NewSchemaParser()
// Second Pass: Parse objects
schemas := &Schemas{m: make(map[string]*Schema)}
for _, dom := range lis {
if dom.Schema() != "schema" {
continue
}
schema := p.ParseSchema(dom)
schemas.m[schema.Name] = schema
}
var err error
for schemaName, schema := range schemas.m {
for name, rule := range schema.specTx {
schema.spec[name], err = p.ParseSpec(rule)
if err != nil {
return nil, fmt.Errorf("parsing schema %s key %s: %w", schemaName, name, err)
}
}
}
return schemas, err
}
// Apply schemas to list of objects.
func (s *Schemas) Apply(lis ...*Object) {
for _, dom := range lis {
o := dom.GetObject()
if _, ok := o.(ObjectCLS); ok {
// Object already has class schema applied.
continue
}
if schema, ok := s.m[dom.Schema()]; ok {
dom.SetObject(&classObject{schema, o})
}
}
}
// Items to iterate over list of schemas.
func (s *Schemas) Items() []*Schema {
lis := make([]*Schema, 0, len(s.m))
for _, schema := range s.m {
lis = append(lis, schema)
}
return lis
}
// Get schema by name.
func (s *Schemas) Get(name string) *Schema {
return s.m[name]
}
// SchemaParser parses schemas from objects.
type SchemaParser struct {
keys *Set
}
// NewSchemaParser creates new parser with list of optional primary keys.
// The primary keys will be derived automatically from parsed schemas.
func NewSchemaParser(keys ...string) *SchemaParser {
return &SchemaParser{keys: NewSet(keys...)}
}
// ParseSchema from object
func (p *SchemaParser) ParseSchema(dom *Object) *Schema {
schema := &Schema{}
schema.Links = make(map[string][]string)
schema.spec = make(map[string]Spec)
schema.specTx = make(map[string][]string)
schema.Rules = make(map[string]*Set)
first := true
for _, row := range dom.object.Attrs() {
switch row.Name {
case "key":
fields := row.Fields()
key := fields[0]
fields = fields[1:]
if first {
first = false
schema.Name = key
schema.Primary = key
}
schema.Rules[key] = NewSet()
for i, rule := range fields {
if rule == ">" {
schema.specTx[key] = fields[i+1:]
break
}
schema.Rules[key].Add(rule)
}
}
}
for key, rules := range schema.Rules {
if rules.Has("primary") {
schema.Primary = key
rules.Add("oneline", "single", "required")
rules.Delete("multiline", "optional", "recommend", "multiline")
}
if !rules.Has("oneline") {
rules.Add("multiline")
}
if !rules.Has("single") {
rules.Add("multiple")
}
}
p.keys.Add(schema.Primary)
return schema
}
func inStrSlice(n string, lis []string) (bool, int) {
for i, s := range lis {
if n == s {
return true, i
}
}
return false, len(lis)
}
// ParseSpec for schema key.
func (p *SchemaParser) ParseSpec(lis []string) (Spec, error) {
if ok, i := inStrSlice("|", lis); ok {
rule := SpecRulePipe{}
for ok || len(lis) > 0 {
s, err := p.ParseSpec(lis[:i])
if err != nil {
return nil, err
}
rule = append(rule, s)
if ok {
lis = lis[i+1:]
ok, i = inStrSlice("|", lis)
continue
}
break
}
return Spec{rule}, nil
}
spec := make([]SpecRule, len(lis))
for i, s := range lis {
r, err := p.parseSpecRule(s)
if err != nil {
return nil, err
}
spec[i] = r
}
return spec, nil
}
func (p *SchemaParser) parseSpecRule(o string) (SpecRule, error) {
switch {
case strings.HasPrefix(o, "lookup="):
o = strings.TrimPrefix(o, "lookup=")
choices := strings.Split(o, ",")
if !p.keys.Has(choices...) {
return nil, fmt.Errorf("choices %v arn't all known objects. known=[%v]", choices, p.keys)
}
return &SpecRuleLookup{Choices: choices}, nil
case o[0] == '{' && o[len(o)-1] == '}':
o = o[1 : len(o)-1]
rule := &SpecRuleEnum{}
if strings.ContainsRune(o, ':') {
sp := strings.SplitN(o, ":", 2)
rule.Name = sp[0]
o = sp[1]
}
rule.Choices = NewSet(strings.Split(o, "|")...)
if rule.Choices.Has("") {
rule.Optional = true
rule.Choices.Delete("")
}
return rule, nil
case o[0] == '[' && o[len(o)-1] == ']':
o = o[1 : len(o)-1]
rule := &SpecRuleLabel{Name: o}
if strings.ContainsRune(o, ',') {
return nil, fmt.Errorf("rule [%s] contains invalid rune ','", o)
}
if strings.ContainsRune(o, ':') {
sp := strings.SplitN(o, ":", 2)
rule.Name, rule.Type = sp[0], sp[1]
}
return rule, nil
case o[0] == '\'' && o[len(o)-1] == '\'':
o = o[1 : len(o)-1]
rule := SpecRuleConst(o)
return rule, nil
case o == "...":
return &SpecRuleText{}, nil
default:
return nil, fmt.Errorf("invalid rule: %s", o)
}
}
package rpsl
import (
"encoding/json"
"fmt"
"net"
"net/mail"
"reflect"
"sort"
"strconv"
"strings"
)
// Spec for schema key parsing into argument types.
type Spec []SpecRule
func (ls Spec) String() string {
lis := make([]string, len(ls))
for i, rule := range ls {
lis[i] = rule.String()
}
return strings.Join(lis, " ")
}
// SpecRule for parsing a givin argument.
type SpecRule interface {
// ApplyArgument to list of strings returning number of items consumed.
// Parsed arguments will be stored into the Argument container passed in.
ApplyArgument(*Arguments, []string) int
fmt.Stringer
}
// SpecRuleEnum parses argument where one of list of choices must match.
// The name is optional. if provided the matched string is assigned to name.
// If not each choice is added as a bool where matched item is true.
type SpecRuleEnum struct {
Name string
Optional bool
Choices *Set
}
var _ SpecRule = (*SpecRuleEnum)(nil)
func (rule *SpecRuleEnum) ApplyArgument(args *Arguments, input []string) int {
var s string
if len(input) > 0 {
s = input[0]
}
if !rule.Choices.Has(s) {
if rule.Optional {
if rule.Name == "" {
for _, name := range rule.Choices.Members() {
args.Set(name, NewBoolArg(false))
}
}
return 0
}
errArg := NewErrorf("option %s not found in choices [%s]", s, rule.Choices.StringSep("|"))
name := rule.Name
if name == "" {
name = s
for _, m := range rule.Choices.Members() {
args.Set(m, NewBoolArg(false))
}
}
args.Set(name, errArg)
return 0
}
if rule.Name != "" {
args.Set(rule.Name, NewStringArg(s))
return 1
}
for _, name := range rule.Choices.Members() {
args.Set(name, NewBoolArg(name == s))
}
return 1
}
func (rule *SpecRuleEnum) String() string {
if rule == nil {
return ""
}
inner := rule.Choices.StringSep("|")
if rule.Name != "" {
inner = rule.Name + ":" + inner
}
if rule.Optional {
inner += "|"
}
return "{" + inner + "}"
}
type SpecRuleLabel struct {
Name string
Type string
}
var _ SpecRule = (*SpecRuleLabel)(nil)
func (rule *SpecRuleLabel) ApplyArgument(args *Arguments, input []string) int {
if len(input) == 0 {
return 0
}
s := input[0]
typ := rule.Type
if typ == "" {
typ = rule.Name
}
switch typ {
case "int":
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
args.Set(rule.Name, WrapError(err))
return 1
}
args.Set(rule.Name, NewIntArg(i))
return 1
case "float":
fl, err := strconv.ParseFloat(s, 64)
if err != nil {
args.Set(rule.Name, WrapError(err))
return 1
}
args.Set(rule.Name, NewFloatArg(fl))
return 1
case "bool":
b, err := strconv.ParseBool(s)
if err != nil {
args.Set(rule.Name, WrapError(err))
return 1
}
args.Set(rule.Name, NewBoolArg(b))
return 1
case "email":
n := 1
if !strings.ContainsRune(s, '@') {
for i := 1; i < len(input); i++ {
if strings.ContainsRune(input[i], '@') || input[i][0] == '<' && input[i][len(input[i])] == '>' {
n = i
s = strings.Join(input[:i+1], " ")
break
}
}
}
a, err := mail.ParseAddress(s)
if err != nil {
args.Set(rule.Name, WrapError(err))
return n
}
args.Set(rule.Name, NewEmailArg(a.Name, a.Address))
return n
case "as-block":
n := 0
var err error
var one, two string
var min, max uint32
if strings.ContainsRune(s, '-') {
n = 1
sp := strings.SplitN(s, "-", 2)
one, two = sp[0], sp[1]
} else if len(input) >= 3 && input[1] == "-" {
n = 3
one, two = input[0], input[2]
} else {
args.Set(rule.Name, NewErrorf("range not detected: %v", input))
return 0
}
if min, err = as2int(one); err != nil {
args.Set(rule.Name, NewErrorf("min value: %w", err))
return 0
}
if max, err = as2int(two); err != nil {
args.Set(rule.Name, NewErrorf("max value: %w", err))
return 0
}
if min == max {
args.Set(rule.Name, NewErrorf("min == max: %s-%s", one, two))
return 0
}
if min > max {
args.Set(rule.Name, NewErrorf("min > max: %s-%s", one, two))
return 0
}
args.Set(rule.Name, NewASBlockArg(min, max))
return n
case "aut-num":
n := 0
var err error
var min uint32
if min, err = as2int(s); err != nil {
args.Set(rule.Name, NewErrorf("as value: %w", err))
return 0
}
args.Set(rule.Name, NewASBlockArg(min, min))
return n
case "cidr":
n := 0
if strings.ContainsRune(s, '-') || strings.ContainsRune(s, '/') {
n = 1
} else if len(input) >= 3 && input[1] == "-" {
n = 3
s = strings.Join(input[:3], "")
} else {
args.Set(rule.Name, NewErrorf("range not detected: %v", input))
return 0
}
r := NewInetRange(s)
args.Set(rule.Name, &inetBlockArg{r: *r})
return n
case "route":
n := 0
if strings.ContainsRune(s, '-') || strings.ContainsRune(s, '/') {
n = 1
} else if len(input) >= 3 && input[1] == "-" {
n = 3
s = strings.Join(input[:3], "")
} else {
args.Set(rule.Name, NewErrorf("range not detected: %v", input))
return 0
}
r := NewRouteRange(s)
args.Set(rule.Name, &inetBlockArg{r: *r})
return n
case "str":
fallthrough
default:
args.Set(rule.Name, NewStringArg(s))
return 1
}
}
func as2int(as string) (uint32, error) {
if !strings.HasPrefix(as, "AS") {
return 0, fmt.Errorf("missing prefix AS: %s", as)
}
if i, err := strconv.ParseUint(as[2:], 10, 32); err != nil {
return 0, err
} else {
return uint32(i), nil
}
}
func (rule *SpecRuleLabel) String() string {
if rule == nil {
return ""
}
inner := rule.Name
if len(rule.Type) > 0 {
inner += ":" + rule.Type
}
return "[" + inner + "]"
}
type SpecRuleLookup struct {
Choices []string
}
var _ SpecRule = (*SpecRuleLookup)(nil)
func (rule *SpecRuleLookup) ApplyArgument(args *Arguments, input []string) int {
choices := rule.Choices
for _, s := range input {
args.Set("lookup", NewLookupArg(s, choices...))
return 1
}
return 0
}
func (rule *SpecRuleLookup) String() string {
if rule == nil {
return ""
}
inner := "lookup"
if len(rule.Choices) > 0 {
inner += "=" + strings.Join(rule.Choices, ",")
}
return inner
}
type SpecRuleConst string
var _ SpecRule = SpecRuleConst("")
func (rule SpecRuleConst) ApplyArgument(args *Arguments, input []string) int {
for i, s := range input {
if s == string(rule) {
return i + 1
}
}
return len(input)
}
func (rule SpecRuleConst) String() string {
return "'" + string(rule) + "'"
}
type SpecRuleText struct{}
var _ SpecRule = (*SpecRuleText)(nil)
func (rule *SpecRuleText) ApplyArgument(args *Arguments, input []string) int {
args.Set("...", NewStringArg(strings.Join(input, " ")))
return len(input)
}
func (rule *SpecRuleText) String() string {
return "..."
}
type SpecRulePipe []Spec
var _ SpecRule = SpecRulePipe(nil)
func (rule SpecRulePipe) ApplyArgument(args *Arguments, input []string) int {
l := 0
exit:
for _, lis := range rule {
l = 0
targs := NewArguments()
for _, r := range lis {
l += r.ApplyArgument(targs, input)
if l == 0 {
continue exit
}
}
for _, a := range targs.Keys() {
if _, hasErr := targs.Get(a).(ErrArg); l == 0 || hasErr {
continue exit
}
}
args.ExtendArguments(targs)
break exit
}
return l
}
func (rule SpecRulePipe) String() string {
lis := make([]string, len(rule))
for i, r := range rule {
lis[i] = r.String()
}
return strings.Join(lis, " | ")
}
// Arguments are parsed values as defined by a schema spec.
type Arguments struct {
m map[string]Argument
}
// NewArguments creates empty argument container.
func NewArguments() *Arguments {
return &Arguments{m: make(map[string]Argument)}
}
func (a *Arguments) ExtendArguments(e *Arguments) {
if a == nil || e == nil {
return
}
for k, v := range e.m {
a.m[k] = v
}
}
// Has argument that matches name.
func (a *Arguments) Has(name string) bool {
_, ok := a.m[name]
return ok
}
// Get argument that matches name.
func (a *Arguments) Get(name string) Argument {
return a.m[name]
}
// Add argument to container with name.
func (a *Arguments) Add(name string, arg Argument) *Arguments {
a.m[name] = arg
return a
}
// Set argument with name.
func (a *Arguments) Set(name string, arg Argument) {
a.m[name] = arg
}
// Keys list of argument names.
func (a *Arguments) Keys() []string {
lis := make([]string, len(a.m))
i := 0
for name := range a.m {
lis[i] = name
i++
}
sort.Strings(lis)
return lis
}
func (a *Arguments) String() string {
lis := make([]string, len(a.m))
i := 0
for k, v := range a.m {
lis[i] = fmt.Sprintf("%s: %v", k, v)
i++
}
sort.Strings(lis)
return strings.Join(lis, " ")
}
func (a *Arguments) MarshalJSON() ([]byte, error) {
return json.Marshal(a.m)
}
// Argument for value defined by schema spec.
type Argument interface {
isArgument()
fmt.Stringer
}
type BoolArg interface {
Argument
Bool() bool
}
type boolArg bool
var _ Argument = boolArg(false)
func NewBoolArg(b bool) boolArg {
return boolArg(b)
}
func (a boolArg) Bool() bool {
return bool(a)
}
func (a boolArg) String() string {
if bool(a) {
return "true"
}
return "false"
}
func (a boolArg) isArgument() {}
type IntArg interface {
Argument
Int64() int64
}
type intArg int64
var _ Argument = intArg(0)
func NewIntArg(i int64) intArg {
return intArg(i)
}
func (a intArg) Int64() int64 {
return int64(a)
}
func (a intArg) isArgument() {}
func (a intArg) String() string {
return strconv.Itoa(int(a))
}
type FloatArg interface {
Argument
Float64() float64
}
type floatArg float64
func NewFloatArg(f float64) floatArg {
return floatArg(f)
}
var _ Argument = floatArg(0.0)
func (a floatArg) Float64() float64 {
return float64(a)
}
func (a floatArg) isArgument() {}
func (a floatArg) String() string {
return fmt.Sprint(float64(a))
}
// StringArg is a string value.
type StringArg interface {
Argument
}
type stringArg string
var _ Argument = stringArg("")
var _ fmt.Formatter = stringArg("")
func NewStringArg(s string) stringArg {
return stringArg(s)
}
// Format adds quotes around string when printed with %v
func (a stringArg) Format(f fmt.State, c rune) {
switch c {
case 'v':
fmt.Fprintf(f, `"%s"`, a.String())
default:
fmt.Fprint(f, a.String())
}
}
func (a stringArg) String() string {
return string(a)
}
func (a stringArg) isArgument() {}
// LookupArg indexes to an linked object.
type LookupArg interface {
Argument
Lookups() []Ptr
}
type lookupArg struct {
Value string
Choices []string
}
var _ Argument = (*lookupArg)(nil)
var _ LookupArg = (*lookupArg)(nil)
func NewLookupArg(value string, choices ...string) *lookupArg {
return &lookupArg{value, choices}
}
// Lookups returns list of possible matches to check index.
func (a *lookupArg) Lookups() []Ptr {
lis := make([]Ptr, len(a.Choices))
for i, c := range a.Choices {
lis[i] = Ptr{primary: c, name: a.Value}
}
return lis
}
func (a *lookupArg) String() string {
var b strings.Builder
for i, c := range a.Lookups() {
if i > 0 {
b.WriteRune('|')
}
b.WriteString(c.Primary())
b.WriteRune('/')
b.WriteString(c.Name())
}
return b.String()
}
func (a *lookupArg) isArgument() {}
// EmailArg parsed email address using RFC 5322
type EmailArg interface {
Name() string
Address() string
Argument
}
type emailArg struct {
m mail.Address
}
var _ Argument = (*emailArg)(nil)
func NewEmailArg(name, address string) *emailArg {
return &emailArg{m: mail.Address{Name: name, Address: address}}
}
func (a *emailArg) Name() string {
if a == nil {
return ""
}
return a.m.Name
}
func (a *emailArg) Address() string {
if a == nil {
return ""
}
return a.m.Address
}
func (a *emailArg) String() string {
if a == nil {
return ""
}
return a.m.String()
}
func (a *emailArg) isArgument() {}
type RangeArg interface {
Range() *Range
}
// ASBlockArg parsed range of aut-nums
type ASBlockArg interface {
MinAS() uint32
MaxAS() uint32
RangeArg
}
type asBlockArg struct {
min uint32
max uint32
}
var _ Argument = (*asBlockArg)(nil)
var _ ASBlockArg = (*asBlockArg)(nil)
func NewASBlockArg(min, max uint32) *asBlockArg {
return &asBlockArg{min, max}
}
func (a *asBlockArg) MinAS() uint32 {
if a == nil {
return 0
}
return a.min
}
func (a *asBlockArg) MaxAS() uint32 {
if a == nil {
return 0
}
return a.max
}
func (a *asBlockArg) Range() *Range {
return NewASRange(a.min, a.max)
}
func (a *asBlockArg) String() string {
if a == nil {
return ""
}
return fmt.Sprintf("AS%d - AS%d", a.min, a.max)
}
func (a *asBlockArg) isArgument() {}
// AutnumArg parsed range of aut-nums
type AutnumArg interface {
Autnum() int32
}
type autnumArg struct {
autnum int32
}
var _ Argument = (*autnumArg)(nil)
var _ AutnumArg = (*autnumArg)(nil)
func (a *autnumArg) Autnum() int32 {
if a == nil {
return 0
}
return a.autnum
}
func (a *autnumArg) String() string {
if a == nil {
return ""
}
return fmt.Sprintf("AS%d", a.autnum)
}
func (a *autnumArg) isArgument() {}
// InetBlockArg parsed range of aut-nums
type InetBlockArg interface {
MinIP() net.IP
MaxIP() net.IP
RangeArg
}
type inetBlockArg struct {
r Range
}
var _ Argument = (*inetBlockArg)(nil)
var _ InetBlockArg = (*inetBlockArg)(nil)
func (a *inetBlockArg) Range() *Range {
return &a.r
}
func (a *inetBlockArg) MinIP() net.IP {
return a.r.MinIP()
}
func (a *inetBlockArg) MaxIP() net.IP {
return a.r.MaxIP()
}
func (a *inetBlockArg) String() string {
if a == nil {
return ""
}
return fmt.Sprintf("%s - %s", a.r.ExpandedNetwork(), a.r.ExpandedBroadcast())
}
func (a *inetBlockArg) isArgument() {}
// ErrArg an error when parsing value. Contains value text and error from parsing.
type ErrArg interface {
Unwrap() error
Argument
}
type errArg struct {
error
}
var _ Argument = (*errArg)(nil)
var _ ErrArg = (*errArg)(nil)
var _ error = (*errArg)(nil)
func WrapError(e error) ErrArg {
return &errArg{error: e}
}
func NewErrorf(format string, args ...interface{}) ErrArg {
return &errArg{error: fmt.Errorf(format, args...)}
}
func (e *errArg) Unwrap() error {
if e == nil {
return nil
}
return e.error
}
func (e *errArg) String() string {
if e == nil || e.error == nil {
return "error"
}
return e.error.Error()
}
func (e *errArg) Error() string {
if e == nil || e.error == nil {
return "error"
}
return e.error.Error()
}
func (e *errArg) isArgument() {}
// Set is a collection of unique string values.
type SetArg interface {
Has(...string) bool
Add(...string)
Delete(...string)
Members() []string
Argument
}
func ArgAs(src Argument, target interface{}) bool {
if target == nil {
panic("ident: target cannot be nil")
}
val := reflect.ValueOf(target)
typ := val.Type()
if typ.Kind() != reflect.Ptr || val.IsNil() {
panic("ident: target must be non-nil pointer")
}
var identType = reflect.TypeOf((*Argument)(nil)).Elem()
if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(identType) {
panic("ident: *target must be interface or implement Ident")
}
targetType := typ.Elem()
if reflect.TypeOf(src).AssignableTo(targetType) {
val.Elem().Set(reflect.ValueOf(src))
return true
}
return false
}
package rpsl
import (
"errors"
"fmt"
"sort"
"strings"
)
// RPSL manages loading, indexing, and parsing rpsl objects.
type RPSL struct {
Schema map[string]*Schema
index Indexer
fetch Fetcher
}
// NewRPSL create a new RPSL
func NewRPSL(opts ...Option) *RPSL {
rpsl := &RPSL{}
for _, o := range opts {
o.Apply(rpsl)
}
if rpsl.Schema == nil {
rpsl.Schema = make(map[string]*Schema)
}
if rpsl.fetch == nil {
rpsl.fetch = &nullFS{}
}
if rpsl.index == nil {
rpsl.index = &nullFS{}
}
return rpsl
}
type Option interface {
Apply(*RPSL)
}
type Fetcher interface {
LoadObject(link ...ObjectPTR) ([]ObjectRO, bool)
}
type Indexer interface {
FindObject(search string) ([]ObjectPTR, bool)
}
var PadLength = 19
type Ptr struct {
primary string
name string
}
var _ ObjectPTR = (*Ptr)(nil)
func NewPtr(primary, name string) Ptr {
return Ptr{primary: primary, name: name}
}
func (o *Ptr) Primary() string { return o.primary }
func (o *Ptr) Name() string { return o.name }
type nullFS struct{}
func (*nullFS) LoadObject(link ...ObjectPTR) ([]ObjectRO, bool) {
return nil, false
}
func (*nullFS) FindObject(search string) ([]ObjectPTR, bool) {
return nil, false
}
var NotFound = errors.New("object not found")
var rootNetwork = NewNode(NewPtr("cidr", "::/0"), NewInetRange("::/0"))
type MemoryIndex struct {
root *Node
m map[Ptr]*Node
o map[Ptr]ObjectRO
p map[string][]Ptr
}
var _ Indexer = (*MemoryIndex)(nil)
func NewMemoryIndex() *MemoryIndex {
i := &MemoryIndex{}
i.m = make(map[Ptr]*Node)
i.o = make(map[Ptr]ObjectRO)
i.p = make(map[string][]Ptr)
i.m[rootNetwork.Ptr] = rootNetwork
i.root = rootNetwork
return i
}
func (idx *MemoryIndex) Add(o *Object) {
if o == nil {
return
}
ptr := FromPtr(o)
idx.o[ptr] = o.GetObject()
if lis, ok := idx.p[o.Name()]; ok {
idx.p[o.Name()] = append(lis, ptr)
} else {
idx.p[o.Name()] = append([]Ptr{}, ptr)
}
r := getRangeFromObject(o)
n := NewNode(ptr, r)
if r == nil {
return
}
if _, ok := idx.m[ptr]; !ok {
idx.m[ptr] = n
idx.root.Push(n)
}
}
func (idx *MemoryIndex) Remove(o *Object) {
if o == nil {
return
}
p := FromPtr(o)
delete(idx.o, p)
if lis, ok := idx.p[o.Name()]; ok {
for i, ptr := range lis {
if ptr == p {
lis = append(lis[:i], lis[i+1:]...)
idx.p[o.Name()] = lis
break
}
}
}
if rm, ok := idx.m[p]; ok {
if idx.root.Pop(rm) {
delete(idx.m, rm.Ptr)
}
}
}
func (idx *MemoryIndex) FindRange(r *Range) ([]ObjectPTR, bool) {
nodes, ok := idx.root.Trace(r)
lis := make([]ObjectPTR, len(nodes))
for i, n := range nodes {
p := NewPtr(n.primary, n.name)
lis[i] = &p
}
return lis, ok
}
func (idx *MemoryIndex) FindObject(search string) ([]ObjectPTR, bool) {
if r := NewRange(search); r != nil {
return idx.FindRange(r)
}
if lis, ok := idx.p[search]; ok {
r := make([]ObjectPTR, len(lis))
for i, ptr := range lis {
r[i] = &ptr
}
return r, ok
}
return nil, false
}
func (idx *MemoryIndex) LoadObject(link ...ObjectPTR) ([]ObjectRO, error) {
lis := make([]ObjectRO, 0, len(link))
for _, ptr := range link {
if o, ok := idx.o[FromPtr(ptr)]; ok {
lis = append(lis, o)
}
}
return nil, nil
}
func (idx *MemoryIndex) String() string {
var buf strings.Builder
buf.WriteString("Nodes(m):\n")
for k, v := range idx.m {
fmt.Fprintf(&buf, " %v: %v\n", k, v)
}
buf.WriteString("Find(p):\n")
for k, v := range idx.p {
fmt.Fprintf(&buf, " %v: %v\n", k, v)
}
buf.WriteString("Objs(o):\n")
for k, v := range idx.o {
fmt.Fprintf(&buf, " %v: %v\n", k, v.Attr(0).Raw())
}
return buf.String()
}
func getRangeFromObject(o *Object) *Range {
if o == nil {
return nil
}
if lis, ok := o.Key(o.Primary()); ok && len(lis) > 0 {
args := o.Attr(lis[0]).Args()
// fmt.Println("spec: ", o.Get(o.Primary()).spec)
// fmt.Println("args: ", o.Name(), args, o.Attr(lis[0]))
for _, name := range args.Keys() {
arg := args.Get(name)
// fmt.Printf("%s %T\n", o.Name(), arg)
if arg, ok := arg.(RangeArg); ok {
return arg.Range()
}
}
}
return nil
}
type Node struct {
Ptr
r *Range
branch NodeSet
leaf NodeSet
}
func NewNode(p Ptr, r *Range) *Node {
n := &Node{Ptr: p, r: r}
if r != nil {
n.branch = make(NodeSet)
n.leaf = make(NodeSet)
}
return n
}
// Push a node into the tree
func (n *Node) Push(node *Node) bool {
if node.r == nil {
return false
}
if node.r.leaf {
for _, sub := range n.branch {
if sub.Push(node) {
return true
}
}
if n.r.Equal(node.r) || n.r.Contains(node.r) {
n.leaf[node.Ptr] = node
return true
}
return false
}
if n.r.Equal(node.r) {
return false
}
if !n.r.Contains(node.r) {
return false
}
// Collect any leafs
for key, sub := range n.leaf {
if node.Push(sub) {
delete(n.leaf, key)
}
}
// Collect any branches
for key, sub := range n.branch {
if node.Push(sub) {
delete(n.branch, key)
}
}
// Check if it falls under another branch
for _, sub := range n.branch {
if sub.Push(node) {
return true
}
}
// else add to current branch.
n.branch[node.Ptr] = node
return true
}
// Pop a node out of the tree
func (n *Node) Pop(node *Node) bool {
if node.r == nil {
return false
}
if node.r.leaf {
if _, ok := n.leaf[node.Ptr]; ok {
delete(n.leaf, node.Ptr)
return true
}
for _, sub := range n.branch {
if (sub.r.Equal(node.r) || sub.r.Contains(node.r)) && sub.Pop(node) {
return true
}
}
return false
}
if _, ok := n.branch[node.Ptr]; ok {
// Transfer branch to current node.
for _, sub := range node.branch {
n.branch[sub.Ptr] = sub
}
// Transfer leafs to current node.
for _, sub := range node.leaf {
n.leaf[sub.Ptr] = sub
}
delete(n.branch, node.Ptr)
return true
}
for _, sub := range n.branch {
if sub.r.Contains(node.r) && sub.Pop(node) {
return true
}
}
return false
}
func (n *Node) Trace(r *Range) ([]*Node, bool) {
return n.trace(0, r)
}
func (n *Node) trace(i int, r *Range) ([]*Node, bool) {
for _, sub := range n.branch {
if sub.r.Contains(r) {
lis, ok := sub.trace(i+1, r)
lis[i] = n
return lis, ok
}
}
if n.r.Equal(r) {
return make([]*Node, i), true
}
if len(n.leaf) > 0 {
lis := make([]*Node, i, i+len(n.leaf))
found := false
for _, sub := range n.leaf {
eq := sub.r.Equal(r)
if eq {
found = eq
}
if sub.r.Contains(r) || eq {
lis = append(lis, sub)
}
}
return lis, found
}
return make([]*Node, i), n.r.Equal(r)
}
func (n *Node) String() string {
if n.r.leaf {
return fmt.Sprintf("%s", n.r)
}
return fmt.Sprintf("%s b[%v] l[%v]", n.r, n.branch, n.leaf)
}
func (n Ptr) String() string {
return fmt.Sprintf("%s/%s", n.primary, n.name)
}
type NodeSet map[Ptr]*Node
func (n NodeSet) String() string {
lis := make([]string, len(n))
i := 0
for p := range n {
lis[i] = p.String()
i++
}
sort.Strings(lis)
return strings.Join(lis, " ")
}
package rpsl
import (
"sort"
"strings"
)
type Set struct {
set map[string]struct{}
}
var _ Argument = (*Set)(nil)
var _ SetArg = (*Set)(nil)
// NewSet with provided members.
func NewSet(members ...string) *Set {
s := &Set{set: make(map[string]struct{})}
for _, member := range members {
s.Add(member)
}
return s
}
// Has all members matching names.
func (s *Set) Has(names ...string) bool {
found := false
for _, n := range names {
if _, found = s.set[n]; !found {
return false
}
}
return found
}
// Add member to set.
func (s *Set) Add(names ...string) {
for _, name := range names {
s.set[name] = struct{}{}
}
}
// Delete member from set.
func (s *Set) Delete(names ...string) {
for _, name := range names {
delete(s.set, name)
}
}
// Members in set.
func (s *Set) Members() []string {
lis := make([]string, len(s.set))
i := 0
for name := range s.set {
lis[i] = name
i++
}
sort.Strings(lis)
return lis
}
func (s *Set) String() string {
return s.StringSep(",")
}
func (s *Set) StringSep(sep string) string {
lis := s.Members()
return strings.Join(lis, sep)
}
func (s *Set) MarshalJSON() ([]byte, error) {
return []byte(`["` + s.StringSep(`", "`) + `"]`), nil
}
func (s *Set) isArgument() {}