243 lines
5.4 KiB
Go
243 lines
5.4 KiB
Go
// Copyright 2013 com authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package com
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"errors"
|
|
"io"
|
|
r "math/rand"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// AESEncrypt encrypts text and given key with AES.
|
|
func AESEncrypt(key, text []byte) ([]byte, error) {
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b := base64.StdEncoding.EncodeToString(text)
|
|
ciphertext := make([]byte, aes.BlockSize+len(b))
|
|
iv := ciphertext[:aes.BlockSize]
|
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
|
return nil, err
|
|
}
|
|
cfb := cipher.NewCFBEncrypter(block, iv)
|
|
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
|
|
return ciphertext, nil
|
|
}
|
|
|
|
// AESDecrypt decrypts text and given key with AES.
|
|
func AESDecrypt(key, text []byte) ([]byte, error) {
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(text) < aes.BlockSize {
|
|
return nil, errors.New("ciphertext too short")
|
|
}
|
|
iv := text[:aes.BlockSize]
|
|
text = text[aes.BlockSize:]
|
|
cfb := cipher.NewCFBDecrypter(block, iv)
|
|
cfb.XORKeyStream(text, text)
|
|
data, err := base64.StdEncoding.DecodeString(string(text))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// IsLetter returns true if the 'l' is an English letter.
|
|
func IsLetter(l uint8) bool {
|
|
n := (l | 0x20) - 'a'
|
|
if n >= 0 && n < 26 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Expand replaces {k} in template with match[k] or subs[atoi(k)] if k is not in match.
|
|
func Expand(template string, match map[string]string, subs ...string) string {
|
|
var p []byte
|
|
var i int
|
|
for {
|
|
i = strings.Index(template, "{")
|
|
if i < 0 {
|
|
break
|
|
}
|
|
p = append(p, template[:i]...)
|
|
template = template[i+1:]
|
|
i = strings.Index(template, "}")
|
|
if s, ok := match[template[:i]]; ok {
|
|
p = append(p, s...)
|
|
} else {
|
|
j, _ := strconv.Atoi(template[:i])
|
|
if j >= len(subs) {
|
|
p = append(p, []byte("Missing")...)
|
|
} else {
|
|
p = append(p, subs[j]...)
|
|
}
|
|
}
|
|
template = template[i+1:]
|
|
}
|
|
p = append(p, template...)
|
|
return string(p)
|
|
}
|
|
|
|
// Reverse s string, support unicode
|
|
func Reverse(s string) string {
|
|
n := len(s)
|
|
runes := make([]rune, n)
|
|
for _, rune := range s {
|
|
n--
|
|
runes[n] = rune
|
|
}
|
|
return string(runes[n:])
|
|
}
|
|
|
|
// RandomCreateBytes generate random []byte by specify chars.
|
|
func RandomCreateBytes(n int, alphabets ...byte) []byte {
|
|
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
var bytes = make([]byte, n)
|
|
var randby bool
|
|
if num, err := rand.Read(bytes); num != n || err != nil {
|
|
r.Seed(time.Now().UnixNano())
|
|
randby = true
|
|
}
|
|
for i, b := range bytes {
|
|
if len(alphabets) == 0 {
|
|
if randby {
|
|
bytes[i] = alphanum[r.Intn(len(alphanum))]
|
|
} else {
|
|
bytes[i] = alphanum[b%byte(len(alphanum))]
|
|
}
|
|
} else {
|
|
if randby {
|
|
bytes[i] = alphabets[r.Intn(len(alphabets))]
|
|
} else {
|
|
bytes[i] = alphabets[b%byte(len(alphabets))]
|
|
}
|
|
}
|
|
}
|
|
return bytes
|
|
}
|
|
|
|
// ToSnakeCase can convert all upper case characters in a string to
|
|
// underscore format.
|
|
//
|
|
// Some samples.
|
|
// "FirstName" => "first_name"
|
|
// "HTTPServer" => "http_server"
|
|
// "NoHTTPS" => "no_https"
|
|
// "GO_PATH" => "go_path"
|
|
// "GO PATH" => "go_path" // space is converted to underscore.
|
|
// "GO-PATH" => "go_path" // hyphen is converted to underscore.
|
|
//
|
|
// From https://github.com/huandu/xstrings
|
|
func ToSnakeCase(str string) string {
|
|
if len(str) == 0 {
|
|
return ""
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
var prev, r0, r1 rune
|
|
var size int
|
|
|
|
r0 = '_'
|
|
|
|
for len(str) > 0 {
|
|
prev = r0
|
|
r0, size = utf8.DecodeRuneInString(str)
|
|
str = str[size:]
|
|
|
|
switch {
|
|
case r0 == utf8.RuneError:
|
|
buf.WriteByte(byte(str[0]))
|
|
|
|
case unicode.IsUpper(r0):
|
|
if prev != '_' {
|
|
buf.WriteRune('_')
|
|
}
|
|
|
|
buf.WriteRune(unicode.ToLower(r0))
|
|
|
|
if len(str) == 0 {
|
|
break
|
|
}
|
|
|
|
r0, size = utf8.DecodeRuneInString(str)
|
|
str = str[size:]
|
|
|
|
if !unicode.IsUpper(r0) {
|
|
buf.WriteRune(r0)
|
|
break
|
|
}
|
|
|
|
// find next non-upper-case character and insert `_` properly.
|
|
// it's designed to convert `HTTPServer` to `http_server`.
|
|
// if there are more than 2 adjacent upper case characters in a word,
|
|
// treat them as an abbreviation plus a normal word.
|
|
for len(str) > 0 {
|
|
r1 = r0
|
|
r0, size = utf8.DecodeRuneInString(str)
|
|
str = str[size:]
|
|
|
|
if r0 == utf8.RuneError {
|
|
buf.WriteRune(unicode.ToLower(r1))
|
|
buf.WriteByte(byte(str[0]))
|
|
break
|
|
}
|
|
|
|
if !unicode.IsUpper(r0) {
|
|
if r0 == '_' || r0 == ' ' || r0 == '-' {
|
|
r0 = '_'
|
|
|
|
buf.WriteRune(unicode.ToLower(r1))
|
|
} else {
|
|
buf.WriteRune('_')
|
|
buf.WriteRune(unicode.ToLower(r1))
|
|
buf.WriteRune(r0)
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
buf.WriteRune(unicode.ToLower(r1))
|
|
}
|
|
|
|
if len(str) == 0 || r0 == '_' {
|
|
buf.WriteRune(unicode.ToLower(r0))
|
|
break
|
|
}
|
|
|
|
default:
|
|
if r0 == ' ' || r0 == '-' {
|
|
r0 = '_'
|
|
}
|
|
|
|
buf.WriteRune(r0)
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|