escape.mx raw

   1  package text
   2  
   3  import "git.smesh.lol/moxie/pkg/mxutil"
   4  
   5  // NostrEscape for JSON encoding according to RFC8259 / NIP-01.
   6  func NostrEscape(dst, src []byte) (buf []byte) {
   7  	l := len(src)
   8  	if dst == nil && l > 0 {
   9  		dst = []byte{:0:l*3/2}
  10  	}
  11  	for i := 0; i < l; i++ {
  12  		c := src[i]
  13  		if c == '"' {
  14  			dst = dst | "\\\""
  15  		} else if c == '\\' {
  16  			if i+1 < l && src[i+1] == 'u' {
  17  				dst = dst | "\\"
  18  			} else {
  19  				dst = dst | "\\\\"
  20  			}
  21  		} else if c == '\b' {
  22  			dst = dst | "\\b"
  23  		} else if c == '\t' {
  24  			dst = dst | "\\t"
  25  		} else if c == '\n' {
  26  			dst = dst | "\\n"
  27  		} else if c == '\f' {
  28  			dst = dst | "\\f"
  29  		} else if c == '\r' {
  30  			dst = dst | "\\r"
  31  		} else if c < 32 {
  32  			dst = dst | "\\u00"
  33  			hexHigh := (c >> 4) & 0x0F
  34  			hexLow := c & 0x0F
  35  			if hexHigh < 10 {
  36  				dst = mxutil.Ensure(dst, 1)
  37  				dst = push(dst, byte('0'+hexHigh))
  38  			} else {
  39  				dst = mxutil.Ensure(dst, 1)
  40  				dst = push(dst, byte('a'+(hexHigh-10)))
  41  			}
  42  			if hexLow < 10 {
  43  				dst = mxutil.Ensure(dst, 1)
  44  				dst = push(dst, byte('0'+hexLow))
  45  			} else {
  46  				dst = mxutil.Ensure(dst, 1)
  47  				dst = push(dst, byte('a'+(hexLow-10)))
  48  			}
  49  		} else {
  50  			dst = mxutil.Ensure(dst, 1)
  51  			dst = push(dst, c)
  52  		}
  53  	}
  54  	return dst
  55  }
  56  
  57  // NostrUnescape reverses NostrEscape in-place.
  58  func NostrUnescape(dst []byte) (buf []byte) {
  59  	var r, w int32
  60  	for ; r < len(dst); r++ {
  61  		if dst[r] == '\\' {
  62  			r++
  63  			c := dst[r]
  64  			switch {
  65  			case c == '"':
  66  				dst[w] = '"'
  67  				w++
  68  			case c == '\\':
  69  				dst[w] = '\\'
  70  				w++
  71  			case c == 'b':
  72  				dst[w] = '\b'
  73  				w++
  74  			case c == 't':
  75  				dst[w] = '\t'
  76  				w++
  77  			case c == 'n':
  78  				dst[w] = '\n'
  79  				w++
  80  			case c == 'f':
  81  				dst[w] = '\f'
  82  				w++
  83  			case c == 'r':
  84  				dst[w] = '\r'
  85  				w++
  86  			case c == 'u':
  87  				if r+4 < len(dst) && dst[r+1] == '0' && dst[r+2] == '0' {
  88  					hexHigh := dst[r+3]
  89  					hexLow := dst[r+4]
  90  					var val byte
  91  					if hexHigh >= '0' && hexHigh <= '9' {
  92  						val = (hexHigh - '0') << 4
  93  					} else if hexHigh >= 'a' && hexHigh <= 'f' {
  94  						val = (hexHigh - 'a' + 10) << 4
  95  					} else if hexHigh >= 'A' && hexHigh <= 'F' {
  96  						val = (hexHigh - 'A' + 10) << 4
  97  					}
  98  					if hexLow >= '0' && hexLow <= '9' {
  99  						val |= hexLow - '0'
 100  					} else if hexLow >= 'a' && hexLow <= 'f' {
 101  						val |= hexLow - 'a' + 10
 102  					} else if hexLow >= 'A' && hexLow <= 'F' {
 103  						val |= hexLow - 'A' + 10
 104  					}
 105  					if val < 32 {
 106  						dst[w] = val
 107  						w++
 108  						r += 4
 109  						continue
 110  					}
 111  				}
 112  				dst[w] = '\\'
 113  				w++
 114  				dst[w] = 'u'
 115  				w++
 116  			case c == '/':
 117  				dst[w] = '\\'
 118  				w++
 119  				dst[w] = '/'
 120  				w++
 121  			case c >= '0' && c <= '9':
 122  				dst[w] = '\\'
 123  				w++
 124  				dst[w] = c
 125  				w++
 126  			default:
 127  				dst[w] = dst[r]
 128  				w++
 129  				dst[w] = c
 130  				w++
 131  			}
 132  		} else {
 133  			dst[w] = dst[r]
 134  			w++
 135  		}
 136  	}
 137  	return dst[:w]
 138  }
 139