types/views,cmd/viewer: add ByteSlice[T] to replace mem.RO

Add a new views.ByteSlice[T ~[]byte] to provide a better API to use
with views.

Updates #cleanup

Signed-off-by: Maisem Ali <maisem@tailscale.com>
pull/8977/head
Maisem Ali 1 year ago committed by Maisem Ali
parent 8a5ec72c85
commit 2548496cef

@ -10,7 +10,6 @@ import (
"errors" "errors"
"net/netip" "net/netip"
"go4.org/mem"
"tailscale.com/types/views" "tailscale.com/types/views"
) )
@ -312,7 +311,7 @@ func (v StructWithSlicesView) Slice() views.Slice[string] { return views.SliceOf
func (v StructWithSlicesView) Prefixes() views.Slice[netip.Prefix] { func (v StructWithSlicesView) Prefixes() views.Slice[netip.Prefix] {
return views.SliceOf(v.ж.Prefixes) return views.SliceOf(v.ж.Prefixes)
} }
func (v StructWithSlicesView) Data() mem.RO { return mem.B(v.ж.Data) } func (v StructWithSlicesView) Data() views.ByteSlice[[]byte] { return views.ByteSliceOf(v.ж.Data) }
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _StructWithSlicesViewNeedsRegeneration = StructWithSlices(struct { var _StructWithSlicesViewNeedsRegeneration = StructWithSlices(struct {

@ -67,7 +67,7 @@ func (v *{{.ViewName}}) UnmarshalJSON(b []byte) error {
{{end}} {{end}}
{{define "valueField"}}func (v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} { return v.ж.{{.FieldName}} } {{define "valueField"}}func (v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} { return v.ж.{{.FieldName}} }
{{end}} {{end}}
{{define "byteSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() mem.RO { return mem.B(v.ж.{{.FieldName}}) } {{define "byteSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.ByteSlice[{{.FieldType}}] { return views.ByteSliceOf(v.ж.{{.FieldName}}) }
{{end}} {{end}}
{{define "sliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.Slice[{{.FieldType}}] { return views.SliceOf(v.ж.{{.FieldName}}) } {{define "sliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.Slice[{{.FieldType}}] { return views.SliceOf(v.ж.{{.FieldName}}) }
{{end}} {{end}}
@ -169,12 +169,12 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, thi
case *types.Slice: case *types.Slice:
slice := underlying slice := underlying
elem := slice.Elem() elem := slice.Elem()
args.FieldType = it.QualifiedName(elem)
switch elem.String() { switch elem.String() {
case "byte": case "byte":
it.Import("go4.org/mem") args.FieldType = it.QualifiedName(fieldType)
writeTemplate("byteSliceField") writeTemplate("byteSliceField")
default: default:
args.FieldType = it.QualifiedName(elem)
it.Import("tailscale.com/types/views") it.Import("tailscale.com/types/views")
shallow, deep, base := requiresCloning(elem) shallow, deep, base := requiresCloning(elem)
if deep { if deep {

@ -73,12 +73,11 @@ func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
// Not subject to tailnet lock. // Not subject to tailnet lock.
continue continue
} }
keySig := tkatype.MarshaledSignature(p.KeySignature().StringCopy()) // TODO(bradfitz,maisem): this is unfortunate. Change tkatype.MarshaledSignature to a string for viewer? if p.KeySignature().Len() == 0 {
if len(keySig) == 0 {
b.logf("Network lock is dropping peer %v(%v) due to missing signature", p.ID(), p.StableID()) b.logf("Network lock is dropping peer %v(%v) due to missing signature", p.ID(), p.StableID())
mak.Set(&toDelete, i, true) mak.Set(&toDelete, i, true)
} else { } else {
if err := b.tka.authority.NodeKeyAuthorized(p.Key(), keySig); err != nil { if err := b.tka.authority.NodeKeyAuthorized(p.Key(), p.KeySignature().AsSlice()); err != nil {
b.logf("Network lock is dropping peer %v(%v) due to failed signature check: %v", p.ID(), p.StableID(), err) b.logf("Network lock is dropping peer %v(%v) due to failed signature check: %v", p.ID(), p.StableID(), err)
mak.Set(&toDelete, i, true) mak.Set(&toDelete, i, true)
} }

@ -11,7 +11,6 @@ import (
"net/netip" "net/netip"
"time" "time"
"go4.org/mem"
"tailscale.com/types/dnstype" "tailscale.com/types/dnstype"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/opt" "tailscale.com/types/opt"
@ -136,7 +135,9 @@ func (v NodeView) User() UserID { return v.ж.User }
func (v NodeView) Sharer() UserID { return v.ж.Sharer } func (v NodeView) Sharer() UserID { return v.ж.Sharer }
func (v NodeView) Key() key.NodePublic { return v.ж.Key } func (v NodeView) Key() key.NodePublic { return v.ж.Key }
func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry } func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry }
func (v NodeView) KeySignature() mem.RO { return mem.B(v.ж.KeySignature) } func (v NodeView) KeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
return views.ByteSliceOf(v.ж.KeySignature)
}
func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine } func (v NodeView) Machine() key.MachinePublic { return v.ж.Machine }
func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey } func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey }
func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) } func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) }
@ -615,7 +616,9 @@ func (v RegisterResponseView) Login() Login { return v.ж.Login }
func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired } func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired }
func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized } func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL } func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL }
func (v RegisterResponseView) NodeKeySignature() mem.RO { return mem.B(v.ж.NodeKeySignature) } func (v RegisterResponseView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
return views.ByteSliceOf(v.ж.NodeKeySignature)
}
func (v RegisterResponseView) Error() string { return v.ж.Error } func (v RegisterResponseView) Error() string { return v.ж.Error }
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
@ -749,7 +752,9 @@ func (v RegisterRequestView) Expiry() time.Time { return v.ж.Expir
func (v RegisterRequestView) Followup() string { return v.ж.Followup } func (v RegisterRequestView) Followup() string { return v.ж.Followup }
func (v RegisterRequestView) Hostinfo() HostinfoView { return v.ж.Hostinfo.View() } func (v RegisterRequestView) Hostinfo() HostinfoView { return v.ж.Hostinfo.View() }
func (v RegisterRequestView) Ephemeral() bool { return v.ж.Ephemeral } func (v RegisterRequestView) Ephemeral() bool { return v.ж.Ephemeral }
func (v RegisterRequestView) NodeKeySignature() mem.RO { return mem.B(v.ж.NodeKeySignature) } func (v RegisterRequestView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
return views.ByteSliceOf(v.ж.NodeKeySignature)
}
func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType } func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType }
func (v RegisterRequestView) Timestamp() *time.Time { func (v RegisterRequestView) Timestamp() *time.Time {
if v.ж.Timestamp == nil { if v.ж.Timestamp == nil {
@ -759,8 +764,12 @@ func (v RegisterRequestView) Timestamp() *time.Time {
return &x return &x
} }
func (v RegisterRequestView) DeviceCert() mem.RO { return mem.B(v.ж.DeviceCert) } func (v RegisterRequestView) DeviceCert() views.ByteSlice[[]byte] {
func (v RegisterRequestView) Signature() mem.RO { return mem.B(v.ж.Signature) } return views.ByteSliceOf(v.ж.DeviceCert)
}
func (v RegisterRequestView) Signature() views.ByteSlice[[]byte] {
return views.ByteSliceOf(v.ж.Signature)
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file. // A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _RegisterRequestViewNeedsRegeneration = RegisterRequest(struct { var _RegisterRequestViewNeedsRegeneration = RegisterRequest(struct {

@ -6,9 +6,12 @@
package views package views
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"maps" "maps"
"go4.org/mem"
) )
func unmarshalSliceFromJSON[T any](b []byte, x *[]T) error { func unmarshalSliceFromJSON[T any](b []byte, x *[]T) error {
@ -21,6 +24,83 @@ func unmarshalSliceFromJSON[T any](b []byte, x *[]T) error {
return json.Unmarshal(b, x) return json.Unmarshal(b, x)
} }
// ByteSlice is a read-only accessor for types that are backed by a []byte.
type ByteSlice[T ~[]byte] struct {
// ж is the underlying mutable value, named with a hard-to-type
// character that looks pointy like a pointer.
// It is named distinctively to make you think of how dangerous it is to escape
// to callers. You must not let callers be able to mutate it.
ж T
}
// ByteSliceOf returns a ByteSlice for the provided slice.
func ByteSliceOf[T ~[]byte](x T) ByteSlice[T] {
return ByteSlice[T]{x}
}
// Len returns the length of the slice.
func (v ByteSlice[T]) Len() int {
return len(v.ж)
}
// IsNil reports whether the underlying slice is nil.
func (v ByteSlice[T]) IsNil() bool {
return v.ж == nil
}
// Mem returns a read-only view of the underlying slice.
func (v ByteSlice[T]) Mem() mem.RO {
return mem.B(v.ж)
}
// Equal reports whether the underlying slice is equal to b.
func (v ByteSlice[T]) Equal(b T) bool {
return bytes.Equal(v.ж, b)
}
// EqualView reports whether the underlying slice is equal to b.
func (v ByteSlice[T]) EqualView(b ByteSlice[T]) bool {
return bytes.Equal(v.ж, b.ж)
}
// AsSlice returns a copy of the underlying slice.
func (v ByteSlice[T]) AsSlice() T {
return v.AppendTo(v.ж[:0:0])
}
// AppendTo appends the underlying slice values to dst.
func (v ByteSlice[T]) AppendTo(dst T) T {
return append(dst, v.ж...)
}
// LenIter returns a slice the same length as the v.Len().
// The caller can then range over it to get the valid indexes.
// It does not allocate.
func (v ByteSlice[T]) LenIter() []struct{} { return make([]struct{}, len(v.ж)) }
// At returns the byte at index `i` of the slice.
func (v ByteSlice[T]) At(i int) byte { return v.ж[i] }
// SliceFrom returns v[i:].
func (v ByteSlice[T]) SliceFrom(i int) ByteSlice[T] { return ByteSlice[T]{v.ж[i:]} }
// SliceTo returns v[:i]
func (v ByteSlice[T]) SliceTo(i int) ByteSlice[T] { return ByteSlice[T]{v.ж[:i]} }
// Slice returns v[i:j]
func (v ByteSlice[T]) Slice(i, j int) ByteSlice[T] { return ByteSlice[T]{v.ж[i:j]} }
// MarshalJSON implements json.Marshaler.
func (v ByteSlice[T]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
// UnmarshalJSON implements json.Unmarshaler.
func (v *ByteSlice[T]) UnmarshalJSON(b []byte) error {
if v.ж != nil {
return errors.New("already initialized")
}
return json.Unmarshal(b, &v.ж)
}
// StructView represents the corresponding StructView of a Viewable. The concrete types are // StructView represents the corresponding StructView of a Viewable. The concrete types are
// typically generated by tailscale.com/cmd/viewer. // typically generated by tailscale.com/cmd/viewer.
type StructView[T any] interface { type StructView[T any] interface {
@ -47,8 +127,9 @@ func SliceOfViews[T ViewCloner[T, V], V StructView[T]](x []T) SliceView[T, V] {
return SliceView[T, V]{x} return SliceView[T, V]{x}
} }
// SliceView is a read-only wrapper around a struct which should only be exposed // SliceView wraps []T to provide accessors which return an immutable view V of
// as a View. // T. It is used to provide the equivalent of SliceOf([]V) without having to
// allocate []V from []T.
type SliceView[T ViewCloner[T, V], V StructView[T]] struct { type SliceView[T ViewCloner[T, V], V StructView[T]] struct {
// ж is the underlying mutable value, named with a hard-to-type // ж is the underlying mutable value, named with a hard-to-type
// character that looks pointy like a pointer. // character that looks pointy like a pointer.

Loading…
Cancel
Save