diff --git a/bsondump/main/bsondump.go b/bsondump/main/bsondump.go index 26f3e1370..5ae45dee2 100644 --- a/bsondump/main/bsondump.go +++ b/bsondump/main/bsondump.go @@ -26,7 +26,7 @@ func main() { opts, err := bsondump.ParseOptions(os.Args[1:], VersionStr, GitCommit) if err != nil { log.Logvf(log.Always, "%v", err) - log.Logvf(log.Always, util.ShortUsage("bsondump")) + log.Logv(log.Always, util.ShortUsage("bsondump")) os.Exit(util.ExitFailure) } diff --git a/buildscript/sa.go b/buildscript/sa.go index ee882ed1b..19234e835 100644 --- a/buildscript/sa.go +++ b/buildscript/sa.go @@ -30,9 +30,9 @@ const ( // the new config file syntax did not seem trivial. eslintVersion = "8.57.0" gitHubCodeownersVersion = "0.2.1" - golangCILintVersion = "2.3.0" + golangCILintVersion = "2.5.0" golinesVersion = "0.12.2" - gosecVersion = "2.20.0" + gosecVersion = "2.22.10" preciousVersion = "0.7.3" ubiVersion = "0.4.2" prettierVersion = "3.4.2" diff --git a/common/archive/prelude.go b/common/archive/prelude.go index 1a7e7d79a..c4240f1ad 100644 --- a/common/archive/prelude.go +++ b/common/archive/prelude.go @@ -13,6 +13,7 @@ import ( "path/filepath" "sync/atomic" + "github.com/ccoveille/go-safecast" "github.com/mongodb/mongo-tools/common/intents" "github.com/mongodb/mongo-tools/common/log" "github.com/mongodb/mongo-tools/common/util" @@ -95,12 +96,16 @@ func NewPrelude( concurrentColls int, serverVersion, toolVersion string, ) (*Prelude, error) { + concurrentCollsi32, err := safecast.ToInt32(concurrentColls) + if err != nil { + return nil, fmt.Errorf("concurrentColls overflows when cast to int32") + } prelude := Prelude{ Header: &Header{ FormatVersion: archiveFormatVersion, ServerVersion: serverVersion, ToolVersion: toolVersion, - ConcurrentCollections: int32(concurrentColls), + ConcurrentCollections: concurrentCollsi32, }, NamespaceMetadatasByDB: make(map[string][]*CollectionMetadata, 0), } diff --git a/common/bsonutil/converter.go b/common/bsonutil/converter.go index 347d9724c..b82838363 100644 --- a/common/bsonutil/converter.go +++ b/common/bsonutil/converter.go @@ -11,6 +11,7 @@ import ( "fmt" "time" + "github.com/ccoveille/go-safecast" "github.com/mongodb/mongo-tools/common/json" "github.com/mongodb/mongo-tools/common/util" "go.mongodb.org/mongo-driver/bson" @@ -319,7 +320,7 @@ func GetBSONValueAsLegacyExtJSON(x interface{}) (interface{}, error) { return v, nil // require no conversion case int: - return json.NumberInt(v), nil + return safecast.ToInt32(v) case primitive.ObjectID: // ObjectId return json.ObjectId(v.Hex()), nil diff --git a/common/db/bson_stream.go b/common/db/bson_stream.go index 18d816a24..037306061 100644 --- a/common/db/bson_stream.go +++ b/common/db/bson_stream.go @@ -10,6 +10,7 @@ import ( "fmt" "io" + "github.com/ccoveille/go-safecast" "go.mongodb.org/mongo-driver/bson" ) @@ -103,7 +104,7 @@ func (bs *BSONSource) LoadNext() []byte { return nil } - bsonSize := int32( + bsonSize, _ := safecast.ToInt32( (uint32(into[0]) << 0) | (uint32(into[1]) << 8) | (uint32(into[2]) << 16) | diff --git a/common/json/decode.go b/common/json/decode.go index e7cf352a3..c8c97eb04 100644 --- a/common/json/decode.go +++ b/common/json/decode.go @@ -26,6 +26,7 @@ import ( "unicode/utf16" "unicode/utf8" + "github.com/ccoveille/go-safecast" "go.mongodb.org/mongo-driver/bson" ) @@ -237,8 +238,12 @@ func (n Number) Float64() (float64, error) { // Int32 returns the number as an int32. func (n Number) Int32() (int32, error) { - x, err := n.Int64() - return int32(x), err + n2, err := n.Int64() + if err != nil { + return int32(0), err + } + x, err := safecast.ToInt32(n2) + return x, err } // Int64 returns the number as an int64. diff --git a/common/options/options.go b/common/options/options.go index b5e4fc41d..cdc833c81 100644 --- a/common/options/options.go +++ b/common/options/options.go @@ -564,20 +564,20 @@ func LogSensitiveOptionWarnings(args []string) { // Log a message for --password, if specified. if tempOpts.Password != "" { - log.Logvf(log.Always, passwordMsg) + log.Logv(log.Always, passwordMsg) } // Log a message for --uri or a positional connection string, if either is specified. uri := tempOpts.ConnectionString if uri != "" { if cs, err := connstring.Parse(uri); err == nil && cs.Password != "" { - log.Logvf(log.Always, uriMsg) + log.Logv(log.Always, uriMsg) } } // Log a message for --sslPEMKeyPassword, if specified. if tempOpts.SSLPEMKeyPassword != "" { - log.Logvf(log.Always, sslMsg) + log.Logv(log.Always, sslMsg) } } diff --git a/common/options/options_fp_disabled.go b/common/options/options_fp_disabled.go index 010044b65..e55121d1e 100644 --- a/common/options/options_fp_disabled.go +++ b/common/options/options_fp_disabled.go @@ -9,7 +9,7 @@ package options -// EnableFailpoints removes the failpoints options +// EnableFailpoints removes the failpoints options. func EnableFailpoints(opts *ToolOptions) { opt := opts.FindOptionByLongName("failpoints") opt.LongName = "" diff --git a/cyclonedx.sbom.json b/cyclonedx.sbom.json index e6a7a077d..63088454b 100644 --- a/cyclonedx.sbom.json +++ b/cyclonedx.sbom.json @@ -800,17 +800,17 @@ "version": "v2.4.0" }, { - "bom-ref": "pkg:golang/std@go1.23.12", + "bom-ref": "pkg:golang/std@go1.25.2", "externalReferences": [ { "type": "website", - "url": "https://pkg.go.dev/None/std@go1.23.12" + "url": "https://pkg.go.dev/None/std@go1.25.2" } ], "name": "std", - "purl": "pkg:golang/std@go1.23.12", + "purl": "pkg:golang/std@go1.25.2", "type": "library", - "version": "go1.23.12" + "version": "go1.25.2" } ], "dependencies": [ @@ -911,7 +911,7 @@ "ref": "pkg:golang/gopkg.in/yaml.v2@v2.4.0" }, { - "ref": "pkg:golang/std@go1.23.12" + "ref": "pkg:golang/std@go1.25.2" } ], "metadata": { diff --git a/go.mod b/go.mod index 5e710f78c..665e71c51 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/mongodb/mongo-tools -go 1.23.0 +go 1.25.0 -toolchain go1.23.8 +toolchain go1.25.2 require ( github.com/craiggwilson/goke v0.0.0-20240206162536-b1c58122d943 @@ -59,6 +59,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect github.com/aws/smithy-go v1.22.4 // indirect + github.com/ccoveille/go-safecast v1.7.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect diff --git a/go.sum b/go.sum index 1ff5bf133..bf547a0c2 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 h1:NFOJ/NXEGV4Rq//71Hs1jC/NvPs1 github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w= github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw= github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/ccoveille/go-safecast v1.7.0 h1:i+0eldPFBeXCTARGyocNS6BNRomquA/GhTZVNEtaIXI= +github.com/ccoveille/go-safecast v1.7.0/go.mod h1:QqwNjxQ7DAqY0C721OIO9InMk9zCwcsO7tnRuHytad8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= diff --git a/mongodump/main/mongodump.go b/mongodump/main/mongodump.go index 76dc2dac5..e471b15db 100644 --- a/mongodump/main/mongodump.go +++ b/mongodump/main/mongodump.go @@ -33,7 +33,7 @@ func main() { opts, err := mongodump.ParseOptions(os.Args[1:], VersionStr, GitCommit) if err != nil { log.Logvf(log.Always, "error parsing command line options: %s", err.Error()) - log.Logvf(log.Always, util.ShortUsage("mongodump")) + log.Logv(log.Always, util.ShortUsage("mongodump")) os.Exit(util.ExitFailure) } diff --git a/mongodump_passthrough/functions.yml b/mongodump_passthrough/functions.yml index e4e8cbf66..f52434e0e 100644 --- a/mongodump_passthrough/functions.yml +++ b/mongodump_passthrough/functions.yml @@ -404,8 +404,8 @@ functions: # XXX - Since mise is scoped to the Mongosync repo root, we cannot easily use it to build things # outside that root. It'd be nice if the migration-verifier also used mise too, but for now this # works okay. - PATH=$PATH:$HOME:/opt/golang/go1.23/bin - GOROOT=/opt/golang/go1.23 + PATH=$PATH:$HOME:/opt/golang/go1.25/bin + GOROOT=/opt/golang/go1.25 ./build.sh # upload the compiled verifier to S3. - command: s3.put diff --git a/mongoexport/main/mongoexport.go b/mongoexport/main/mongoexport.go index 749423f41..981eb485c 100644 --- a/mongoexport/main/mongoexport.go +++ b/mongoexport/main/mongoexport.go @@ -25,7 +25,7 @@ func main() { opts, err := mongoexport.ParseOptions(os.Args[1:], VersionStr, GitCommit) if err != nil { log.Logvf(log.Always, "error parsing command line options: %v", err) - log.Logvf(log.Always, util.ShortUsage("mongoexport")) + log.Logv(log.Always, util.ShortUsage("mongoexport")) os.Exit(util.ExitFailure) } diff --git a/mongofiles/main/mongofiles.go b/mongofiles/main/mongofiles.go index 55ba9bc93..e022ec075 100644 --- a/mongofiles/main/mongofiles.go +++ b/mongofiles/main/mongofiles.go @@ -46,7 +46,7 @@ func main() { if err != nil { log.Logv(log.Always, err.Error()) if setupErr, ok := err.(util.SetupError); ok && setupErr.Message != "" { - log.Logvf(log.Always, setupErr.Message) + log.Logv(log.Always, setupErr.Message) } os.Exit(util.ExitFailure) } diff --git a/mongofiles/mongofiles.go b/mongofiles/mongofiles.go index 5d3cb0db4..446bf90ab 100644 --- a/mongofiles/mongofiles.go +++ b/mongofiles/mongofiles.go @@ -336,7 +336,7 @@ func (mf *MongoFiles) handleDeleteID() error { if err := file.Delete(); err != nil { return err } - log.Logvf(log.Always, fmt.Sprintf("successfully deleted file with _id %v from GridFS", mf.Id)) + log.Logv(log.Always, fmt.Sprintf("successfully deleted file with _id %v from GridFS", mf.Id)) return nil } @@ -392,7 +392,7 @@ func (mf *MongoFiles) writeGFSFileToLocal(gridFile *gfsFile) (err error) { return fmt.Errorf("error while writing Data into local file '%v': %v", localFileName, err) } - log.Logvf(log.Always, fmt.Sprintf("finished writing to %s\n", localFileName)) + log.Logv(log.Always, fmt.Sprintf("finished writing to %s\n", localFileName)) return nil } diff --git a/mongoimport/main/mongoimport.go b/mongoimport/main/mongoimport.go index cf5eed000..7e4c2999b 100644 --- a/mongoimport/main/mongoimport.go +++ b/mongoimport/main/mongoimport.go @@ -25,7 +25,7 @@ func main() { opts, err := mongoimport.ParseOptions(os.Args[1:], VersionStr, GitCommit) if err != nil { log.Logvf(log.Always, "error parsing command line options: %v", err) - log.Logvf(log.Always, util.ShortUsage("mongoimport")) + log.Logv(log.Always, util.ShortUsage("mongoimport")) os.Exit(util.ExitFailure) } @@ -43,7 +43,7 @@ func main() { m, err := mongoimport.New(opts) if err != nil { - log.Logvf(log.Always, err.Error()) + log.Logv(log.Always, err.Error()) os.Exit(util.ExitFailure) } defer m.Close() diff --git a/mongoimport/mongoimport.go b/mongoimport/mongoimport.go index c3277f152..6190588ec 100644 --- a/mongoimport/mongoimport.go +++ b/mongoimport/mongoimport.go @@ -17,6 +17,7 @@ import ( "sync" "sync/atomic" + "github.com/ccoveille/go-safecast" "github.com/mongodb/mongo-tools/common/db" "github.com/mongodb/mongo-tools/common/log" "github.com/mongodb/mongo-tools/common/options" @@ -488,18 +489,14 @@ readLoop: } func (imp *MongoImport) updateCounts(result *mongo.BulkWriteResult, err error) { + i, _ := safecast.ToUint64(result.InsertedCount) + m, _ := safecast.ToUint64(result.ModifiedCount) + u, _ := safecast.ToUint64(result.UpsertedCount) + d, _ := safecast.ToUint64(result.DeletedCount) + if result != nil { atomic.AddUint64( - &imp.processedCount, - uint64( - result.InsertedCount, - )+uint64( - result.ModifiedCount, - )+uint64( - result.UpsertedCount, - )+uint64( - result.DeletedCount, - ), + &imp.processedCount, i+m+u+d, ) } if bwe, ok := err.(mongo.BulkWriteException); ok { diff --git a/mongorestore/main/mongorestore.go b/mongorestore/main/mongorestore.go index 275a77085..c465f096f 100644 --- a/mongorestore/main/mongorestore.go +++ b/mongorestore/main/mongorestore.go @@ -26,7 +26,7 @@ func main() { if err != nil { log.Logvf(log.Always, "error parsing command line options: %s", err.Error()) - log.Logvf(log.Always, util.ShortUsage("mongorestore")) + log.Logv(log.Always, util.ShortUsage("mongorestore")) os.Exit(util.ExitFailure) } @@ -41,7 +41,7 @@ func main() { restore, err := mongorestore.New(opts) if err != nil { - log.Logvf(log.Always, err.Error()) + log.Logv(log.Always, err.Error()) os.Exit(util.ExitFailure) } defer restore.Close() diff --git a/mongorestore/oplog.go b/mongorestore/oplog.go index 511768302..394f61be8 100644 --- a/mongorestore/oplog.go +++ b/mongorestore/oplog.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "github.com/ccoveille/go-safecast" "github.com/mongodb/mongo-tools/common/bsonutil" "github.com/mongodb/mongo-tools/common/db" "github.com/mongodb/mongo-tools/common/dumprestore" @@ -467,8 +468,13 @@ func ParseTimestampFlag(ts string) (primitive.Timestamp, error) { increment = 0 } } + t, err := safecast.ToUint32(seconds) + if err != nil { + return primitive.Timestamp{}, err + } + i, err2 := safecast.ToUint32(increment) - return primitive.Timestamp{T: uint32(seconds), I: uint32(increment)}, nil + return primitive.Timestamp{T: t, I: i}, err2 } // Server versions 3.6.0-3.6.8 and 4.0.0-4.0.2 require a 'ui' field diff --git a/mongostat/main/mongostat.go b/mongostat/main/mongostat.go index 4efcbb7b0..8012572ec 100644 --- a/mongostat/main/mongostat.go +++ b/mongostat/main/mongostat.go @@ -59,7 +59,7 @@ func main() { opts, err := mongostat.ParseOptions(os.Args[1:], VersionStr, GitCommit) if err != nil { log.Logvf(log.Always, "error parsing command line options: %s", err.Error()) - log.Logvf(log.Always, util.ShortUsage("mongostat")) + log.Logv(log.Always, util.ShortUsage("mongostat")) os.Exit(util.ExitFailure) } diff --git a/mongotop/main/mongotop.go b/mongotop/main/mongotop.go index 57923fba8..79bced6c6 100644 --- a/mongotop/main/mongotop.go +++ b/mongotop/main/mongotop.go @@ -29,7 +29,7 @@ func main() { opts, err := mongotop.ParseOptions(os.Args[1:], VersionStr, GitCommit) if err != nil { log.Logvf(log.Always, "error parsing command line options: %s", err.Error()) - log.Logvf(log.Always, util.ShortUsage("mongotop")) + log.Logv(log.Always, util.ShortUsage("mongotop")) os.Exit(util.ExitFailure) } diff --git a/release/release.go b/release/release.go index 3a301796d..12f3a90e4 100644 --- a/release/release.go +++ b/release/release.go @@ -584,7 +584,7 @@ func buildDeb() { for _, path := range md5sumsOrder { md5sum, ok := md5sums[path] if !ok { - log.Fatalf("could not find md5sum for " + path) + log.Fatalf("could not find md5sum for %#q", path) } _, err = f.WriteString(md5sum + " ") check(err, "write md5sum to md5sums") diff --git a/set_goenv.sh b/set_goenv.sh index bb42454b4..a6a762609 100755 --- a/set_goenv.sh +++ b/set_goenv.sh @@ -13,11 +13,11 @@ set_goenv() { UNAME_S=$(PATH="/usr/bin:/bin" uname -s) case $UNAME_S in CYGWIN*) - PREF_GOROOT="c:/golang/go1.23" - PREF_PATH="/cygdrive/c/golang/go1.22/bin:/cygdrive/c/mingw-w64/x86_64-4.9.1-posix-seh-rt_v3-rev1/mingw64/bin:$PATH" + PREF_GOROOT="c:/golang/go1.25" + PREF_PATH="/cygdrive/c/golang/go1.25/bin:/cygdrive/c/mingw-w64/x86_64-4.9.1-posix-seh-rt_v3-rev1/mingw64/bin:$PATH" ;; *) - PREF_GOROOT="/opt/golang/go1.23" + PREF_GOROOT="/opt/golang/go1.25" PREF_PATH="$PREF_GOROOT/bin:$PATH" ;; esac diff --git a/vendor/github.com/ccoveille/go-safecast/.editorconfig b/vendor/github.com/ccoveille/go-safecast/.editorconfig new file mode 100644 index 000000000..f32456b1d --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.editorconfig @@ -0,0 +1,25 @@ +# More information about this file +# https://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf + +[*.md] +tab_width = 2 +trim_trailing_whitespace = false # can be used for indentation with double spaces +# Not enforced, markdownlint already reports it +#insert_final_newline = true + +[*.{yml,yaml}] +tab_width = 2 +trim_trailing_whitespace = true +# Not enforced, markdownlint already reports it +#insert_final_newline = true + +[*.toml,.ini] +tab_width = 2 +trim_trailing_whitespace = true +insert_final_newline = true + diff --git a/vendor/github.com/ccoveille/go-safecast/.gitattributes b/vendor/github.com/ccoveille/go-safecast/.gitattributes new file mode 100644 index 000000000..a681ce365 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.gitattributes @@ -0,0 +1,4 @@ +# ensure that line endings for Windows builds are properly formatted +# see https://github.com/golangci/golangci-lint-action?tab=readme-ov-file#how-to-use +# at "Multiple OS Example" section +*.go text eol=lf diff --git a/vendor/github.com/ccoveille/go-safecast/.golangci.yml b/vendor/github.com/ccoveille/go-safecast/.golangci.yml new file mode 100644 index 000000000..1c2d26df1 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.golangci.yml @@ -0,0 +1,190 @@ +--- +# golangci-lint configuration file made by @ccoVeille +# Source: https://github.com/ccoVeille/golangci-lint-config-examples/ +# Author: @ccoVeille +# License: MIT +# Variant: 03-safe +# Version: v2.0.0 +# +version: "2" + +formatters: + enable: + # format the code + - gofmt + # format the block of imports + - gci + + settings: + # format the code with Go standard library + gofmt: + # simplify the code + # https://pkg.go.dev/cmd/gofmt#hdr-The_simplify_command + simplify: true + rewrite-rules: + # replace `interface{}` with `any` in the code on format + - pattern: 'interface{}' + replacement: 'any' + + # make sure imports are always in a deterministic order + # https://github.com/daixiang0/gci/ + gci: # define the section orders for imports + sections: + # Standard section: captures all standard packages. + - standard + # Default section: catchall that is not standard or custom + - default + # linters that related to local tool, so they should be separated + - localmodule + +linters: + enable: + # Errcheck is a program for checking for unchecked errors in Go code. + - errcheck + + # Vet examines Go source code and reports suspicious constructs. + - govet + + # Detects when assignments to existing variables are not used. + - ineffassign + + # It's a set of rules from staticcheck. See https://staticcheck.io/ + - staticcheck + + # Checks Go code for unused constants, variables, functions and types. + - unused + + # Fast, configurable, extensible, flexible, and beautiful linter for Go. + # Drop-in replacement of golint. + - revive + + # make sure to use t.Helper() when needed + - thelper + + # checks if package imports are in a list of acceptable packages. + - depguard + + # mirror suggests rewrites to avoid unnecessary []byte/string conversion + - mirror + + # detect the possibility to use variables/constants from the Go standard library. + - usestdlibvars + + # Finds commonly misspelled English words. + - misspell + + # Checks for duplicate words in the source code. + - dupword + + # gosec is a security linter for Go code. + - gosec + + # Reports uses of functions with replacement inside the testing package. + - usetesting + + + settings: + + depguard: + rules: + # enforce the library has no dependencies except the standard library + code: + files: + - '!$test' + allow: + - $gostd + # enforce the test files have no dependencies except the standard library and the library itself + test: + files: + - $test + allow: + - $gostd + - github.com/ccoveille/go-safecast + + revive: + rules: + # Blank import should be only in a main or test package, or have a comment justifying it. + - name: blank-imports + + # context.Context() should be the first parameter of a function when provided as argument. + - name: context-as-argument + arguments: + - allowTypesBefore: "*testing.T" + + # Basic types should not be used as a key in `context.WithValue` + - name: context-keys-type + + # Importing with `.` makes the programs much harder to understand + - name: dot-imports + + # Empty blocks make code less readable and could be a symptom of a bug or unfinished refactoring. + - name: empty-block + + # for better readability, variables of type `error` must be named with the prefix `err`. + - name: error-naming + + # for better readability, the errors should be last in the list of returned values by a function. + - name: error-return + + # for better readability, error messages should not be capitalized or end with punctuation or a newline. + - name: error-strings + + # report when replacing `errors.New(fmt.Sprintf())` with `fmt.Errorf()` is possible + - name: errorf + + # exported functions, structs, and methods should have comments. + - name: exported + arguments: + # make error messages clearer + - "sayRepetitiveInsteadOfStutters" + + # enforces conventions on source file names. + - name: filename-format + + # incrementing an integer variable by 1 is recommended to be done using the `++` operator + - name: increment-decrement + + # highlights redundant else-blocks that can be eliminated from the code + - name: indent-error-flow + + # This rule suggests a shorter way of writing ranges that do not use the second value. + - name: range + + # receiver names in a method should reflect the struct name (p for Person, for example) + - name: receiver-naming + + # redefining built in names (true, false, append, make) can lead to bugs very difficult to detect. + - name: redefines-builtin-id + + # redundant else-blocks that can be eliminated from the code. + - name: superfluous-else + + # prevent confusing name for variables when using `time` package + - name: time-naming + + # warns when an exported function or method returns a value of an un-exported type. + - name: unexported-return + + # spots and proposes to remove unreachable code. also helps to spot errors + - name: unreachable-code + + # Functions or methods with unused parameters can be a symptom of an unfinished refactoring or a bug. + - name: unused-parameter + + # report when a variable declaration can be simplified + - name: var-declaration + + # warns when initialism, variable or package naming conventions are not followed. + - name: var-naming + + misspell: + # Correct spellings using locale preferences for US or UK. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + # Default ("") is to use a neutral variety of English. + locale: US + +output: + sort-order: + - linter + - severity + - file # filepath, line, and column. diff --git a/vendor/github.com/ccoveille/go-safecast/.ls-lint.yml b/vendor/github.com/ccoveille/go-safecast/.ls-lint.yml new file mode 100644 index 000000000..9e34b8a8b --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.ls-lint.yml @@ -0,0 +1,17 @@ +--- +# ls-lint configuration file. More information on the file format can be found on https://ls-lint.org/ +ls: + .md: screamingsnakecase # README.md or CODE_OF_CONDUCT.md + + # DEACTIVATED: Go files should are linted via golangci-lint revive filename-format rule + # .go: snakecase + +ignore: + # .git folder cannot be linted + - .git + # .github folder contains configuration files with specific name, and should not be linted + - .github + # dot files are usually configuration files with specific name + - .ls-lint.yml + - .markdownlint.yml + - .yamllint.yml diff --git a/vendor/github.com/ccoveille/go-safecast/.markdownlint.yml b/vendor/github.com/ccoveille/go-safecast/.markdownlint.yml new file mode 100644 index 000000000..fe687e0ca --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.markdownlint.yml @@ -0,0 +1,14 @@ +--- + +# Default state for all rules +default: true + +MD013: + # Overload the default value (120) + line_length: 200 + +MD033: + allowed_elements: + #
are useful for spoilers + - summary + - details diff --git a/vendor/github.com/ccoveille/go-safecast/.yamllint.yml b/vendor/github.com/ccoveille/go-safecast/.yamllint.yml new file mode 100644 index 000000000..f67627c99 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/.yamllint.yml @@ -0,0 +1,14 @@ +--- +extends: default +ignore: + # These files are imported by vale from an external repository + .vale/styles/RedHat/ +rules: + line-length: + max: 200 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + truthy: + # the node "on:" present in each GitHub Actions workflow file + ignore: | + .github/ diff --git a/vendor/github.com/ccoveille/go-safecast/LICENSE b/vendor/github.com/ccoveille/go-safecast/LICENSE new file mode 100644 index 000000000..7f8b751c2 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 ccoVeille + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/ccoveille/go-safecast/README.md b/vendor/github.com/ccoveille/go-safecast/README.md new file mode 100644 index 000000000..6935a3a76 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/README.md @@ -0,0 +1,164 @@ +# 🪄 go-safecast: safe numbers conversion + +[![Go Report Card](https://goreportcard.com/badge/github.com/ccoveille/go-safecast)](https://goreportcard.com/report/github.com/ccoveille/go-safecast) +[![GoDoc](https://godoc.org/github.com/ccoVeille/go-safecast?status.svg)](https://godoc.org/github.com/ccoVeille/go-safecast) +[![codecov](https://codecov.io/gh/ccoVeille/go-safecast/graph/badge.svg?token=VW0VO503U6)](https://codecov.io/gh/ccoVeille/go-safecast) +[![Go Imports](https://img.shields.io/github/search?query=%22%5C%22github.com%2Fccoveille%2Fgo-safecast%5C%22%22%20language%3Ago%20%20-is%3Afork%20-is%3Aarchived%20&label=Go%20imports)](https://github.com/search?q=%22%5C%22github.com%2Fccoveille%2Fgo-safecast%5C%22%22+language%3Ago++-is%3Afork+-is%3Aarchived+&type=code) +![GitHub Repo stars](https://img.shields.io/github/stars/ccoveille/go-safecast) + +go-safecast solves the type conversion issues in Go + +In Go, integer type conversion can lead to a silent and unexpected behavior and errors if not handled carefully. + +This package helps to convert any number to another, and report an error when if there would be a [loss or overflow in the conversion](#conversion-issues) + +## Usage + +```go +package main + +import ( + "fmt" + "math" + + "github.com/ccoveille/go-safecast" +) + +func main() { + var a int + + a = 42 + b, err := safecast.ToUint8(a) // everything is fine + if err != nil { + fmt.Println(err) + } + fmt.Println(b) + // Output: 42 + + a = 255 + 1 + _, err = safecast.ToUint8(a) // 256 is greater than uint8 maximum value + if err != nil { + fmt.Println(err) + // Output: conversion issue: 256 (int) is greater than 255 (uint8): maximum value for this type exceeded + } + + a = -1 + _, err = safecast.ToUint8(a) // -1 cannot fit in uint8 + if err != nil { + fmt.Println(err) + // Output: conversion issue: -1 (int) is less than 0 (uint8): minimum value for this type exceeded + } + + str := "\x99" // ASCII code 153 for Trademark symbol + e := str[0] + _, err = safecast.ToInt8(e) + if err != nil { + fmt.Println(err) + // Output: conversion issue: 153 (uint8) is greater than 127 (int8): maximum value for this type exceeded + } +} +``` + +[Go Playground](https://go.dev/play/p/nelJshulOnj) + +## Conversion issues + +Issues can happen when converting between signed and unsigned integers, or when converting to a smaller integer type. + +```go +package main + +import "fmt" + +func main() { + var a int64 + a = 42 + b := uint8(a) + fmt.Println(b) // 42 + + a = 255 // this is the math.MaxUint8 + b = uint8(a) + fmt.Println(b) // 255 + + a = 255 + 1 + b = uint8(a) + fmt.Println(b) // 0 conversion overflow + + a = -1 + b = uint8(a) + fmt.Println(b) // 255 conversion overflow +} +``` + +[Go Playground](https://go.dev/play/p/DHfNUcZBvVn) + +So you need to adapt your code to write something like this. + +```go +package main + +import "fmt" + +func main() { + var a int64 + a = 42 + if a < 0 || a > math.MaxUint8 { + log.Println("overflow") // Output: overflow + } + fmt.Println(b) // 42 + + a = 255 // this is the math.MaxUint8 + b = uint8(a) + fmt.Println(b) // 255 + + a = 255 + 1 + b = uint8(a) + if a < 0 || a > math.MaxUint8 { + log.Println("overflow") // Output: overflow + } + fmt.Println(b) // Output: 0 + + a = -1 + b = uint8(a) + if a < 0 || a > math.MaxUint8 { + log.Println("overflow") // Output: overflow + } + fmt.Println(b) // Output:255 +} +``` + +[Go Playground](https://go.dev/play/p/qAHGyy4NCLP) + +`go-safecast` is there to avoid boilerplate copy pasta. + +## Motivation + +The gosec project raised this to my attention when the gosec [G115 rule was added](https://github.com/securego/gosec/pull/1149) + +> G115: Potential overflow when converting between integer types. + +This issue was way more complex than expected, and required multiple fixes. + +[CWE-190](https://cwe.mitre.org/data/definitions/190.html) explains in detail. + +But to sum it up, you can face: + +- infinite loop +- access to wrong resource by id +- grant access to someone who exhausted their quota + +The gosec G115 will now report issues in a lot of project. + +## Alternatives + +Some libraries existed, but they were not able to cover all the use cases. + +- [github.com/rung/go-safecast](https://github.com/rung/go-safecast): + Unmaintained, not architecture agnostic, do not support `uint` -> `int` conversion + +- [github.com/cybergarage/go-safecast](https://github.com/cybergarage/go-safecast) + Work with pointer like `json.Marshall` + +## Stargazers over time + +[![Stargazers over time](https://starchart.cc/ccoVeille/go-safecast.svg?variant=adaptive)](https://starchart.cc/ccoVeille/go-safecast) diff --git a/vendor/github.com/ccoveille/go-safecast/conversion.go b/vendor/github.com/ccoveille/go-safecast/conversion.go new file mode 100644 index 000000000..4325b0673 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/conversion.go @@ -0,0 +1,301 @@ +package safecast + +import ( + "errors" + "math" + "reflect" + "strconv" + "strings" +) + +// Convert attempts to convert any value to the desired type +// - If the conversion is possible, the converted value is returned. +// - If the conversion results in a value outside the range of the desired type, an [ErrRangeOverflow] error is wrapped in the returned error. +// - If the conversion exceeds the maximum value of the desired type, an [ErrExceedMaximumValue] error is wrapped in the returned error. +// - If the conversion exceeds the minimum value of the desired type, an [ErrExceedMinimumValue] error is wrapped in the returned error. +// - If the conversion is not possible for the desired type, an [ErrUnsupportedConversion] error is wrapped in the returned error. +// - If the conversion fails from string, an [ErrStringConversion] error is wrapped in the returned error. +// - If the conversion results in an error, an [ErrConversionIssue] error is wrapped in the returned error. +func Convert[NumOut Number, NumIn Input](orig NumIn) (converted NumOut, err error) { + v := reflect.ValueOf(orig) + switch v.Kind() { + case reflect.Int: + return convertFromNumber[NumOut](int(v.Int())) + case reflect.Uint: + return convertFromNumber[NumOut](uint(v.Uint())) + case reflect.Int8: + //nolint:gosec // the int8 is confirmed + return convertFromNumber[NumOut](int8(v.Int())) + case reflect.Uint8: + //nolint:gosec // the uint8 is confirmed + return convertFromNumber[NumOut](uint8(v.Uint())) + case reflect.Int16: + //nolint:gosec // the int16 is confirmed + return convertFromNumber[NumOut](int16(v.Int())) + case reflect.Uint16: + //nolint:gosec // the uint16 is confirmed + return convertFromNumber[NumOut](uint16(v.Uint())) + case reflect.Int32: + //nolint:gosec // the int32 is confirmed + return convertFromNumber[NumOut](int32(v.Int())) + case reflect.Uint32: + //nolint:gosec // the uint32 is confirmed + return convertFromNumber[NumOut](uint32(v.Uint())) + case reflect.Int64: + return convertFromNumber[NumOut](int64(v.Int())) + case reflect.Uint64: + return convertFromNumber[NumOut](uint64(v.Uint())) + case reflect.Float32: + return convertFromNumber[NumOut](float32(v.Float())) + case reflect.Float64: + return convertFromNumber[NumOut](float64(v.Float())) + case reflect.Bool: + if v.Bool() { + return NumOut(1), nil + } + return NumOut(0), nil + case reflect.String: + converted, err = convertFromString[NumOut](v.String()) + // this falls through to default statement is a deliberate hack for increasing the code coverage. + // without this, the default case would be impossible to reach in tests. + fallthrough + default: + return converted, err + } +} + +// MustConvert calls [Convert] to convert the value to the desired type, and panics if the conversion fails. +func MustConvert[NumOut Number, NumIn Input](orig NumIn) NumOut { + converted, err := Convert[NumOut](orig) + if err != nil { + panic(err) + } + return converted +} + +// TestingT is an interface wrapper used by [RequireConvert] that we need for testing purposes. +// +// Only the methods used by [RequireConvert] are expected to be implemented. +// +// [*testing.T], [*testing.B], or [*testing.F] types satisfy this interface. +type TestingT interface { + Helper() + Fatal(args ...any) +} + +// RequireConvert is a test helper that calls [Convert] that converts the value to the desired type, +// and fails the test if the conversion fails. +func RequireConvert[NumOut Number, NumIn Input](t TestingT, orig NumIn) (converted NumOut) { + t.Helper() + + converted, err := Convert[NumOut](orig) + if err != nil { + t.Fatal(err) + } + return converted +} + +func convertFromNumber[NumOut Number, NumIn Number](orig NumIn) (NumOut, error) { + converted := NumOut(orig) + if isFloat64[NumIn]() { + floatOrig := float64(orig) + if math.IsInf(floatOrig, 1) || math.IsInf(floatOrig, -1) { + return 0, getRangeError[NumOut](orig) + } + if math.IsNaN(floatOrig) { + return 0, errorHelper[NumOut]{ + value: orig, + err: ErrUnsupportedConversion, + } + } + } + + if isFloat64[NumOut]() { + // float64 cannot overflow, so we don't have to worry about it + return converted, nil + } + + if isFloat32[NumOut]() { + // check boundary + if math.Abs(float64(orig)) < math.MaxFloat32 { + // the value is within float32 range, there is no overflow + return converted, nil + } + + // TODO: check for numbers close to math.MaxFloat32 + + return 0, getRangeError[NumOut](orig) + } + + if !sameSign(orig, converted) { + return 0, getRangeError[NumOut](orig) + } + + // and compare + base := orig + if isFloat[NumIn]() { + base = NumIn(math.Trunc(float64(orig))) + } + + // convert back to the original type + cast := NumIn(converted) + if cast != base { + return 0, getRangeError[NumOut](orig) + } + + return converted, nil +} + +func convertFromString[NumOut Number](s string) (converted NumOut, err error) { + numberBase := 0 + + s = strings.TrimSpace(s) + + if b, err := strconv.ParseBool(s); err == nil { + if b { + return NumOut(1), nil + } + return NumOut(0), nil + } + + if strings.Contains(s, ".") { + o, err := strconv.ParseFloat(s, 64) + if err != nil { + return 0, errorHelper[NumOut]{ + value: s, + err: ErrStringConversion, + } + } + return convertFromNumber[NumOut](o) + } + + if strings.HasPrefix(s, "-") { + o, err := strconv.ParseInt(s, numberBase, 64) + if err != nil { + if errors.Is(err, strconv.ErrRange) { + return 0, errorHelper[NumOut]{ + value: s, + err: ErrExceedMinimumValue, + } + } + return 0, errorHelper[NumOut]{ + value: s, + err: ErrStringConversion, + } + } + + return convertFromNumber[NumOut](o) + } + + o, err := strconv.ParseUint(s, numberBase, 64) + if err != nil { + if errors.Is(err, strconv.ErrRange) { + return 0, errorHelper[NumOut]{ + value: s, + err: ErrExceedMaximumValue, + } + } + + return 0, errorHelper[NumOut]{ + value: s, + err: ErrStringConversion, + } + } + return convertFromNumber[NumOut](o) +} + +func getRangeError[NumOut Number, NumIn Number](value NumIn) error { + err := ErrExceedMaximumValue + if value < 0 { + err = ErrExceedMinimumValue + } + + return errorHelper[NumOut]{ + value: value, + err: err, + } +} + +// ToInt attempts to convert any [Type] value to an int. +// If the conversion results in a value outside the range of an int, +// an [ErrConversionIssue] error is returned. +func ToInt[T Number](i T) (int, error) { + return convertFromNumber[int](i) +} + +// ToUint attempts to convert any [Number] value to an uint. +// If the conversion results in a value outside the range of an uint, +// an [ErrConversionIssue] error is returned. +func ToUint[T Number](i T) (uint, error) { + return convertFromNumber[uint](i) +} + +// ToInt8 attempts to convert any [Number] value to an int8. +// If the conversion results in a value outside the range of an int8, +// an [ErrConversionIssue] error is returned. +func ToInt8[T Number](i T) (int8, error) { + return convertFromNumber[int8](i) +} + +// ToUint8 attempts to convert any [Number] value to an uint8. +// If the conversion results in a value outside the range of an uint8, +// an [ErrConversionIssue] error is returned. +func ToUint8[T Number](i T) (uint8, error) { + return convertFromNumber[uint8](i) +} + +// ToInt16 attempts to convert any [Number] value to an int16. +// If the conversion results in a value outside the range of an int16, +// an [ErrConversionIssue] error is returned. +func ToInt16[T Number](i T) (int16, error) { + return convertFromNumber[int16](i) +} + +// ToUint16 attempts to convert any [Number] value to an uint16. +// If the conversion results in a value outside the range of an uint16, +// an [ErrConversionIssue] error is returned. +func ToUint16[T Number](i T) (uint16, error) { + return convertFromNumber[uint16](i) +} + +// ToInt32 attempts to convert any [Number] value to an int32. +// If the conversion results in a value outside the range of an int32, +// an [ErrConversionIssue] error is returned. +func ToInt32[T Number](i T) (int32, error) { + return convertFromNumber[int32](i) +} + +// ToUint32 attempts to convert any [Number] value to an uint32. +// If the conversion results in a value outside the range of an uint32, +// an [ErrConversionIssue] error is returned. +func ToUint32[T Number](i T) (uint32, error) { + return convertFromNumber[uint32](i) +} + +// ToInt64 attempts to convert any [Number] value to an int64. +// If the conversion results in a value outside the range of an int64, +// an [ErrConversionIssue] error is returned. +func ToInt64[T Number](i T) (int64, error) { + return convertFromNumber[int64](i) +} + +// ToUint64 attempts to convert any [Number] value to an uint64. +// If the conversion results in a value outside the range of an uint64, +// an [ErrConversionIssue] error is returned. +func ToUint64[T Number](i T) (uint64, error) { + return convertFromNumber[uint64](i) +} + +// ToFloat32 attempts to convert any [Number] value to a float32. +// If the conversion results in a value outside the range of a float32, +// an [ErrConversionIssue] error is returned. +func ToFloat32[T Number](i T) (float32, error) { + return convertFromNumber[float32](i) +} + +// ToFloat64 attempts to convert any [Number] value to a float64. +// If the conversion results in a value outside the range of a float64, +// an [ErrConversionIssue] error is returned. +func ToFloat64[T Number](i T) (float64, error) { + return convertFromNumber[float64](i) +} diff --git a/vendor/github.com/ccoveille/go-safecast/doc.go b/vendor/github.com/ccoveille/go-safecast/doc.go new file mode 100644 index 000000000..b236245c0 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/doc.go @@ -0,0 +1,5 @@ +// Package safecast solves the type conversion issues in Go +// +// In Go, integer type conversion can lead to unexpected behavior and errors if not handled carefully. +// Issues can happen when converting between signed and unsigned integers, or when converting to a smaller integer type. +package safecast diff --git a/vendor/github.com/ccoveille/go-safecast/errors.go b/vendor/github.com/ccoveille/go-safecast/errors.go new file mode 100644 index 000000000..d934b8cb7 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/errors.go @@ -0,0 +1,69 @@ +package safecast + +import ( + "errors" + "fmt" +) + +// ErrConversionIssue is a generic error for type conversion issues +// It is used to wrap other errors +var ErrConversionIssue = errors.New("conversion issue") + +// ErrRangeOverflow is an error for when the value is outside the range of the desired type +var ErrRangeOverflow = errors.New("range overflow") + +// ErrExceedMaximumValue is an error for when the value is greater than the maximum value of the desired type. +var ErrExceedMaximumValue = errors.New("maximum value for this type exceeded") + +// ErrExceedMinimumValue is an error for when the value is less than the minimum value of the desired type. +var ErrExceedMinimumValue = errors.New("minimum value for this type exceeded") + +// ErrUnsupportedConversion is an error for when the conversion is not supported from the provided type. +var ErrUnsupportedConversion = errors.New("unsupported type") + +// ErrStringConversion is an error for when the conversion fails from string. +var ErrStringConversion = errors.New("cannot convert from string") + +// errorHelper is a helper struct for error messages +// It is used to wrap other errors, and provides additional information +type errorHelper[NumOut Number] struct { + value any + err error +} + +func (e errorHelper[NumOut]) Error() string { + errMessage := ErrConversionIssue.Error() + + switch { + case errors.Is(e.err, ErrExceedMaximumValue): + boundary := maxOf[NumOut]() + errMessage = fmt.Sprintf("%s: %v (%T) is greater than %v (%T)", errMessage, e.value, e.value, boundary, boundary) + case errors.Is(e.err, ErrExceedMinimumValue): + boundary := minOf[NumOut]() + errMessage = fmt.Sprintf("%s: %v (%T) is less than %v (%T)", errMessage, e.value, e.value, boundary, boundary) + case errors.Is(e.err, ErrUnsupportedConversion): + errMessage = fmt.Sprintf("%s: %v (%T) is not supported", errMessage, e.value, e.value) + case errors.Is(e.err, ErrStringConversion): + targetType := NumOut(0) + return fmt.Sprintf("%s: cannot convert from string %s to %T (base auto-detection)", errMessage, e.value, targetType) + } + + if e.err != nil { + errMessage = fmt.Sprintf("%s: %s", errMessage, e.err.Error()) + } + return errMessage +} + +func (e errorHelper[NumOut]) Unwrap() []error { + errs := []error{ErrConversionIssue} + if e.err != nil { + switch { + case + errors.Is(e.err, ErrExceedMaximumValue), + errors.Is(e.err, ErrExceedMinimumValue): + errs = append(errs, ErrRangeOverflow) + } + errs = append(errs, e.err) + } + return errs +} diff --git a/vendor/github.com/ccoveille/go-safecast/types.go b/vendor/github.com/ccoveille/go-safecast/types.go new file mode 100644 index 000000000..bfbc427a1 --- /dev/null +++ b/vendor/github.com/ccoveille/go-safecast/types.go @@ -0,0 +1,110 @@ +package safecast + +import ( + "math" +) + +// This files is highly inspired from https://pkg.go.dev/golang.org/x/exp/constraints +// I didn't import it as it would have added an unnecessary dependency + +// Signed is a constraint for all signed integers: int, int8, int16, int32, and int64 types. +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +// Unsigned is a constraint for all unsigned integers: uint, uint8, uint16, uint32, and uint64 types. +// TODO: support uintpr +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 +} + +// Integer is a constraint for the all unsigned and signed integers +type Integer interface { + Signed | Unsigned +} + +// Float is a constraint for the float32 and float64 types. +type Float interface { + ~float32 | ~float64 +} + +// Number is a constraint for all integers and floats +// TODO: consider using complex, but not sure there is a need +type Number interface { + Integer | Float +} + +// Input is a constraint for all types that can be used as input for [Convert], and [MustConvert] +type Input interface { + Number | ~string | ~bool +} + +func isNegative[T Number](t T) bool { + return t < 0 +} + +func sameSign[T1, T2 Number](a T1, b T2) bool { + return isNegative(a) == isNegative(b) +} + +func isUnsigned[T Number]() bool { + v := -1 + return T(v) >= 0 +} +func isFloat[T Number]() bool { + v := 0.1 + return T(v) != 0 +} + +func isFloat32[T Number]() bool { + if !isFloat[T]() { + return false + } + v := math.SmallestNonzeroFloat64 + return T(v*0.9) == 0 +} + +func isFloat64[T Number]() bool { + return isFloat[T]() && !isFloat32[T]() +} + +func sizeOf[T Number]() uint64 { + if isFloat32[T]() { + return 4 + } + + x := uint16(1 << 8) + y := uint32(2 << 16) + z := uint64(4 << 32) + + return 1 + uint64(T(x))>>8 + uint64(T(y))>>16 + uint64(T(z))>>32 +} + +func minOf[T Number]() any { + switch { + case isFloat64[T](): + return float64(-math.MaxFloat64) + case isFloat32[T](): + return float32(-math.MaxFloat32) + case isUnsigned[T](): + return T(0) + } + v := int64(1) << (8*sizeOf[T]() - 1) + return T(v) +} + +func maxOf[T Number]() any { + switch { + case isFloat64[T](): + return float64(math.MaxFloat64) + // there is no case for float64, as nothing can overflow it + case isFloat32[T](): + return float32(math.MaxFloat32) + } + v := uint64(1)<<(8*sizeOf[T]()-1) - 1 + if isUnsigned[T]() { + v = v*2 + 1 + } + + return T(v) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5e502d0d9..9871d70ce 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -240,6 +240,9 @@ github.com/aws/smithy-go/tracing github.com/aws/smithy-go/transport/http github.com/aws/smithy-go/transport/http/internal/io github.com/aws/smithy-go/waiter +# github.com/ccoveille/go-safecast v1.7.0 +## explicit; go 1.21 +github.com/ccoveille/go-safecast # github.com/cpuguy83/go-md2man/v2 v2.0.4 ## explicit; go 1.11 github.com/cpuguy83/go-md2man/v2/md2man