Skip to content

Commit

Permalink
negentropy: do the algorithm entirely in hex.
Browse files Browse the repository at this point in the history
plus:
  - nicer iterators
  - some optimizations here and there.
  - something else I forgot.
  • Loading branch information
fiatjaf committed Sep 14, 2024
1 parent b5f8d48 commit 286040c
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 195 deletions.
64 changes: 38 additions & 26 deletions nip77/negentropy/encoding.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package negentropy

import (
"bytes"
"encoding/hex"
"fmt"

"github.com/nbd-wtf/go-nostr"
)

func (n *Negentropy) DecodeTimestampIn(reader *bytes.Reader) (nostr.Timestamp, error) {
func (n *Negentropy) DecodeTimestampIn(reader *StringHexReader) (nostr.Timestamp, error) {
t, err := decodeVarInt(reader)
if err != nil {
return 0, err
Expand All @@ -28,47 +27,42 @@ func (n *Negentropy) DecodeTimestampIn(reader *bytes.Reader) (nostr.Timestamp, e
return timestamp, nil
}

func (n *Negentropy) DecodeBound(reader *bytes.Reader) (Bound, error) {
func (n *Negentropy) DecodeBound(reader *StringHexReader) (Bound, error) {
timestamp, err := n.DecodeTimestampIn(reader)
if err != nil {
return Bound{}, err
return Bound{}, fmt.Errorf("failed to decode bound timestamp: %w", err)
}

length, err := decodeVarInt(reader)
if err != nil {
return Bound{}, err
return Bound{}, fmt.Errorf("failed to decode bound length: %w", err)
}

id := make([]byte, length)
if _, err = reader.Read(id); err != nil {
return Bound{}, err
id, err := reader.ReadString(length * 2)
if err != nil {
return Bound{}, fmt.Errorf("failed to read bound id: %w", err)
}

return Bound{Item{timestamp, hex.EncodeToString(id)}}, nil
return Bound{Item{timestamp, id}}, nil
}

func (n *Negentropy) encodeTimestampOut(timestamp nostr.Timestamp) []byte {
func (n *Negentropy) encodeTimestampOut(w *StringHexWriter, timestamp nostr.Timestamp) {
if timestamp == maxTimestamp {
n.lastTimestampOut = maxTimestamp
return encodeVarInt(0)
encodeVarIntToHex(w, 0)
return
}
temp := timestamp
timestamp -= n.lastTimestampOut
n.lastTimestampOut = temp
return encodeVarInt(int(timestamp + 1))
encodeVarIntToHex(w, int(timestamp+1))
return
}

func (n *Negentropy) encodeBound(bound Bound) []byte {
var output []byte

t := n.encodeTimestampOut(bound.Timestamp)
idlen := encodeVarInt(len(bound.ID) / 2)
output = append(output, t...)
output = append(output, idlen...)
id, _ := hex.DecodeString(bound.Item.ID)

output = append(output, id...)
return output
func (n *Negentropy) encodeBound(w *StringHexWriter, bound Bound) {
n.encodeTimestampOut(w, bound.Timestamp)
encodeVarIntToHex(w, len(bound.ID)/2)
w.WriteHex(bound.Item.ID)
}

func getMinimalBound(prev, curr Item) Bound {
Expand All @@ -89,11 +83,11 @@ func getMinimalBound(prev, curr Item) Bound {
return Bound{Item{curr.Timestamp, curr.ID[:(sharedPrefixBytes+1)*2]}}
}

func decodeVarInt(reader *bytes.Reader) (int, error) {
func decodeVarInt(reader *StringHexReader) (int, error) {
var res int = 0

for {
b, err := reader.ReadByte()
b, err := reader.ReadHexByte()
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -124,3 +118,21 @@ func encodeVarInt(n int) []byte {

return o
}

func encodeVarIntToHex(w *StringHexWriter, n int) {
if n == 0 {
w.WriteByte(0)
}

var o []byte
for n != 0 {
o = append([]byte{byte(n & 0x7F)}, o...)
n >>= 7
}

for i := 0; i < len(o)-1; i++ {
o[i] |= 0x80
}

w.WriteBytes(o)
}
96 changes: 96 additions & 0 deletions nip77/negentropy/hex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package negentropy

import (
"encoding/hex"
"io"
)

func NewStringHexReader(source string) *StringHexReader {
return &StringHexReader{source, 0, make([]byte, 1)}
}

type StringHexReader struct {
source string
idx int

tmp []byte
}

func (r *StringHexReader) Len() int {
return len(r.source) - r.idx
}

func (r *StringHexReader) ReadHexBytes(buf []byte) error {
n := len(buf) * 2
r.idx += n
if len(r.source) < r.idx {
return io.EOF
}
_, err := hex.Decode(buf, []byte(r.source[r.idx-n:r.idx]))
return err
}

func (r *StringHexReader) ReadHexByte() (byte, error) {
err := r.ReadHexBytes(r.tmp)
return r.tmp[0], err
}

func (r *StringHexReader) ReadString(size int) (string, error) {
if size == 0 {
return "", nil
}
r.idx += size
if len(r.source) < r.idx {
return "", io.EOF
}
return r.source[r.idx-size : r.idx], nil
}

func NewStringHexWriter(buf []byte) *StringHexWriter {
return &StringHexWriter{buf, make([]byte, 2)}
}

type StringHexWriter struct {
hexbuf []byte

tmp []byte
}

func (r *StringHexWriter) Len() int {
return len(r.hexbuf)
}

func (r *StringHexWriter) Hex() string {
return string(r.hexbuf)
}

func (r *StringHexWriter) Reset() {
r.hexbuf = r.hexbuf[:0]
}

func (r *StringHexWriter) WriteHex(hexString string) {
r.hexbuf = append(r.hexbuf, hexString...)
return
}

func (r *StringHexWriter) WriteByte(b byte) error {
hex.Encode(r.tmp, []byte{b})
r.hexbuf = append(r.hexbuf, r.tmp...)
return nil
}

func (r *StringHexWriter) WriteBytes(in []byte) {
r.hexbuf = hex.AppendEncode(r.hexbuf, in)

// curr := len(r.hexbuf)
// next := curr + len(in)*2
// for cap(r.hexbuf) < next {
// r.hexbuf = append(r.hexbuf, in...)
// }
// r.hexbuf = r.hexbuf[0:next]
// dst := r.hexbuf[curr:next]

// hex.Encode(dst, in)

return
}
Loading

0 comments on commit 286040c

Please sign in to comment.