test index generation
This commit is contained in:
24
apputil/apputil.go
Normal file
24
apputil/apputil.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package apputil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// EnsureDir checks a file could be written to a path, creates the directories
|
||||
// as needed
|
||||
func EnsureDir(fileName string) {
|
||||
dirName := filepath.Dir(fileName)
|
||||
if _, serr := os.Stat(dirName); serr != nil {
|
||||
merr := os.MkdirAll(dirName, os.ModePerm)
|
||||
if merr != nil {
|
||||
panic(merr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FileExists reports whether the named file or directory exists.
|
||||
func FileExists(filePath string) bool {
|
||||
_, e := os.Stat(filePath)
|
||||
return e == nil
|
||||
}
|
||||
2
apputil/doc.go
Normal file
2
apputil/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package apputil provides some simple filesystem functions
|
||||
package apputil
|
||||
@@ -5,6 +5,6 @@ import (
|
||||
)
|
||||
|
||||
type I interface {
|
||||
MarshalWrite(w io.Writer)
|
||||
MarshalWrite(w io.Writer) (err error)
|
||||
UnmarshalRead(r io.Reader) (err error)
|
||||
}
|
||||
|
||||
115
database/fulltext.go
Normal file
115
database/fulltext.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/clipperhouse/uax29/words"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/database/indexes"
|
||||
"x.realy.lol/database/indexes/types/fulltext"
|
||||
"x.realy.lol/database/indexes/types/serial"
|
||||
"x.realy.lol/database/indexes/types/size"
|
||||
"x.realy.lol/event"
|
||||
"x.realy.lol/hex"
|
||||
"x.realy.lol/kind"
|
||||
)
|
||||
|
||||
type Words struct {
|
||||
ser *serial.S
|
||||
ev *event.E
|
||||
wordMap map[string]int
|
||||
}
|
||||
|
||||
func (d *D) GetFulltextKeys(ev *event.E, ser *serial.S) (keys [][]byte, err error) {
|
||||
w := d.GetWordsFromContent(ev)
|
||||
for i := range w {
|
||||
ft := fulltext.New()
|
||||
ft.FromWord([]byte(i))
|
||||
pos := size.New()
|
||||
pos.FromUint32(uint32(w[i]))
|
||||
buf := new(bytes.Buffer)
|
||||
if err = indexes.FullTextWordEnc(ft, pos, ser).MarshalWrite(buf); chk.E(err) {
|
||||
return
|
||||
}
|
||||
keys = append(keys, buf.Bytes())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *D) GetWordsFromContent(ev *event.E) (wordMap map[string]int) {
|
||||
wordMap = make(map[string]int)
|
||||
if kind.IsText(ev.Kind) {
|
||||
content := ev.Content
|
||||
seg := words.NewSegmenter([]byte(content))
|
||||
var counter int
|
||||
for seg.Next() {
|
||||
w := seg.Bytes()
|
||||
w = bytes.ToLower(w)
|
||||
var ru rune
|
||||
ru, _ = utf8.DecodeRune(w)
|
||||
// ignore the most common things that aren't words
|
||||
if !unicode.IsSpace(ru) &&
|
||||
!unicode.IsPunct(ru) &&
|
||||
!unicode.IsSymbol(ru) &&
|
||||
!bytes.HasSuffix(w, []byte(".jpg")) &&
|
||||
!bytes.HasSuffix(w, []byte(".png")) &&
|
||||
!bytes.HasSuffix(w, []byte(".jpeg")) &&
|
||||
!bytes.HasSuffix(w, []byte(".mp4")) &&
|
||||
!bytes.HasSuffix(w, []byte(".mov")) &&
|
||||
!bytes.HasSuffix(w, []byte(".aac")) &&
|
||||
!bytes.HasSuffix(w, []byte(".mp3")) &&
|
||||
!IsEntity(w) &&
|
||||
!bytes.Contains(w, []byte(".")) {
|
||||
if len(w) == 64 || len(w) == 128 {
|
||||
if _, err := hex.Dec(string(w)); !chk.E(err) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
wordMap[string(w)] = counter
|
||||
counter++
|
||||
}
|
||||
}
|
||||
content = content[:0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func IsEntity(w []byte) (is bool) {
|
||||
var b []byte
|
||||
b = []byte("nostr:")
|
||||
if bytes.Contains(w, b) && len(b)+10 < len(w) {
|
||||
return true
|
||||
}
|
||||
b = []byte("npub")
|
||||
if bytes.Contains(w, b) && len(b)+5 < len(w) {
|
||||
return true
|
||||
}
|
||||
b = []byte("nsec")
|
||||
if bytes.Contains(w, b) && len(b)+5 < len(w) {
|
||||
return true
|
||||
}
|
||||
b = []byte("nevent")
|
||||
if bytes.Contains(w, b) && len(b)+5 < len(w) {
|
||||
return true
|
||||
}
|
||||
b = []byte("naddr")
|
||||
if bytes.Contains(w, b) && len(b)+5 < len(w) {
|
||||
return true
|
||||
}
|
||||
b = []byte("note")
|
||||
if bytes.Contains(w, b) && len(b)+20 < len(w) {
|
||||
return true
|
||||
}
|
||||
b = []byte("lnurl")
|
||||
if bytes.Contains(w, b) && len(b)+20 < len(w) {
|
||||
return true
|
||||
}
|
||||
b = []byte("cashu")
|
||||
if bytes.Contains(w, b) && len(b)+20 < len(w) {
|
||||
return true
|
||||
}
|
||||
return
|
||||
}
|
||||
263
database/get-event-indexes.go
Normal file
263
database/get-event-indexes.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/database/indexes"
|
||||
"x.realy.lol/database/indexes/types/fullid"
|
||||
identhash "x.realy.lol/database/indexes/types/identHash"
|
||||
"x.realy.lol/database/indexes/types/idhash"
|
||||
"x.realy.lol/database/indexes/types/kindidx"
|
||||
"x.realy.lol/database/indexes/types/letter"
|
||||
"x.realy.lol/database/indexes/types/pubhash"
|
||||
"x.realy.lol/database/indexes/types/serial"
|
||||
"x.realy.lol/database/indexes/types/timestamp"
|
||||
"x.realy.lol/event"
|
||||
"x.realy.lol/hex"
|
||||
"x.realy.lol/tags"
|
||||
)
|
||||
|
||||
// GetEventIndexes generates a set of indexes for a new event record. The first record is the
|
||||
// key that should have the binary encoded event as its value.
|
||||
func (d *D) GetEventIndexes(ev *event.E) (indices [][]byte, err error) {
|
||||
// log.I.F("getting event indices for\n%s", ev.Serialize())
|
||||
// get a new serial
|
||||
ser := serial.New()
|
||||
var s uint64
|
||||
if s, err = d.Serial(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
ser.FromSerial(s)
|
||||
// create the event id key
|
||||
id := idhash.New()
|
||||
var idb []byte
|
||||
if idb, err = ev.IdBytes(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if err = id.FromId(idb); chk.E(err) {
|
||||
return
|
||||
}
|
||||
evIDB := new(bytes.Buffer)
|
||||
if err = indexes.IdEnc(id, ser).MarshalWrite(evIDB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIDB.Bytes())
|
||||
// create the full index key
|
||||
fid := fullid.New()
|
||||
if err = fid.FromId(idb); chk.E(err) {
|
||||
return
|
||||
}
|
||||
p := pubhash.New()
|
||||
var pk []byte
|
||||
if pk, err = ev.PubBytes(); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if err = p.FromPubkey(pk); chk.E(err) {
|
||||
return
|
||||
}
|
||||
ki := kindidx.FromKind(ev.Kind)
|
||||
ca := ×tamp.T{}
|
||||
ca.FromInt64(int64(ev.CreatedAt))
|
||||
evIFiB := new(bytes.Buffer)
|
||||
if err = indexes.FullIndexEnc(fid, p, ki, ca, ser).MarshalWrite(evIFiB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIFiB.Bytes())
|
||||
// pubkey index
|
||||
evIPkB := new(bytes.Buffer)
|
||||
if err = indexes.PubkeyEnc(p, ser).MarshalWrite(evIPkB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIPkB.Bytes())
|
||||
// pubkey-created_at index
|
||||
evIPkCaB := new(bytes.Buffer)
|
||||
if err = indexes.PubkeyCreatedAtEnc(p, ca, ser).MarshalWrite(evIPkCaB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIPkCaB.Bytes())
|
||||
// created_at index
|
||||
evICaB := new(bytes.Buffer)
|
||||
if err = indexes.CreatedAtEnc(ca, ser).MarshalWrite(evICaB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evICaB.Bytes())
|
||||
// FirstSeen index
|
||||
evIFsB := new(bytes.Buffer)
|
||||
fs := ×tamp.T{}
|
||||
fs.FromInt64(time.Now().Unix())
|
||||
if err = indexes.FirstSeenEnc(ser, fs).MarshalWrite(evIFsB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIFsB.Bytes())
|
||||
// Kind index
|
||||
evIKiB := new(bytes.Buffer)
|
||||
if err = indexes.KindEnc(ki, ser).MarshalWrite(evIKiB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIKiB.Bytes())
|
||||
// tags
|
||||
// TagA index
|
||||
var atags []tags.Tag_a
|
||||
var tagAs []indexes.TagA
|
||||
atags = ev.Tags.Get_a_Tags()
|
||||
for _, v := range atags {
|
||||
aki, apk, aid, _ := indexes.TagAVars()
|
||||
aki.Set(v.Kind)
|
||||
if err = apk.FromPubkey(v.Pubkey); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
if err = aid.FromIdent([]byte(v.Ident)); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
tagAs = append(tagAs, indexes.TagA{
|
||||
Ki: aki, P: apk, Id: aid, Ser: ser,
|
||||
})
|
||||
}
|
||||
for _, v := range tagAs {
|
||||
evITaB := new(bytes.Buffer)
|
||||
if err = indexes.TagAEnc(v.Ki, v.P, v.Id, ser).MarshalWrite(evITaB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evITaB.Bytes())
|
||||
}
|
||||
// TagEvent index
|
||||
eTags := ev.Tags.GetAllExactKeys("e")
|
||||
for _, v := range eTags {
|
||||
eid := v.Value()
|
||||
var eh []byte
|
||||
if eh, err = hex.Dec(eid); chk.E(err) {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
ih := idhash.New()
|
||||
if err = ih.FromId(eh); chk.E(err) {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
evIeB := new(bytes.Buffer)
|
||||
if err = indexes.TagEventEnc(ih, ser).MarshalWrite(evIeB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIeB.Bytes())
|
||||
}
|
||||
// TagPubkey index
|
||||
pTags := ev.Tags.GetAllExactKeys("p")
|
||||
for _, v := range pTags {
|
||||
pt := v.Value()
|
||||
var pkb []byte
|
||||
if pkb, err = hex.Dec(pt); err != nil {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
ph := pubhash.New()
|
||||
if err = ph.FromPubkey(pkb); chk.E(err) {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
evIpB := new(bytes.Buffer)
|
||||
if err = indexes.TagPubkeyEnc(ph, ser).MarshalWrite(evIpB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIpB.Bytes())
|
||||
}
|
||||
// TagHashtag index
|
||||
ttags := ev.Tags.GetAllExactKeys("t")
|
||||
for _, v := range ttags {
|
||||
ht := v.Value()
|
||||
hh := identhash.New()
|
||||
if err = hh.FromIdent([]byte(ht)); chk.E(err) {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
evIhB := new(bytes.Buffer)
|
||||
if err = indexes.TagHashtagEnc(hh, ser).MarshalWrite(evIhB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIhB.Bytes())
|
||||
}
|
||||
// TagIdentifier index
|
||||
dtags := ev.Tags.GetAllExactKeys("d")
|
||||
for _, v := range dtags {
|
||||
dt := v.Value()
|
||||
dh := identhash.New()
|
||||
if err = dh.FromIdent([]byte(dt)); chk.E(err) {
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
evIidB := new(bytes.Buffer)
|
||||
if err = indexes.TagIdentifierEnc(dh, ser).MarshalWrite(evIidB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIidB.Bytes())
|
||||
}
|
||||
// TagLetter index, TagProtected, TagNonstandard
|
||||
for _, v := range ev.Tags {
|
||||
key := v.Key()
|
||||
if len(key) == 1 {
|
||||
switch key {
|
||||
case "t", "p", "e":
|
||||
// we already made indexes for these letters
|
||||
continue
|
||||
case "-":
|
||||
// TagProtected
|
||||
evIprotB := new(bytes.Buffer)
|
||||
if err = indexes.TagProtectedEnc(p, ser).MarshalWrite(evIprotB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIprotB.Bytes())
|
||||
default:
|
||||
if !((key[0] >= 'a' && key[0] <= 'z') || (key[0] >= 'A' && key[0] <= 'Z')) {
|
||||
// this is not a single letter tag or protected. nonstandard
|
||||
nk, nv := identhash.New(), identhash.New()
|
||||
_ = nk.FromIdent([]byte(key))
|
||||
if len(v) > 1 {
|
||||
_ = nv.FromIdent([]byte(v.Value()))
|
||||
} else {
|
||||
_ = nv.FromIdent([]byte{})
|
||||
}
|
||||
evInsB := new(bytes.Buffer)
|
||||
if err = indexes.TagNonstandardEnc(nk, nv, ser).MarshalWrite(evInsB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evInsB.Bytes())
|
||||
continue
|
||||
}
|
||||
}
|
||||
// we have a single letter that is not e, p or t
|
||||
l := letter.New(key[0])
|
||||
val := identhash.New()
|
||||
// this can be empty, but the hash would still be distinct
|
||||
if err = val.FromIdent([]byte(v.Value())); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
evIlB := new(bytes.Buffer)
|
||||
if err = indexes.TagLetterEnc(l, val, ser).MarshalWrite(evIlB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evIlB.Bytes())
|
||||
} else {
|
||||
// TagNonstandard
|
||||
nk, nv := identhash.New(), identhash.New()
|
||||
_ = nk.FromIdent([]byte(key))
|
||||
if len(v) > 1 {
|
||||
_ = nv.FromIdent([]byte(v.Value()))
|
||||
} else {
|
||||
_ = nv.FromIdent([]byte{})
|
||||
}
|
||||
evInsB := new(bytes.Buffer)
|
||||
if err = indexes.TagNonstandardEnc(nk, nv, ser).MarshalWrite(evInsB); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, evInsB.Bytes())
|
||||
}
|
||||
}
|
||||
// FullTextWord index
|
||||
var ftk [][]byte
|
||||
if ftk, err = d.GetFulltextKeys(ev, ser); chk.E(err) {
|
||||
return
|
||||
}
|
||||
indices = append(indices, ftk...)
|
||||
return
|
||||
}
|
||||
142
database/get-event-indexes_test.go
Normal file
142
database/get-event-indexes_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"x.realy.lol/apputil"
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/event"
|
||||
"x.realy.lol/log"
|
||||
"x.realy.lol/units"
|
||||
)
|
||||
|
||||
var ExampleEvents []byte
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if !apputil.FileExists("examples.jsonl") {
|
||||
var req *http.Request
|
||||
req, err = http.NewRequest("GET", "https://files.mleku.dev/examples.jsonl", nil)
|
||||
if err != nil {
|
||||
panic("wtf")
|
||||
}
|
||||
var res *http.Response
|
||||
if res, err = http.DefaultClient.Do(req); chk.E(err) {
|
||||
panic("wtf")
|
||||
}
|
||||
var fh *os.File
|
||||
if fh, err = os.OpenFile("examples.jsonl", os.O_CREATE|os.O_RDWR, 0600); chk.E(err) {
|
||||
panic("wtf")
|
||||
}
|
||||
if _, err = io.Copy(fh, res.Body); chk.E(err) {
|
||||
panic("wtf")
|
||||
}
|
||||
res.Body.Close()
|
||||
}
|
||||
log.I.F("loading file...")
|
||||
var oh *os.File
|
||||
if oh, err = os.Open("examples.jsonl"); chk.E(err) {
|
||||
panic("wtf")
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err = io.Copy(buf, oh); chk.E(err) {
|
||||
panic("wtf")
|
||||
}
|
||||
ExampleEvents = buf.Bytes()
|
||||
oh.Close()
|
||||
}
|
||||
|
||||
func TestGetEventIndexes(t *testing.T) {
|
||||
var err error
|
||||
d := New()
|
||||
tmpDir := filepath.Join(os.TempDir(), "testrealy")
|
||||
if err = d.Init(tmpDir); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer d.Close()
|
||||
defer os.RemoveAll(tmpDir)
|
||||
buf := bytes.NewBuffer(ExampleEvents)
|
||||
scan := bufio.NewScanner(buf)
|
||||
scan.Buffer(make([]byte, 5120000), 5120000)
|
||||
var count, errs, encErrs, datasize, size, binsize int
|
||||
start := time.Now()
|
||||
for scan.Scan() {
|
||||
b := scan.Bytes()
|
||||
ev := event.New()
|
||||
if err = ev.Unmarshal(b); chk.E(err) {
|
||||
t.Fatalf("%s:\n%s", err, b)
|
||||
}
|
||||
// verify the signature on the event
|
||||
var ok bool
|
||||
if ok, err = ev.Verify(); chk.E(err) {
|
||||
errs++
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
errs++
|
||||
log.E.F("event signature is invalid\n%s", b)
|
||||
continue
|
||||
}
|
||||
// check the event encodes to binary, decodes, and produces the identical canonical form
|
||||
binE := new(bytes.Buffer)
|
||||
if err = ev.MarshalWrite(binE); chk.E(err) {
|
||||
// log.I.F("bogus tags probably: %s", b)
|
||||
encErrs++
|
||||
// events that marshal with errors have e and p tag values that aren't hex and should not be accepted
|
||||
continue
|
||||
}
|
||||
ev2 := event.New()
|
||||
bin2 := bytes.NewBuffer(binE.Bytes())
|
||||
if err = ev2.UnmarshalRead(bin2); chk.E(err) {
|
||||
encErrs++
|
||||
continue
|
||||
}
|
||||
var can1, can2 []byte
|
||||
ev.ToCanonical(can1)
|
||||
ev2.ToCanonical(can2)
|
||||
if !bytes.Equal(can1, can2) {
|
||||
encErrs++
|
||||
log.I.S(can1, can2)
|
||||
continue
|
||||
}
|
||||
binsize += len(binE.Bytes())
|
||||
var valid bool
|
||||
if valid, err = ev.Verify(); chk.E(err) {
|
||||
log.I.F("%s", b)
|
||||
encErrs++
|
||||
continue
|
||||
}
|
||||
if !valid {
|
||||
t.Fatalf("event failed to verify\n%s", b)
|
||||
}
|
||||
var indices [][]byte
|
||||
if indices, err = d.GetEventIndexes(ev); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// log.I.F("%s", b)
|
||||
// log.I.S(indices)
|
||||
datasize += len(b)
|
||||
for _, v := range indices {
|
||||
size += len(v)
|
||||
}
|
||||
_ = indices
|
||||
count++
|
||||
// if count == 10000 {
|
||||
// break
|
||||
// }
|
||||
}
|
||||
log.I.F("unmarshaled, verified and indexed %d events in %s, %d Mb of indexes from %d Mb of events, %d Mb as binary, failed verify %d, failed encode %d", count, time.Now().Sub(start), size/units.Mb, datasize/units.Mb, binsize/units.Mb, errs, encErrs)
|
||||
d.Close()
|
||||
os.RemoveAll(tmpDir)
|
||||
}
|
||||
|
||||
var _ = `wdawdad\nhttps://cdn.discordapp.com/attachments/1277777226397388800/1278018649860472874/grain.png?ex=66cf471e&is=66cdf59e&hm=790aced618bb517ebd560e1fd3def537351ef130e239c0ee86d43ff63c44a146&","sig":"2abc1b3bb119071209daba6bf2b6c76cdad036249aad624938a5a2736739d6c139adb7aa94d24550bc53a972e75c40549513a74d9ace8c4435a5d262c172300b`
|
||||
var _ = ``
|
||||
@@ -5,18 +5,18 @@ import (
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/codec"
|
||||
"x.realy.lol/indexes/prefixes"
|
||||
"x.realy.lol/indexes/types/fullid"
|
||||
"x.realy.lol/indexes/types/fulltext"
|
||||
identhash "x.realy.lol/indexes/types/identHash"
|
||||
"x.realy.lol/indexes/types/idhash"
|
||||
"x.realy.lol/indexes/types/kindidx"
|
||||
"x.realy.lol/indexes/types/letter"
|
||||
"x.realy.lol/indexes/types/prefix"
|
||||
"x.realy.lol/indexes/types/pubhash"
|
||||
"x.realy.lol/indexes/types/serial"
|
||||
"x.realy.lol/indexes/types/size"
|
||||
"x.realy.lol/indexes/types/timestamp"
|
||||
"x.realy.lol/database/indexes/prefixes"
|
||||
"x.realy.lol/database/indexes/types/fullid"
|
||||
"x.realy.lol/database/indexes/types/fulltext"
|
||||
"x.realy.lol/database/indexes/types/identHash"
|
||||
"x.realy.lol/database/indexes/types/idhash"
|
||||
"x.realy.lol/database/indexes/types/kindidx"
|
||||
"x.realy.lol/database/indexes/types/letter"
|
||||
"x.realy.lol/database/indexes/types/prefix"
|
||||
"x.realy.lol/database/indexes/types/pubhash"
|
||||
"x.realy.lol/database/indexes/types/serial"
|
||||
"x.realy.lol/database/indexes/types/size"
|
||||
"x.realy.lol/database/indexes/types/timestamp"
|
||||
)
|
||||
|
||||
type Encs []codec.I
|
||||
@@ -31,10 +31,13 @@ type T struct {
|
||||
// decode variant does not add the prefix encoder because it has been read by prefixes.Identify.
|
||||
func New(encoders ...codec.I) (i *T) { return &T{encoders} }
|
||||
|
||||
func (t *T) MarshalWrite(w io.Writer) {
|
||||
func (t *T) MarshalWrite(w io.Writer) (err error) {
|
||||
for _, e := range t.Encs {
|
||||
e.MarshalWrite(w)
|
||||
if err = e.MarshalWrite(w); chk.E(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
@@ -42,36 +45,35 @@ func (t *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if err = e.UnmarshalRead(r); chk.E(err) {
|
||||
return
|
||||
}
|
||||
// log.I.S(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func EventVars() (ser *serial.T) {
|
||||
func EventVars() (ser *serial.S) {
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func EventEnc(ser *serial.T) (enc *T) {
|
||||
func EventEnc(ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.Event), ser)
|
||||
}
|
||||
func EventDec(ser *serial.T) (enc *T) {
|
||||
func EventDec(ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), ser)
|
||||
}
|
||||
|
||||
func IdVars() (id *idhash.T, ser *serial.T) {
|
||||
func IdVars() (id *idhash.T, ser *serial.S) {
|
||||
id = idhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func IdEnc(id *idhash.T, ser *serial.T) (enc *T) {
|
||||
func IdEnc(id *idhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.Id), id, ser)
|
||||
}
|
||||
func IdDec(id *idhash.T, ser *serial.T) (enc *T) {
|
||||
func IdDec(id *idhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), id, ser)
|
||||
}
|
||||
|
||||
func FullIndexVars() (t *fullid.T, p *pubhash.T, ki *kindidx.T,
|
||||
ca *timestamp.T, ser *serial.T) {
|
||||
ca *timestamp.T, ser *serial.S) {
|
||||
t = fullid.New()
|
||||
p = pubhash.New()
|
||||
ki = kindidx.FromKind(0)
|
||||
@@ -80,206 +82,213 @@ func FullIndexVars() (t *fullid.T, p *pubhash.T, ki *kindidx.T,
|
||||
return
|
||||
}
|
||||
func FullIndexEnc(t *fullid.T, p *pubhash.T, ki *kindidx.T,
|
||||
ca *timestamp.T, ser *serial.T) (enc *T) {
|
||||
ca *timestamp.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.FullIndex), t, p, ki, ca, ser)
|
||||
}
|
||||
func FullIndexDec(t *fullid.T, p *pubhash.T, ki *kindidx.T,
|
||||
ca *timestamp.T, ser *serial.T) (enc *T) {
|
||||
ca *timestamp.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), t, p, ki, ca, ser)
|
||||
}
|
||||
|
||||
func PubkeyVars() (p *pubhash.T, ser *serial.T) {
|
||||
func PubkeyVars() (p *pubhash.T, ser *serial.S) {
|
||||
p = pubhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func PubkeyEnc(p *pubhash.T, ser *serial.T) (enc *T) {
|
||||
func PubkeyEnc(p *pubhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.Pubkey), p, ser)
|
||||
}
|
||||
func PubkeyDec(p *pubhash.T, ser *serial.T) (enc *T) {
|
||||
func PubkeyDec(p *pubhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), p, ser)
|
||||
}
|
||||
|
||||
func PubkeyCreatedAtVars() (p *pubhash.T, ca *timestamp.T, ser *serial.T) {
|
||||
func PubkeyCreatedAtVars() (p *pubhash.T, ca *timestamp.T, ser *serial.S) {
|
||||
p = pubhash.New()
|
||||
ca = ×tamp.T{}
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func PubkeyCreatedAtEnc(p *pubhash.T, ca *timestamp.T, ser *serial.T) (enc *T) {
|
||||
func PubkeyCreatedAtEnc(p *pubhash.T, ca *timestamp.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.PubkeyCreatedAt), p, ca, ser)
|
||||
}
|
||||
func PubkeyCreatedAtDec(p *pubhash.T, ca *timestamp.T, ser *serial.T) (enc *T) {
|
||||
func PubkeyCreatedAtDec(p *pubhash.T, ca *timestamp.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), p, ca, ser)
|
||||
}
|
||||
|
||||
func CreatedAtVars() (ca *timestamp.T, ser *serial.T) {
|
||||
func CreatedAtVars() (ca *timestamp.T, ser *serial.S) {
|
||||
ca = ×tamp.T{}
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func CreatedAtEnc(ca *timestamp.T, ser *serial.T) (enc *T) {
|
||||
func CreatedAtEnc(ca *timestamp.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.CreatedAt), ca, ser)
|
||||
}
|
||||
func CreatedAtDec(ca *timestamp.T, ser *serial.T) (enc *T) {
|
||||
func CreatedAtDec(ca *timestamp.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), ca, ser)
|
||||
}
|
||||
|
||||
func FirstSeenVars() (ser *serial.T, ts *timestamp.T) {
|
||||
func FirstSeenVars() (ser *serial.S, ts *timestamp.T) {
|
||||
ts = ×tamp.T{}
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func FirstSeenEnc(ser *serial.T, ts *timestamp.T) (enc *T) {
|
||||
func FirstSeenEnc(ser *serial.S, ts *timestamp.T) (enc *T) {
|
||||
return New(prefix.New(prefixes.FirstSeen), ser, ts)
|
||||
}
|
||||
func FirstSeenDec(ser *serial.T, ts *timestamp.T) (enc *T) {
|
||||
func FirstSeenDec(ser *serial.S, ts *timestamp.T) (enc *T) {
|
||||
return New(prefix.New(), ser, ts)
|
||||
}
|
||||
|
||||
func KindVars() (ki *kindidx.T, ser *serial.T) {
|
||||
func KindVars() (ki *kindidx.T, ser *serial.S) {
|
||||
ki = kindidx.FromKind(0)
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func KindEnc(ki *kindidx.T, ser *serial.T) (enc *T) {
|
||||
func KindEnc(ki *kindidx.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.Kind), ki, ser)
|
||||
}
|
||||
func KindDec(ki *kindidx.T, ser *serial.T) (enc *T) {
|
||||
func KindDec(ki *kindidx.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), ki, ser)
|
||||
}
|
||||
|
||||
func TagAVars() (ki *kindidx.T, p *pubhash.T, id *identhash.T, ser *serial.T) {
|
||||
type TagA struct {
|
||||
Ki *kindidx.T
|
||||
P *pubhash.T
|
||||
Id *identhash.T
|
||||
Ser *serial.S
|
||||
}
|
||||
|
||||
func TagAVars() (ki *kindidx.T, p *pubhash.T, id *identhash.T, ser *serial.S) {
|
||||
ki = kindidx.FromKind(0)
|
||||
p = pubhash.New()
|
||||
id = identhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagAEnc(ki *kindidx.T, p *pubhash.T, id *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagAEnc(ki *kindidx.T, p *pubhash.T, id *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagA), ki, p, id, ser)
|
||||
}
|
||||
func TagADec(ki *kindidx.T, p *pubhash.T, id *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagADec(ki *kindidx.T, p *pubhash.T, id *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), ki, p, id, ser)
|
||||
}
|
||||
|
||||
func TagEventVars() (id *idhash.T, ser *serial.T) {
|
||||
func TagEventVars() (id *idhash.T, ser *serial.S) {
|
||||
id = idhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagEventEnc(id *idhash.T, ser *serial.T) (enc *T) {
|
||||
func TagEventEnc(id *idhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagEvent), id, ser)
|
||||
}
|
||||
func TagEventDec(id *idhash.T, ser *serial.T) (enc *T) {
|
||||
func TagEventDec(id *idhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), id, ser)
|
||||
}
|
||||
|
||||
func TagPubkeyVars() (p *pubhash.T, ser *serial.T) {
|
||||
func TagPubkeyVars() (p *pubhash.T, ser *serial.S) {
|
||||
p = pubhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagPubkeyEnc(p *pubhash.T, ser *serial.T) (enc *T) {
|
||||
func TagPubkeyEnc(p *pubhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagPubkey), p, ser)
|
||||
}
|
||||
func TagPubkeyDec(p *pubhash.T, ser *serial.T) (enc *T) {
|
||||
func TagPubkeyDec(p *pubhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), p, ser)
|
||||
}
|
||||
|
||||
func TagHashtagVars() (hashtag *identhash.T, ser *serial.T) {
|
||||
func TagHashtagVars() (hashtag *identhash.T, ser *serial.S) {
|
||||
hashtag = identhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagHashtagEnc(hashtag *identhash.T, ser *serial.T) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagA), hashtag, ser)
|
||||
func TagHashtagEnc(hashtag *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagHashtag), hashtag, ser)
|
||||
}
|
||||
func TagHashtagDec(hashtag *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagHashtagDec(hashtag *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), hashtag, ser)
|
||||
}
|
||||
|
||||
func TagIdentifierVars() (ident *identhash.T, ser *serial.T) {
|
||||
func TagIdentifierVars() (ident *identhash.T, ser *serial.S) {
|
||||
ident = identhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagIdentifierEnc(ident *identhash.T, ser *serial.T) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagA), ident, ser)
|
||||
func TagIdentifierEnc(ident *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagIdentifier), ident, ser)
|
||||
}
|
||||
func TagIdentifierDec(ident *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagIdentifierDec(ident *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), ident, ser)
|
||||
}
|
||||
|
||||
func TagLetterVars() (l *letter.T, val *identhash.T, ser *serial.T) {
|
||||
func TagLetterVars() (l *letter.T, val *identhash.T, ser *serial.S) {
|
||||
l = letter.New(0)
|
||||
val = identhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagLetterEnc(l *letter.T, val *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagLetterEnc(l *letter.T, val *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagLetter), l, val, ser)
|
||||
}
|
||||
func TagLetterDec(l *letter.T, val *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagLetterDec(l *letter.T, val *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), l, val, ser)
|
||||
}
|
||||
|
||||
func TagProtectedVars() (p *pubhash.T, ser *serial.T) {
|
||||
func TagProtectedVars() (p *pubhash.T, ser *serial.S) {
|
||||
p = pubhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagProtectedEnc(p *pubhash.T, ser *serial.T) (enc *T) {
|
||||
func TagProtectedEnc(p *pubhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagProtected), p, ser)
|
||||
}
|
||||
func TagProtectedDec(p *pubhash.T, ser *serial.T) (enc *T) {
|
||||
func TagProtectedDec(p *pubhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), p, ser)
|
||||
}
|
||||
|
||||
func TagNonstandardVars() (key, value *identhash.T, ser *serial.T) {
|
||||
func TagNonstandardVars() (key, value *identhash.T, ser *serial.S) {
|
||||
key = identhash.New()
|
||||
value = identhash.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func TagNonstandardEnc(key, value *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagNonstandardEnc(key, value *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.TagNonstandard), key, value, ser)
|
||||
}
|
||||
func TagNonstandardDec(key, value *identhash.T, ser *serial.T) (enc *T) {
|
||||
func TagNonstandardDec(key, value *identhash.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), key, value, ser)
|
||||
}
|
||||
|
||||
func FullTextWordVars() (fw *fulltext.T, pos *size.T, ser *serial.T) {
|
||||
func FullTextWordVars() (fw *fulltext.T, pos *size.T, ser *serial.S) {
|
||||
fw = fulltext.New()
|
||||
pos = size.New()
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func FullTextWordEnc(fw *fulltext.T, pos *size.T, ser *serial.T) (enc *T) {
|
||||
func FullTextWordEnc(fw *fulltext.T, pos *size.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.FulltextWord), fw, pos, ser)
|
||||
}
|
||||
func FullTextWordDec(fw *fulltext.T, pos *size.T, ser *serial.T) (enc *T) {
|
||||
func FullTextWordDec(fw *fulltext.T, pos *size.T, ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), fw, pos, ser)
|
||||
}
|
||||
|
||||
func LastAccessedVars() (ser *serial.T) {
|
||||
func LastAccessedVars() (ser *serial.S) {
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func LastAccessedEnc(ser *serial.T) (enc *T) {
|
||||
func LastAccessedEnc(ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.LastAccessed), ser)
|
||||
}
|
||||
func LastAccessedDec(ser *serial.T) (enc *T) {
|
||||
func LastAccessedDec(ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), ser)
|
||||
}
|
||||
|
||||
func AccessCounterVars() (ser *serial.T) {
|
||||
func AccessCounterVars() (ser *serial.S) {
|
||||
ser = serial.New()
|
||||
return
|
||||
}
|
||||
func AccessCounterEnc(ser *serial.T) (enc *T) {
|
||||
func AccessCounterEnc(ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(prefixes.AccessCounter), ser)
|
||||
}
|
||||
func AccessCounterDec(ser *serial.T) (enc *T) {
|
||||
func AccessCounterDec(ser *serial.S) (enc *T) {
|
||||
return New(prefix.New(), ser)
|
||||
}
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"lukechampine.com/frand"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/database/indexes/prefixes"
|
||||
"x.realy.lol/database/indexes/types/prefix"
|
||||
"x.realy.lol/ec/schnorr"
|
||||
"x.realy.lol/indexes/prefixes"
|
||||
"x.realy.lol/indexes/types/prefix"
|
||||
)
|
||||
|
||||
func TestEvent(t *testing.T) {
|
||||
@@ -26,7 +26,10 @@ func (fi *T) FromId(id []byte) (err error) {
|
||||
}
|
||||
func (fi *T) Bytes() (b []byte) { return fi.val }
|
||||
|
||||
func (fi *T) MarshalWrite(w io.Writer) { _, _ = w.Write(fi.val) }
|
||||
func (fi *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(fi.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (fi *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(fi.val) < Len {
|
||||
@@ -17,9 +17,10 @@ func (ft *T) FromWord(word []byte) { ft.val = word }
|
||||
|
||||
func (ft *T) Bytes() (b []byte) { return ft.val }
|
||||
|
||||
func (ft *T) MarshalWrite(w io.Writer) {
|
||||
func (ft *T) MarshalWrite(w io.Writer) (err error) {
|
||||
varint.Encode(w, uint64(len(ft.val)))
|
||||
_, _ = w.Write(ft.val)
|
||||
_, err = w.Write(ft.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (ft *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
@@ -19,7 +19,10 @@ func (i *T) FromIdent(id []byte) (err error) {
|
||||
|
||||
func (i *T) Bytes() (b []byte) { return i.val }
|
||||
|
||||
func (i *T) MarshalWrite(w io.Writer) { _, _ = w.Write(i.val) }
|
||||
func (i *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(i.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (i *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(i.val) < Len {
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
|
||||
"github.com/minio/sha256-simd"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/errorf"
|
||||
"x.realy.lol/helpers"
|
||||
"x.realy.lol/hex"
|
||||
)
|
||||
|
||||
const Len = 8
|
||||
@@ -24,9 +26,26 @@ func (i *T) FromId(id []byte) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (i *T) FromIdHex(idh string) (err error) {
|
||||
var id []byte
|
||||
if id, err = hex.Dec(idh); chk.E(err) {
|
||||
return
|
||||
}
|
||||
if len(id) != sha256.Size {
|
||||
err = errorf.E("invalid Id length, got %d require %d", len(id), sha256.Size)
|
||||
return
|
||||
}
|
||||
i.val = helpers.Hash(id)[:Len]
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (i *T) Bytes() (b []byte) { return i.val }
|
||||
|
||||
func (i *T) MarshalWrite(w io.Writer) { _, _ = w.Write(i.val) }
|
||||
func (i *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(i.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (i *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(i.val) < Len {
|
||||
@@ -37,7 +37,10 @@ func (k *T) ToKind() (kind int) {
|
||||
}
|
||||
func (k *T) Bytes() (b []byte) { return k.val }
|
||||
|
||||
func (k *T) MarshalWrite(w io.Writer) { _, _ = w.Write(k.val) }
|
||||
func (k *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(k.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (k *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(k.val) < Len {
|
||||
@@ -16,7 +16,10 @@ func (p *T) Set(lb byte) { p.val = []byte{lb} }
|
||||
|
||||
func (p *T) Letter() byte { return p.val[0] }
|
||||
|
||||
func (p *T) MarshalWrite(w io.Writer) { _, _ = w.Write(p.val) }
|
||||
func (p *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(p.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
val := make([]byte, 1)
|
||||
@@ -3,7 +3,7 @@ package prefix
|
||||
import (
|
||||
"io"
|
||||
|
||||
"x.realy.lol/indexes/prefixes"
|
||||
"x.realy.lol/database/indexes/prefixes"
|
||||
)
|
||||
|
||||
const Len = 2
|
||||
@@ -22,7 +22,10 @@ func New(prf ...int) (p *T) {
|
||||
|
||||
func (p *T) Bytes() (b []byte) { return p.val }
|
||||
|
||||
func (p *T) MarshalWrite(w io.Writer) { _, _ = w.Write(p.val) }
|
||||
func (p *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(p.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
_, err = r.Read(p.val)
|
||||
@@ -25,7 +25,10 @@ func (ph *T) FromPubkey(pk []byte) (err error) {
|
||||
|
||||
func (ph *T) Bytes() (b []byte) { return ph.val }
|
||||
|
||||
func (ph *T) MarshalWrite(w io.Writer) { _, _ = w.Write(ph.val) }
|
||||
func (ph *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(ph.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (ph *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(ph.val) < Len {
|
||||
@@ -9,34 +9,37 @@ import (
|
||||
|
||||
const Len = 8
|
||||
|
||||
type T struct{ val []byte }
|
||||
type S struct{ val []byte }
|
||||
|
||||
func New() (s *T) { return &T{make([]byte, Len)} }
|
||||
func New() (s *S) { return &S{make([]byte, Len)} }
|
||||
|
||||
func (s *T) FromSerial(ser uint64) {
|
||||
func (s *S) FromSerial(ser uint64) {
|
||||
binary.LittleEndian.PutUint64(s.val, ser)
|
||||
return
|
||||
}
|
||||
|
||||
func FromBytes(ser []byte) (s *T, err error) {
|
||||
func FromBytes(ser []byte) (s *S, err error) {
|
||||
if len(ser) != Len {
|
||||
err = errorf.E("serial must be %d bytes long, got %d", Len, len(ser))
|
||||
return
|
||||
}
|
||||
s = &T{val: ser}
|
||||
s = &S{val: ser}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *T) ToSerial() (ser uint64) {
|
||||
func (s *S) ToSerial() (ser uint64) {
|
||||
ser = binary.LittleEndian.Uint64(s.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *T) Bytes() (b []byte) { return s.val }
|
||||
func (s *S) Bytes() (b []byte) { return s.val }
|
||||
|
||||
func (s *T) MarshalWrite(w io.Writer) { _, _ = w.Write(s.val) }
|
||||
func (s *S) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(s.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
func (s *S) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(s.val) < Len {
|
||||
s.val = make([]byte, Len)
|
||||
} else {
|
||||
@@ -35,7 +35,10 @@ func (s *T) ToUint32() (ser uint32) {
|
||||
|
||||
func (s *T) Bytes() (b []byte) { return s.val }
|
||||
|
||||
func (s *T) MarshalWrite(w io.Writer) { _, _ = w.Write(s.val) }
|
||||
func (s *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(s.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(s.val) < Len {
|
||||
@@ -32,7 +32,10 @@ func (ts *T) ToTimestamp() (timestamp timeStamp.Timestamp) {
|
||||
}
|
||||
func (ts *T) Bytes() (b []byte) { return ts.val }
|
||||
|
||||
func (ts *T) MarshalWrite(w io.Writer) { _, _ = w.Write(ts.val) }
|
||||
func (ts *T) MarshalWrite(w io.Writer) (err error) {
|
||||
_, err = w.Write(ts.val)
|
||||
return
|
||||
}
|
||||
|
||||
func (ts *T) UnmarshalRead(r io.Reader) (err error) {
|
||||
if len(ts.val) < Len {
|
||||
69
database/log.go
Normal file
69
database/log.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"x.realy.lol/atomic"
|
||||
"x.realy.lol/log"
|
||||
"x.realy.lol/lol"
|
||||
)
|
||||
|
||||
// NewLogger creates a new badger logger.
|
||||
func NewLogger(logLevel int, label string) (l *logger) {
|
||||
log.T.Ln("getting logger for", label)
|
||||
l = &logger{Label: label}
|
||||
l.Level.Store(int32(logLevel))
|
||||
return
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
Level atomic.Int32
|
||||
Label string
|
||||
}
|
||||
|
||||
// SetLogLevel atomically adjusts the log level to the given log level code.
|
||||
func (l *logger) SetLogLevel(level int) {
|
||||
l.Level.Store(int32(level))
|
||||
}
|
||||
|
||||
// Errorf is a log printer for this level of message.
|
||||
func (l *logger) Errorf(s string, i ...interface{}) {
|
||||
if l.Level.Load() >= lol.Error {
|
||||
s = l.Label + ": " + s
|
||||
txt := fmt.Sprintf(s, i...)
|
||||
_, file, line, _ := runtime.Caller(2)
|
||||
log.E.F("%s\n%s:%d", strings.TrimSpace(txt), file, line)
|
||||
}
|
||||
}
|
||||
|
||||
// Warningf is a log printer for this level of message.
|
||||
func (l *logger) Warningf(s string, i ...interface{}) {
|
||||
if l.Level.Load() >= lol.Warn {
|
||||
s = l.Label + ": " + s
|
||||
txt := fmt.Sprintf(s, i...)
|
||||
_, file, line, _ := runtime.Caller(2)
|
||||
log.D.F("%s\n%s:%d", strings.TrimSpace(txt), file, line)
|
||||
}
|
||||
}
|
||||
|
||||
// Infof is a log printer for this level of message.
|
||||
func (l *logger) Infof(s string, i ...interface{}) {
|
||||
if l.Level.Load() >= lol.Info {
|
||||
s = l.Label + ": " + s
|
||||
txt := fmt.Sprintf(s, i...)
|
||||
_, file, line, _ := runtime.Caller(2)
|
||||
log.D.F("%s\n%s:%d", strings.TrimSpace(txt), file, line)
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf is a log printer for this level of message.
|
||||
func (l *logger) Debugf(s string, i ...interface{}) {
|
||||
if l.Level.Load() >= lol.Debug {
|
||||
s = l.Label + ": " + s
|
||||
txt := fmt.Sprintf(s, i...)
|
||||
_, file, line, _ := runtime.Caller(2)
|
||||
log.T.F("%s\n%s:%d", strings.TrimSpace(txt), file, line)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,60 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/dgraph-io/badger/v4"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/log"
|
||||
"x.realy.lol/units"
|
||||
)
|
||||
|
||||
type D struct {
|
||||
dataDir string
|
||||
BlockCacheSize int
|
||||
Logger *logger
|
||||
InitLogLevel int
|
||||
// DB is the badger db
|
||||
*badger.DB
|
||||
// seq is the monotonic collision free index for raw event storage.
|
||||
seq *badger.Sequence
|
||||
}
|
||||
|
||||
func New() (d *D) {
|
||||
d = &D{BlockCacheSize: units.Gb}
|
||||
return
|
||||
}
|
||||
|
||||
// Path returns the path where the database files are stored.
|
||||
func (d *D) Path() string { return d.dataDir }
|
||||
|
||||
// Init sets up the database with the loaded configuration.
|
||||
func (d *D) Init(path string) (err error) {
|
||||
d.dataDir = path
|
||||
log.I.Ln("opening realy event store at", d.dataDir)
|
||||
opts := badger.DefaultOptions(d.dataDir)
|
||||
opts.BlockCacheSize = int64(d.BlockCacheSize)
|
||||
opts.BlockSize = units.Gb
|
||||
opts.CompactL0OnClose = true
|
||||
opts.LmaxCompaction = true
|
||||
d.Logger = NewLogger(d.InitLogLevel, d.dataDir)
|
||||
opts.Logger = d.Logger
|
||||
if d.DB, err = badger.Open(opts); chk.E(err) {
|
||||
return err
|
||||
}
|
||||
log.T.Ln("getting event store sequence index", d.dataDir)
|
||||
if d.seq, err = d.DB.GetSequence([]byte("events"), 1000); chk.E(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (d *D) Close() (err error) { return d.DB.Close() }
|
||||
|
||||
// Serial returns the next monotonic conflict free unique serial on the database.
|
||||
func (d *D) Serial() (ser uint64, err error) {
|
||||
if ser, err = d.seq.Next(); chk.E(err) {
|
||||
}
|
||||
// log.T.ToSliceOfBytes("serial %x", ser)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,12 +5,17 @@ import (
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/ec/schnorr"
|
||||
"x.realy.lol/errorf"
|
||||
"x.realy.lol/hex"
|
||||
"x.realy.lol/timestamp"
|
||||
"x.realy.lol/varint"
|
||||
)
|
||||
|
||||
// MarshalBinary writes a binary encoding of an event.
|
||||
// todo: maybe we should make e and p tag values binary to reduce space usage
|
||||
|
||||
// MarshalWrite writes a binary encoding of an event.
|
||||
//
|
||||
// NOTE: Event must not be nil or this will panic. Use event.New.
|
||||
//
|
||||
// [ 32 bytes Id ]
|
||||
// [ 32 bytes Pubkey ]
|
||||
@@ -25,7 +30,10 @@ import (
|
||||
//
|
||||
// [ varint Content length ]
|
||||
// [ 64 bytes Sig ]
|
||||
func (ev *E) MarshalWrite(w io.Writer) {
|
||||
func (ev *E) MarshalWrite(w io.Writer) (err error) {
|
||||
if ev == nil {
|
||||
panic("cannot marshal a nil event")
|
||||
}
|
||||
_, _ = w.Write(ev.GetIdBytes())
|
||||
_, _ = w.Write(ev.GetPubkeyBytes())
|
||||
varint.Encode(w, uint64(ev.CreatedAt))
|
||||
@@ -33,18 +41,44 @@ func (ev *E) MarshalWrite(w io.Writer) {
|
||||
varint.Encode(w, uint64(len(ev.Tags)))
|
||||
for _, x := range ev.Tags {
|
||||
varint.Encode(w, uint64(len(x)))
|
||||
for _, y := range x {
|
||||
varint.Encode(w, uint64(len(y)))
|
||||
_, _ = w.Write([]byte(y))
|
||||
// e and p tag values should be hex
|
||||
var isBin bool
|
||||
if len(x) > 1 && (x[0] == "e" || x[0] == "p") {
|
||||
isBin = true
|
||||
}
|
||||
for i, y := range x {
|
||||
if i == 1 && isBin {
|
||||
var b []byte
|
||||
b, err = hex.Dec(y)
|
||||
if err != nil {
|
||||
err = errorf.E("e or p tag value not hex: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if len(b) != 32 {
|
||||
err = errorf.E("e or p tag value with invalid decoded byte length %d", len(b))
|
||||
return
|
||||
}
|
||||
varint.Encode(w, uint64(len(b)))
|
||||
_, _ = w.Write(b)
|
||||
} else {
|
||||
varint.Encode(w, uint64(len(y)))
|
||||
_, _ = w.Write([]byte(y))
|
||||
}
|
||||
}
|
||||
}
|
||||
varint.Encode(w, uint64(len(ev.Content)))
|
||||
_, _ = w.Write([]byte(ev.Content))
|
||||
_, _ = w.Write(ev.GetSigBytes())
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalRead decodes an event in binary form into an allocated event struct.
|
||||
//
|
||||
// NOTE: Event must not be nil or this will panic. Use event.New.
|
||||
func (ev *E) UnmarshalRead(r io.Reader) (err error) {
|
||||
if ev == nil {
|
||||
panic("cannot unmarshal into nil event struct")
|
||||
}
|
||||
id := make([]byte, 32)
|
||||
if _, err = r.Read(id); chk.E(err) {
|
||||
return
|
||||
@@ -75,7 +109,8 @@ func (ev *E) UnmarshalRead(r io.Reader) (err error) {
|
||||
return
|
||||
}
|
||||
var t []string
|
||||
for range nField {
|
||||
var isBin bool
|
||||
for i := range nField {
|
||||
var lenField uint64
|
||||
if lenField, err = varint.Decode(r); chk.E(err) {
|
||||
return
|
||||
@@ -84,6 +119,17 @@ func (ev *E) UnmarshalRead(r io.Reader) (err error) {
|
||||
if _, err = r.Read(field); chk.E(err) {
|
||||
return
|
||||
}
|
||||
// if it is first field, length 1 and is e or p, the value field must be binary
|
||||
if i == 0 && len(field) == 1 && (field[0] == 'e' || field[0] == 'p') {
|
||||
isBin = true
|
||||
}
|
||||
if i == 1 && isBin {
|
||||
// this is a binary value, was an e or p tag key, 32 bytes long, encode value
|
||||
// field to hex
|
||||
f := make([]byte, 64)
|
||||
_ = hex.EncBytes(f, field)
|
||||
field = f
|
||||
}
|
||||
t = append(t, string(field))
|
||||
}
|
||||
ev.Tags = append(ev.Tags, t)
|
||||
|
||||
@@ -22,7 +22,7 @@ type E struct {
|
||||
Id string `json:"id"`
|
||||
Pubkey string `json:"pubkey"`
|
||||
CreatedAt timestamp.Timestamp `json:"created_at"`
|
||||
Kind int `json:"kind`
|
||||
Kind int `json:"kind"`
|
||||
Tags tags.Tags `json:"tags"`
|
||||
Content string `json:"content"`
|
||||
Sig string `json:"sig"`
|
||||
@@ -30,14 +30,45 @@ type E struct {
|
||||
|
||||
func New() (ev *E) { return &E{} }
|
||||
|
||||
func (ev *E) Marshal() (b []byte, err error) {
|
||||
if b, err = json.Marshal(ev); chk.E(err) {
|
||||
func (ev *E) IdBytes() (idBytes []byte, err error) {
|
||||
if idBytes, err = hex.Dec(ev.Id); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ev *E) PubBytes() (pubBytes []byte, err error) {
|
||||
if pubBytes, err = hex.Dec(ev.Pubkey); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ev *E) SigBytes() (sigBytes []byte, err error) {
|
||||
if sigBytes, err = hex.Dec(ev.Sig); chk.E(err) {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ev *E) Marshal() (b []byte, err error) {
|
||||
if ev == nil {
|
||||
panic("cannot marshal a nil event")
|
||||
}
|
||||
if b, err = json.Marshal(ev); chk.E(err) {
|
||||
return
|
||||
}
|
||||
// there is a problem with some specific characters here
|
||||
b = bytes.ReplaceAll(b, []byte("\\u0026"), []byte("&"))
|
||||
return
|
||||
}
|
||||
|
||||
func (ev *E) Unmarshal(b []byte) (err error) {
|
||||
if ev == nil {
|
||||
panic("cannot unmarshal into a nil event")
|
||||
}
|
||||
// there is a problem with some specific characters here
|
||||
b = bytes.ReplaceAll(b, []byte("\\u0026"), []byte("&"))
|
||||
if err = json.Unmarshal(b, ev); chk.E(err) {
|
||||
return
|
||||
}
|
||||
@@ -48,9 +79,14 @@ func (ev *E) Unmarshal(b []byte) (err error) {
|
||||
// logging.
|
||||
func (ev *E) Serialize() (b []byte) {
|
||||
var err error
|
||||
if len(ev.Tags) == 1 && len(ev.Tags[0]) == 1 {
|
||||
ev.Tags = ev.Tags[:0]
|
||||
}
|
||||
if b, err = json.Marshal(ev); chk.E(err) {
|
||||
return
|
||||
}
|
||||
// there is a problem with some specific characters here
|
||||
b = bytes.ReplaceAll(b, []byte("\\u0026"), []byte("&"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,7 +116,7 @@ func (ev *E) Verify() (valid bool, err error) {
|
||||
// check that this isn't because of a bogus Id
|
||||
id := ev.GenIdBytes()
|
||||
if !bytes.Equal(id, ev.GetIdBytes()) {
|
||||
log.E.Ln("event Id incorrect")
|
||||
log.E.F("event Id incorrect\n%s\n%s", ev.Serialize(), ev.ToCanonical(nil))
|
||||
ev.Id = hex.Enc(id)
|
||||
err = nil
|
||||
if valid, err = keys.Verify(ev.GetIdBytes(), ev.GetSigBytes()); chk.E(err) {
|
||||
|
||||
16
go.mod
16
go.mod
@@ -4,7 +4,9 @@ go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/alexflint/go-arg v1.5.1
|
||||
github.com/clipperhouse/uax29 v1.14.3
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dgraph-io/badger/v4 v4.7.0
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/mailru/easyjson v0.9.0
|
||||
@@ -22,18 +24,32 @@ require (
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/alexflint/go-scalar v1.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/felixge/fgprof v0.9.5 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/flatbuffers v25.2.10+incompatible // indirect
|
||||
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/templexxx/cpu v0.1.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250530174510-65e920069ea6 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
61
go.sum
61
go.sum
@@ -1,11 +1,11 @@
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/alexflint/go-arg v1.5.1 h1:nBuWUCpuRy0snAG+uIJ6N0UvYxpxA0/ghA/AaHxlT8Y=
|
||||
github.com/alexflint/go-arg v1.5.1/go.mod h1:A7vTJzvjoaSTypg4biM5uYNTkJ27SkNTArtYXnlqVO8=
|
||||
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
|
||||
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
@@ -15,19 +15,36 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/clipperhouse/uax29 v1.14.3 h1:pJ0hZWycgsBrJ8SSsvPCrlMTpW8C+fdcA/0mehFDCU0=
|
||||
github.com/clipperhouse/uax29 v1.14.3/go.mod h1:paNABhygWmmjkg0ROxKQoenJAX4dM9AS8biVkXmAK0c=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y=
|
||||
github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA=
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
||||
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
|
||||
@@ -38,10 +55,15 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
@@ -56,6 +78,8 @@ github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -70,28 +94,30 @@ github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b h1:XeDLE6c9mzHpdv3W
|
||||
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b/go.mod h1:7rwmCH0wC2fQvNEvPZ3sKXukhyCTyiaZ5VTZMQYpZKQ=
|
||||
go-simpler.org/env v0.12.0 h1:kt/lBts0J1kjWJAnB740goNdvwNxt5emhYngL0Fzufs=
|
||||
go-simpler.org/env v0.12.0/go.mod h1:cc/5Md9JCUM7LVLtN0HYjPTDcI3Q8TDaPlNTAlDU+WI=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/exp v0.0.0-20250530174510-65e920069ea6 h1:gllJVKwONftmCc4KlNbN8o/LvmbxotqQy6zzi6yDQOQ=
|
||||
golang.org/x/exp v0.0.0-20250530174510-65e920069ea6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=
|
||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250530174510-65e920069ea6 h1:Gq937g8bNiCnWB/wsoyxuxnfDpAE9cpYo4sLIp9t0LA=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250530174510-65e920069ea6/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ=
|
||||
golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA=
|
||||
golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -102,14 +128,17 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
1
ints/base10k.txt
Normal file
1
ints/base10k.txt
Normal file
File diff suppressed because one or more lines are too long
20
ints/gen/pregen.go
Normal file
20
ints/gen/pregen.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Package main is a generator for the base10000 (4 digit) encoding of the ints
|
||||
// library.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fh, err := os.Create("pkg/ints/base10k.txt")
|
||||
if chk.E(err) {
|
||||
panic(err)
|
||||
}
|
||||
for i := range 10000 {
|
||||
fmt.Fprintf(fh, "%04d", i)
|
||||
}
|
||||
}
|
||||
134
ints/ints.go
Normal file
134
ints/ints.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Package ints is an optimised encoder for decimal numbers in ASCII format,
|
||||
// that simplifies and accelerates encoding and decoding decimal strings. It is
|
||||
// faster than strconv in part because it uses a base of 10000 and a lookup
|
||||
// table.
|
||||
package ints
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"io"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
|
||||
"x.realy.lol/errorf"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// T is an integer with a fast codec to decimal ASCII.
|
||||
type T struct {
|
||||
N uint64
|
||||
}
|
||||
|
||||
func New[V constraints.Integer](n V) *T {
|
||||
return &T{uint64(n)}
|
||||
}
|
||||
|
||||
// Uint64 returns the int.T as a uint64 (the base type)
|
||||
func (n *T) Uint64() uint64 { return n.N }
|
||||
|
||||
// Int64 returns an int64 from the base number (may cause truncation)
|
||||
func (n *T) Int64() int64 { return int64(n.N) }
|
||||
|
||||
// Uint16 returns an uint16 from the base number (may cause truncation)
|
||||
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'
|
||||
|
||||
// Marshal the int.T into a byte string.
|
||||
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 no larger than math.MaxUint64,
|
||||
// skipping any non-numeric content before it.
|
||||
//
|
||||
// Note that leading zeros are not considered valid, but basically no 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
|
||||
}
|
||||
// skip non-number characters
|
||||
for i, v := range b {
|
||||
if v >= '0' && v <= '9' {
|
||||
b = b[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(b) == 0 {
|
||||
err = io.EOF
|
||||
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
|
||||
}
|
||||
79
ints/ints_test.go
Normal file
79
ints/ints_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package ints
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"lukechampine.com/frand"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
291
kind/kinds.go
291
kind/kinds.go
@@ -1,135 +1,135 @@
|
||||
package kind
|
||||
|
||||
const (
|
||||
ProfileMetadata int = 0
|
||||
TextNote int = 1
|
||||
RecommendServer int = 2
|
||||
FollowList int = 3
|
||||
EncryptedDirectMessage int = 4
|
||||
Deletion int = 5
|
||||
Repost int = 6
|
||||
Reaction int = 7
|
||||
BadgeAward int = 8
|
||||
SimpleGroupChatMessage int = 9
|
||||
SimpleGroupThreadedReply int = 10
|
||||
SimpleGroupThread int = 11
|
||||
SimpleGroupReply int = 12
|
||||
Seal int = 13
|
||||
DirectMessage int = 14
|
||||
GenericRepost int = 16
|
||||
ReactionToWebsite int = 17
|
||||
ChannelCreation int = 40
|
||||
ChannelMetadata int = 41
|
||||
ChannelMessage int = 42
|
||||
ChannelHideMessage int = 43
|
||||
ChannelMuteUser int = 44
|
||||
Chess int = 64
|
||||
MergeRequests int = 818
|
||||
Bid int = 1021
|
||||
BidConfirmation int = 1022
|
||||
OpenTimestamps int = 1040
|
||||
GiftWrap int = 1059
|
||||
FileMetadata int = 1063
|
||||
LiveChatMessage int = 1311
|
||||
Patch int = 1617
|
||||
Issue int = 1621
|
||||
Reply int = 1622
|
||||
StatusOpen int = 1630
|
||||
StatusApplied int = 1631
|
||||
StatusClosed int = 1632
|
||||
StatusDraft int = 1633
|
||||
ProblemTracker int = 1971
|
||||
Reporting int = 1984
|
||||
Label int = 1985
|
||||
RelayReviews int = 1986
|
||||
AIEmbeddings int = 1987
|
||||
Torrent int = 2003
|
||||
TorrentComment int = 2004
|
||||
CoinjoinPool int = 2022
|
||||
CommunityPostApproval int = 4550
|
||||
JobFeedback int = 7000
|
||||
SimpleGroupPutUser int = 9000
|
||||
SimpleGroupRemoveUser int = 9001
|
||||
SimpleGroupEditMetadata int = 9002
|
||||
SimpleGroupDeleteEvent int = 9005
|
||||
SimpleGroupCreateGroup int = 9007
|
||||
SimpleGroupDeleteGroup int = 9008
|
||||
SimpleGroupCreateInvite int = 9009
|
||||
SimpleGroupJoinRequest int = 9021
|
||||
SimpleGroupLeaveRequest int = 9022
|
||||
ZapGoal int = 9041
|
||||
TidalLogin int = 9467
|
||||
ZapRequest int = 9734
|
||||
Zap int = 9735
|
||||
Highlights int = 9802
|
||||
MuteList int = 10000
|
||||
PinList int = 10001
|
||||
RelayListMetadata int = 10002
|
||||
BookmarkList int = 10003
|
||||
CommunityList int = 10004
|
||||
PublicChatList int = 10005
|
||||
BlockedRelayList int = 10006
|
||||
SearchRelayList int = 10007
|
||||
SimpleGroupList int = 10009
|
||||
InterestList int = 10015
|
||||
EmojiList int = 10030
|
||||
DMRelayList int = 10050
|
||||
UserServerList int = 10063
|
||||
FileStorageServerList int = 10096
|
||||
GoodWikiAuthorList int = 10101
|
||||
GoodWikiRelayList int = 10102
|
||||
NWCWalletInfo int = 13194
|
||||
LightningPubRPC int = 21000
|
||||
ClientAuthentication int = 22242
|
||||
NWCWalletRequest int = 23194
|
||||
NWCWalletResponse int = 23195
|
||||
NostrConnect int = 24133
|
||||
Blobs int = 24242
|
||||
HTTPAuth int = 27235
|
||||
CategorizedPeopleList int = 30000
|
||||
CategorizedBookmarksList int = 30001
|
||||
RelaySets int = 30002
|
||||
BookmarkSets int = 30003
|
||||
CuratedSets int = 30004
|
||||
CuratedVideoSets int = 30005
|
||||
MuteSets int = 30007
|
||||
ProfileBadges int = 30008
|
||||
BadgeDefinition int = 30009
|
||||
InterestSets int = 30015
|
||||
StallDefinition int = 30017
|
||||
ProductDefinition int = 30018
|
||||
MarketplaceUI int = 30019
|
||||
ProductSoldAsAuction int = 30020
|
||||
Article int = 30023
|
||||
DraftArticle int = 30024
|
||||
EmojiSets int = 30030
|
||||
ModularArticleHeader int = 30040
|
||||
ModularArticleContent int = 30041
|
||||
ReleaseArtifactSets int = 30063
|
||||
ApplicationSpecificData int = 30078
|
||||
LiveEvent int = 30311
|
||||
UserStatuses int = 30315
|
||||
ClassifiedListing int = 30402
|
||||
DraftClassifiedListing int = 30403
|
||||
RepositoryAnnouncement int = 30617
|
||||
RepositoryState int = 30618
|
||||
SimpleGroupMetadata int = 39000
|
||||
SimpleGroupAdmins int = 39001
|
||||
SimpleGroupMembers int = 39002
|
||||
SimpleGroupRoles int = 39003
|
||||
WikiArticle int = 30818
|
||||
Redirects int = 30819
|
||||
Feed int = 31890
|
||||
DateCalendarEvent int = 31922
|
||||
TimeCalendarEvent int = 31923
|
||||
Calendar int = 31924
|
||||
CalendarEventRSVP int = 31925
|
||||
HandlerRecommendation int = 31989
|
||||
HandlerInformation int = 31990
|
||||
VideoEvent int = 34235
|
||||
ShortVideoEvent int = 34236
|
||||
VideoViewEvent int = 34237
|
||||
CommunityDefinition int = 34550
|
||||
ProfileMetadata = 0
|
||||
TextNote = 1
|
||||
RecommendServer = 2
|
||||
FollowList = 3
|
||||
EncryptedDirectMessage = 4
|
||||
Deletion = 5
|
||||
Repost = 6
|
||||
Reaction = 7
|
||||
BadgeAward = 8
|
||||
SimpleGroupChatMessage = 9
|
||||
SimpleGroupThreadedReply = 10
|
||||
SimpleGroupThread = 11
|
||||
SimpleGroupReply = 12
|
||||
Seal = 13
|
||||
DirectMessage = 14
|
||||
GenericRepost = 16
|
||||
ReactionToWebsite = 17
|
||||
ChannelCreation = 40
|
||||
ChannelMetadata = 41
|
||||
ChannelMessage = 42
|
||||
ChannelHideMessage = 43
|
||||
ChannelMuteUser = 44
|
||||
Chess = 64
|
||||
MergeRequests = 818
|
||||
Bid = 1021
|
||||
BidConfirmation = 1022
|
||||
OpenTimestamps = 1040
|
||||
GiftWrap = 1059
|
||||
FileMetadata = 1063
|
||||
LiveChatMessage = 1311
|
||||
Patch = 1617
|
||||
Issue = 1621
|
||||
Reply = 1622
|
||||
StatusOpen = 1630
|
||||
StatusApplied = 1631
|
||||
StatusClosed = 1632
|
||||
StatusDraft = 1633
|
||||
ProblemTracker = 1971
|
||||
Reporting = 1984
|
||||
Label = 1985
|
||||
RelayReviews = 1986
|
||||
AIEmbeddings = 1987
|
||||
Torrent = 2003
|
||||
TorrentComment = 2004
|
||||
CoinjoinPool = 2022
|
||||
CommunityPostApproval = 4550
|
||||
JobFeedback = 7000
|
||||
SimpleGroupPutUser = 9000
|
||||
SimpleGroupRemoveUser = 9001
|
||||
SimpleGroupEditMetadata = 9002
|
||||
SimpleGroupDeleteEvent = 9005
|
||||
SimpleGroupCreateGroup = 9007
|
||||
SimpleGroupDeleteGroup = 9008
|
||||
SimpleGroupCreateInvite = 9009
|
||||
SimpleGroupJoinRequest = 9021
|
||||
SimpleGroupLeaveRequest = 9022
|
||||
ZapGoal = 9041
|
||||
TidalLogin = 9467
|
||||
ZapRequest = 9734
|
||||
Zap = 9735
|
||||
Highlights = 9802
|
||||
MuteList = 10000
|
||||
PinList = 10001
|
||||
RelayListMetadata = 10002
|
||||
BookmarkList = 10003
|
||||
CommunityList = 10004
|
||||
PublicChatList = 10005
|
||||
BlockedRelayList = 10006
|
||||
SearchRelayList = 10007
|
||||
SimpleGroupList = 10009
|
||||
InterestList = 10015
|
||||
EmojiList = 10030
|
||||
DMRelayList = 10050
|
||||
UserServerList = 10063
|
||||
FileStorageServerList = 10096
|
||||
GoodWikiAuthorList = 10101
|
||||
GoodWikiRelayList = 10102
|
||||
NWCWalletInfo = 13194
|
||||
LightningPubRPC = 21000
|
||||
ClientAuthentication = 22242
|
||||
NWCWalletRequest = 23194
|
||||
NWCWalletResponse = 23195
|
||||
NostrConnect = 24133
|
||||
Blobs = 24242
|
||||
HTTPAuth = 27235
|
||||
CategorizedPeopleList = 30000
|
||||
CategorizedBookmarksList = 30001
|
||||
RelaySets = 30002
|
||||
BookmarkSets = 30003
|
||||
CuratedSets = 30004
|
||||
CuratedVideoSets = 30005
|
||||
MuteSets = 30007
|
||||
ProfileBadges = 30008
|
||||
BadgeDefinition = 30009
|
||||
InterestSets = 30015
|
||||
StallDefinition = 30017
|
||||
ProductDefinition = 30018
|
||||
MarketplaceUI = 30019
|
||||
ProductSoldAsAuction = 30020
|
||||
Article = 30023
|
||||
DraftArticle = 30024
|
||||
EmojiSets = 30030
|
||||
ModularArticleHeader = 30040
|
||||
ModularArticleContent = 30041
|
||||
ReleaseArtifactSets = 30063
|
||||
ApplicationSpecificData = 30078
|
||||
LiveEvent = 30311
|
||||
UserStatuses = 30315
|
||||
ClassifiedListing = 30402
|
||||
DraftClassifiedListing = 30403
|
||||
RepositoryAnnouncement = 30617
|
||||
RepositoryState = 30618
|
||||
SimpleGroupMetadata = 39000
|
||||
SimpleGroupAdmins = 39001
|
||||
SimpleGroupMembers = 39002
|
||||
SimpleGroupRoles = 39003
|
||||
WikiArticle = 30818
|
||||
Redirects = 30819
|
||||
Feed = 31890
|
||||
DateCalendarEvent = 31922
|
||||
TimeCalendarEvent = 31923
|
||||
Calendar = 31924
|
||||
CalendarEventRSVP = 31925
|
||||
HandlerRecommendation = 31989
|
||||
HandlerInformation = 31990
|
||||
VideoEvent = 34235
|
||||
ShortVideoEvent = 34236
|
||||
VideoViewEvent = 34237
|
||||
CommunityDefinition = 34550
|
||||
)
|
||||
|
||||
func IsRegularKind(kind int) bool {
|
||||
@@ -147,3 +147,36 @@ func IsEphemeralKind(kind int) bool {
|
||||
func IsAddressableKind(kind int) bool {
|
||||
return 30000 <= kind && kind < 40000
|
||||
}
|
||||
|
||||
var Text = []int{
|
||||
ProfileMetadata,
|
||||
TextNote,
|
||||
Article,
|
||||
SimpleGroupThread,
|
||||
Reply,
|
||||
Repost,
|
||||
Issue,
|
||||
Reply,
|
||||
MergeRequests,
|
||||
WikiArticle,
|
||||
Issue,
|
||||
StatusOpen,
|
||||
StatusApplied,
|
||||
StatusClosed,
|
||||
StatusDraft,
|
||||
Torrent,
|
||||
TorrentComment,
|
||||
DateCalendarEvent,
|
||||
TimeCalendarEvent,
|
||||
Calendar,
|
||||
CalendarEventRSVP,
|
||||
}
|
||||
|
||||
func IsText(ki int) bool {
|
||||
for _, v := range Text {
|
||||
if v == ki {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
81
tags/tags.go
81
tags/tags.go
@@ -7,7 +7,10 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"x.realy.lol/chk"
|
||||
"x.realy.lol/helpers"
|
||||
"x.realy.lol/hex"
|
||||
"x.realy.lol/ints"
|
||||
"x.realy.lol/normalize"
|
||||
)
|
||||
|
||||
@@ -106,6 +109,18 @@ func (tags Tags) GetAll(tagPrefix []string) Tags {
|
||||
return result
|
||||
}
|
||||
|
||||
func (tags Tags) GetAllExactKeys(key string) Tags {
|
||||
result := make(Tags, 0, len(tags))
|
||||
for _, v := range tags {
|
||||
if v.StartsWith([]string{key}) {
|
||||
if v.Key() == key {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// All returns an iterator for all the tags that match the prefix, see [Tag.StartsWith]
|
||||
func (tags Tags) All(tagPrefix []string) iter.Seq2[int, Tag] {
|
||||
return func(yield func(int, Tag) bool) {
|
||||
@@ -131,17 +146,18 @@ func (tags Tags) FilterOut(tagPrefix []string) Tags {
|
||||
}
|
||||
|
||||
// FilterOutInPlace removes all tags that match the prefix, but potentially reorders the tags in unpredictable ways, see [Tag.StartsWith]
|
||||
func (tags *Tags) FilterOutInPlace(tagPrefix []string) {
|
||||
for i := 0; i < len(*tags); i++ {
|
||||
tag := (*tags)[i]
|
||||
func (tags Tags) FilterOutInPlace(tagPrefix []string) (t Tags) {
|
||||
for i := 0; i < len(tags); i++ {
|
||||
tag := (tags)[i]
|
||||
if tag.StartsWith(tagPrefix) {
|
||||
// remove this by swapping the last tag into this place
|
||||
last := len(*tags) - 1
|
||||
(*tags)[i] = (*tags)[last]
|
||||
*tags = (*tags)[0:last]
|
||||
last := len(tags) - 1
|
||||
(tags)[i] = (tags)[last]
|
||||
tags = (tags)[0:last]
|
||||
i-- // this is so we can match this just swapped item in the next iteration
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
// AppendUnique appends a tag if it doesn't exist yet, otherwise does nothing.
|
||||
@@ -158,7 +174,7 @@ func (tags Tags) AppendUnique(tag Tag) Tags {
|
||||
return tags
|
||||
}
|
||||
|
||||
func (t *Tags) Scan(src any) error {
|
||||
func (tags Tags) Scan(src any) (t Tags, err error) {
|
||||
var jtags []byte
|
||||
|
||||
switch v := src.(type) {
|
||||
@@ -167,11 +183,11 @@ func (t *Tags) Scan(src any) error {
|
||||
case string:
|
||||
jtags = []byte(v)
|
||||
default:
|
||||
return errors.New("couldn't scan tags, it's not a json string")
|
||||
err = errors.New("couldn't scan tags, it's not a json string")
|
||||
return
|
||||
}
|
||||
|
||||
json.Unmarshal(jtags, &t)
|
||||
return nil
|
||||
err = json.Unmarshal(jtags, &tags)
|
||||
return
|
||||
}
|
||||
|
||||
func (tags Tags) ContainsAny(tagName string, values []string) bool {
|
||||
@@ -218,3 +234,46 @@ func (tags Tags) marshalTo(dst []byte) []byte {
|
||||
dst = append(dst, ']')
|
||||
return dst
|
||||
}
|
||||
|
||||
type Tag_a struct {
|
||||
Kind int
|
||||
Pubkey []byte
|
||||
Ident string
|
||||
}
|
||||
|
||||
func (tags Tags) Get_a_Tags() (atags []Tag_a) {
|
||||
a := tags.GetAll([]string{"a"})
|
||||
var err error
|
||||
if len(a) > 0 {
|
||||
for _, v := range a {
|
||||
if v[0] == "a" && len(v) > 1 {
|
||||
// try to split it
|
||||
parts := strings.Split(v[1], ":")
|
||||
// there must be a kind first
|
||||
ki := ints.New(0)
|
||||
if _, err = ki.Unmarshal([]byte(parts[0])); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
atag := Tag_a{
|
||||
Kind: int(ki.Uint16()),
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
// next must be a pubkey
|
||||
var pk []byte
|
||||
if pk, err = hex.Dec(parts[1]); chk.E(err) {
|
||||
continue
|
||||
}
|
||||
atag.Pubkey = pk
|
||||
// there possibly can be nothing after this
|
||||
if len(parts) >= 3 {
|
||||
// third part is the identifier (d tag)
|
||||
atag.Ident = parts[2]
|
||||
}
|
||||
atags = append(atags, atag)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
12
units/units.go
Normal file
12
units/units.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package units is a convenient set of names designating data sizes in bytes
|
||||
// using common ISO names (base 10).
|
||||
package units
|
||||
|
||||
const (
|
||||
Kilobyte = 1000
|
||||
Kb = Kilobyte
|
||||
Megabyte = Kilobyte * Kilobyte
|
||||
Mb = Megabyte
|
||||
Gigabyte = Megabyte * Kilobyte
|
||||
Gb = Gigabyte
|
||||
)
|
||||
Reference in New Issue
Block a user