Add core packages, configuration system, and initial application structure
This commit is contained in:
127
pkg/encoders/text/escape.go
Normal file
127
pkg/encoders/text/escape.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package text
|
||||
|
||||
// NostrEscape for JSON encoding according to RFC8259.
|
||||
//
|
||||
// This is the efficient implementation based on the NIP-01 specification:
|
||||
//
|
||||
// To prevent implementation differences from creating a different event ID for
|
||||
// the same event, the following rules MUST be followed while serializing:
|
||||
//
|
||||
// No whitespace, line breaks or other unnecessary formatting should be included
|
||||
// in the output JSON. No characters except the following should be escaped, and
|
||||
// instead should be included verbatim:
|
||||
//
|
||||
// - A line break, 0x0A, as \n
|
||||
// - A double quote, 0x22, as \"
|
||||
// - A backslash, 0x5C, as \\
|
||||
// - A carriage return, 0x0D, as \r
|
||||
// - A tab character, 0x09, as \t
|
||||
// - A backspace, 0x08, as \b
|
||||
// - A form feed, 0x0C, as \f
|
||||
//
|
||||
// UTF-8 should be used for encoding.
|
||||
func NostrEscape(dst, src []byte) []byte {
|
||||
l := len(src)
|
||||
for i := 0; i < l; i++ {
|
||||
c := src[i]
|
||||
switch {
|
||||
case c == '"':
|
||||
dst = append(dst, '\\', '"')
|
||||
case c == '\\':
|
||||
// if i+1 < l && src[i+1] == 'u' || i+1 < l && src[i+1] == '/' {
|
||||
if i+1 < l && src[i+1] == 'u' {
|
||||
dst = append(dst, '\\')
|
||||
} else {
|
||||
dst = append(dst, '\\', '\\')
|
||||
}
|
||||
case c == '\b':
|
||||
dst = append(dst, '\\', 'b')
|
||||
case c == '\t':
|
||||
dst = append(dst, '\\', 't')
|
||||
case c == '\n':
|
||||
dst = append(dst, '\\', 'n')
|
||||
case c == '\f':
|
||||
dst = append(dst, '\\', 'f')
|
||||
case c == '\r':
|
||||
dst = append(dst, '\\', 'r')
|
||||
default:
|
||||
dst = append(dst, c)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// NostrUnescape reverses the operation of NostrEscape except instead of
|
||||
// appending it to the provided slice, it rewrites it, eliminating a memory
|
||||
// copy. Keep in mind that the original JSON will be mangled by this operation,
|
||||
// but the resultant slices will cost zero allocations.
|
||||
func NostrUnescape(dst []byte) (b []byte) {
|
||||
var r, w int
|
||||
for ; r < len(dst); r++ {
|
||||
if dst[r] == '\\' {
|
||||
r++
|
||||
c := dst[r]
|
||||
switch {
|
||||
|
||||
// nip-01 specifies the following single letter C-style escapes for control
|
||||
// codes under 0x20.
|
||||
//
|
||||
// no others are specified but must be preserved, so only these can be
|
||||
// safely decoded at runtime as they must be re-encoded when marshalled.
|
||||
case c == '"':
|
||||
dst[w] = '"'
|
||||
w++
|
||||
case c == '\\':
|
||||
dst[w] = '\\'
|
||||
w++
|
||||
case c == 'b':
|
||||
dst[w] = '\b'
|
||||
w++
|
||||
case c == 't':
|
||||
dst[w] = '\t'
|
||||
w++
|
||||
case c == 'n':
|
||||
dst[w] = '\n'
|
||||
w++
|
||||
case c == 'f':
|
||||
dst[w] = '\f'
|
||||
w++
|
||||
case c == 'r':
|
||||
dst[w] = '\r'
|
||||
w++
|
||||
|
||||
// special cases for non-nip-01 specified json escapes (must be preserved for ID
|
||||
// generation).
|
||||
case c == 'u':
|
||||
dst[w] = '\\'
|
||||
w++
|
||||
dst[w] = 'u'
|
||||
w++
|
||||
case c == '/':
|
||||
dst[w] = '\\'
|
||||
w++
|
||||
dst[w] = '/'
|
||||
w++
|
||||
|
||||
// special case for octal escapes (must be preserved for ID generation).
|
||||
case c >= '0' && c <= '9':
|
||||
dst[w] = '\\'
|
||||
w++
|
||||
dst[w] = c
|
||||
w++
|
||||
|
||||
// anything else after a reverse solidus just preserve it.
|
||||
default:
|
||||
dst[w] = dst[r]
|
||||
w++
|
||||
dst[w] = c
|
||||
w++
|
||||
}
|
||||
} else {
|
||||
dst[w] = dst[r]
|
||||
w++
|
||||
}
|
||||
}
|
||||
b = dst[:w]
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user