Files
next.orly.dev/pkg/encoders/text/helpers.go
mleku a4fc3d8d9b
Some checks failed
Go / build (push) Has been cancelled
Go / release (push) Has been cancelled
Implement spider functionality for event synchronization
- Introduced a new `spider` package to manage connections to admin relays and synchronize events for followed pubkeys.
- Added configuration options for spider mode in the application settings, allowing for different operational modes (e.g., follows).
- Implemented callback mechanisms to dynamically retrieve admin relays and follow lists.
- Enhanced the main application to initialize and manage the spider, including starting and stopping its operation.
- Added tests to validate spider creation, callbacks, and operational behavior.
- Bumped version to v0.17.14.
2025-10-22 22:24:21 +01:00

274 lines
5.5 KiB
Go

package text
import (
"io"
"github.com/templexxx/xhex"
"lol.mleku.dev/chk"
"lol.mleku.dev/errorf"
"next.orly.dev/pkg/encoders/hex"
"next.orly.dev/pkg/utils"
)
// JSONKey generates the JSON format for an object key and terminates with the semicolon.
func JSONKey(dst, k []byte) (b []byte) {
dst = append(dst, '"')
dst = append(dst, k...)
dst = append(dst, '"', ':')
b = dst
return
}
// UnmarshalHex takes a byte string that should contain a quoted hexadecimal
// encoded value, decodes it using a SIMD hex codec and returns the decoded
// bytes in a newly allocated buffer.
func UnmarshalHex(b []byte) (h []byte, rem []byte, err error) {
rem = b[:]
var inQuote bool
var start int
for i := 0; i < len(b); i++ {
if !inQuote {
if b[i] == '"' {
inQuote = true
start = i + 1
}
} else if b[i] == '"' {
hexStr := b[start:i]
rem = b[i+1:]
l := len(hexStr)
if l%2 != 0 {
err = errorf.E(
"invalid length for hex: %d, %0x",
len(hexStr), hexStr,
)
return
}
// Allocate a new buffer for the decoded data
h = make([]byte, l/2)
if err = xhex.Decode(h, hexStr); chk.E(err) {
return
}
return
}
}
if !inQuote {
err = io.EOF
return
}
return
}
// UnmarshalQuoted performs an in-place unquoting of NIP-01 quoted byte string.
func UnmarshalQuoted(b []byte) (content, rem []byte, err error) {
if len(b) == 0 {
err = io.EOF
return
}
rem = b[:]
for ; len(rem) >= 0; rem = rem[1:] {
if len(rem) == 0 {
err = io.EOF
return
}
// advance to open quotes
if rem[0] == '"' {
rem = rem[1:]
content = rem
break
}
}
if len(rem) == 0 {
err = io.EOF
return
}
var escaping bool
var contentLen int
for len(rem) > 0 {
if rem[0] == '\\' {
if !escaping {
escaping = true
contentLen++
rem = rem[1:]
} else {
escaping = false
contentLen++
rem = rem[1:]
}
} else if rem[0] == '"' {
if !escaping {
rem = rem[1:]
content = content[:contentLen]
// Create a copy of the content to avoid corrupting the original input buffer
contentCopy := make([]byte, len(content))
copy(contentCopy, content)
content = NostrUnescape(contentCopy)
return
}
contentLen++
rem = rem[1:]
escaping = false
} else {
escaping = false
switch rem[0] {
// none of these characters are allowed inside a JSON string:
//
// backspace, tab, newline, form feed or carriage return.
case '\b', '\t', '\n', '\f', '\r':
pos := len(content) - len(rem)
contextStart := pos - 10
if contextStart < 0 {
contextStart = 0
}
contextEnd := pos + 10
if contextEnd > len(content) {
contextEnd = len(content)
}
err = errorf.E(
"invalid character '%s' in quoted string (position %d, context: %q)",
NostrEscape(nil, rem[:1]),
pos,
string(content[contextStart:contextEnd]),
)
return
}
contentLen++
rem = rem[1:]
}
}
return
}
func MarshalHexArray(dst []byte, ha [][]byte) (b []byte) {
dst = append(dst, '[')
for i := range ha {
dst = AppendQuote(dst, ha[i], hex.EncAppend)
if i != len(ha)-1 {
dst = append(dst, ',')
}
}
dst = append(dst, ']')
b = dst
return
}
// UnmarshalHexArray unpacks a JSON array containing strings with hexadecimal, and checks all
// values have the specified byte size.
func UnmarshalHexArray(b []byte, size int) (t [][]byte, rem []byte, err error) {
rem = b
var openBracket bool
for ; len(rem) > 0; rem = rem[1:] {
if rem[0] == '[' {
openBracket = true
} else if openBracket {
if rem[0] == ',' {
continue
} else if rem[0] == ']' {
rem = rem[1:]
return
} else if rem[0] == '"' {
var h []byte
if h, rem, err = UnmarshalHex(rem); chk.E(err) {
return
}
if len(h) != size {
err = errorf.E(
"invalid hex array size, got %d expect %d",
2*len(h), 2*size,
)
return
}
t = append(t, h)
if rem[0] == ']' {
rem = rem[1:]
// done
return
}
}
}
}
return
}
// UnmarshalStringArray unpacks a JSON array containing strings.
func UnmarshalStringArray(b []byte) (t [][]byte, rem []byte, err error) {
rem = b
var openBracket bool
for ; len(rem) > 0; rem = rem[1:] {
if rem[0] == '[' {
openBracket = true
} else if openBracket {
if rem[0] == ',' {
continue
} else if rem[0] == ']' {
rem = rem[1:]
return
} else if rem[0] == '"' {
var h []byte
if h, rem, err = UnmarshalQuoted(rem); chk.E(err) {
return
}
t = append(t, h)
if rem[0] == ']' {
rem = rem[1:]
// done
return
}
}
}
}
return
}
func True() []byte { return []byte("true") }
func False() []byte { return []byte("false") }
func MarshalBool(src []byte, truth bool) []byte {
if truth {
return append(src, True()...)
}
return append(src, False()...)
}
func UnmarshalBool(src []byte) (rem []byte, truth bool, err error) {
rem = src
t, f := True(), False()
for i := range rem {
if rem[i] == t[0] {
if len(rem) < i+len(t) {
err = io.EOF
return
}
if utils.FastEqual(t, rem[i:i+len(t)]) {
truth = true
rem = rem[i+len(t):]
return
}
}
if rem[i] == f[0] {
if len(rem) < i+len(f) {
err = io.EOF
return
}
if utils.FastEqual(f, rem[i:i+len(f)]) {
rem = rem[i+len(f):]
return
}
}
}
// if a truth value is not found in the string it will run to the end
err = io.EOF
return
}
func Comma(b []byte) (rem []byte, err error) {
rem = b
for i := range rem {
if rem[i] == ',' {
rem = rem[i:]
return
}
}
err = io.EOF
return
}