diff --git a/g/container/gtype/byte.go b/g/container/gtype/byte.go new file mode 100644 index 000000000..9855e404e --- /dev/null +++ b/g/container/gtype/byte.go @@ -0,0 +1,34 @@ +// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. + +package gtype + +import ( + "sync/atomic" +) + +type Byte struct { + val int32 +} + +func NewByte(value...byte) *Byte { + if len(value) > 0 { + return &Byte{val : int32(value[0])} + } + return &Byte{} +} + +func (t *Byte)Set(value byte) { + atomic.StoreInt32(&t.val, int32(value)) +} + +func (t *Byte)Val() byte { + return byte(atomic.LoadInt32(&t.val)) +} + +func (t *Byte)Add(delta int) byte { + return byte(atomic.AddInt32(&t.val, int32(delta))) +} diff --git a/g/encoding/gjson/gjson.go b/g/encoding/gjson/gjson.go index 197abe523..e6b9dd976 100644 --- a/g/encoding/gjson/gjson.go +++ b/g/encoding/gjson/gjson.go @@ -4,7 +4,8 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://gitee.com/johng/gf. -// JSON解析/封装 +// JSON解析/封装. +// 单元测试请参考gpaser包. package gjson import ( @@ -20,12 +21,58 @@ import ( "gitee.com/johng/gf/g/encoding/gtoml" ) +const ( + gDEFAULT_SPLIT_CHAR = '.' // 默认层级分隔符号 +) + // json解析结果存放数组 type Json struct { mu sync.RWMutex p *interface{} // 注意这是一个指针 + c byte // 层级分隔符,默认为"." + vc bool // 是否执行分隔符冲突检测(默认为true,检测会比较影响检索效率) } +// 将变量转换为Json对象进行处理,该变量至少应当是一个map或者array,否者转换没有意义 +func New(value interface{}) *Json { + switch value.(type) { + case map[string]interface{}: + return &Json{ + p : &value, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : true , + } + case []interface{}: + return &Json{ + p : &value, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : true , + } + default: + // 这里效率会比较低 + b, _ := Encode(value) + v, _ := Decode(b) + return &Json{ + p : &v, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : true, + } + } +} + +// 设置自定义的层级分隔符号 +func (j *Json) SetSplitChar(char byte) { + j.mu.Lock() + j.c = char + j.mu.Unlock() +} + +// 设置自定义的层级分隔符号 +func (j *Json) SetViolenceCheck(check bool) { + j.mu.Lock() + j.vc = check + j.mu.Unlock() +} // 编码go变量为json字符串,并返回json字符串指针 func Encode (v interface{}) ([]byte, error) { return json.Marshal(v) @@ -51,7 +98,7 @@ func DecodeToJson (b []byte) (*Json, error) { if v, err := Decode(b); err != nil { return nil, err } else { - return NewJson(v), nil + return New(v), nil } } @@ -94,26 +141,11 @@ func LoadContent (data []byte, t string) (*Json, error) { if err := json.Unmarshal(data, &result); err != nil { return nil, err } - return NewJson(result), nil -} - -// 将变量转换为Json对象进行处理,该变量至少应当是一个map或者array,否者转换没有意义 -func NewJson(value interface{}) *Json { - switch value.(type) { - case map[string]interface{}: - return &Json{ p: &value } - case []interface{}: - return &Json{ p: &value } - default: - // 这里效率会比较低 - b, _ := Encode(value) - v, _ := Decode(b) - return &Json{ p: &v } - } + return New(result), nil } // 将指定的json内容转换为指定结构返回,查找失败或者转换失败,目标对象转换为nil -// 注意第二个参数需要给的是变量地址 +// 注意第二个参数需要给的是**变量地址** func (j *Json) GetToVar(pattern string, v interface{}) error { r := j.Get(pattern) if r != nil { @@ -144,7 +176,7 @@ func (j *Json) GetMap(pattern string) map[string]interface{} { func (j *Json) GetJson(pattern string) *Json { result := j.Get(pattern) if result != nil { - return NewJson(result) + return New(result) } return nil } @@ -199,27 +231,22 @@ func (j *Json) Remove(pattern string) error { // 根据pattern查找并设置数据 // 注意: -// 1、写入的时候"."符号只能表示层级,不能使用带"."符号的键名; -// 2、写入的value为nil且removed为true时,表示删除; -// 3、里面的层级处理比较复杂,逻辑较复杂的地方在于层级检索及节点创建,叶子赋值; +// 1、写入的value为nil且removed为true时,表示删除; +// 2、里面的层级处理比较复杂,逻辑较复杂的地方在于层级检索及节点创建,叶子赋值; func (j *Json) setValue(pattern string, value interface{}, removed bool) error { + array := strings.Split(pattern, string(j.c)) + length := len(array) + value = j.convertValue(value) // 初始化判断 if *j.p == nil { - if isNumeric(pattern) { + if isNumeric(array[0]) { *j.p = make([]interface{}, 0) } else { *j.p = make(map[string]interface{}) } } - var pparent *interface{} - var pointer *interface{} - - pointer = j.p - pparent = nil - value = j.convertValue(value) - array := strings.Split(pattern, ".") - length := len(array) - + var pparent *interface{} = nil // 父级元素项(设置时需要根据子级的内容确定数据类型,所以必须记录父级) + var pointer *interface{} = j.p // 当前操作层级项 j.mu.Lock() for i:= 0; i < length; i++ { switch (*pointer).(type) { @@ -403,13 +430,31 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter func (j *Json) Get(pattern string) interface{} { j.mu.RLock() defer j.mu.RUnlock() - if r := j.getPointerByPattern(pattern); r != nil { - return *r + + var result *interface{} + if j.vc { + result = j.getPointerByPattern(pattern) + } else { + result = j.getPointerByPatternWithoutSplitCharViolenceCheck(pattern) + } + if result != nil { + return *result } return nil } -// 根据pattern层级查找变量指针 +// 根据pattern层级查找**变量指针** +// 检索方式:例如检索 a.a.a ,值为1 +// 1. 检索 a.a.a.a 是否存在对应map的键名; +// 2. 检索 a.a.a 是否存在对应map的键名; +// 3. 检索 a.a 是否存在对应map的键名; +// 4. 检索 a 是否存在对应map的键名,如果检索出这是一个map,假如为变量m1; +// 5. 在m1中检索 a.a.a 否存在对应map的键名; +// 6. 在m1中检索 a.a 否存在对应map的键名; +// 7. 在m1中检索 a 否存在对应map的键名,如果检索出这是一个map,假如为变量m2; +// 8. 在m2中检索 a.a 否存在对应map的键名; +// 9. 在m2中检索 a 否存在对应map的键名,检索到有值,值为1; +// 这样检索的复杂度很高,主要是为了避免键名中存在分隔符号(默认为".")的情况,避免歧义。 func (j *Json) getPointerByPattern(pattern string) *interface{} { index := len(pattern) start := 0 @@ -432,7 +477,8 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} { pointer = r } } else { - index = strings.LastIndex(pattern[start:index], ".") + // 查找下一个分割符号的索引位置 + index = strings.LastIndexByte(pattern[start:index], j.c) if index != -1 && length > 0 { index += length + 1 } @@ -444,17 +490,38 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} { return nil } -// 判断给定的pattern在当前的pointer下是否有值,并返回对应的pointer +// 层级检索,内部不执行分隔符冲突检查,检索效率会有所提高,但是冲突需要开发者自己根据自定义的分隔符来进行解决 +func (j *Json) getPointerByPatternWithoutSplitCharViolenceCheck(pattern string) *interface{} { + pointer := j.p + if len(pattern) == 0 { + return pointer + } + array := strings.Split(pattern, string(j.c)) + for k, v := range array { + if r := j.checkPatternByPointer(v, pointer); r != nil { + if k == len(array) - 1 { + return r + } else { + pointer = r + } + } else { + break + } + } + return nil +} + +// 判断给定的key在当前的pointer下是否有值,并返回对应的pointer // 注意这里返回的指针都是临时变量的内存地址 -func (j *Json) checkPatternByPointer(pattern string, pointer *interface{}) *interface{} { +func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} { switch (*pointer).(type) { case map[string]interface{}: - if v, ok := (*pointer).(map[string]interface{})[pattern]; ok { + if v, ok := (*pointer).(map[string]interface{})[key]; ok { return &v } case []interface{}: - if isNumeric(pattern) { - n, err := strconv.Atoi(pattern) + if isNumeric(key) { + n, err := strconv.Atoi(key) if err == nil && len((*pointer).([]interface{})) > n { return &(*pointer).([]interface{})[n] } diff --git a/g/encoding/gparser/gparser.go b/g/encoding/gparser/gparser.go index da1256416..cb40beceb 100644 --- a/g/encoding/gparser/gparser.go +++ b/g/encoding/gparser/gparser.go @@ -20,9 +20,9 @@ type Parser struct { // 该参数为非必需参数,默认为创建一个空的Parser对象 func New (values...interface{}) *Parser { if len(values) > 0 { - return &Parser{gjson.NewJson(values[0])} + return &Parser{gjson.New(values[0])} } - return &Parser{gjson.NewJson(nil)} + return &Parser{gjson.New(nil)} } func Load (path string) (*Parser, error) { diff --git a/g/encoding/gparser/gparser_test.go b/g/encoding/gparser/gparser_test.go index 65bf3dc2d..0273d5c7c 100644 --- a/g/encoding/gparser/gparser_test.go +++ b/g/encoding/gparser/gparser_test.go @@ -156,6 +156,19 @@ func Test_Set9(t *testing.T) { } +func Test_Set10(t *testing.T) { + e := []byte(`{"a":{"b":{"c":1}}`) + p := gparser.New(nil) + p.Set("a.b.c", 1) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index 077863fcf..5c2dc36e9 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -25,8 +25,7 @@ const ( gHTTP_METHODS = "GET,POST,DELETE,PUT,PATCH,HEAD,CONNECT,OPTIONS,TRACE" gDEFAULT_SERVER = "default" gDEFAULT_DOMAIN = "default" - gDEFAULT_METHOD = "all" - + gDEFAULT_METHOD = "ALL" gDEFAULT_COOKIE_PATH = "/" // 默认path gDEFAULT_COOKIE_MAX_AGE = 86400*365 // 默认cookie有效期(一年) gDEFAULT_SESSION_MAX_AGE = 600 // 默认session有效期(600秒) @@ -35,15 +34,17 @@ const ( // http server结构体 type Server struct { - hmu sync.RWMutex // handlerMap互斥锁 + hmmu sync.RWMutex // handlerMap互斥锁 + htmu sync.RWMutex // handlerTree互斥锁 name string // 服务名称,方便识别 server http.Server // 底层http server对象 config ServerConfig // 配置对象 status int8 // 当前服务器状态(0:未启动,1:运行中) - handlerMap HandlerMap // 所有注册的回调函数 methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充) - closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象) + handlerMap HandlerMap // 所有注册的回调函数(静态匹配) + handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配) hooksMap *gmap.StringInterfaceMap // 钩子注册方法map,键值为按照注册顺序生成的glist,用于hook顺序调用 + closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象) servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况) cookieMaxAge *gtype.Int // Cookie有效期 sessionMaxAge *gtype.Int // Session有效期 @@ -54,13 +55,17 @@ type Server struct { } // 域名、URI与回调函数的绑定记录表 -type HandlerMap map[string]HandlerItem +type HandlerMap map[string]*HandlerItem // http回调函数注册信息 type HandlerItem struct { - ctype reflect.Type // 控制器类型 - fname string // 回调方法名称 - faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一) + ctype reflect.Type // 控制器类型 + fname string // 回调方法名称 + faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一) + uri string // 注册时的pattern - uri + method string // 注册时的pattern - method + domain string // 注册时的pattern - domain + priority int // 优先级,用于链表排序,值越大优先级越高 } // http注册函数 @@ -81,11 +86,12 @@ func GetServer(names...string) (*Server) { } s := &Server { name : name, - handlerMap : make(HandlerMap), methodsMap : make(map[string]bool), + handlerMap : make(HandlerMap), + handlerTree : make(map[string]interface{}), + hooksMap : gmap.NewStringInterfaceMap(), servedCount : gtype.NewInt(), closeQueue : gqueue.New(), - hooksMap : gmap.NewStringInterfaceMap(), routers : gcache.New(), cookies : gmap.NewIntInterfaceMap(), sessions : gcache.New(), @@ -136,9 +142,16 @@ func (s *Server) handlerKey(domain, method, pattern string) string { } // 注册服务处理方法 -func (s *Server) setHandler(domain, method, pattern string, item HandlerItem) { - s.hmu.Lock() - defer s.hmu.Unlock() +func (s *Server) setHandler(pattern string, item *HandlerItem) error { + domain, method, uri, err := s.parsePatternForBindHandler(pattern) + if err != nil { + return errors.New("invalid pattern") + } + item.uri = uri + item.domain = domain + item.method = method + // 静态注册 + s.hmmu.Lock() if method == gDEFAULT_METHOD { for v, _ := range s.methodsMap { s.handlerMap[s.handlerKey(domain, v, pattern)] = item @@ -146,7 +159,36 @@ func (s *Server) setHandler(domain, method, pattern string, item HandlerItem) { } else { s.handlerMap[s.handlerKey(domain, method, pattern)] = item } + s.hmmu.Unlock() + // 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中 + if s.isUriHasRule(uri) { + array := strings.Split(uri, "/") + item.priority = len(array) + pattern := "" + for _, v := range array { + switch v[0] { + case ':': + case '*': + default: + if p == nil { + p = make(map[string]interface{}) + p = p.(map[string]interface{}) + } + if _, ok := p[v]; !ok { + p[v] = make(map[string]interface{}) + } + } + } + } + return nil +} +// 判断URI中是否包含动态注册规则 +func (s *Server) isUriHasRule(uri string) bool { + if len(uri) > 1 && (strings.Index(uri, "/:") != -1 || strings.Index(uri, "/*") != -1) { + return true + } + return false } // 解析pattern @@ -171,16 +213,11 @@ func (s *Server)parsePatternForBindHandler(pattern string) (domain, method, uri // 绑定URI到操作函数/方法 // pattern的格式形如:/user/list, put:/user, delete:/user, post:/user@johng.cn // 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行 -func (s *Server)bindHandlerItem(pattern string, item HandlerItem) error { +func (s *Server)bindHandlerItem(pattern string, item *HandlerItem) error { if s.status == 1 { return errors.New("server handlers cannot be changed while running") } - domain, method, uri, err := s.parsePatternForBindHandler(pattern) - if err != nil { - return errors.New("invalid pattern") - } - s.setHandler(domain, method, uri, item) - return nil + return s.setHandler(pattern, item) } // 通过映射数组绑定URI到操作函数/方法 @@ -216,7 +253,11 @@ func (s *Server)appendMethodNameToUriWithPattern(pattern string, name string) st // 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑 func (s *Server)BindHandler(pattern string, handler HandlerFunc) error { - return s.bindHandlerItem(pattern, HandlerItem{nil, "", handler}) + return s.bindHandlerItem(pattern, &HandlerItem{ + ctype : nil, + fname : "", + faddr : handler, + }) } // 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面 @@ -228,7 +269,11 @@ func (s *Server)BindObject(pattern string, obj interface{}) error { for i := 0; i < v.NumMethod(); i++ { name := t.Method(i).Name key := s.appendMethodNameToUriWithPattern(pattern, name) - m[key] = HandlerItem{nil, "", v.Method(i).Interface().(func(*Request))} + m[key] = &HandlerItem{ + ctype : nil, + fname : "", + faddr : v.Method(i).Interface().(func(*Request)), + } } return s.bindHandlerByMap(m) } @@ -244,7 +289,11 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string return errors.New("invalid method name:" + method) } key := s.appendMethodNameToUriWithPattern(pattern, method) - m[key] = HandlerItem{nil, "", fval.Interface().(func(*Request))} + m[key] = &HandlerItem{ + ctype : nil, + fname : "", + faddr : fval.Interface().(func(*Request)), + } } return s.bindHandlerByMap(m) } @@ -261,7 +310,11 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { continue } key := name + ":" + pattern - m[key] = HandlerItem{nil, "", v.Method(i).Interface().(func(*Request))} + m[key] = &HandlerItem{ + ctype : nil, + fname : "", + faddr : v.Method(i).Interface().(func(*Request)), + } } return s.bindHandlerByMap(m) } @@ -279,7 +332,11 @@ func (s *Server)BindController(pattern string, c Controller) error { continue } key := s.appendMethodNameToUriWithPattern(pattern, name) - m[key] = HandlerItem{v.Elem().Type(), name, nil} + m[key] = &HandlerItem{ + ctype : v.Elem().Type(), + fname : name, + faddr : nil, + } } return s.bindHandlerByMap(m) } @@ -297,16 +354,21 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error { for _, v := range strings.Split(gHTTP_METHODS, ",") { methods[v] = true } + // 如果存在与HttpMethod对应名字的方法,那么绑定这些方法 for i := 0; i < v.NumMethod(); i++ { - name := t.Method(i).Name + name := strings.ToUpper(t.Method(i).Name) if name == "Init" || name == "Shut" { continue } - if _, ok := s.methodsMap[strings.ToUpper(name)]; !ok { + if _, ok := s.methodsMap[name]; !ok { continue } key := name + ":" + pattern - m[key] = HandlerItem{v.Elem().Type(), name, nil} + m[key] = &HandlerItem{ + ctype : v.Elem().Type(), + fname : name, + faddr : nil, + } } return s.bindHandlerByMap(m) } @@ -323,31 +385,35 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, methods strin return errors.New("invalid method name:" + method) } key := s.appendMethodNameToUriWithPattern(pattern, method) - m[key] = HandlerItem{ctype, method, nil} + m[key] = &HandlerItem{ + ctype : ctype, + fname : method, + faddr : nil, + } } return s.bindHandlerByMap(m) } // 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写 func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error { - domain, method, uri, err := s.parsePatternForBindHandler(pattern) - if err != nil { - return errors.New("invalid pattern") - } - var l *glist.List - if method == gDEFAULT_METHOD { - for v, _ := range s.methodsMap { - if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, v, uri, hook), glist.New()); v != nil { - l = v.(*glist.List) - } - l.PushBack(handler) - } - } else { - if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, method, uri, hook), glist.New()); v == nil { - l = v.(*glist.List) - } - l.PushBack(handler) - } + //domain, method, uri, err := s.parsePatternForBindHookHandler(pattern) + //if err != nil { + // return errors.New("invalid pattern") + //} + //var l *glist.List + //if method == gDEFAULT_METHOD { + // for v, _ := range s.methodsMap { + // if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, v, uri, hook), glist.New()); v != nil { + // l = v.(*glist.List) + // } + // l.PushBack(handler) + // } + //} else { + // if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, method, uri, hook), glist.New()); v == nil { + // l = v.(*glist.List) + // } + // l.PushBack(handler) + //} return nil } diff --git a/geg/encoding/json.go b/geg/encoding/gjson.go similarity index 62% rename from geg/encoding/json.go rename to geg/encoding/gjson.go index 01e8f7962..609f5f83e 100644 --- a/geg/encoding/json.go +++ b/geg/encoding/gjson.go @@ -4,6 +4,7 @@ import ( "fmt" "gitee.com/johng/gf/g/os/glog" "gitee.com/johng/gf/g/encoding/gjson" + "gitee.com/johng/gf/g/os/gtime" ) func getByPattern() { @@ -95,6 +96,67 @@ func testConvert() { } } + +func testSplitChar() { + var v interface{} + j := gjson.New(nil) + t1 := gtime.Nanosecond() + j.Set("a.b.c", 1) + t2 := gtime.Nanosecond() + fmt.Println(t2 - t1) + + t5 := gtime.Nanosecond() + v = j.Get("a.b.c.d.e.f.g.h.i.j.k") + t6 := gtime.Nanosecond() + b, _ := j.ToJsonIndent() + fmt.Println(string(b)) + fmt.Println(v) + fmt.Println(t6 - t5) + + j.SetSplitChar('#') + + + + t7 := gtime.Nanosecond() + v = j.Get("a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#a") + t8 := gtime.Nanosecond() + fmt.Println(v) + fmt.Println(t8 - t7) +} + + +func testViolenceCheck() { + j := gjson.New(nil) + t1 := gtime.Nanosecond() + j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1) + t2 := gtime.Nanosecond() + fmt.Println(t2 - t1) + + t3 := gtime.Nanosecond() + j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1) + t4 := gtime.Nanosecond() + fmt.Println(t4 - t3) + + t5 := gtime.Nanosecond() + j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a") + t6 := gtime.Nanosecond() + fmt.Println(t6 - t5) + + + j.SetViolenceCheck(false) + + + t7 := gtime.Nanosecond() + j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1) + t8 := gtime.Nanosecond() + fmt.Println(t8 - t7) + + t9 := gtime.Nanosecond() + j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a") + t10 := gtime.Nanosecond() + fmt.Println(t10 - t9) +} + func main() { - testSet() + testSplitChar() } \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 17f642a51..143150c7a 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,16 +1,39 @@ package main import ( + "gitee.com/johng/gf/g/encoding/gjson" "fmt" - "gitee.com/johng/gf/g/util/gregx" "gitee.com/johng/gf/g/os/gtime" ) func main() { - t1 := gtime.Microsecond() - for i := 0; i < 10000; i++ { - gregx.MatchString(`([a-zA-Z]+)\^([a-zA-Z]+):(.+)@([\w\.\-]+)`, "a^b:c@d") - } - t2 := gtime.Microsecond() + j := gjson.New(nil) + t1 := gtime.Nanosecond() + j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1) + t2 := gtime.Nanosecond() fmt.Println(t2 - t1) + + t3 := gtime.Nanosecond() + j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1) + t4 := gtime.Nanosecond() + fmt.Println(t4 - t3) + + t5 := gtime.Nanosecond() + j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a") + t6 := gtime.Nanosecond() + fmt.Println(t6 - t5) + + + j.SetViolenceCheck(false) + + + t7 := gtime.Nanosecond() + j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1) + t8 := gtime.Nanosecond() + fmt.Println(t8 - t7) + + t9 := gtime.Nanosecond() + j.Get("a.a") + t10 := gtime.Nanosecond() + fmt.Println(t10 - t9) } \ No newline at end of file