forgejo/modules/base/tool.go

270 lines
6.7 KiB
Go
Raw Normal View History

2014-02-18 23:31:16 +01:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2014-02-18 23:31:16 +01:00
2014-03-07 23:22:15 +01:00
package base
2014-02-18 23:31:16 +01:00
import (
"crypto/md5"
2014-03-19 12:21:23 +01:00
"crypto/sha1"
[v1.21] [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 5caea2d75aeac78fb306f58a3cf7809d5b70c7f2) (cherry picked from commit 08da542cce2c1571cedd4183268a903ab581d2e3)
2023-09-30 00:45:31 +02:00
"crypto/sha256"
2014-11-07 20:46:13 +01:00
"encoding/base64"
2014-02-18 23:31:16 +01:00
"encoding/hex"
"errors"
2014-03-14 07:32:11 +01:00
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
2014-03-14 07:32:11 +01:00
"time"
2016-02-20 23:10:05 +01:00
"unicode"
"unicode/utf8"
2014-05-26 02:11:25 +02: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 23:31:16 +01:00
)
2015-11-25 00:49:34 +01:00
// EncodeMD5 encodes string to md5 hex value.
func EncodeMD5(str string) string {
2014-02-18 23:31:16 +01:00
m := md5.New()
2019-06-12 21:41:28 +02:00
_, _ = m.Write([]byte(str))
2014-02-18 23:31:16 +01:00
return hex.EncodeToString(m.Sum(nil))
}
2014-03-14 07:32:11 +01:00
2016-11-24 08:17:44 +01:00
// EncodeSha1 string to sha1 hex value.
2014-11-12 12:48:50 +01:00
func EncodeSha1(str string) string {
h := sha1.New()
2019-06-12 21:41:28 +02:00
_, _ = h.Write([]byte(str))
2014-11-12 12:48:50 +01:00
return hex.EncodeToString(h.Sum(nil))
}
// EncodeSha256 string to sha256 hex value.
2019-05-04 17:45:34 +02:00
func EncodeSha256(str string) string {
h := sha256.New()
2019-06-12 21:41:28 +02:00
_, _ = h.Write([]byte(str))
2019-05-04 17:45:34 +02:00
return hex.EncodeToString(h.Sum(nil))
}
2016-11-07 23:14:50 +01:00
// ShortSha is basically just truncating.
// It is DEPRECATED and will be removed in the future.
2015-11-13 23:10:25 +01:00
func ShortSha(sha1 string) string {
return TruncateString(sha1, 10)
2015-11-13 23:10:25 +01:00
}
2016-11-24 08:17:44 +01:00
// BasicAuthDecode decode basic auth string
2014-12-10 11:10:26 +01:00
func BasicAuthDecode(encoded string) (string, string, error) {
s, err := base64.StdEncoding.DecodeString(encoded)
2014-11-07 20:46:13 +01:00
if err != nil {
2014-12-10 11:10:26 +01:00
return "", "", err
2014-11-07 20:46:13 +01:00
}
2014-12-10 11:01:17 +01:00
auth := strings.SplitN(string(s), ":", 2)
if len(auth) != 2 {
return "", "", errors.New("invalid basic authentication")
}
2014-12-10 11:10:26 +01:00
return auth[0], auth[1], nil
2014-11-07 20:46:13 +01:00
}
2016-11-24 08:17:44 +01:00
// BasicAuthEncode encode basic auth string
2014-11-07 20:46:13 +01:00
func BasicAuthEncode(username, password string) string {
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
}
2016-11-24 08:17:44 +01:00
// VerifyTimeLimitCode verify time limit code
2014-03-19 17:50:44 +01: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 17:50:44 +01: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 17:50:44 +01:00
now := time.Now()
if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
return true
}
}
return false
}
2016-11-24 08:17:44 +01:00
// TimeLimitCodeLength default value for time limit code
2014-03-19 17:50:44 +01:00
const TimeLimitCodeLength = 12 + 6 + 40
2016-11-24 08:17:44 +01:00
// CreateTimeLimitCode create a time limit code
2014-03-19 12:21:23 +01: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 12:21:23 +01: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 12:21:23 +01:00
} else {
// use start string create code
startStr = startInf.(string)
start, _ = time.ParseInLocation(format, startStr, time.Local)
startStr = start.Format(format)
2014-03-19 12:21:23 +01:00
}
end = start.Add(time.Minute * time.Duration(minutes))
endStr = end.Format(format)
2014-03-19 12:21:23 +01: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 12:21:23 +01:00
encoded := hex.EncodeToString(sh.Sum(nil))
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
return code
}
2014-03-15 17:29:49 +01:00
// FileSize calculates the file size and generate user-friendly string.
2014-03-15 17:31:12 +01: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 10:52:08 +02:00
// StringsToInt64s converts a slice of string to a slice of int64.
func StringsToInt64s(strs []string) ([]int64, error) {
2015-08-10 10:52:08 +02: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 10:52:08 +02:00
}
return ints, nil
2015-08-10 10:52:08 +02:00
}
2015-08-25 17:22:05 +02: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 17:22:05 +02: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 23:10:05 +01: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 23:10:05 +01: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 18:02:41 +01: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)
}