Files
cloudpods/vendor/github.com/buger/jsonparser/escape.go
彭镜肇 010fdfee3c Pengjingzhao ospp v2 (#23309)
* feat(mcp-server): 增加mcclient sdk适配器结构体以及对应的认证方法

* feat(mcp-server): 增加资源查询的sdk适配器方法

* feat(mcp-server): 增加资源操作的sdk适配器方法

* feat(mcp-server): 增加区域资源查询工具

* feat(mcp-server): 增加网络资源查询工具

* feat(mcp-server): 增加镜像资源查询工具

* feat(mcp-server): 增加虚拟机资源查询工具

* feat(mcp-server): 增加vpc资源查询工具

* feat(mcp-server): 增加存储资源查询工具

* feat(mcp-server): 增加套餐资源查询工具

* feat(mcp-server): 增加虚拟机创建工具

* feat(mcp-server): 增加虚拟机监控工具

* feat(mcp-server): 增加虚拟机操作工具,包括启动、重启、停止、重置密码和删除

* optimize(mcp-server): 增加工具函数接口定义

* feat(mcp-server): 增加工具函数所使用的结构体模型

* feat(mcp-server): 新增工具统一注册中心

* feat(mcp-server): 增加mcp服务中心

* feat(mcp-server): 增加统一配置中心

* feat(mcp-server): 增加服务启动主入口

* doc(mcp-server): 增加mcp-server相关的说明,安装和使用文档

* fix(mcp-server): 更正文档位置以及补充图片

* refactor(mcp-server): 修正了service以及配置解析的逻辑

* refactor(mcp-server): 将日志打印相关代码改成使用log

* feat(mcp-server): 增加mcclient sdk适配器结构体以及对应的认证方法

* feat(mcp-server): 增加资源查询的sdk适配器方法

* feat(mcp-server): 增加资源操作的sdk适配器方法

* feat(mcp-server): 增加区域资源查询工具

* feat(mcp-server): 增加网络资源查询工具

* feat(mcp-server): 增加镜像资源查询工具

* feat(mcp-server): 增加虚拟机资源查询工具

* feat(mcp-server): 增加vpc资源查询工具

* feat(mcp-server): 增加存储资源查询工具

* feat(mcp-server): 增加套餐资源查询工具

* feat(mcp-server): 增加虚拟机创建工具

* feat(mcp-server): 增加虚拟机监控工具

* feat(mcp-server): 增加虚拟机操作工具,包括启动、重启、停止、重置密码和删除

* optimize(mcp-server): 增加工具函数接口定义

* feat(mcp-server): 增加工具函数所使用的结构体模型

* feat(mcp-server): 新增工具统一注册中心

* feat(mcp-server): 增加mcp服务中心

* feat(mcp-server): 增加统一配置中心

* feat(mcp-server): 增加服务启动主入口

* doc(mcp-server): 增加mcp-server相关的说明,安装和使用文档

* fix(mcp-server): 更正文档位置以及补充图片

* refactor(mcp-server): 修正了service以及配置解析的逻辑

* refactor(mcp-server): 将日志打印相关代码改成使用log

* fix(mcp-server): 修复依赖导入以及缺失等问题

* refactor(mcp-server): 复用common_options

* fix: 修复配置结构体字段重复的问题

* doc(mcp-server): 更正文档错误

* style(mcp-server): 格式化import顺序

* style(mcp-server): 格式化import导入

* style(mcp-server): 规范import语句

* doc(mcp-server): 给目录生成doc文件

---------

Co-authored-by: 屈轩 <qu_xuan@icloud.com>
2025-09-22 10:32:21 +08:00

174 lines
5.1 KiB
Go

package jsonparser
import (
"bytes"
"unicode/utf8"
)
// JSON Unicode stuff: see https://tools.ietf.org/html/rfc7159#section-7
const supplementalPlanesOffset = 0x10000
const highSurrogateOffset = 0xD800
const lowSurrogateOffset = 0xDC00
const basicMultilingualPlaneReservedOffset = 0xDFFF
const basicMultilingualPlaneOffset = 0xFFFF
func combineUTF16Surrogates(high, low rune) rune {
return supplementalPlanesOffset + (high-highSurrogateOffset)<<10 + (low - lowSurrogateOffset)
}
const badHex = -1
func h2I(c byte) int {
switch {
case c >= '0' && c <= '9':
return int(c - '0')
case c >= 'A' && c <= 'F':
return int(c - 'A' + 10)
case c >= 'a' && c <= 'f':
return int(c - 'a' + 10)
}
return badHex
}
// decodeSingleUnicodeEscape decodes a single \uXXXX escape sequence. The prefix \u is assumed to be present and
// is not checked.
// In JSON, these escapes can either come alone or as part of "UTF16 surrogate pairs" that must be handled together.
// This function only handles one; decodeUnicodeEscape handles this more complex case.
func decodeSingleUnicodeEscape(in []byte) (rune, bool) {
// We need at least 6 characters total
if len(in) < 6 {
return utf8.RuneError, false
}
// Convert hex to decimal
h1, h2, h3, h4 := h2I(in[2]), h2I(in[3]), h2I(in[4]), h2I(in[5])
if h1 == badHex || h2 == badHex || h3 == badHex || h4 == badHex {
return utf8.RuneError, false
}
// Compose the hex digits
return rune(h1<<12 + h2<<8 + h3<<4 + h4), true
}
// isUTF16EncodedRune checks if a rune is in the range for non-BMP characters,
// which is used to describe UTF16 chars.
// Source: https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
func isUTF16EncodedRune(r rune) bool {
return highSurrogateOffset <= r && r <= basicMultilingualPlaneReservedOffset
}
func decodeUnicodeEscape(in []byte) (rune, int) {
if r, ok := decodeSingleUnicodeEscape(in); !ok {
// Invalid Unicode escape
return utf8.RuneError, -1
} else if r <= basicMultilingualPlaneOffset && !isUTF16EncodedRune(r) {
// Valid Unicode escape in Basic Multilingual Plane
return r, 6
} else if r2, ok := decodeSingleUnicodeEscape(in[6:]); !ok { // Note: previous decodeSingleUnicodeEscape success guarantees at least 6 bytes remain
// UTF16 "high surrogate" without manditory valid following Unicode escape for the "low surrogate"
return utf8.RuneError, -1
} else if r2 < lowSurrogateOffset {
// Invalid UTF16 "low surrogate"
return utf8.RuneError, -1
} else {
// Valid UTF16 surrogate pair
return combineUTF16Surrogates(r, r2), 12
}
}
// backslashCharEscapeTable: when '\X' is found for some byte X, it is to be replaced with backslashCharEscapeTable[X]
var backslashCharEscapeTable = [...]byte{
'"': '"',
'\\': '\\',
'/': '/',
'b': '\b',
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t',
}
// unescapeToUTF8 unescapes the single escape sequence starting at 'in' into 'out' and returns
// how many characters were consumed from 'in' and emitted into 'out'.
// If a valid escape sequence does not appear as a prefix of 'in', (-1, -1) to signal the error.
func unescapeToUTF8(in, out []byte) (inLen int, outLen int) {
if len(in) < 2 || in[0] != '\\' {
// Invalid escape due to insufficient characters for any escape or no initial backslash
return -1, -1
}
// https://tools.ietf.org/html/rfc7159#section-7
switch e := in[1]; e {
case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
// Valid basic 2-character escapes (use lookup table)
out[0] = backslashCharEscapeTable[e]
return 2, 1
case 'u':
// Unicode escape
if r, inLen := decodeUnicodeEscape(in); inLen == -1 {
// Invalid Unicode escape
return -1, -1
} else {
// Valid Unicode escape; re-encode as UTF8
outLen := utf8.EncodeRune(out, r)
return inLen, outLen
}
}
return -1, -1
}
// unescape unescapes the string contained in 'in' and returns it as a slice.
// If 'in' contains no escaped characters:
// Returns 'in'.
// Else, if 'out' is of sufficient capacity (guaranteed if cap(out) >= len(in)):
// 'out' is used to build the unescaped string and is returned with no extra allocation
// Else:
// A new slice is allocated and returned.
func Unescape(in, out []byte) ([]byte, error) {
firstBackslash := bytes.IndexByte(in, '\\')
if firstBackslash == -1 {
return in, nil
}
// Get a buffer of sufficient size (allocate if needed)
if cap(out) < len(in) {
out = make([]byte, len(in))
} else {
out = out[0:len(in)]
}
// Copy the first sequence of unescaped bytes to the output and obtain a buffer pointer (subslice)
copy(out, in[:firstBackslash])
in = in[firstBackslash:]
buf := out[firstBackslash:]
for len(in) > 0 {
// Unescape the next escaped character
inLen, bufLen := unescapeToUTF8(in, buf)
if inLen == -1 {
return nil, MalformedStringEscapeError
}
in = in[inLen:]
buf = buf[bufLen:]
// Copy everything up until the next backslash
nextBackslash := bytes.IndexByte(in, '\\')
if nextBackslash == -1 {
copy(buf, in)
buf = buf[len(in):]
break
} else {
copy(buf, in[:nextBackslash])
buf = buf[nextBackslash:]
in = in[nextBackslash:]
}
}
// Trim the out buffer to the amount that was actually emitted
return out[:len(out)-len(buf)], nil
}