return i == na
}
+// Includes returns true if an array includes a certain value.
+func Includes(array interface{}, val interface{}) bool {
+ va := reflect.ValueOf(array)
+ if va.Kind() != reflect.Array && va.Kind() != reflect.Slice {
+ return false
+ }
+ if 0 >= va.Len() || reflect.TypeOf(val) != reflect.TypeOf(va.Index(0).Interface()) {
+ return false
+ }
+
+ for i := 0; i < va.Len(); i++ {
+ if va.Index(i).Interface() == val {
+ return true
+ }
+ }
+ return false
+}
+
// IncludesAt returns index if array includes val.
func IncludesAt(array interface{}, val interface{}) int {
va, vv := reflect.ValueOf(array), reflect.ValueOf(val)
--- /dev/null
+// Copyright 2010-2024 nanzoom.com. All Rights Reserved.
+
+package dsutil
+
+import (
+ "crypto/rand"
+ "fmt"
+ "io"
+)
+
+// GenerateRandomBytes returns an random bytes with length:n.
+func GenerateRandomBytes(n int) ([]byte, error) {
+ b := make([]byte, n)
+ if _, err := io.ReadFull(rand.Reader, b); err != nil {
+ return nil, err
+ }
+ return b, nil
+}
+
+// GenerateUUID returns an UUID followin RFC 4122
+func GenerateUUID() (string, error) {
+ uuid, err := GenerateRandomBytes(16)
+ if err != nil {
+ return "", err
+ }
+
+ // set version:4(0100)
+ uuid[6] = (uuid[6] & 0x0f) | 0x40
+ // set variant:8(1000)
+ uuid[8] = (uuid[8] & 0x3f) | 0x80
+
+ return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
+}
"os"
"path/filepath"
"strings"
+ "time"
"git.uzoombox.com/git/uzsdk/utils/osutil"
)
+type ZipNode struct {
+ Path string // realpath on filesystem of the file.
+ Size int64 // length in bytes of the file.
+ Mtime time.Time // modification time.
+ Isdir bool // is directory.
+}
+
+// ZipNamedTree use zip to archive a tree(an kvs that key is relative filepath and value is a zipnode) and output to zipfile,
+// returns nil if succeed, else returns an error.
+// the tree MUST sorted by key with ASC.
+func ZipNamedTree(tree map[string]ZipNode, target string) error {
+ zf, err := os.Create(target)
+ if err != nil {
+ return err
+ }
+ defer zf.Close()
+
+ zw := zip.NewWriter(zf)
+ defer zw.Close()
+
+ for key, node := range tree {
+ zipZipnode(zw, key, &node)
+ }
+
+ return nil
+}
+
// ZipFilepath use zip to archive a path(s) and output to zipfile,
// if successful, it return nil, else return an error.
// options will be [recursive, files]:
_, err = io.Copy(f, fr)
return err
}
+
+func zipZipnode(w *zip.Writer, key string, node *ZipNode) error {
+ // create a local file header.
+ var fh = &zip.FileHeader{
+ Name: key,
+ Method: zip.Deflate,
+ Modified: node.Mtime,
+ UncompressedSize64: uint64(node.Size),
+ }
+ if node.Isdir {
+ fh.Name += "/"
+ }
+
+ // create writer for this file header.
+ fw, err := w.CreateHeader(fh)
+ if err != nil {
+ return err
+ }
+
+ if node.Isdir {
+ return nil
+ }
+
+ // save content of the file.
+ f, err := os.Open(node.Path)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ _, err = io.Copy(fw, f)
+ return err
+}
import (
"bytes"
+ "crypto"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
+ "crypto/sha256"
"fmt"
+ "hash"
"io"
"os"
"path/filepath"
return
}
-// Md5file returns the MD5 checksum of a file.
-func Md5file(path string) string {
- var sum string
- if f, err := os.Open(path); err == nil {
- h := md5.New()
- if _, err := io.Copy(h, f); err == nil {
- sum = fmt.Sprintf("%x", h.Sum(nil))
+// Sha256sum returns the SHA256 checksum of file path.
+func Sha256sum(path string) (sum string) {
+ fi, err := os.Stat(path)
+ if err != nil {
+ return
+ }
+
+ if fi.IsDir() {
+ if false {
+ names := make([]string, 0)
+ filepath.WalkDir(path, func(p string, d os.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ names = append(names, p)
+ if d.IsDir() && path != p {
+ return filepath.SkipDir
+ }
+ return nil
+ })
+
+ sort.Strings(names)
+ sum = Sha256string(names...)
+ } else {
+ sum = ""
}
- f.Close()
+ } else {
+ sum = Sha256file(path)
}
- return sum
+
+ return
+}
+
+// Md5file returns the MD5 checksum of a file.
+func Md5file(path string) string {
+ return hashFile(crypto.MD5, path)
+}
+
+// Sha256file returns the SHA256 checksum of a file.
+func Sha256file(path string) string {
+ return hashFile(crypto.SHA256, path)
}
// Md5string returns the MD5 checksum of string(s).
func Md5string(ss ...string) string {
- h := md5.New()
- for _, s := range ss {
- io.WriteString(h, s)
- }
- return fmt.Sprintf("%x", h.Sum(nil))
+ return hashString(crypto.MD5, ss...)
+}
+
+// Sha256string returns the SHA256 checksum of string(s).
+func Sha256string(ss ...string) string {
+ return hashString(crypto.SHA256, ss...)
}
// AesEncrypt use keyval to encrypt data with AES, and returns the result.
}
return key
}
+
+func hashFile(t crypto.Hash, path string) string {
+ var h hash.Hash
+ switch t {
+ case crypto.MD5:
+ h = md5.New()
+ case crypto.SHA256:
+ h = sha256.New()
+ default:
+ return ""
+ }
+
+ var sum string
+ if f, err := os.Open(path); err == nil {
+ if _, err := io.Copy(h, f); err == nil {
+ sum = fmt.Sprintf("%x", h.Sum(nil))
+ }
+ f.Close()
+ }
+ return sum
+}
+
+func hashString(t crypto.Hash, ss ...string) string {
+ var h hash.Hash
+ switch t {
+ case crypto.MD5:
+ h = md5.New()
+ case crypto.SHA256:
+ h = sha256.New()
+ default:
+ return ""
+ }
+
+ for _, s := range ss {
+ io.WriteString(h, s)
+ }
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
// Mimetype returns the MIME type associated with the file path,
// if path is a directory, returns "httpd/unix-directory".
-// Parameters WOULD include [fi:string/fs.FileInfo, re:*regexp.Regexp]
+// options would be:
+//
+// file string: path of file.
+// fi fs.FileInfo: fileinfo.
+// re *regexp.Regexp: regexp.
func Mimetype(options ...interface{}) string {
var fi fs.FileInfo
var re *regexp.Regexp