ident: isolate non-system component of member

This enables more efficient representation in contexts inherently namespaced to the current system.

Signed-off-by: Yonah <contrib@gensokyo.uk>
This commit is contained in:
2026-03-22 17:28:52 +09:00
parent 87f59d0cf9
commit 22b8cc3884
3 changed files with 108 additions and 30 deletions

View File

@@ -117,15 +117,17 @@ func TestFM(t *testing.T) {
{"valid", ident.F[ident.M, *ident.M]{ {"valid", ident.F[ident.M, *ident.M]{
I: &ident.M{ I: &ident.M{
Serial: 0xfdfdfdfdfdfdfdfd, PartM: ident.PartM{
Serial: 0xfdfdfdfdfdfdfdfd,
Time: uint64(time.Date( Time: uint64(time.Date(
0xfd, 7, 15, 0xfd, 7, 15,
23, 59, 59, 0xcab, 23, 59, 59, 0xcab,
time.UTC, time.UTC,
).UnixNano()), ).UnixNano()),
ID: 0x2e736e64, ID: 0x2e736e64,
},
System: ident.S{ System: ident.S{
Site: ident.TrivialSite, Site: ident.TrivialSite,

View File

@@ -6,8 +6,8 @@ import (
"unsafe" "unsafe"
) )
// M represents a unique member identifier. // PartM represents the first half of [M].
type M struct { type PartM struct {
// A per-system value incremented by some unspecified amount every time the // A per-system value incremented by some unspecified amount every time the
// metadata of a member first appears to the backend. // metadata of a member first appears to the backend.
Serial uint64 Serial uint64
@@ -18,7 +18,11 @@ type M struct {
// Randomly generated value. The implementation must guarantee that the same // Randomly generated value. The implementation must guarantee that the same
// value cannot be emitted for a Time value. // value cannot be emitted for a Time value.
ID uint64 ID uint64
}
// M represents a unique member identifier.
type M struct {
PartM
// Underlying system. // Underlying system.
System S System S
} }
@@ -35,19 +39,35 @@ const (
// EncodedSize returns the number of bytes appended by Encode. // EncodedSize returns the number of bytes appended by Encode.
func (*M) EncodedSize() int { return EncodedSizeMember } func (*M) EncodedSize() int { return EncodedSizeMember }
// Encode appends the canonical string representation of mid to dst and returns // Encode appends the canonical string representation of pm to dst and returns
// the extended buffer. // the extended buffer.
func (mid *M) Encode(dst []byte) []byte { func (pm *PartM) Encode(dst []byte) []byte {
var buf [SizeMember - SizeSystem]byte var buf [SizeMember - SizeSystem]byte
p := buf[:] p := buf[:]
binary.LittleEndian.PutUint64(p, mid.Serial) binary.LittleEndian.PutUint64(p, pm.Serial)
p = p[8:] p = p[8:]
binary.LittleEndian.PutUint64(p, mid.Time) binary.LittleEndian.PutUint64(p, pm.Time)
p = p[8:] p = p[8:]
binary.LittleEndian.PutUint64(p, mid.ID) binary.LittleEndian.PutUint64(p, pm.ID)
dst = base64.URLEncoding.AppendEncode(dst, buf[:]) return base64.URLEncoding.AppendEncode(dst, buf[:])
}
// String returns the canonical string representation of pm.
func (pm *PartM) String() string {
s := pm.Encode(nil)
return unsafe.String(unsafe.SliceData(s), len(s))
}
// MarshalText returns the result of Encode.
func (pm *PartM) MarshalText() (data []byte, err error) {
return pm.Encode(nil), nil
}
// Encode appends the canonical string representation of mid to dst and returns
// the extended buffer.
func (mid *M) Encode(dst []byte) []byte {
dst = mid.PartM.Encode(dst)
return mid.System.Encode(dst) return mid.System.Encode(dst)
} }
@@ -62,16 +82,16 @@ func (mid *M) MarshalText() (data []byte, err error) {
return mid.Encode(nil), nil return mid.Encode(nil), nil
} }
// UnmarshalText strictly decodes data into mid. // UnmarshalText strictly decodes data into pm.
func (mid *M) UnmarshalText(data []byte) error { func (pm *PartM) UnmarshalText(data []byte) error {
if len(data) != EncodedSizeMember { if len(data) != EncodedSizeMember-EncodedSizeSystem {
return &UnexpectedSizeError{data, EncodedSizeMember} return &UnexpectedSizeError{data, EncodedSizeMember - EncodedSizeSystem}
} }
var buf [SizeMember - SizeSystem]byte var buf [SizeMember - SizeSystem]byte
if n, err := base64.URLEncoding.Decode( if n, err := base64.URLEncoding.Decode(
buf[:], buf[:],
data[:EncodedSizeMember-EncodedSizeSystem], data,
); err != nil { ); err != nil {
return err return err
} else if n != SizeMember-SizeSystem { } else if n != SizeMember-SizeSystem {
@@ -79,11 +99,23 @@ func (mid *M) UnmarshalText(data []byte) error {
} }
p := buf[:] p := buf[:]
mid.Serial = binary.LittleEndian.Uint64(p) pm.Serial = binary.LittleEndian.Uint64(p)
p = p[8:] p = p[8:]
mid.Time = binary.LittleEndian.Uint64(p) pm.Time = binary.LittleEndian.Uint64(p)
p = p[8:] p = p[8:]
mid.ID = binary.LittleEndian.Uint64(p) pm.ID = binary.LittleEndian.Uint64(p)
return nil
}
// UnmarshalText strictly decodes data into mid.
func (mid *M) UnmarshalText(data []byte) error {
if len(data) != EncodedSizeMember {
return &UnexpectedSizeError{data, EncodedSizeMember}
}
if err := mid.PartM.UnmarshalText(
data[:EncodedSizeMember-EncodedSizeSystem],
); err != nil {
return err
}
return mid.System.UnmarshalText(data[EncodedSizeMember-EncodedSizeSystem:]) return mid.System.UnmarshalText(data[EncodedSizeMember-EncodedSizeSystem:])
} }

View File

@@ -9,6 +9,48 @@ import (
"git.gensokyo.uk/cofront/cof-spec/ident" "git.gensokyo.uk/cofront/cof-spec/ident"
) )
func TestPartM(t *testing.T) {
t.Parallel()
rTestCases[ident.PartM, *ident.PartM]{
{"short", ident.PartM{}, nil, &ident.UnexpectedSizeError{
Data: nil,
Want: ident.EncodedSizeMember - ident.EncodedSizeSystem,
}},
{"malformed", ident.PartM{}, []byte{
0xfe, 0xe1, 0xde, 0xad,
0xfe, 0xe1, 0xde, 0xad,
0xfe, 0xe1, 0xde, 0xad,
0xfe, 0xe1, 0xde, 0xad,
0xfe, 0xe1, 0xde, 0xad,
0xfe, 0xe1, 0xde, 0xad,
0xfe, 0xe1, 0xde, 0xad,
0xfe, 0xe1, 0xde, 0xad,
}, base64.CorruptInputError(0)},
{"newline", ident.PartM{}, []byte(
"AAAA" + strings.Repeat("\n", ident.EncodedSizeMember-ident.EncodedSizeSystem-4),
), ident.ErrNewline},
{"valid", ident.PartM{
Serial: 0xfdfdfdfdfdfdfdfd,
Time: uint64(time.Date(
0xfd, 7, 15,
23, 59, 59, 0xcab,
time.UTC,
).UnixNano()),
ID: 0x2e736e64,
}, base64.URLEncoding.AppendEncode(nil, []byte{
/* serial: */ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
/* time: */ 0xab, 0x42, 0x42, 0xce, 0xf1, 0x92, 0x4a, 0x10,
/* id: */ 0x64, 0x6e, 0x73, 0x2e, 0, 0, 0, 0,
}), nil},
}.run(t)
}
func TestM(t *testing.T) { func TestM(t *testing.T) {
t.Parallel() t.Parallel()
@@ -43,15 +85,17 @@ func TestM(t *testing.T) {
), ident.ErrNewline}, ), ident.ErrNewline},
{"valid", ident.M{ {"valid", ident.M{
Serial: 0xfdfdfdfdfdfdfdfd, PartM: ident.PartM{
Serial: 0xfdfdfdfdfdfdfdfd,
Time: uint64(time.Date( Time: uint64(time.Date(
0xfd, 7, 15, 0xfd, 7, 15,
23, 59, 59, 0xcab, 23, 59, 59, 0xcab,
time.UTC, time.UTC,
).UnixNano()), ).UnixNano()),
ID: 0x2e736e64, ID: 0x2e736e64,
},
System: ident.S{ System: ident.S{
Site: ident.TrivialSite, Site: ident.TrivialSite,