diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index a87cb8b41..0e16736de 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -36,7 +36,7 @@ func New(safe ...bool) *Array { return NewArraySize(0, 0, safe...) } -// See New. +// NewArray is alias of New, please see New. func NewArray(safe ...bool) *Array { return NewArraySize(0, 0, safe...) } @@ -422,7 +422,7 @@ func (a *Array) SubSlice(offset int, length ...int) []interface{} { } } -// See PushRight. +// Append is alias of PushRight, please See PushRight. func (a *Array) Append(value ...interface{}) *Array { a.PushRight(value...) return a diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index c58020eb2..44e577383 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -149,10 +149,7 @@ func (a *IntArray) Sort(reverse ...bool) *IntArray { defer a.mu.Unlock() if len(reverse) > 0 && reverse[0] { sort.Slice(a.array, func(i, j int) bool { - if a.array[i] < a.array[j] { - return false - } - return true + return a.array[i] >= a.array[j] }) } else { sort.Ints(a.array) @@ -427,7 +424,7 @@ func (a *IntArray) SubSlice(offset int, length ...int) []int { } } -// See PushRight. +// Append is alias of PushRight,please See PushRight. func (a *IntArray) Append(value ...int) *IntArray { a.mu.Lock() a.array = append(a.array, value...) diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index be23bba9d..61fd0e8d0 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -136,10 +136,7 @@ func (a *StrArray) Sort(reverse ...bool) *StrArray { defer a.mu.Unlock() if len(reverse) > 0 && reverse[0] { sort.Slice(a.array, func(i, j int) bool { - if strings.Compare(a.array[i], a.array[j]) < 0 { - return false - } - return true + return strings.Compare(a.array[i], a.array[j]) >= 0 }) } else { sort.Strings(a.array) @@ -414,7 +411,7 @@ func (a *StrArray) SubSlice(offset int, length ...int) []string { } } -// See PushRight. +// Append is alias of PushRight,please See PushRight. func (a *StrArray) Append(value ...string) *StrArray { a.mu.Lock() a.array = append(a.array, value...) diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index cd14d6252..f120809a5 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -156,9 +156,7 @@ func (a *SortedArray) Append(values ...interface{}) *SortedArray { if cmp > 0 { index++ } - rear := append([]interface{}{}, a.array[index:]...) - a.array = append(a.array[0:index], value) - a.array = append(a.array, rear...) + a.array = append(a.array[:index], append([]interface{}{value}, a.array[index:]...)...) } return a } @@ -453,7 +451,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result mid := 0 cmp := -2 for min <= max { - mid = min + int((max-min)/2) + mid = min + (max-min)/2 cmp = a.getComparator()(value, a.array[mid]) switch { case cmp < 0: diff --git a/container/garray/garray_z_bench_any_test.go b/container/garray/garray_z_bench_any_test.go new file mode 100644 index 000000000..477c18a19 --- /dev/null +++ b/container/garray/garray_z_bench_any_test.go @@ -0,0 +1,39 @@ +// Copyright GoFrame Author(https://goframe.org). 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://github.com/gogf/gf. + +package garray_test + +import ( + "github.com/gogf/gf/container/garray" + "testing" +) + +type anySortedArrayItem struct { + priority int64 + value interface{} +} + +var ( + anyArray = garray.NewArray() + anySortedArray = garray.NewSortedArray(func(a, b interface{}) int { + return int(a.(anySortedArrayItem).priority - b.(anySortedArrayItem).priority) + }) +) + +func Benchmark_AnyArray_Add(b *testing.B) { + for i := 0; i < b.N; i++ { + anyArray.Append(i) + } +} + +func Benchmark_AnySortedArray_Add(b *testing.B) { + for i := 0; i < b.N; i++ { + anySortedArray.Add(anySortedArrayItem{ + priority: int64(i), + value: i, + }) + } +} diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index abbb101f8..e003bc1d9 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -455,9 +455,8 @@ func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string } } else { for _, column := range columns { - // If it's SAVE operation, - // do not automatically update the creating time. - if c.isSoftCreatedFilledName(column) { + // If it's SAVE operation, do not automatically update the creating time. + if c.isSoftCreatedFieldName(column) { continue } if len(onDuplicateStr) > 0 { @@ -679,8 +678,8 @@ func (c *Core) HasTable(name string) (bool, error) { return false, nil } -// isSoftCreatedFilledName checks and returns whether given filed name is an automatic-filled created time. -func (c *Core) isSoftCreatedFilledName(fieldName string) bool { +// isSoftCreatedFieldName checks and returns whether given filed name is an automatic-filled created time. +func (c *Core) isSoftCreatedFieldName(fieldName string) bool { if fieldName == "" { return false } diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 6f29229d4..90250c81b 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -217,7 +217,6 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err nowString = gtime.Now().String() fieldNameCreate = m.getSoftFieldNameCreated() fieldNameUpdate = m.getSoftFieldNameUpdated() - fieldNameDelete = m.getSoftFieldNameDeleted() ) newData, err := m.filterDataForInsertOrUpdate(m.data) if err != nil { @@ -286,7 +285,6 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err // Automatic handling for creating/updating time. if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { for k, v := range list { - gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete) if fieldNameCreate != "" { v[fieldNameCreate] = nowString } diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index 80d7ad2a8..12f2ab492 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -15,7 +15,6 @@ import ( "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/gutil" ) // Update does "UPDATE ... " statement for the model. @@ -43,9 +42,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro } var ( updateData = m.data - fieldNameCreate = m.getSoftFieldNameCreated() fieldNameUpdate = m.getSoftFieldNameUpdated() - fieldNameDelete = m.getSoftFieldNameDeleted() conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, false) ) // Automatically update the record updating time. @@ -61,7 +58,6 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro switch refKind { case reflect.Map, reflect.Struct: dataMap := ConvertDataForTableRecord(m.data) - gutil.MapDelete(dataMap, fieldNameCreate, fieldNameUpdate, fieldNameDelete) if fieldNameUpdate != "" { dataMap[fieldNameUpdate] = gtime.Now().String() } diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 120a4cd55..860518fc7 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -663,6 +663,9 @@ CREATE TABLE %s ( } defer dropTable(table) + //db.SetDebug(true) + //defer db.SetDebug(false) + type Entity struct { Id uint64 `orm:"id,primary" json:"id"` Name string `orm:"name" json:"name"` @@ -679,7 +682,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err := db.Model(table).Data(dataInsert).Insert() + r, err := db.Model(table).Data(dataInsert).OmitEmpty().Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) @@ -702,7 +705,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Model(table).Data(dataSave).Save() + r, err = db.Model(table).Data(dataSave).OmitEmpty().Save() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) @@ -726,7 +729,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() + r, err = db.Model(table).Data(dataUpdate).WherePri(1).OmitEmpty().Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) @@ -747,7 +750,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Model(table).Data(dataReplace).Replace() + r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index b961be831..7e1e9af03 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -142,10 +142,14 @@ func WrapSkipf(skip int, err error, format string, args ...interface{}) error { } // NewCode creates and returns an error that has error code and given text. -func NewCode(code int, text string) error { +func NewCode(code int, text ...string) error { + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ stack: callers(), - text: text, + text: errText, code: code, } } @@ -161,10 +165,14 @@ func NewCodef(code int, format string, args ...interface{}) error { // NewCodeSkip creates and returns an error which has error code and is formatted from given text. // The parameter specifies the stack callers skipped amount. -func NewCodeSkip(code, skip int, text string) error { +func NewCodeSkip(code, skip int, text ...string) error { + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ stack: callers(skip), - text: text, + text: errText, code: code, } } @@ -181,14 +189,18 @@ func NewCodeSkipf(code, skip int, format string, args ...interface{}) error { // WrapCode wraps error with code and text. // It returns nil if given err is nil. -func WrapCode(code int, err error, text string) error { +func WrapCode(code int, err error, text ...string) error { if err == nil { return nil } + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ error: err, stack: callers(), - text: text, + text: errText, code: code, } } @@ -210,14 +222,18 @@ func WrapCodef(code int, err error, format string, args ...interface{}) error { // WrapCodeSkip wraps error with code and text. // It returns nil if given err is nil. // The parameter specifies the stack callers skipped amount. -func WrapCodeSkip(code, skip int, err error, text string) error { +func WrapCodeSkip(code, skip int, err error, text ...string) error { if err == nil { return nil } + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ error: err, stack: callers(skip), - text: text, + text: errText, code: code, } } @@ -238,14 +254,24 @@ func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{} } // Code returns the error code of current error. -// It returns -1 if it has no error code or it does not implements interface Code. +// It returns CodeNil if it has no error code or it does not implements interface Code. func Code(err error) int { if err != nil { if e, ok := err.(apiCode); ok { return e.Code() } } - return -1 + return CodeNil +} + +// CodeMessage retrieves and returns the error code message from given error. +func CodeMessage(err error) string { + return Message(Code(err)) +} + +// Message returns the message string for specified code. +func Message(code int) string { + return codeMessageMap[code] } // Cause returns the root cause error of . diff --git a/errors/gerror/gerror_code.go b/errors/gerror/gerror_code.go index 055ea0ab0..699deaa11 100644 --- a/errors/gerror/gerror_code.go +++ b/errors/gerror/gerror_code.go @@ -9,32 +9,62 @@ package gerror // Reserved internal error code of framework: code < 1000. const ( - // =============================================================================== - // Common system codes. - // =============================================================================== - - CodeNil = -1 // No error code specified. - CodeOk = 0 // It is OK. - CodeInternalError = 50 + iota // An error occurred internally. - CodeValidationFailed // Data validation failed. - CodeDbOperationError // Database operation error. - CodeInvalidParameter // The given parameter for current operation is invalid. - CodeMissingParameter // Parameter for current operation is missing. - CodeInvalidOperation // The function cannot be used like this. - CodeInvalidConfiguration // The configuration is invalid for current operation. - CodeMissingConfiguration // The configuration is missing for current operation. - CodeNotImplemented // The operation is not implemented yet. - CodeNotSupported // The operation is not supported yet. - CodeOperationFailed // I tried, but I cannot give you what you want. - CodeNotAuthorized // Not Authorized. - CodeSecurityReason // Security Reason. - CodeServerBusy // Server is busy, please try again later. - CodeUnknown // Unknown error. - CodeResourceNotExist // Resource does not exist. - - // =============================================================================== - // Common business codes. - // =============================================================================== - - CodeBusinessValidationFailed = 300 + iota // Business validation failed. + CodeNil = -1 // No error code specified. + CodeOk = 0 // It is OK. + CodeInternalError = 50 // An error occurred internally. + CodeValidationFailed = 51 // Data validation failed. + CodeDbOperationError = 52 // Database operation error. + CodeInvalidParameter = 53 // The given parameter for current operation is invalid. + CodeMissingParameter = 54 // Parameter for current operation is missing. + CodeInvalidOperation = 55 // The function cannot be used like this. + CodeInvalidConfiguration = 56 // The configuration is invalid for current operation. + CodeMissingConfiguration = 57 // The configuration is missing for current operation. + CodeNotImplemented = 58 // The operation is not implemented yet. + CodeNotSupported = 59 // The operation is not supported yet. + CodeOperationFailed = 60 // I tried, but I cannot give you what you want. + CodeNotAuthorized = 61 // Not Authorized. + CodeSecurityReason = 62 // Security Reason. + CodeServerBusy = 63 // Server is busy, please try again later. + CodeUnknown = 64 // Unknown error. + CodeResourceNotExist = 65 // Resource does not exist. + CodeInvalidRequest = 66 // Invalid request. + CodeBusinessValidationFailed = 300 // Business validation failed. ) + +var ( + // codeMessageMap is the mapping from code to according string message. + codeMessageMap = map[int]string{ + CodeNil: "", + CodeOk: "OK", + CodeInternalError: "Internal Error", + CodeValidationFailed: "Validation Failed", + CodeDbOperationError: "Database Operation Error", + CodeInvalidParameter: "Invalid Parameter", + CodeMissingParameter: "Missing Parameter", + CodeInvalidOperation: "Invalid Operation", + CodeInvalidConfiguration: "Invalid Configuration", + CodeMissingConfiguration: "Missing Configuration", + CodeNotImplemented: "Not Implemented", + CodeNotSupported: "Not Supported", + CodeOperationFailed: "Operation Failed", + CodeNotAuthorized: "Not Authorized", + CodeSecurityReason: "Security Reason", + CodeServerBusy: "Server Is Busy", + CodeUnknown: "Unknown Error", + CodeResourceNotExist: "Resource Not Exist", + CodeInvalidRequest: "Invalid Request", + CodeBusinessValidationFailed: "Business Validation Failed", + } +) + +// RegisterCode registers custom error code to global error codes for gerror recognition. +func RegisterCode(code int, message string) { + codeMessageMap[code] = message +} + +// RegisterCodeMap registers custom error codes to global error codes for gerror recognition using map. +func RegisterCodeMap(codes map[int]string) { + for k, v := range codes { + codeMessageMap[k] = v + } +} diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 045385d84..97e22f0cd 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -47,8 +47,11 @@ func (err *Error) Error() string { return "" } errStr := err.text + if errStr == "" { + errStr = Message(err.code) + } if err.error != nil { - if err.text != "" { + if errStr != "" { errStr += ": " } errStr += err.error.Error() @@ -57,10 +60,10 @@ func (err *Error) Error() string { } // Code returns the error code. -// It returns -1 if it has no error code. +// It returns CodeNil if it has no error code. func (err *Error) Code() int { if err == nil { - return -1 + return CodeNil } return err.code } @@ -159,6 +162,7 @@ func (err *Error) Current() error { error: nil, stack: err.stack, text: err.text, + code: err.code, } } diff --git a/errors/gerror/gerror_z_unit_test.go b/errors/gerror/gerror_z_unit_test.go index e60243aa7..0bffed596 100644 --- a/errors/gerror/gerror_z_unit_test.go +++ b/errors/gerror/gerror_z_unit_test.go @@ -261,8 +261,8 @@ func Test_Code(t *testing.T) { t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { - err := gerror.NewCode(1, "123") - t.Assert(gerror.Code(err), 1) + err := gerror.NewCode(gerror.CodeUnknown, "123") + t.Assert(gerror.Code(err), gerror.CodeUnknown) t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { @@ -318,3 +318,36 @@ func Test_Json(t *testing.T) { t.Assert(string(b), `"2: 1"`) }) } + +func Test_Message(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gerror.Message(-100), ``) + t.Assert(gerror.Message(gerror.CodeNil), ``) + t.Assert(gerror.Message(gerror.CodeOk), `OK`) + }) +} + +func Test_CodeMessage(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := gerror.NewCode(gerror.CodeUnknown) + t.Assert(gerror.CodeMessage(err), `Unknown Error`) + }) +} + +func Test_RegisterCode(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + gerror.RegisterCode(10086, "MobileTelecom") + t.Assert(gerror.Message(10086), `MobileTelecom`) + }) +} + +func Test_RegisterCodeMap(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + gerror.RegisterCodeMap(map[int]string{ + 10087: "MobileTelecom 10087", + 10088: "MobileTelecom 10088", + }) + t.Assert(gerror.Message(10087), `MobileTelecom 10087`) + t.Assert(gerror.Message(10088), `MobileTelecom 10088`) + }) +} diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index ebd65b982..1bb55d049 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -141,7 +141,7 @@ func Database(name ...string) gdb.DB { } return db } else { - // It panics often because it dose not find its configuration for given group. + // If panics, often because it does not find its configuration for given group. panic(err) } return nil diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index 9b5e09be2..2c569c0ac 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -37,12 +37,12 @@ func niceCallFunc(f func()) { // of the real error point. if err, ok := exception.(error); ok { if gerror.Code(err) != gerror.CodeNil { - panic(gerror.Wrap(err, "")) + panic(err) } else { - panic(gerror.WrapCode(gerror.CodeInternalError, err, "")) + panic(gerror.WrapCodeSkip(gerror.CodeInternalError, 1, err, "")) } } else { - panic(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception)) + panic(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%+v", exception)) } } } diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index ff8c5a539..8554b4f3f 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -133,33 +133,33 @@ func (m *middleware) callHandlerFunc(funcInfo handlerFuncInfo) { } if funcInfo.Type.NumIn() == 2 { var ( - request reflect.Value + inputObject reflect.Value ) if funcInfo.Type.In(1).Kind() == reflect.Ptr { - request = reflect.New(funcInfo.Type.In(1).Elem()) - m.request.handlerResponse.Error = m.request.Parse(request.Interface()) + inputObject = reflect.New(funcInfo.Type.In(1).Elem()) + m.request.handlerResponse.Error = m.request.Parse(inputObject.Interface()) } else { - request = reflect.New(funcInfo.Type.In(1).Elem()).Elem() - m.request.handlerResponse.Error = m.request.Parse(request.Addr().Interface()) + inputObject = reflect.New(funcInfo.Type.In(1).Elem()).Elem() + m.request.handlerResponse.Error = m.request.Parse(inputObject.Addr().Interface()) } if m.request.handlerResponse.Error != nil { return } - inputValues = append(inputValues, request) + inputValues = append(inputValues, inputObject) } // Call handler with dynamic created parameter values. results := funcInfo.Value.Call(inputValues) switch len(results) { case 1: - m.request.handlerResponse.Error = results[0].Interface().(error) + if !results[0].IsNil() { + m.request.handlerResponse.Error = results[0].Interface().(error) + } case 2: m.request.handlerResponse.Object = results[0].Interface() if !results[1].IsNil() { - if v := results[1].Interface(); v != nil { - m.request.handlerResponse.Error = v.(error) - } + m.request.handlerResponse.Error = results[1].Interface().(error) } } } diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 1da2af60a..e0381e854 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -13,6 +13,7 @@ import ( "github.com/gogf/gf/encoding/gjson" "github.com/gogf/gf/encoding/gurl" "github.com/gogf/gf/encoding/gxml" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/text/gregex" @@ -299,7 +300,7 @@ func (r *Request) parseQuery() { var err error r.queryMap, err = gstr.Parse(r.URL.RawQuery) if err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidParameter, err, "")) } } } @@ -354,12 +355,12 @@ func (r *Request) parseForm() { if gstr.Contains(contentType, "multipart/") { // multipart/form-data, multipart/mixed if err = r.ParseMultipartForm(r.Server.config.FormParsingMemory); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidRequest, err, "")) } } else if gstr.Contains(contentType, "form") { // application/x-www-form-urlencoded if err = r.Request.ParseForm(); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidRequest, err, "")) } } if len(r.PostForm) > 0 { @@ -402,7 +403,7 @@ func (r *Request) parseForm() { } if params != "" { if r.formMap, err = gstr.Parse(params); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidParameter, err, "")) } } } diff --git a/net/ghttp/ghttp_request_param_page.go b/net/ghttp/ghttp_request_param_page.go index 20e0c8f55..b63a290a0 100644 --- a/net/ghttp/ghttp_request_param_page.go +++ b/net/ghttp/ghttp_request_param_page.go @@ -17,13 +17,15 @@ import ( // NOTE THAT the page parameter name from client is constantly defined as gpage.DefaultPageName // for simplification and convenience. func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { - // It must has Router object attribute. + // It must have Router object attribute. if r.Router == nil { panic("Router object not found") } - url := *r.URL - urlTemplate := url.Path - uriHasPageName := false + var ( + url = *r.URL + urlTemplate = url.Path + uriHasPageName = false + ) // Check the page variable in the URI. if len(r.Router.RegNames) > 0 { for _, name := range r.Router.RegNames { @@ -39,12 +41,17 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { for i, name := range r.Router.RegNames { rule := fmt.Sprintf(`[:\*]%s|\{%s\}`, name, name) if name == gpage.DefaultPageName { - urlTemplate, _ = gregex.ReplaceString(rule, gpage.DefaultPagePlaceHolder, urlTemplate) + urlTemplate, err = gregex.ReplaceString(rule, gpage.DefaultPagePlaceHolder, urlTemplate) } else { - urlTemplate, _ = gregex.ReplaceString(rule, match[i+1], urlTemplate) + urlTemplate, err = gregex.ReplaceString(rule, match[i+1], urlTemplate) + } + if err != nil { + panic(err) } } } + } else { + panic(err) } } } diff --git a/net/ghttp/ghttp_response_write.go b/net/ghttp/ghttp_response_write.go index f8aca67a6..c7dbd10f8 100644 --- a/net/ghttp/ghttp_response_write.go +++ b/net/ghttp/ghttp_response_write.go @@ -71,7 +71,7 @@ func (r *Response) WritefExit(format string, params ...interface{}) { r.Request.Exit() } -// Writef writes the response with and new line. +// Writeln writes the response with and new line. func (r *Response) Writeln(content ...interface{}) { if len(content) == 0 { r.Write("\n") diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 041b182e7..a0153aa0c 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -107,7 +107,7 @@ func GetServer(name ...interface{}) *Server { } // Initialize the server using default configurations. if err := s.SetConfig(NewConfig()); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidConfiguration, err, "")) } // Record the server to internal server mapping by name. serverMapping.Set(serverName, s) diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index e461a9222..7803a295d 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -68,12 +68,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { request.Response.WriteStatus(http.StatusInternalServerError) if err, ok := exception.(error); ok { if code := gerror.Code(err); code != gerror.CodeNil { - s.handleErrorLog(gerror.Wrap(err, ""), request) + s.handleErrorLog(err, request) } else { - s.handleErrorLog(gerror.WrapCode(gerror.CodeInternalError, err, ""), request) + s.handleErrorLog(gerror.WrapCodeSkip(gerror.CodeInternalError, 1, err, ""), request) } } else { - s.handleErrorLog(gerror.NewCodef(gerror.CodeInternalError, "%v", exception), request) + s.handleErrorLog(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%+v", exception), request) } } } diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index 2f36302e1..d3614312e 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -8,6 +8,7 @@ package ghttp import ( "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "strings" @@ -175,8 +176,8 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han parsedItemList.PushBack(parsedItem) // The middleware is inserted before the serving handler. - // If there're multiple middleware, they're inserted into the result list by their registering order. - // The middleware are also executed by their registered order. + // If there are multiple middleware, they're inserted into the result list by their registering order. + // The middleware is also executed by their registered order. case handlerTypeMiddleware: if lastMiddlewareElem == nil { lastMiddlewareElem = parsedItemList.PushFront(parsedItem) @@ -190,7 +191,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han parsedItemList.PushBack(parsedItem) default: - panic(fmt.Sprintf(`invalid handler type %d`, item.Type)) + panic(gerror.NewCodef(gerror.CodeInternalError, `invalid handler type %d`, item.Type)) } } } diff --git a/net/ghttp/ghttp_unit_config_test.go b/net/ghttp/ghttp_unit_config_test.go index 95ba5abfb..cd5949b97 100644 --- a/net/ghttp/ghttp_unit_config_test.go +++ b/net/ghttp/ghttp_unit_config_test.go @@ -152,7 +152,7 @@ func Test_ClientMaxBodySize_File(t *testing.T) { defer gfile.Remove(path) t.Assert( gstr.Trim(c.PostContent("/", "name=john&file=@file:"+path)), - "http: request body too large", + "Invalid Request: http: request body too large", ) }) } diff --git a/net/ghttp/ghttp_unit_router_handler_extended_test.go b/net/ghttp/ghttp_unit_router_handler_extended_test.go index 49f00095c..77d50c8f2 100644 --- a/net/ghttp/ghttp_unit_router_handler_extended_test.go +++ b/net/ghttp/ghttp_unit_router_handler_extended_test.go @@ -77,6 +77,6 @@ func Test_Router_Handler_Extended_Handler_WithObject(t *testing.T) { client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) t.Assert(client.GetContent("/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`) - t.Assert(client.GetContent("/test/error"), `{"code":52,"message":"error","data":null}`) + t.Assert(client.GetContent("/test/error"), `{"code":50,"message":"error","data":null}`) }) } diff --git a/os/gfile/gfile_size.go b/os/gfile/gfile_size.go index 615b9e765..9e3d8f0a6 100644 --- a/os/gfile/gfile_size.go +++ b/os/gfile/gfile_size.go @@ -22,6 +22,11 @@ func Size(path string) int64 { return s.Size() } +// SizeFormat returns the size of file specified by in format string. +func SizeFormat(path string) string { + return FormatSize(Size(path)) +} + // ReadableSize formats size of file given by , for more human readable. func ReadableSize(path string) string { return FormatSize(Size(path)) diff --git a/os/gfile/gfile_z_size_test.go b/os/gfile/gfile_z_size_test.go index 481f84ef7..9425fa866 100644 --- a/os/gfile/gfile_z_size_test.go +++ b/os/gfile/gfile_z_size_test.go @@ -33,6 +33,25 @@ func Test_Size(t *testing.T) { }) } +func Test_SizeFormat(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + paths1 = "/testfile_t1.txt" + sizes string + ) + + createTestFile(paths1, "abcdefghijklmn") + defer delTestFiles(paths1) + + sizes = gfile.SizeFormat(testpath() + paths1) + t.Assert(sizes, "14.00B") + + sizes = gfile.SizeFormat("") + t.Assert(sizes, "0.00B") + + }) +} + func Test_StrToSize(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.Assert(gfile.StrToSize("0.00B"), 0) diff --git a/os/glog/glog_logger_rotate.go b/os/glog/glog_logger_rotate.go index 697793854..9262b989b 100644 --- a/os/glog/glog_logger_rotate.go +++ b/os/glog/glog_logger_rotate.go @@ -43,6 +43,13 @@ func (l *Logger) doRotateFile(filePath string) error { } defer gmlock.Unlock(memoryLockKey) + intlog.PrintFunc(l.ctx, func() string { + return fmt.Sprintf(`start rotating file by size: %s, file: %s`, gfile.SizeFormat(filePath), filePath) + }) + defer intlog.PrintFunc(l.ctx, func() string { + return fmt.Sprintf(`done rotating file by size: %s, size: %s`, gfile.SizeFormat(filePath), filePath) + }) + // No backups, it then just removes the current logging file. if l.config.RotateBackupLimit == 0 { if err := gfile.Remove(filePath); err != nil { @@ -90,6 +97,7 @@ func (l *Logger) doRotateFile(filePath string) error { intlog.Printf(l.ctx, `rotation file exists, continue: %s`, newFilePath) } } + intlog.Printf(l.ctx, "rotating file by size from %s to %s", filePath, newFilePath) if err := gfile.Rename(filePath, newFilePath); err != nil { return err } diff --git a/os/gmlock/gmlock_locker.go b/os/gmlock/gmlock_locker.go index 11f195057..075d139e2 100644 --- a/os/gmlock/gmlock_locker.go +++ b/os/gmlock/gmlock_locker.go @@ -11,9 +11,9 @@ import ( "github.com/gogf/gf/os/gmutex" ) -// Memory locker. +// Locker is a memory based locker. // Note that there's no cache expire mechanism for mutex in locker. -// You need remove certain mutex manually when you do not want use it any more. +// You need remove certain mutex manually when you do not want use it anymore. type Locker struct { m *gmap.StrAnyMap } @@ -28,7 +28,7 @@ func New() *Locker { // Lock locks the with writing lock. // If there's a write/reading lock the , -// it will blocks until the lock is released. +// it will block until the lock is released. func (l *Locker) Lock(key string) { l.getOrNewMutex(key).Lock() } @@ -68,7 +68,7 @@ func (l *Locker) RUnlock(key string) { // LockFunc locks the with writing lock and callback function . // If there's a write/reading lock the , -// it will blocks until the lock is released. +// it will block until the lock is released. // // It releases the lock after is executed. func (l *Locker) LockFunc(key string, f func()) { @@ -79,7 +79,7 @@ func (l *Locker) LockFunc(key string, f func()) { // RLockFunc locks the with reading lock and callback function . // If there's a writing lock the , -// it will blocks until the lock is released. +// it will block until the lock is released. // // It releases the lock after is executed. func (l *Locker) RLockFunc(key string, f func()) { diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index fe537fb4c..3cb587369 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -24,6 +24,7 @@ import ( const ( // Short writes for common usage durations. + D = 24 * time.Hour H = time.Hour M = time.Minute diff --git a/os/gtime/gtime_time_zone.go b/os/gtime/gtime_time_zone.go index d7a50eb2c..8b828bee8 100644 --- a/os/gtime/gtime_time_zone.go +++ b/os/gtime/gtime_time_zone.go @@ -15,6 +15,7 @@ var ( // locationMap is time zone name to its location object. // Time zone name is like: Asia/Shanghai. locationMap = make(map[string]*time.Location) + // locationMu is used for concurrent safety for `locationMap`. locationMu = sync.RWMutex{} ) diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index beebf21a6..65aac5e3d 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -33,12 +33,12 @@ func (entry *Entry) Status() int { // Run runs the timer job asynchronously. func (entry *Entry) Run() { leftRunningTimes := entry.times.Add(-1) - // Running times exceeding checks. + // It checks its running times exceeding. if leftRunningTimes < 0 { entry.status.Set(StatusClosed) return } - // This means it does not limit the running times. + // This means it has no limit in running times. if leftRunningTimes == math.MaxInt32-1 { entry.times.Set(math.MaxInt32) } diff --git a/os/gtimer/gtimer_queue.go b/os/gtimer/gtimer_queue.go index 6dd1dfc5f..54443bdf9 100644 --- a/os/gtimer/gtimer_queue.go +++ b/os/gtimer/gtimer_queue.go @@ -18,9 +18,9 @@ import ( // high priority is served before an element with low priority. // priorityQueue is based on heap structure. type priorityQueue struct { - mu sync.Mutex // use sync.Mutex instead of sync.RWMutex for performance purpose. - heap *priorityQueueHeap // the underlying queue items manager using heap. - latestPriority *gtype.Int64 // latestPriority stores the most priority value of the heap, which is used to check if necessary to call the Pop of heap by Timer. + mu sync.Mutex + heap *priorityQueueHeap // the underlying queue items manager using heap. + nextPriority *gtype.Int64 // nextPriority stores the next priority value of the heap, which is used to check if necessary to call the Pop of heap by Timer. } // priorityQueueHeap is a heap manager, of which the underlying `array` is a array implementing a heap structure. @@ -30,32 +30,23 @@ type priorityQueueHeap struct { // priorityQueueItem stores the queue item which has a `priority` attribute to sort itself in heap. type priorityQueueItem struct { - value interface{} // queue value. - priority int64 // The lesser the `priority` value the higher priority of the `value`, for example: priority of 0 is greater than priority of 1. + value interface{} + priority int64 } // newPriorityQueue creates and returns a priority queue. func newPriorityQueue() *priorityQueue { queue := &priorityQueue{ - heap: &priorityQueueHeap{ - array: make([]priorityQueueItem, 0), - }, - latestPriority: gtype.NewInt64(math.MaxInt64), + heap: &priorityQueueHeap{array: make([]priorityQueueItem, 0)}, + nextPriority: gtype.NewInt64(math.MaxInt64), } heap.Init(queue.heap) return queue } -// Len retrieves and returns the length of the queue. -func (q *priorityQueue) Len() int { - q.mu.Lock() - defer q.mu.Unlock() - return q.heap.Len() -} - -// LatestPriority retrieves and returns the minimum and the most priority value of the queue. -func (q *priorityQueue) LatestPriority() int64 { - return q.latestPriority.Val() +// NextPriority retrieves and returns the minimum and the most priority value of the queue. +func (q *priorityQueue) NextPriority() int64 { + return q.nextPriority.Val() } // Push pushes a value to the queue. @@ -68,11 +59,12 @@ func (q *priorityQueue) Push(value interface{}, priority int64) { value: value, priority: priority, }) - // Update the minimum priority using atomic operation. - if priority < q.latestPriority.Val() { - q.latestPriority.Set(priority) + nextPriority := q.nextPriority.Val() + if priority >= nextPriority { + return } + q.nextPriority.Set(priority) } // Pop retrieves, removes and returns the most high priority value from the queue. @@ -80,10 +72,12 @@ func (q *priorityQueue) Pop() interface{} { q.mu.Lock() defer q.mu.Unlock() if v := heap.Pop(q.heap); v != nil { - item := v.(priorityQueueItem) - // Update the minimum priority using atomic operation. - q.latestPriority.Set(item.priority) - return item.value + var nextPriority int64 = math.MaxInt64 + if len(q.heap.array) > 0 { + nextPriority = q.heap.array[0].priority + } + q.nextPriority.Set(nextPriority) + return v.(priorityQueueItem).value } return nil } diff --git a/os/gtimer/gtimer_queue_heap.go b/os/gtimer/gtimer_queue_heap.go index 30879f770..c4b2f5dbe 100644 --- a/os/gtimer/gtimer_queue_heap.go +++ b/os/gtimer/gtimer_queue_heap.go @@ -12,6 +12,7 @@ func (h *priorityQueueHeap) Len() int { } // Less is used to implement the interface of sort.Interface. +// The least one is placed to the top of the heap. func (h *priorityQueueHeap) Less(i, j int) bool { return h.array[i].priority < h.array[j].priority } diff --git a/os/gtimer/gtimer_timer_loop.go b/os/gtimer/gtimer_timer_loop.go index 61e2ee672..ae94bd311 100644 --- a/os/gtimer/gtimer_timer_loop.go +++ b/os/gtimer/gtimer_timer_loop.go @@ -23,8 +23,7 @@ func (t *Timer) loop() { switch t.status.Val() { case StatusRunning: // Timer proceeding. - currentTimerTicks = t.ticks.Add(1) - if currentTimerTicks >= t.queue.LatestPriority() { + if currentTimerTicks = t.ticks.Add(1); currentTimerTicks >= t.queue.NextPriority() { t.proceed(currentTimerTicks) } @@ -40,7 +39,7 @@ func (t *Timer) loop() { }() } -// proceed proceeds the timer job checking and running logic. +// proceed function proceeds the timer job checking and running logic. func (t *Timer) proceed(currentTimerTicks int64) { var ( value interface{} @@ -51,9 +50,9 @@ func (t *Timer) proceed(currentTimerTicks int64) { break } entry := value.(*Entry) - // It checks if it meets the ticks requirement. + // It checks if it meets the ticks' requirement. if jobNextTicks := entry.nextTicks.Val(); currentTimerTicks < jobNextTicks { - // It push the job back if current ticks does not meet its running ticks requirement. + // It pushes the job back if current ticks does not meet its running ticks requirement. t.queue.Push(entry, entry.nextTicks.Val()) break } diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index 9882d21c3..785fd9823 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -46,3 +46,38 @@ func TestTimer_Proceed(t *testing.T) { t.Assert(array.Len(), 2) }) } + +func TestTimer_PriorityQueue(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + queue := newPriorityQueue() + queue.Push(1, 1) + queue.Push(4, 4) + queue.Push(5, 5) + queue.Push(2, 2) + queue.Push(3, 3) + t.Assert(queue.Pop(), 1) + t.Assert(queue.Pop(), 2) + t.Assert(queue.Pop(), 3) + t.Assert(queue.Pop(), 4) + t.Assert(queue.Pop(), 5) + }) +} + +func TestTimer_PriorityQueue_FirstOneInArrayIsTheLeast(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + size = 1000000 + array = garray.NewIntArrayRange(0, size, 1) + ) + array.Shuffle() + queue := newPriorityQueue() + array.Iterator(func(k int, v int) bool { + queue.Push(v, int64(v)) + return true + }) + for i := 0; i < size; i++ { + t.Assert(queue.Pop(), i) + t.Assert(queue.heap.array[0].priority, i+1) + } + }) +} diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 77e492fe8..0a72171f8 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -53,7 +53,7 @@ type doConvertInput struct { Extra []interface{} // Extra values for implementing the converting. } -// doConvert does common used types converting. +// doConvert does commonly used types converting. func doConvert(input doConvertInput) interface{} { switch input.ToTypeName { case "int": @@ -366,7 +366,7 @@ func Runes(any interface{}) []rune { } // String converts `any` to string. -// It's most common used converting function. +// It's most commonly used converting function. func String(any interface{}) string { if any == nil { return "" @@ -459,7 +459,7 @@ func String(any interface{}) string { if kind == reflect.Ptr { return String(rv.Elem().Interface()) } - // Finally we use json.Marshal to convert. + // Finally, we use json.Marshal to convert. if jsonContent, err := json.Marshal(value); err != nil { return fmt.Sprint(value) } else {