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>
This commit is contained in:
parent
8a5ec72c85
commit
2548496cef
|
@ -10,7 +10,6 @@ import (
|
|||
"errors"
|
||||
"net/netip"
|
||||
|
||||
"go4.org/mem"
|
||||
"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] {
|
||||
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.
|
||||
var _StructWithSlicesViewNeedsRegeneration = StructWithSlices(struct {
|
||||
|
|
|
@ -67,7 +67,7 @@ func (v *{{.ViewName}}) UnmarshalJSON(b []byte) error {
|
|||
{{end}}
|
||||
{{define "valueField"}}func (v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} { return v.ж.{{.FieldName}} }
|
||||
{{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}}
|
||||
{{define "sliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.Slice[{{.FieldType}}] { return views.SliceOf(v.ж.{{.FieldName}}) }
|
||||
{{end}}
|
||||
|
@ -169,12 +169,12 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, thi
|
|||
case *types.Slice:
|
||||
slice := underlying
|
||||
elem := slice.Elem()
|
||||
args.FieldType = it.QualifiedName(elem)
|
||||
switch elem.String() {
|
||||
case "byte":
|
||||
it.Import("go4.org/mem")
|
||||
args.FieldType = it.QualifiedName(fieldType)
|
||||
writeTemplate("byteSliceField")
|
||||
default:
|
||||
args.FieldType = it.QualifiedName(elem)
|
||||
it.Import("tailscale.com/types/views")
|
||||
shallow, deep, base := requiresCloning(elem)
|
||||
if deep {
|
||||
|
|
|
@ -73,12 +73,11 @@ func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
|
|||
// Not subject to tailnet lock.
|
||||
continue
|
||||
}
|
||||
keySig := tkatype.MarshaledSignature(p.KeySignature().StringCopy()) // TODO(bradfitz,maisem): this is unfortunate. Change tkatype.MarshaledSignature to a string for viewer?
|
||||
if len(keySig) == 0 {
|
||||
if p.KeySignature().Len() == 0 {
|
||||
b.logf("Network lock is dropping peer %v(%v) due to missing signature", p.ID(), p.StableID())
|
||||
mak.Set(&toDelete, i, true)
|
||||
} 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)
|
||||
mak.Set(&toDelete, i, true)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"net/netip"
|
||||
"time"
|
||||
|
||||
"go4.org/mem"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/opt"
|
||||
|
@ -129,14 +128,16 @@ func (v *NodeView) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v NodeView) ID() NodeID { return v.ж.ID }
|
||||
func (v NodeView) StableID() StableNodeID { return v.ж.StableID }
|
||||
func (v NodeView) Name() string { return v.ж.Name }
|
||||
func (v NodeView) User() UserID { return v.ж.User }
|
||||
func (v NodeView) Sharer() UserID { return v.ж.Sharer }
|
||||
func (v NodeView) Key() key.NodePublic { return v.ж.Key }
|
||||
func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry }
|
||||
func (v NodeView) KeySignature() mem.RO { return mem.B(v.ж.KeySignature) }
|
||||
func (v NodeView) ID() NodeID { return v.ж.ID }
|
||||
func (v NodeView) StableID() StableNodeID { return v.ж.StableID }
|
||||
func (v NodeView) Name() string { return v.ж.Name }
|
||||
func (v NodeView) User() UserID { return v.ж.User }
|
||||
func (v NodeView) Sharer() UserID { return v.ж.Sharer }
|
||||
func (v NodeView) Key() key.NodePublic { return v.ж.Key }
|
||||
func (v NodeView) KeyExpiry() time.Time { return v.ж.KeyExpiry }
|
||||
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) DiscoKey() key.DiscoPublic { return v.ж.DiscoKey }
|
||||
func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) }
|
||||
|
@ -610,13 +611,15 @@ func (v *RegisterResponseView) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v RegisterResponseView) User() UserView { return v.ж.User.View() }
|
||||
func (v RegisterResponseView) Login() Login { return v.ж.Login }
|
||||
func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired }
|
||||
func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
||||
func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL }
|
||||
func (v RegisterResponseView) NodeKeySignature() mem.RO { return mem.B(v.ж.NodeKeySignature) }
|
||||
func (v RegisterResponseView) Error() string { return v.ж.Error }
|
||||
func (v RegisterResponseView) User() UserView { return v.ж.User.View() }
|
||||
func (v RegisterResponseView) Login() Login { return v.ж.Login }
|
||||
func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired }
|
||||
func (v RegisterResponseView) MachineAuthorized() bool { return v.ж.MachineAuthorized }
|
||||
func (v RegisterResponseView) AuthURL() string { return v.ж.AuthURL }
|
||||
func (v RegisterResponseView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
|
||||
return views.ByteSliceOf(v.ж.NodeKeySignature)
|
||||
}
|
||||
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.
|
||||
var _RegisterResponseViewNeedsRegeneration = RegisterResponse(struct {
|
||||
|
@ -749,8 +752,10 @@ func (v RegisterRequestView) Expiry() time.Time { return v.ж.Expir
|
|||
func (v RegisterRequestView) Followup() string { return v.ж.Followup }
|
||||
func (v RegisterRequestView) Hostinfo() HostinfoView { return v.ж.Hostinfo.View() }
|
||||
func (v RegisterRequestView) Ephemeral() bool { return v.ж.Ephemeral }
|
||||
func (v RegisterRequestView) NodeKeySignature() mem.RO { return mem.B(v.ж.NodeKeySignature) }
|
||||
func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType }
|
||||
func (v RegisterRequestView) NodeKeySignature() views.ByteSlice[tkatype.MarshaledSignature] {
|
||||
return views.ByteSliceOf(v.ж.NodeKeySignature)
|
||||
}
|
||||
func (v RegisterRequestView) SignatureType() SignatureType { return v.ж.SignatureType }
|
||||
func (v RegisterRequestView) Timestamp() *time.Time {
|
||||
if v.ж.Timestamp == nil {
|
||||
return nil
|
||||
|
@ -759,8 +764,12 @@ func (v RegisterRequestView) Timestamp() *time.Time {
|
|||
return &x
|
||||
}
|
||||
|
||||
func (v RegisterRequestView) DeviceCert() mem.RO { return mem.B(v.ж.DeviceCert) }
|
||||
func (v RegisterRequestView) Signature() mem.RO { return mem.B(v.ж.Signature) }
|
||||
func (v RegisterRequestView) DeviceCert() views.ByteSlice[[]byte] {
|
||||
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.
|
||||
var _RegisterRequestViewNeedsRegeneration = RegisterRequest(struct {
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"maps"
|
||||
|
||||
"go4.org/mem"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// typically generated by tailscale.com/cmd/viewer.
|
||||
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}
|
||||
}
|
||||
|
||||
// SliceView is a read-only wrapper around a struct which should only be exposed
|
||||
// as a View.
|
||||
// SliceView wraps []T to provide accessors which return an immutable view V of
|
||||
// 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 {
|
||||
// ж is the underlying mutable value, named with a hard-to-type
|
||||
// character that looks pointy like a pointer.
|
||||
|
|
Loading…
Reference in New Issue