escape.mx raw

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