mirror of
https://github.com/kataras/iris.git
synced 2026-01-09 13:05:56 +00:00
Version 3.0.0-beta cleaned
This commit is contained in:
6
utils/README.md
Normal file
6
utils/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## Package information
|
||||
|
||||
This package contains helpful functions that iris uses, you can use them to your project also!
|
||||
|
||||
|
||||
**That's it.**
|
||||
58
utils/bytes.go
Normal file
58
utils/bytes.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
)
|
||||
|
||||
// SerializeBytes serializa bytes using gob encoder and returns them
|
||||
func SerializeBytes(m interface{}) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
enc := gob.NewEncoder(buf)
|
||||
err := enc.Encode(m)
|
||||
if err == nil {
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// DeserializeBytes converts the bytes to an object using gob decoder
|
||||
func DeserializeBytes(b []byte, m interface{}) error {
|
||||
dec := gob.NewDecoder(bytes.NewBuffer(b))
|
||||
return dec.Decode(m) //no reference here otherwise doesn't work because of go remote object
|
||||
}
|
||||
|
||||
// BufferPool implements a pool of bytes.Buffers in the form of a bounded channel.
|
||||
// Pulled from the github.com/oxtoacart/bpool package (Apache licensed).
|
||||
type BufferPool struct {
|
||||
c chan *bytes.Buffer
|
||||
}
|
||||
|
||||
// NewBufferPool creates a new BufferPool bounded to the given size.
|
||||
func NewBufferPool(size int) (bp *BufferPool) {
|
||||
return &BufferPool{
|
||||
c: make(chan *bytes.Buffer, size),
|
||||
}
|
||||
}
|
||||
|
||||
// Get gets a Buffer from the BufferPool, or creates a new one if none are
|
||||
// available in the pool.
|
||||
func (bp *BufferPool) Get() (b *bytes.Buffer) {
|
||||
select {
|
||||
case b = <-bp.c:
|
||||
// reuse existing buffer
|
||||
default:
|
||||
// create new buffer
|
||||
b = bytes.NewBuffer([]byte{})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Put returns the given Buffer to the BufferPool.
|
||||
func (bp *BufferPool) Put(b *bytes.Buffer) {
|
||||
b.Reset()
|
||||
select {
|
||||
case bp.c <- b:
|
||||
default: // Discard the buffer if the pool is full.
|
||||
}
|
||||
}
|
||||
22
utils/errors.go
Normal file
22
utils/errors.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoZip returns an error with message: 'While creating file '+filename'. It's not a zip'
|
||||
ErrNoZip = errors.New("While installing file '%s'. It's not a zip")
|
||||
// ErrFileOpen returns an error with message: 'While opening a file. Trace: +specific error'
|
||||
ErrFileOpen = errors.New("While opening a file. Trace: %s")
|
||||
// ErrFileCreate returns an error with message: 'While creating a file. Trace: +specific error'
|
||||
ErrFileCreate = errors.New("While creating a file. Trace: %s")
|
||||
// ErrFileRemove returns an error with message: 'While removing a file. Trace: +specific error'
|
||||
ErrFileRemove = errors.New("While removing a file. Trace: %s")
|
||||
// ErrFileCopy returns an error with message: 'While copying files. Trace: +specific error'
|
||||
ErrFileCopy = errors.New("While copying files. Trace: %s")
|
||||
// ErrFileDownload returns an error with message: 'While downloading from +specific url. Trace: +specific error'
|
||||
ErrFileDownload = errors.New("While downloading from %s. Trace: %s")
|
||||
// ErrDirCreate returns an error with message: 'Unable to create directory on '+root dir'. Trace: +specific error
|
||||
ErrDirCreate = errors.New("Unable to create directory on '%s'. Trace: %s")
|
||||
)
|
||||
114
utils/exec.go
Normal file
114
utils/exec.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// PathSeparator is the string of os.PathSeparator
|
||||
PathSeparator = string(os.PathSeparator)
|
||||
)
|
||||
|
||||
type (
|
||||
// Cmd is a custom struch which 'implements' the *exec.Cmd
|
||||
Cmd struct {
|
||||
*exec.Cmd
|
||||
}
|
||||
)
|
||||
|
||||
// Arguments sets the command line arguments, including the command as Args[0].
|
||||
// If the args parameter is empty or nil, Run uses {Path}.
|
||||
//
|
||||
// In typical use, both Path and args are set by calling Command.
|
||||
func (cmd *Cmd) Arguments(args ...string) *Cmd {
|
||||
cmd.Cmd.Args = append(cmd.Cmd.Args[0:1], args...) //we need the first argument which is the command
|
||||
return cmd
|
||||
}
|
||||
|
||||
// AppendArguments appends the arguments to the exists
|
||||
func (cmd *Cmd) AppendArguments(args ...string) *Cmd {
|
||||
cmd.Cmd.Args = append(cmd.Cmd.Args, args...)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ResetArguments resets the arguments
|
||||
func (cmd *Cmd) ResetArguments() *Cmd {
|
||||
cmd.Args = cmd.Args[0:1] //keep only the first because is the command
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Directory sets the working directory of the command.
|
||||
// If workingDirectory is the empty string, Run runs the command in the
|
||||
// calling process's current directory.
|
||||
func (cmd *Cmd) Directory(workingDirectory string) *Cmd {
|
||||
cmd.Cmd.Dir = workingDirectory
|
||||
return cmd
|
||||
}
|
||||
|
||||
// CommandBuilder creates a Cmd object and returns it
|
||||
// accepts 2 parameters, one is optionally
|
||||
// first parameter is the command (string)
|
||||
// second variatic parameter is the argument(s) (slice of string)
|
||||
//
|
||||
// the difference from the normal Command function is that you can re-use this Cmd, it doesn't execute until you call its Command function
|
||||
func CommandBuilder(command string, args ...string) *Cmd {
|
||||
return &Cmd{Cmd: exec.Command(command, args...)}
|
||||
}
|
||||
|
||||
//the below is just for exec.Command:
|
||||
|
||||
// Command executes a command in shell and returns it's output, it's block version
|
||||
func Command(command string, a ...string) (output string, err error) {
|
||||
var out []byte
|
||||
//if no args given, try to get them from the command
|
||||
if len(a) == 0 {
|
||||
commandArgs := strings.Split(command, " ")
|
||||
for _, commandArg := range commandArgs {
|
||||
if commandArg[0] == '-' { // if starts with - means that this is an argument, append it to the arguments
|
||||
a = append(a, commandArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
out, err = exec.Command(command, a...).Output()
|
||||
|
||||
if err == nil {
|
||||
output = string(out)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MustCommand executes a command in shell and returns it's output, it's block version. It panics on an error
|
||||
func MustCommand(command string, a ...string) (output string) {
|
||||
var out []byte
|
||||
var err error
|
||||
if len(a) == 0 {
|
||||
commandArgs := strings.Split(command, " ")
|
||||
for _, commandArg := range commandArgs {
|
||||
if commandArg[0] == '-' { // if starts with - means that this is an argument, append it to the arguments
|
||||
a = append(a, commandArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out, err = exec.Command(command, a...).Output()
|
||||
if err != nil {
|
||||
argsToString := strings.Join(a, " ")
|
||||
panic(fmt.Sprintf("\nError running the command %s", command+" "+argsToString))
|
||||
}
|
||||
|
||||
output = string(out)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Exists returns true if directory||file exists
|
||||
func Exists(dir string) bool {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
366
utils/file.go
Normal file
366
utils/file.go
Normal file
@@ -0,0 +1,366 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// ContentBINARY is the string of "application/octet-stream response headers
|
||||
ContentBINARY = "application/octet-stream"
|
||||
)
|
||||
|
||||
// DirectoryExists returns true if a directory(or file) exists, otherwise false
|
||||
func DirectoryExists(dir string) bool {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DownloadZip downloads a zip file returns the downloaded filename and an error.
|
||||
//
|
||||
// An indicator is always shown up to the terminal, so the user will know if (a plugin) try to download something
|
||||
func DownloadZip(zipURL string, newDir string) (string, error) {
|
||||
var err error
|
||||
var size int64
|
||||
finish := make(chan bool)
|
||||
|
||||
go func() {
|
||||
print("\n|")
|
||||
print("_")
|
||||
print("|")
|
||||
|
||||
for {
|
||||
select {
|
||||
case v := <-finish:
|
||||
{
|
||||
if v {
|
||||
print("\010\010\010") //remove the loading chars
|
||||
close(finish)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
default:
|
||||
print("\010\010-")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010\\")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010|")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010/")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010-")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("|")
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
os.MkdirAll(newDir, os.ModeDir)
|
||||
tokens := strings.Split(zipURL, "/")
|
||||
fileName := newDir + tokens[len(tokens)-1]
|
||||
if !strings.HasSuffix(fileName, ".zip") {
|
||||
return "", ErrNoZip.Format(fileName)
|
||||
}
|
||||
|
||||
output, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return "", ErrFileCreate.Format(err.Error())
|
||||
}
|
||||
defer output.Close()
|
||||
response, err := http.Get(zipURL)
|
||||
if err != nil {
|
||||
return "", ErrFileDownload.Format(zipURL, err.Error())
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
size, err = io.Copy(output, response.Body)
|
||||
if err != nil {
|
||||
return "", ErrFileCopy.Format(err.Error())
|
||||
}
|
||||
finish <- true
|
||||
print("OK ", size, " bytes downloaded") //we keep that here so developer will always see in the terminal if a plugin downloads something
|
||||
return fileName, nil
|
||||
|
||||
}
|
||||
|
||||
// Unzip extracts a zipped file to the target location
|
||||
//
|
||||
// it removes the zipped file after successfully completion
|
||||
// returns a string with the path of the created folder (if any) and an error (if any)
|
||||
func Unzip(archive string, target string) (string, error) {
|
||||
reader, err := zip.OpenReader(archive)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(target, 0755); err != nil {
|
||||
return "", ErrDirCreate.Format(target, err.Error())
|
||||
}
|
||||
createdFolder := ""
|
||||
for _, file := range reader.File {
|
||||
path := filepath.Join(target, file.Name)
|
||||
if file.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, file.Mode())
|
||||
if createdFolder == "" {
|
||||
// this is the new directory that zip has
|
||||
createdFolder = path
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
fileReader, err := file.Open()
|
||||
if err != nil {
|
||||
return "", ErrFileOpen.Format(err.Error())
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||
if err != nil {
|
||||
return "", ErrFileOpen.Format(err.Error())
|
||||
}
|
||||
defer targetFile.Close()
|
||||
|
||||
if _, err := io.Copy(targetFile, fileReader); err != nil {
|
||||
return "", ErrFileCopy.Format(err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
reader.Close()
|
||||
return createdFolder, nil
|
||||
}
|
||||
|
||||
// RemoveFile removes a file and returns an error, if any
|
||||
func RemoveFile(filePath string) error {
|
||||
return ErrFileRemove.With(os.Remove(filePath))
|
||||
}
|
||||
|
||||
// Install is just the flow of: downloadZip -> unzip -> removeFile(zippedFile)
|
||||
// accepts 2 parameters
|
||||
//
|
||||
// first parameter is the remote url file zip
|
||||
// second parameter is the target directory
|
||||
// returns a string(installedDirectory) and an error
|
||||
//
|
||||
// (string) installedDirectory is the directory which the zip file had, this is the real installation path, you don't need to know what it's because these things maybe change to the future let's keep it to return the correct path.
|
||||
// the installedDirectory is not empty when the installation is succed, the targetDirectory is not already exists and no error happens
|
||||
// the installedDirectory is empty when the installation is already done by previous time or an error happens
|
||||
func Install(remoteFileZip string, targetDirectory string) (installedDirectory string, err error) {
|
||||
var zipFile string
|
||||
|
||||
zipFile, err = DownloadZip(remoteFileZip, targetDirectory)
|
||||
if err == nil {
|
||||
installedDirectory, err = Unzip(zipFile, targetDirectory)
|
||||
if err == nil {
|
||||
installedDirectory += string(os.PathSeparator)
|
||||
RemoveFile(zipFile)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CopyFile copy a file, accepts full path of the source and full path of destination, if file exists it's overrides it
|
||||
// this function doesn't checks for permissions and all that, it returns an error if didn't worked
|
||||
func CopyFile(source string, destination string) error {
|
||||
reader, err := os.Open(source)
|
||||
|
||||
if err != nil {
|
||||
return ErrFileOpen.Format(err.Error())
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
|
||||
writer, err := os.Create(destination)
|
||||
if err != nil {
|
||||
return ErrFileCreate.Format(err.Error())
|
||||
}
|
||||
|
||||
defer writer.Close()
|
||||
|
||||
_, err = io.Copy(writer, reader)
|
||||
if err != nil {
|
||||
return ErrFileCopy.Format(err.Error())
|
||||
}
|
||||
|
||||
err = writer.Sync()
|
||||
if err != nil {
|
||||
return ErrFileCopy.Format(err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyDir
|
||||
// Recursively copies a directory tree, attempting to preserve permissions.
|
||||
// Source directory must exist.
|
||||
//
|
||||
// Note: the CopyDir function was not written by me, but its working well
|
||||
func CopyDir(source string, dest string) (err error) {
|
||||
|
||||
// get properties of source dir
|
||||
fi, err := os.Stat(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
return fmt.Errorf("Source is not a directory! Source path: %s", source)
|
||||
}
|
||||
|
||||
/*_, err = os.Open(dest)
|
||||
if !os.IsNotExist(err) {
|
||||
return nil // Destination already exists
|
||||
}*/
|
||||
|
||||
// create dest dir
|
||||
|
||||
err = os.MkdirAll(dest, fi.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries, err := ioutil.ReadDir(source)
|
||||
|
||||
for _, entry := range entries {
|
||||
|
||||
sfp := source + "/" + entry.Name()
|
||||
dfp := dest + "/" + entry.Name()
|
||||
if entry.IsDir() {
|
||||
err = CopyDir(sfp, dfp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// perform copy
|
||||
err = CopyFile(sfp, dfp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TypeByExtension returns the MIME type associated with the file extension ext.
|
||||
// The extension ext should begin with a leading dot, as in ".html".
|
||||
// When ext has no associated type, TypeByExtension returns "".
|
||||
//
|
||||
// Extensions are looked up first case-sensitively, then case-insensitively.
|
||||
//
|
||||
// The built-in table is small but on unix it is augmented by the local
|
||||
// system's mime.types file(s) if available under one or more of these
|
||||
// names:
|
||||
//
|
||||
// /etc/mime.types
|
||||
// /etc/apache2/mime.types
|
||||
// /etc/apache/mime.types
|
||||
//
|
||||
// On Windows, MIME types are extracted from the registry.
|
||||
//
|
||||
// Text types have the charset parameter set to "utf-8" by default.
|
||||
func TypeByExtension(fullfilename string) (t string) {
|
||||
ext := filepath.Ext(fullfilename)
|
||||
//these should be found by the windows(registry) and unix(apache) but on windows some machines have problems on this part.
|
||||
if t = mime.TypeByExtension(ext); t == "" {
|
||||
// no use of map here because we will have to lock/unlock it, by hand is better, no problem:
|
||||
if ext == ".json" {
|
||||
t = "application/json"
|
||||
} else if ext == ".zip" {
|
||||
t = "application/zip"
|
||||
} else if ext == ".3gp" {
|
||||
t = "video/3gpp"
|
||||
} else if ext == ".7z" {
|
||||
t = "application/x-7z-compressed"
|
||||
} else if ext == ".ace" {
|
||||
t = "application/x-ace-compressed"
|
||||
} else if ext == ".aac" {
|
||||
t = "audio/x-aac"
|
||||
} else if ext == ".ico" { // for any case
|
||||
t = "image/x-icon"
|
||||
} else {
|
||||
t = ContentBINARY
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetParentDir returns the parent directory(string) of the passed targetDirectory (string)
|
||||
func GetParentDir(targetDirectory string) string {
|
||||
lastSlashIndex := strings.LastIndexByte(targetDirectory, os.PathSeparator)
|
||||
//check if the slash is at the end , if yes then re- check without the last slash, we don't want /path/to/ , we want /path/to in order to get the /path/ which is the parent directory of the /path/to
|
||||
if lastSlashIndex == len(targetDirectory)-1 {
|
||||
lastSlashIndex = strings.LastIndexByte(targetDirectory[0:lastSlashIndex], os.PathSeparator)
|
||||
}
|
||||
|
||||
parentDirectory := targetDirectory[0:lastSlashIndex]
|
||||
return parentDirectory
|
||||
}
|
||||
|
||||
/*
|
||||
// 3-BSD License for package fsnotify/fsnotify
|
||||
// Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
// Copyright (c) 2012 fsnotify Authors. All rights reserved.
|
||||
"github.com/fsnotify/fsnotify"
|
||||
//
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/kataras/iris/logger"
|
||||
|
||||
// WatchDirectoryChanges watches for directory changes and calls the 'evt' callback parameter
|
||||
// unused after v2 but propably I will bring it back on v3
|
||||
|
||||
func WatchDirectoryChanges(rootPath string, evt func(filename string), logger ...*logger.Logger) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
|
||||
if err != nil {
|
||||
if len(logger) > 0 {
|
||||
errors.Printf(logger[0], err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
var lastChange = time.Now()
|
||||
var i = 0
|
||||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
//this is received two times, the last time is the real changed file, so
|
||||
i++
|
||||
if i%2 == 0 {
|
||||
if time.Now().After(lastChange.Add(time.Duration(1) * time.Second)) {
|
||||
lastChange = time.Now()
|
||||
evt(event.Name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
if len(logger) > 0 {
|
||||
errors.Printf(logger[0], err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err = watcher.Add(rootPath)
|
||||
if err != nil {
|
||||
if len(logger) > 0 {
|
||||
errors.Printf(logger[0], err)
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
120
utils/strings.go
Normal file
120
utils/strings.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/gob"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//THESE ARE FROM Go Authors
|
||||
var htmlReplacer = strings.NewReplacer(
|
||||
"&", "&",
|
||||
"<", "<",
|
||||
">", ">",
|
||||
// """ is shorter than """.
|
||||
`"`, """,
|
||||
// "'" is shorter than "'" and apos was not in HTML until HTML5.
|
||||
"'", "'",
|
||||
)
|
||||
|
||||
// HtmlEscape returns a string which has no valid html code
|
||||
func HtmlEscape(s string) string {
|
||||
return htmlReplacer.Replace(s)
|
||||
}
|
||||
|
||||
// FindLower returns the smaller number between a and b
|
||||
func FindLower(a, b int) int {
|
||||
if a <= b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// BytesToString accepts bytes and returns their string presentation
|
||||
// instead of string() this method doesn't generate memory allocations,
|
||||
// BUT it is not safe to use anywhere because it points
|
||||
// this helps on 0 memory allocations
|
||||
func BytesToString(b []byte) string {
|
||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
sh := reflect.StringHeader{bh.Data, bh.Len}
|
||||
return *(*string)(unsafe.Pointer(&sh))
|
||||
}
|
||||
|
||||
// StringToBytes accepts string and returns their []byte presentation
|
||||
// instead of byte() this method doesn't generate memory allocations,
|
||||
// BUT it is not safe to use anywhere because it points
|
||||
// this helps on 0 memory allocations
|
||||
func StringToBytes(s string) []byte {
|
||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh := reflect.SliceHeader{sh.Data, sh.Len, 0}
|
||||
return *(*[]byte)(unsafe.Pointer(&bh))
|
||||
}
|
||||
|
||||
//
|
||||
const (
|
||||
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||
)
|
||||
|
||||
var src = rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
// Random takes a parameter (int) and returns random slice of byte
|
||||
// ex: var randomstrbytes []byte; randomstrbytes = utils.Random(32)
|
||||
func Random(n int) []byte {
|
||||
b := make([]byte, n)
|
||||
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
|
||||
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
|
||||
if remain == 0 {
|
||||
cache, remain = src.Int63(), letterIdxMax
|
||||
}
|
||||
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
||||
b[i] = letterBytes[idx]
|
||||
i--
|
||||
}
|
||||
cache >>= letterIdxBits
|
||||
remain--
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// RandomString accepts a number(10 for example) and returns a random string using simple but fairly safe random algorithm
|
||||
func RandomString(n int) string {
|
||||
return string(Random(n))
|
||||
}
|
||||
|
||||
// Serialize serialize any type to gob bytes and after returns its the base64 encoded string
|
||||
func Serialize(m interface{}) (string, error) {
|
||||
b := bytes.Buffer{}
|
||||
encoder := gob.NewEncoder(&b)
|
||||
err := encoder.Encode(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
|
||||
}
|
||||
|
||||
// Deserialize accepts an encoded string and a data struct which will be filled with the desierialized string
|
||||
// using gob decoder
|
||||
func Deserialize(str string, m interface{}) error {
|
||||
by, err := base64.StdEncoding.DecodeString(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b := bytes.Buffer{}
|
||||
b.Write(by)
|
||||
d := gob.NewDecoder(&b)
|
||||
// d := gob.NewDecoder(bytes.NewBufferString(str))
|
||||
err = d.Decode(&m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
64
utils/ticker.go
Normal file
64
utils/ticker.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/* ticker.go: after version 1, we don't need this atm, but we keep it. */
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Ticker is the timer which is used in cache
|
||||
type Ticker struct {
|
||||
ticker *time.Ticker
|
||||
started bool
|
||||
tickHandlers []func()
|
||||
}
|
||||
|
||||
// NewTicker returns a new Ticker
|
||||
func NewTicker() *Ticker {
|
||||
return &Ticker{tickHandlers: make([]func(), 0), started: false}
|
||||
}
|
||||
|
||||
// OnTick add event handlers/ callbacks which are called on each timer's tick
|
||||
func (c *Ticker) OnTick(h func()) {
|
||||
c.tickHandlers = append(c.tickHandlers, h)
|
||||
}
|
||||
|
||||
// Start starts the timer and execute all listener's when tick
|
||||
func (c *Ticker) Start(duration time.Duration) {
|
||||
if c.started {
|
||||
return
|
||||
}
|
||||
|
||||
if c.ticker != nil {
|
||||
panic("Iris Ticker: Cannot re-start a cache timer, if you stop it, it is not recommented to resume it,\n Just create a new CacheTimer.")
|
||||
}
|
||||
|
||||
c.ticker = time.NewTicker(duration)
|
||||
|
||||
go func() {
|
||||
for t := range c.ticker.C {
|
||||
_ = t
|
||||
// c.mu.Lock()
|
||||
// c.mu.Unlock()
|
||||
//I can make it a clojure to handle only handlers that are registed before .start() but we are ok with this, it is not map no need to Lock, for now.
|
||||
for i := range c.tickHandlers {
|
||||
c.tickHandlers[i]()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
c.started = true
|
||||
}
|
||||
|
||||
// Stop stops the ticker
|
||||
func (c *Ticker) Stop() {
|
||||
if c.started {
|
||||
c.ticker.Stop()
|
||||
c.started = false
|
||||
}
|
||||
}
|
||||
|
||||
// ITick is the interface which all ticker's listeners must implement
|
||||
type ITick interface {
|
||||
OnTick()
|
||||
}
|
||||
Reference in New Issue
Block a user