add timestamp
This commit is contained in:
@@ -5,13 +5,15 @@ import (
|
|||||||
"protocol.realy.lol/pkg/event/types"
|
"protocol.realy.lol/pkg/event/types"
|
||||||
"protocol.realy.lol/pkg/pubkey"
|
"protocol.realy.lol/pkg/pubkey"
|
||||||
"protocol.realy.lol/pkg/signature"
|
"protocol.realy.lol/pkg/signature"
|
||||||
|
"protocol.realy.lol/pkg/tags"
|
||||||
|
"protocol.realy.lol/pkg/timestamp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Type *types.T
|
Type *types.T
|
||||||
Pubkey *pubkey.P
|
Pubkey *pubkey.P
|
||||||
Timestamp int64
|
Timestamp *timestamp.T
|
||||||
Tags [][]byte
|
Tags *tags.T
|
||||||
Content *content.C
|
Content *content.C
|
||||||
Signature *signature.S
|
Signature *signature.S
|
||||||
}
|
}
|
||||||
|
|||||||
1
pkg/timestamp/base10k.txt
Normal file
1
pkg/timestamp/base10k.txt
Normal file
File diff suppressed because one or more lines are too long
9
pkg/timestamp/gen/log.go
Normal file
9
pkg/timestamp/gen/log.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"protocol.realy.lol/pkg/lol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
|
||||||
|
)
|
||||||
17
pkg/timestamp/gen/pregen.go
Normal file
17
pkg/timestamp/gen/pregen.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Create("base10k.txt"); chk.E(err) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i := range 10000 {
|
||||||
|
fmt.Fprintf(fh, "%04d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
pkg/timestamp/log.go
Normal file
9
pkg/timestamp/log.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package timestamp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"protocol.realy.lol/pkg/lol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
log, chk, errorf = lol.Main.Log, lol.Main.Check, lol.Main.Errorf
|
||||||
|
)
|
||||||
109
pkg/timestamp/timestamp.go
Normal file
109
pkg/timestamp/timestamp.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package timestamp
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// run this to regenerate (pointlessly) the base 10 array of 4 places per entry
|
||||||
|
//go:generate go run ./gen/.
|
||||||
|
|
||||||
|
//go:embed base10k.txt
|
||||||
|
var base10k []byte
|
||||||
|
|
||||||
|
const base = 10000
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
N uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func New[V constraints.Integer](n V) *T { return &T{uint64(n)} }
|
||||||
|
|
||||||
|
func (n *T) Uint64() uint64 { return n.N }
|
||||||
|
func (n *T) Int64() int64 { return int64(n.N) }
|
||||||
|
func (n *T) Uint16() uint16 { return uint16(n.N) }
|
||||||
|
|
||||||
|
var powers = []*T{
|
||||||
|
{1},
|
||||||
|
{1_0000},
|
||||||
|
{1_0000_0000},
|
||||||
|
{1_0000_0000_0000},
|
||||||
|
{1_0000_0000_0000_0000},
|
||||||
|
}
|
||||||
|
|
||||||
|
const zero = '0'
|
||||||
|
const nine = '9'
|
||||||
|
|
||||||
|
func (n *T) Marshal(dst []byte) (b []byte) {
|
||||||
|
nn := n.N
|
||||||
|
b = dst
|
||||||
|
if n.N == 0 {
|
||||||
|
b = append(b, '0')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
var trimmed bool
|
||||||
|
k := len(powers)
|
||||||
|
for k > 0 {
|
||||||
|
k--
|
||||||
|
q := n.N / powers[k].N
|
||||||
|
if !trimmed && q == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
offset := q * 4
|
||||||
|
bb := base10k[offset : offset+4]
|
||||||
|
if !trimmed {
|
||||||
|
for i = range bb {
|
||||||
|
if bb[i] != '0' {
|
||||||
|
bb = bb[i:]
|
||||||
|
trimmed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b = append(b, bb...)
|
||||||
|
n.N = n.N - q*powers[k].N
|
||||||
|
}
|
||||||
|
n.N = nn
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal reads a string, which must be a positive integer int larger than math.MaxUint64,
|
||||||
|
// skipping any non-numeric content before it.
|
||||||
|
//
|
||||||
|
// Note that leading zeros are not considered valid, but basically int such thing as machine
|
||||||
|
// generated JSON integers with leading zeroes. Until this is disproven, this is the fastest way
|
||||||
|
// to read a positive json integer, and a leading zero is decoded as a zero, and the remainder
|
||||||
|
// returned.
|
||||||
|
func (n *T) Unmarshal(b []byte) (r []byte, err error) {
|
||||||
|
if len(b) < 1 {
|
||||||
|
err = errorf.E("zero length number")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var sLen int
|
||||||
|
if b[0] == zero {
|
||||||
|
r = b[1:]
|
||||||
|
n.N = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// count the digits
|
||||||
|
for ; sLen < len(b) && b[sLen] >= zero && b[sLen] <= nine && b[sLen] != ','; sLen++ {
|
||||||
|
}
|
||||||
|
if sLen == 0 {
|
||||||
|
err = errorf.E("zero length number")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if sLen > 20 {
|
||||||
|
err = errorf.E("too big number for uint64")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// the length of the string found
|
||||||
|
r = b[sLen:]
|
||||||
|
b = b[:sLen]
|
||||||
|
for _, ch := range b {
|
||||||
|
ch -= zero
|
||||||
|
n.N = n.N*10 + uint64(ch)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
77
pkg/timestamp/timestamp_test.go
Normal file
77
pkg/timestamp/timestamp_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package timestamp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"lukechampine.com/frand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarshalUnmarshal(t *testing.T) {
|
||||||
|
b := make([]byte, 0, 8)
|
||||||
|
var rem []byte
|
||||||
|
var n *T
|
||||||
|
var err error
|
||||||
|
for _ = range 10000000 {
|
||||||
|
n = New(uint64(frand.Intn(math.MaxInt64)))
|
||||||
|
b = n.Marshal(b)
|
||||||
|
m := New(0)
|
||||||
|
if rem, err = m.Unmarshal(b); chk.E(err) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if n.N != m.N {
|
||||||
|
t.Fatalf("failed to convert to int64 at %d %s %d", n.N, b, m.N)
|
||||||
|
}
|
||||||
|
if len(rem) > 0 {
|
||||||
|
t.Fatalf("leftover bytes after converting back: '%s'", rem)
|
||||||
|
}
|
||||||
|
b = b[:0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkByteStringToInt64(bb *testing.B) {
|
||||||
|
b := make([]byte, 0, 19)
|
||||||
|
var i int
|
||||||
|
const nTests = 10000000
|
||||||
|
testInts := make([]*T, nTests)
|
||||||
|
for i = range nTests {
|
||||||
|
testInts[i] = New(frand.Intn(math.MaxInt64))
|
||||||
|
}
|
||||||
|
bb.Run("Marshal", func(bb *testing.B) {
|
||||||
|
bb.ReportAllocs()
|
||||||
|
for i = 0; i < bb.N; i++ {
|
||||||
|
n := testInts[i%10000]
|
||||||
|
b = n.Marshal(b)
|
||||||
|
b = b[:0]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
bb.Run("Itoa", func(bb *testing.B) {
|
||||||
|
bb.ReportAllocs()
|
||||||
|
var s string
|
||||||
|
for i = 0; i < bb.N; i++ {
|
||||||
|
n := testInts[i%10000]
|
||||||
|
s = strconv.Itoa(int(n.N))
|
||||||
|
_ = s
|
||||||
|
}
|
||||||
|
})
|
||||||
|
bb.Run("MarshalUnmarshal", func(bb *testing.B) {
|
||||||
|
bb.ReportAllocs()
|
||||||
|
m := New(0)
|
||||||
|
for i = 0; i < bb.N; i++ {
|
||||||
|
n := testInts[i%10000]
|
||||||
|
b = m.Marshal(b)
|
||||||
|
_, _ = n.Unmarshal(b)
|
||||||
|
b = b[:0]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
bb.Run("ItoaAtoi", func(bb *testing.B) {
|
||||||
|
bb.ReportAllocs()
|
||||||
|
var s string
|
||||||
|
for i = 0; i < bb.N; i++ {
|
||||||
|
n := testInts[i%10000]
|
||||||
|
s = strconv.Itoa(int(n.N))
|
||||||
|
_, _ = strconv.Atoi(s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user