json.go raw

   1  package helpers
   2  
   3  // Minimal JSON serialization for Nostr events.
   4  // No encoding/json dependency.
   5  
   6  // JsonString returns a JSON-escaped string with surrounding quotes.
   7  // Uses string concat to preserve non-ASCII in tinyjs.
   8  func JsonString(s string) string {
   9  	result := "\""
  10  	start := 0
  11  	for i := 0; i < len(s); i++ {
  12  		c := s[i]
  13  		var esc string
  14  		switch c {
  15  		case '"':
  16  			esc = "\\\""
  17  		case '\\':
  18  			esc = "\\\\"
  19  		case '\n':
  20  			esc = "\\n"
  21  		case '\r':
  22  			esc = "\\r"
  23  		case '\t':
  24  			esc = "\\t"
  25  		case '\b':
  26  			esc = "\\b"
  27  		case '\f':
  28  			esc = "\\f"
  29  		default:
  30  			if c < 0x20 {
  31  				esc = "\\u00" + string(hexChars[c>>4]) + string(hexChars[c&0x0f])
  32  			} else {
  33  				continue
  34  			}
  35  		}
  36  		result += s[start:i] + esc
  37  		start = i + 1
  38  	}
  39  	return result + s[start:] + "\""
  40  }
  41  
  42  // JsonGetString extracts a string value for the given key from a JSON object.
  43  // Returns empty string if not found. Handles basic escape sequences.
  44  // Uses string concat (not []byte) to preserve non-ASCII characters in tinyjs.
  45  func JsonGetString(s, key string) string {
  46  	kq := "\"" + key + "\""
  47  	kqLen := len(kq)
  48  	for i := 0; i <= len(s)-kqLen; i++ {
  49  		if s[i:i+kqLen] == kq {
  50  			j := i + kqLen
  51  			for j < len(s) && (s[j] == ' ' || s[j] == '\t' || s[j] == '\n' || s[j] == '\r') {
  52  				j++
  53  			}
  54  			if j >= len(s) || s[j] != ':' {
  55  				continue
  56  			}
  57  			j++
  58  			for j < len(s) && (s[j] == ' ' || s[j] == '\t' || s[j] == '\n' || s[j] == '\r') {
  59  				j++
  60  			}
  61  			if j >= len(s) || s[j] != '"' {
  62  				continue
  63  			}
  64  			j++
  65  			start := j
  66  			result := ""
  67  			for j < len(s) {
  68  				if s[j] == '\\' && j+1 < len(s) {
  69  					result += s[start:j]
  70  					j++
  71  					switch s[j] {
  72  					case '"', '\\', '/':
  73  						result += s[j : j+1]
  74  					case 'n':
  75  						result += "\n"
  76  					case 'r':
  77  						result += "\r"
  78  					case 't':
  79  						result += "\t"
  80  					default:
  81  						result += s[j : j+1]
  82  					}
  83  					j++
  84  					start = j
  85  					continue
  86  				}
  87  				if s[j] == '"' {
  88  					return result + s[start:j]
  89  				}
  90  				j++
  91  			}
  92  		}
  93  	}
  94  	return ""
  95  }
  96  
  97  // Itoa converts int64 to decimal string.
  98  func Itoa(n int64) string {
  99  	if n == 0 {
 100  		return "0"
 101  	}
 102  	neg := false
 103  	if n < 0 {
 104  		neg = true
 105  		n = -n
 106  	}
 107  	var buf [20]byte
 108  	i := len(buf)
 109  	for n > 0 {
 110  		i--
 111  		buf[i] = byte('0' + n%10)
 112  		n /= 10
 113  	}
 114  	if neg {
 115  		i--
 116  		buf[i] = '-'
 117  	}
 118  	return string(buf[i:])
 119  }
 120