forgejo/modules/base/tool.go

270 lines
6.7 KiB
Go
Raw Normal View History

2014-02-18 22:31:16 +00:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2014-02-18 22:31:16 +00:00
2014-03-07 22:22:15 +00:00
package base
2014-02-18 22:31:16 +00:00
import (
"crypto/md5"
2014-03-19 11:21:23 +00:00
"crypto/sha1"
[GITEA] Drop sha256-simd in favor of stdlib - In Go 1.21 the crypto/sha256 [got a massive improvement](https://go.dev/doc/go1.21#crypto/sha256) by utilizing the SHA instructions for AMD64 CPUs, which sha256-simd already was doing. The performance is now on par and I think it's preferable to use the standard library rather than a package when possible. ``` cpu: AMD Ryzen 5 3600X 6-Core Processor │ simd.txt │ go.txt │ │ sec/op │ sec/op vs base │ Hash/8Bytes-12 63.25n ± 1% 73.38n ± 1% +16.02% (p=0.002 n=6) Hash/64Bytes-12 98.73n ± 1% 105.30n ± 1% +6.65% (p=0.002 n=6) Hash/1K-12 567.2n ± 1% 572.8n ± 1% +0.99% (p=0.002 n=6) Hash/8K-12 4.062µ ± 1% 4.062µ ± 1% ~ (p=0.396 n=6) Hash/1M-12 512.1µ ± 0% 510.6µ ± 1% ~ (p=0.485 n=6) Hash/5M-12 2.556m ± 1% 2.564m ± 0% ~ (p=0.093 n=6) Hash/10M-12 5.112m ± 0% 5.127m ± 0% ~ (p=0.093 n=6) geomean 13.82µ 14.27µ +3.28% │ simd.txt │ go.txt │ │ B/s │ B/s vs base │ Hash/8Bytes-12 120.6Mi ± 1% 104.0Mi ± 1% -13.81% (p=0.002 n=6) Hash/64Bytes-12 618.2Mi ± 1% 579.8Mi ± 1% -6.22% (p=0.002 n=6) Hash/1K-12 1.682Gi ± 1% 1.665Gi ± 1% -0.98% (p=0.002 n=6) Hash/8K-12 1.878Gi ± 1% 1.878Gi ± 1% ~ (p=0.310 n=6) Hash/1M-12 1.907Gi ± 0% 1.913Gi ± 1% ~ (p=0.485 n=6) Hash/5M-12 1.911Gi ± 1% 1.904Gi ± 0% ~ (p=0.093 n=6) Hash/10M-12 1.910Gi ± 0% 1.905Gi ± 0% ~ (p=0.093 n=6) geomean 1.066Gi 1.032Gi -3.18% ``` (cherry picked from commit abd94ff5b59c86e793fd9bf12187ea6cfd1f3fa1) (cherry picked from commit 15e81637abf70576a564cf9eecaa9640228afb5b) Conflicts: go.mod https://codeberg.org/forgejo/forgejo/pulls/1581 (cherry picked from commit 325d92917f655c999b81b08832ee623d6b669f0f) Conflicts: modules/context/context_cookie.go https://codeberg.org/forgejo/forgejo/pulls/1617 (cherry picked from commit 358819e8959886faa171ac16541097500d0a703e) (cherry picked from commit 362fd7aae17832fa922fa017794bc564ca43060d) (cherry picked from commit 4f64ee294ee05c93042b6ec68f0a179ec249dab9) (cherry picked from commit 4bde77f7b13c5f961c141c01b6da1f9eda5ec387) (cherry picked from commit 1311e30a811675eb623692349e4e808a85aabef6) (cherry picked from commit 57b69e334c2973118488b9b5dbdc8a2c88135756) (cherry picked from commit 52dc892fadecf39e89c3c351edc9efb42522257b) (cherry picked from commit 77f54f4187869c6eabcc837742fd3f908093a76c)
2023-09-29 22:45:31 +00:00
"crypto/sha256"
2014-11-07 19:46:13 +00:00
"encoding/base64"
2014-02-18 22:31:16 +00:00
"encoding/hex"
"errors"
2014-03-14 06:32:11 +00:00
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
2014-03-14 06:32:11 +00:00
"time"
2016-02-20 22:10:05 +00:00
"unicode"
"unicode/utf8"
2014-05-26 00:11:25 +00:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/dustin/go-humanize"
2014-02-18 22:31:16 +00:00
)
2015-11-24 23:49:34 +00:00
// EncodeMD5 encodes string to md5 hex value.
func EncodeMD5(str string) string {
2014-02-18 22:31:16 +00:00
m := md5.New()
2019-06-12 19:41:28 +00:00
_, _ = m.Write([]byte(str))
2014-02-18 22:31:16 +00:00
return hex.EncodeToString(m.Sum(nil))
}
2014-03-14 06:32:11 +00:00
2016-11-24 07:17:44 +00:00
// EncodeSha1 string to sha1 hex value.
2014-11-12 11:48:50 +00:00
func EncodeSha1(str string) string {
h := sha1.New()
2019-06-12 19:41:28 +00:00
_, _ = h.Write([]byte(str))
2014-11-12 11:48:50 +00:00
return hex.EncodeToString(h.Sum(nil))
}
// EncodeSha256 string to sha256 hex value.
2019-05-04 15:45:34 +00:00
func EncodeSha256(str string) string {
h := sha256.New()
2019-06-12 19:41:28 +00:00
_, _ = h.Write([]byte(str))
2019-05-04 15:45:34 +00:00
return hex.EncodeToString(h.Sum(nil))
}
2016-11-07 22:14:50 +00:00
// ShortSha is basically just truncating.
// It is DEPRECATED and will be removed in the future.
2015-11-13 22:10:25 +00:00
func ShortSha(sha1 string) string {
return TruncateString(sha1, 10)
2015-11-13 22:10:25 +00:00
}
2016-11-24 07:17:44 +00:00
// BasicAuthDecode decode basic auth string
2014-12-10 10:10:26 +00:00
func BasicAuthDecode(encoded string) (string, string, error) {
s, err := base64.StdEncoding.DecodeString(encoded)
2014-11-07 19:46:13 +00:00
if err != nil {
2014-12-10 10:10:26 +00:00
return "", "", err
2014-11-07 19:46:13 +00:00
}
2014-12-10 10:01:17 +00:00
auth := strings.SplitN(string(s), ":", 2)
if len(auth) != 2 {
return "", "", errors.New("invalid basic authentication")
}
2014-12-10 10:10:26 +00:00
return auth[0], auth[1], nil
2014-11-07 19:46:13 +00:00
}
2016-11-24 07:17:44 +00:00
// BasicAuthEncode encode basic auth string
2014-11-07 19:46:13 +00:00
func BasicAuthEncode(username, password string) string {
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
}
2016-11-24 07:17:44 +00:00
// VerifyTimeLimitCode verify time limit code
2014-03-19 16:50:44 +00:00
func VerifyTimeLimitCode(data string, minutes int, code string) bool {
if len(code) <= 18 {
return false
}
// split code
start := code[:12]
lives := code[12:18]
if d, err := strconv.ParseInt(lives, 10, 0); err == nil {
minutes = int(d)
2014-03-19 16:50:44 +00:00
}
// right active code
retCode := CreateTimeLimitCode(data, minutes, start)
if retCode == code && minutes > 0 {
// check time is expired or not
before, _ := time.ParseInLocation("200601021504", start, time.Local)
2014-03-19 16:50:44 +00:00
now := time.Now()
if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
return true
}
}
return false
}
2016-11-24 07:17:44 +00:00
// TimeLimitCodeLength default value for time limit code
2014-03-19 16:50:44 +00:00
const TimeLimitCodeLength = 12 + 6 + 40
2016-11-24 07:17:44 +00:00
// CreateTimeLimitCode create a time limit code
2014-03-19 11:21:23 +00:00
// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
func CreateTimeLimitCode(data string, minutes int, startInf any) string {
format := "200601021504"
2014-03-19 11:21:23 +00:00
var start, end time.Time
var startStr, endStr string
if startInf == nil {
// Use now time create code
start = time.Now()
startStr = start.Format(format)
2014-03-19 11:21:23 +00:00
} else {
// use start string create code
startStr = startInf.(string)
start, _ = time.ParseInLocation(format, startStr, time.Local)
startStr = start.Format(format)
2014-03-19 11:21:23 +00:00
}
end = start.Add(time.Minute * time.Duration(minutes))
endStr = end.Format(format)
2014-03-19 11:21:23 +00:00
// create sha1 encode string
sh := sha1.New()
_, _ = sh.Write([]byte(fmt.Sprintf("%s%s%s%s%d", data, setting.SecretKey, startStr, endStr, minutes)))
2014-03-19 11:21:23 +00:00
encoded := hex.EncodeToString(sh.Sum(nil))
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
return code
}
2014-03-15 16:29:49 +00:00
// FileSize calculates the file size and generate user-friendly string.
2014-03-15 16:31:12 +00:00
func FileSize(s int64) string {
return humanize.IBytes(uint64(s))
}
// EllipsisString returns a truncated short string,
// it appends '...' in the end of the length of string is too large.
func EllipsisString(str string, length int) string {
if length <= 3 {
return "..."
}
if utf8.RuneCountInString(str) <= length {
return str
}
return string([]rune(str)[:length-3]) + "..."
}
// TruncateString returns a truncated string with given limit,
// it returns input string if length is not reached limit.
func TruncateString(str string, limit int) string {
if utf8.RuneCountInString(str) < limit {
return str
}
return string([]rune(str)[:limit])
}
2015-08-10 08:52:08 +00:00
// StringsToInt64s converts a slice of string to a slice of int64.
func StringsToInt64s(strs []string) ([]int64, error) {
2015-08-10 08:52:08 +00:00
ints := make([]int64, len(strs))
for i := range strs {
n, err := strconv.ParseInt(strs[i], 10, 64)
if err != nil {
return ints, err
}
ints[i] = n
2015-08-10 08:52:08 +00:00
}
return ints, nil
2015-08-10 08:52:08 +00:00
}
2015-08-25 15:22:05 +00:00
// Int64sToStrings converts a slice of int64 to a slice of string.
func Int64sToStrings(ints []int64) []string {
strs := make([]string, len(ints))
for i := range ints {
strs[i] = strconv.FormatInt(ints[i], 10)
2015-08-25 15:22:05 +00:00
}
return strs
}
// Int64sContains returns if a int64 in a slice of int64
func Int64sContains(intsSlice []int64, a int64) bool {
for _, c := range intsSlice {
if c == a {
return true
}
}
return false
}
2016-02-20 22:10:05 +00:00
// IsLetter reports whether the rune is a letter (category L).
// https://github.com/golang/go/blob/c3b4918/src/go/scanner/scanner.go#L342
2016-02-20 22:10:05 +00:00
func IsLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}
// EntryIcon returns the octicon class for displaying files/directories
func EntryIcon(entry *git.TreeEntry) string {
switch {
case entry.IsLink():
te, err := entry.FollowLink()
if err != nil {
log.Debug(err.Error())
return "file-symlink-file"
}
if te.IsDir() {
return "file-directory-symlink"
}
return "file-symlink-file"
case entry.IsDir():
return "file-directory-fill"
case entry.IsSubModule():
return "file-submodule"
}
Add Octicon SVG spritemap (#10107) * Add octicon SVG sprite Signed-off-by: jolheiser <john.olheiser@gmail.com> * Static prefix Signed-off-by: jolheiser <john.olheiser@gmail.com> * SVG for all repo icons Signed-off-by: jolheiser <john.olheiser@gmail.com> * make vendor Signed-off-by: jolheiser <john.olheiser@gmail.com> * Swap out octicons Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move octicons to top of less imports Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix JS Signed-off-by: jolheiser <john.olheiser@gmail.com> * Definitely not a search/replace Signed-off-by: jolheiser <john.olheiser@gmail.com> * Missed regex Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move to more generic calls and webpack Signed-off-by: jolheiser <john.olheiser@gmail.com> * make svg -> make webpack Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove svg-sprite Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Missed a test Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove svg from makefile Signed-off-by: jolheiser <john.olheiser@gmail.com> * Suggestions Signed-off-by: jolheiser <john.olheiser@gmail.com> * Attempt to fix test Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Revert timetracking test Signed-off-by: jolheiser <john.olheiser@gmail.com> * Swap .octicon for .svg in less Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add aria-hidden Signed-off-by: jolheiser <john.olheiser@gmail.com> * Replace mega-octicon Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix webpack globbing on Windows Signed-off-by: jolheiser <john.olheiser@gmail.com> * Revert Co-Authored-By: silverwind <me@silverwind.io> * Fix octions from upstream Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix Vue and missed JS function Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add JS helper and PWA Signed-off-by: jolheiser <john.olheiser@gmail.com> * Preload SVG Signed-off-by: jolheiser <john.olheiser@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: techknowlogick <matti@mdranta.net>
2020-02-11 17:02:41 +00:00
return "file"
}
// SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value
func SetupGiteaRoot() string {
giteaRoot := os.Getenv("GITEA_ROOT")
if giteaRoot == "" {
_, filename, _, _ := runtime.Caller(0)
giteaRoot = strings.TrimSuffix(filename, "modules/base/tool.go")
wd, err := os.Getwd()
if err != nil {
rel, err := filepath.Rel(giteaRoot, wd)
if err != nil && strings.HasPrefix(filepath.ToSlash(rel), "../") {
giteaRoot = wd
}
}
if _, err := os.Stat(filepath.Join(giteaRoot, "gitea")); os.IsNotExist(err) {
giteaRoot = ""
} else if err := os.Setenv("GITEA_ROOT", giteaRoot); err != nil {
giteaRoot = ""
}
}
return giteaRoot
}
// FormatNumberSI format a number
func FormatNumberSI(data any) string {
var num int64
if num1, ok := data.(int64); ok {
num = num1
} else if num1, ok := data.(int); ok {
num = int64(num1)
} else {
return ""
}
if num < 1000 {
return fmt.Sprintf("%d", num)
} else if num < 1000000 {
num2 := float32(num) / float32(1000.0)
return fmt.Sprintf("%.1fk", num2)
} else if num < 1000000000 {
num2 := float32(num) / float32(1000000.0)
return fmt.Sprintf("%.1fM", num2)
}
num2 := float32(num) / float32(1000000000.0)
return fmt.Sprintf("%.1fG", num2)
}