mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-05-19 09:27:07 +00:00
feat[golang]: 添加一些go的常用工具类
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,9 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -158,3 +160,214 @@ func HexToBye(hex string) []byte {
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
type (
|
||||
// Bytes struct
|
||||
Bytes struct{}
|
||||
)
|
||||
|
||||
// binary units (IEC 60027)
|
||||
const (
|
||||
_ = 1.0 << (10 * iota) // ignore first value by assigning to blank identifier
|
||||
KiB
|
||||
MiB
|
||||
GiB
|
||||
TiB
|
||||
PiB
|
||||
EiB
|
||||
)
|
||||
|
||||
// decimal units (SI international system of units)
|
||||
const (
|
||||
KB = 1000
|
||||
MB = KB * 1000
|
||||
GB = MB * 1000
|
||||
TB = GB * 1000
|
||||
PB = TB * 1000
|
||||
EB = PB * 1000
|
||||
)
|
||||
|
||||
var (
|
||||
patternBinary = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]iB?)$`)
|
||||
patternDecimal = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]B?|B?)$`)
|
||||
global = New()
|
||||
)
|
||||
|
||||
// New creates a Bytes instance.
|
||||
func New() *Bytes {
|
||||
return &Bytes{}
|
||||
}
|
||||
|
||||
// Format formats bytes integer to human readable string according to IEC 60027.
|
||||
// For example, 31323 bytes will return 30.59KB.
|
||||
func (b *Bytes) Format(value int64) string {
|
||||
return b.FormatBinary(value)
|
||||
}
|
||||
|
||||
// FormatBinary formats bytes integer to human readable string according to IEC 60027.
|
||||
// For example, 31323 bytes will return 30.59KB.
|
||||
func (*Bytes) FormatBinary(value int64) string {
|
||||
multiple := ""
|
||||
val := float64(value)
|
||||
|
||||
switch {
|
||||
case value >= EiB:
|
||||
val /= EiB
|
||||
multiple = "EiB"
|
||||
case value >= PiB:
|
||||
val /= PiB
|
||||
multiple = "PiB"
|
||||
case value >= TiB:
|
||||
val /= TiB
|
||||
multiple = "TiB"
|
||||
case value >= GiB:
|
||||
val /= GiB
|
||||
multiple = "GiB"
|
||||
case value >= MiB:
|
||||
val /= MiB
|
||||
multiple = "MiB"
|
||||
case value >= KiB:
|
||||
val /= KiB
|
||||
multiple = "KiB"
|
||||
case value == 0:
|
||||
return "0"
|
||||
default:
|
||||
return strconv.FormatInt(value, 10) + "B"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.2f%s", val, multiple)
|
||||
}
|
||||
|
||||
// FormatDecimal formats bytes integer to human readable string according to SI international system of units.
|
||||
// For example, 31323 bytes will return 31.32KB.
|
||||
func (*Bytes) FormatDecimal(value int64) string {
|
||||
multiple := ""
|
||||
val := float64(value)
|
||||
|
||||
switch {
|
||||
case value >= EB:
|
||||
val /= EB
|
||||
multiple = "EB"
|
||||
case value >= PB:
|
||||
val /= PB
|
||||
multiple = "PB"
|
||||
case value >= TB:
|
||||
val /= TB
|
||||
multiple = "TB"
|
||||
case value >= GB:
|
||||
val /= GB
|
||||
multiple = "GB"
|
||||
case value >= MB:
|
||||
val /= MB
|
||||
multiple = "MB"
|
||||
case value >= KB:
|
||||
val /= KB
|
||||
multiple = "KB"
|
||||
case value == 0:
|
||||
return "0"
|
||||
default:
|
||||
return strconv.FormatInt(value, 10) + "B"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.2f%s", val, multiple)
|
||||
}
|
||||
|
||||
// Parse parses human readable bytes string to bytes integer.
|
||||
// For example, 6GiB (6Gi is also valid) will return 6442450944, and
|
||||
// 6GB (6G is also valid) will return 6000000000.
|
||||
func (b *Bytes) Parse(value string) (int64, error) {
|
||||
|
||||
i, err := b.ParseBinary(value)
|
||||
if err == nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
return b.ParseDecimal(value)
|
||||
}
|
||||
|
||||
// ParseBinary parses human readable bytes string to bytes integer.
|
||||
// For example, 6GiB (6Gi is also valid) will return 6442450944.
|
||||
func (*Bytes) ParseBinary(value string) (i int64, err error) {
|
||||
parts := patternBinary.FindStringSubmatch(value)
|
||||
if len(parts) < 3 {
|
||||
return 0, fmt.Errorf("error parsing value=%s", value)
|
||||
}
|
||||
bytesString := parts[1]
|
||||
multiple := strings.ToUpper(parts[2])
|
||||
bytes, err := strconv.ParseFloat(bytesString, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch multiple {
|
||||
case "KI", "KIB":
|
||||
return int64(bytes * KiB), nil
|
||||
case "MI", "MIB":
|
||||
return int64(bytes * MiB), nil
|
||||
case "GI", "GIB":
|
||||
return int64(bytes * GiB), nil
|
||||
case "TI", "TIB":
|
||||
return int64(bytes * TiB), nil
|
||||
case "PI", "PIB":
|
||||
return int64(bytes * PiB), nil
|
||||
case "EI", "EIB":
|
||||
return int64(bytes * EiB), nil
|
||||
default:
|
||||
return int64(bytes), nil
|
||||
}
|
||||
}
|
||||
|
||||
// ParseDecimal parses human readable bytes string to bytes integer.
|
||||
// For example, 6GB (6G is also valid) will return 6000000000.
|
||||
func (*Bytes) ParseDecimal(value string) (i int64, err error) {
|
||||
parts := patternDecimal.FindStringSubmatch(value)
|
||||
if len(parts) < 3 {
|
||||
return 0, fmt.Errorf("error parsing value=%s", value)
|
||||
}
|
||||
bytesString := parts[1]
|
||||
multiple := strings.ToUpper(parts[2])
|
||||
bytes, err := strconv.ParseFloat(bytesString, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch multiple {
|
||||
case "K", "KB":
|
||||
return int64(bytes * KB), nil
|
||||
case "M", "MB":
|
||||
return int64(bytes * MB), nil
|
||||
case "G", "GB":
|
||||
return int64(bytes * GB), nil
|
||||
case "T", "TB":
|
||||
return int64(bytes * TB), nil
|
||||
case "P", "PB":
|
||||
return int64(bytes * PB), nil
|
||||
case "E", "EB":
|
||||
return int64(bytes * EB), nil
|
||||
default:
|
||||
return int64(bytes), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Format wraps global Bytes's Format function.
|
||||
func Format(value int64) string {
|
||||
return global.Format(value)
|
||||
}
|
||||
|
||||
// FormatBinary wraps global Bytes's FormatBinary function.
|
||||
func FormatBinary(value int64) string {
|
||||
return global.FormatBinary(value)
|
||||
}
|
||||
|
||||
// FormatDecimal wraps global Bytes's FormatDecimal function.
|
||||
func FormatDecimal(value int64) string {
|
||||
return global.FormatDecimal(value)
|
||||
}
|
||||
|
||||
// Parse wraps global Bytes's Parse function.
|
||||
func Parse(value string) (int64, error) {
|
||||
return global.Parse(value)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo 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 bitset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"sync"
|
||||
|
||||
)
|
||||
|
||||
// BitSet bit set
|
||||
type BitSet struct {
|
||||
set []byte
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// New creates a bit set object.
|
||||
func New(init ...byte) *BitSet {
|
||||
return &BitSet{set: init}
|
||||
}
|
||||
|
||||
// NewFromHex creates a bit set object from hex string.
|
||||
func NewFromHex(s string) (*BitSet, error) {
|
||||
init, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BitSet{set: init}, nil
|
||||
}
|
||||
|
||||
// Set sets the bit bool value on the specified offset,
|
||||
// and returns the value of before setting.
|
||||
// NOTE:
|
||||
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on;
|
||||
// If offset>=len(b.set), automatically grow the bit set;
|
||||
// If the bit offset is out of the left range, returns error.
|
||||
func (b *BitSet) Set(offset int, value bool) (bool, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
size := b.size()
|
||||
// 0 means the 1st bit, -1 means the bottom 1th bit,
|
||||
// -2 means the bottom 2th bit and so on.
|
||||
if offset < 0 {
|
||||
offset += size
|
||||
}
|
||||
if offset < 0 {
|
||||
return false, errors.New("the bit offset is out of the left range")
|
||||
}
|
||||
|
||||
// the bit group index
|
||||
gi := offset / 8
|
||||
// the bit index of in the group
|
||||
bi := offset % 8
|
||||
|
||||
// if the bit offset is out of the right range, automatically grow.
|
||||
if gi >= len(b.set) {
|
||||
newSet := make([]byte, gi+1)
|
||||
copy(newSet, b.set)
|
||||
b.set = newSet
|
||||
}
|
||||
|
||||
gb := b.set[gi]
|
||||
rOff := byte(7 - bi)
|
||||
var mask byte = 1 << rOff
|
||||
oldVal := gb & mask >> rOff
|
||||
if (oldVal == 1) != value {
|
||||
if oldVal == 1 {
|
||||
b.set[gi] = gb &^ mask
|
||||
} else {
|
||||
b.set[gi] = gb | mask
|
||||
}
|
||||
}
|
||||
return oldVal == 1, nil
|
||||
}
|
||||
|
||||
// Get gets the bit bool value on the specified offset.
|
||||
// NOTE:
|
||||
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on;
|
||||
// If offset>=len(b.set), returns false.
|
||||
func (b *BitSet) Get(offset int) bool {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
size := b.size()
|
||||
// 0 means the 1st bit, -1 means the bottom 1th bit,
|
||||
// -2 means the bottom 2th bit and so on.
|
||||
if offset < 0 {
|
||||
offset += size
|
||||
}
|
||||
if offset < 0 || offset >= size {
|
||||
return false
|
||||
}
|
||||
return getBit(b.set[offset/8], byte(offset%8)) == 1
|
||||
}
|
||||
|
||||
// Range calls f sequentially for each bit present in the bit set.
|
||||
// If f returns false, range stops the iteration.
|
||||
func (b *BitSet) Range(f func(offset int, truth bool) bool) {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
size := b.size()
|
||||
if size == 0 {
|
||||
return
|
||||
}
|
||||
for offset := 0; offset < size; offset++ {
|
||||
if !f(offset, getBit(b.set[offset/8], byte(offset%8)) == 1) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getBit(gb, offset byte) byte {
|
||||
var rOff = 7 - offset
|
||||
var mask byte = 1 << rOff
|
||||
return gb & mask >> rOff
|
||||
}
|
||||
|
||||
// Count counts the amount of bit set to 1 within the specified range of the bit set.
|
||||
// NOTE:
|
||||
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on.
|
||||
func (b *BitSet) Count(start, end int) int {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
sgi, sbi, egi, ebi, valid := b.validRange(start, end)
|
||||
if !valid {
|
||||
return 0
|
||||
}
|
||||
var n int
|
||||
n += bits.OnesCount8(b.set[sgi] << sbi)
|
||||
for _, v := range b.set[sgi+1 : egi] {
|
||||
n += bits.OnesCount8(v)
|
||||
}
|
||||
n += bits.OnesCount8(b.set[egi] >> (7 - ebi) << (7 - ebi))
|
||||
return n
|
||||
}
|
||||
|
||||
func (b *BitSet) validRange(start, end int) (sgi, sbi, egi, ebi uint, valid bool) {
|
||||
size := b.size()
|
||||
if start < 0 {
|
||||
start += size
|
||||
}
|
||||
if start >= size {
|
||||
return
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end < 0 {
|
||||
end += size
|
||||
}
|
||||
if end >= size {
|
||||
end = size - 1
|
||||
}
|
||||
if start > end {
|
||||
return
|
||||
}
|
||||
valid = true
|
||||
sgi, sbi = uint(start/8), uint(start%8)
|
||||
egi, ebi = uint(end/8), uint(end%8)
|
||||
return
|
||||
}
|
||||
|
||||
// Not returns ^b.
|
||||
func (b *BitSet) Not() *BitSet {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
rBitSet := &BitSet{
|
||||
set: make([]byte, len(b.set)),
|
||||
}
|
||||
for i, gb := range b.set {
|
||||
rBitSet.set[i] = ^gb
|
||||
}
|
||||
return rBitSet
|
||||
}
|
||||
|
||||
// And returns all the "AND" bit sets.
|
||||
// NOTE:
|
||||
// If the bitSets are empty, returns b.
|
||||
func (b *BitSet) And(bitSets ...*BitSet) *BitSet {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
if len(bitSets) == 0 {
|
||||
return b
|
||||
}
|
||||
var (
|
||||
maxLen = len(b.set)
|
||||
minLen = maxLen
|
||||
currLen int
|
||||
)
|
||||
for _, g := range bitSets {
|
||||
g.mu.RLock()
|
||||
defer g.mu.RUnlock()
|
||||
|
||||
currLen = len(g.set)
|
||||
if currLen > maxLen {
|
||||
maxLen = currLen
|
||||
} else if currLen < minLen {
|
||||
minLen = currLen
|
||||
}
|
||||
}
|
||||
rBitSet := &BitSet{
|
||||
set: make([]byte, maxLen),
|
||||
}
|
||||
for i := range rBitSet.set[:minLen] {
|
||||
rBitSet.set[i] = b.set[i]
|
||||
for _, g := range bitSets {
|
||||
rBitSet.set[i] &= g.set[i]
|
||||
}
|
||||
}
|
||||
return rBitSet
|
||||
}
|
||||
|
||||
// Or returns all the "OR" bit sets.
|
||||
// NOTE:
|
||||
// If the bitSets are empty, returns b.
|
||||
func (b *BitSet) Or(bitSet ...*BitSet) *BitSet {
|
||||
return b.op("|", bitSet)
|
||||
}
|
||||
|
||||
// Xor returns all the "XOR" bit sets.
|
||||
// NOTE:
|
||||
// If the bitSets are empty, returns b.
|
||||
func (b *BitSet) Xor(bitSet ...*BitSet) *BitSet {
|
||||
return b.op("^", bitSet)
|
||||
}
|
||||
|
||||
// AndNot returns all the "&^" bit sets.
|
||||
// NOTE:
|
||||
// If the bitSets are empty, returns b.
|
||||
func (b *BitSet) AndNot(bitSet ...*BitSet) *BitSet {
|
||||
return b.op("&^", bitSet)
|
||||
}
|
||||
|
||||
func (b *BitSet) op(op string, bitSets []*BitSet) *BitSet {
|
||||
if len(bitSets) == 0 {
|
||||
return b
|
||||
}
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
var (
|
||||
maxLen, currLen = len(b.set), 0
|
||||
)
|
||||
for _, g := range bitSets {
|
||||
g.mu.RLock()
|
||||
defer g.mu.RUnlock()
|
||||
currLen = len(g.set)
|
||||
if currLen > maxLen {
|
||||
maxLen = currLen
|
||||
}
|
||||
}
|
||||
rBitSet := &BitSet{
|
||||
set: make([]byte, maxLen),
|
||||
}
|
||||
copy(rBitSet.set, b.set)
|
||||
for _, g := range bitSets {
|
||||
for i, gb := range g.set {
|
||||
switch op {
|
||||
case "|":
|
||||
rBitSet.set[i] = rBitSet.set[i] | gb
|
||||
case "^":
|
||||
rBitSet.set[i] = rBitSet.set[i] ^ gb
|
||||
case "&^":
|
||||
rBitSet.set[i] = rBitSet.set[i] &^ gb
|
||||
}
|
||||
}
|
||||
}
|
||||
return rBitSet
|
||||
}
|
||||
|
||||
// Clear clears the bit set.
|
||||
func (b *BitSet) Clear() {
|
||||
b.mu.Lock()
|
||||
for i := range b.set {
|
||||
b.set[i] = 0
|
||||
}
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
// Size returns the bits size.
|
||||
func (b *BitSet) Size() int {
|
||||
b.mu.RLock()
|
||||
size := b.size()
|
||||
b.mu.RUnlock()
|
||||
return size
|
||||
}
|
||||
|
||||
func (b *BitSet) size() int {
|
||||
size := len(b.set) * 8
|
||||
if size/8 != len(b.set) {
|
||||
panic("overflow when calculating the bit set size")
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// Bytes returns the bit set copy bytes.
|
||||
func (b *BitSet) Bytes() []byte {
|
||||
b.mu.RLock()
|
||||
set := make([]byte, len(b.set))
|
||||
copy(set, b.set)
|
||||
b.mu.RUnlock()
|
||||
return set
|
||||
}
|
||||
|
||||
|
||||
// String returns the bit set by hex type.
|
||||
func (b *BitSet) String() string {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
return hex.EncodeToString(b.set)
|
||||
}
|
||||
|
||||
// Sub returns the bit subset within the specified range of the bit set.
|
||||
// NOTE:
|
||||
// 0 means the 1st bit, -1 means the bottom 1th bit, -2 means the bottom 2th bit and so on.
|
||||
func (b *BitSet) Sub(start, end int) *BitSet {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
newBitSet := &BitSet{
|
||||
set: make([]byte, 0, len(b.set)),
|
||||
}
|
||||
sgi, sbi, egi, ebi, valid := b.validRange(start, end)
|
||||
if !valid {
|
||||
return newBitSet
|
||||
}
|
||||
pre := b.set[sgi] << sbi
|
||||
for _, v := range b.set[sgi+1 : egi] {
|
||||
newBitSet.set = append(newBitSet.set, pre|v>>(7-sbi))
|
||||
pre = v << sbi
|
||||
}
|
||||
last := b.set[egi] >> (7 - ebi) << (7 - ebi)
|
||||
newBitSet.set = append(newBitSet.set, pre|last>>(7-sbi))
|
||||
if sbi < ebi {
|
||||
newBitSet.set = append(newBitSet.set, last<<sbi)
|
||||
}
|
||||
return newBitSet
|
||||
}
|
||||
@@ -13,6 +13,7 @@ package fileutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
@@ -171,3 +172,71 @@ func GetCurrentDirectory() string {
|
||||
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
return strings.Replace(dir, "\\", "/", -1)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GetGopaths returns the list of Go path directories.
|
||||
func GetGopaths() []string {
|
||||
var all []string
|
||||
for _, p := range filepath.SplitList(build.Default.GOPATH) {
|
||||
if p == "" || p == build.Default.GOROOT {
|
||||
// Empty paths are uninteresting.
|
||||
// If the path is the GOROOT, ignore it.
|
||||
// People sometimes set GOPATH=$GOROOT.
|
||||
// Do not get confused by this common mistake.
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(p, "~") {
|
||||
// Path segments starting with ~ on Unix are almost always
|
||||
// users who have incorrectly quoted ~ while setting GOPATH,
|
||||
// preventing it from expanding to $HOME.
|
||||
// The situation is made more confusing by the fact that
|
||||
// bash allows quoted ~ in $PATH (most shells do not).
|
||||
// Do not get confused by this, and do not try to use the path.
|
||||
// It does not exist, and printing errors about it confuses
|
||||
// those users even more, because they think "sure ~ exists!".
|
||||
// The go command diagnoses this situation and prints a
|
||||
// useful error.
|
||||
// On Windows, ~ is used in short names, such as c:\progra~1
|
||||
// for c:\program files.
|
||||
continue
|
||||
}
|
||||
all = append(all, p)
|
||||
}
|
||||
for k, v := range all {
|
||||
// GOPATH should end with / or \
|
||||
if strings.HasSuffix(v, "/") || strings.HasSuffix(v, string(os.PathSeparator)) {
|
||||
continue
|
||||
}
|
||||
v += string(os.PathSeparator)
|
||||
all[k] = v
|
||||
}
|
||||
return all
|
||||
}
|
||||
|
||||
// GetFirstGopath gets the first $GOPATH value.
|
||||
func GetFirstGopath(allowAutomaticGuessing bool) (gopath string, err error) {
|
||||
a := GetGopaths()
|
||||
if len(a) > 0 {
|
||||
gopath = a[0]
|
||||
}
|
||||
defer func() {
|
||||
gopath = strings.Replace(gopath, "/", string(os.PathSeparator), -1)
|
||||
}()
|
||||
if gopath != "" {
|
||||
return
|
||||
}
|
||||
if !allowAutomaticGuessing {
|
||||
err = errors.New("not found GOPATH")
|
||||
return
|
||||
}
|
||||
p, _ := os.Getwd()
|
||||
p = strings.Replace(p, "\\", "/", -1) + "/"
|
||||
i := strings.LastIndex(p, "/src/")
|
||||
if i == -1 {
|
||||
err = errors.New("not found GOPATH")
|
||||
return
|
||||
}
|
||||
gopath = p[:i+1]
|
||||
return
|
||||
}
|
||||
|
||||
@@ -14,12 +14,16 @@ package fileutil
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -96,3 +100,447 @@ func Download(sourceUrl, targetPath string) error {
|
||||
func Name(filePath string) string {
|
||||
return filepath.Base(filePath)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
// RelPath gets relative path.
|
||||
func RelPath(targpath string) string {
|
||||
basepath, _ := filepath.Abs("./")
|
||||
rel, _ := filepath.Rel(basepath, targpath)
|
||||
return strings.Replace(rel, `\`, `/`, -1)
|
||||
}
|
||||
|
||||
var curpath = SelfDir()
|
||||
|
||||
// SelfChdir switch the working path to my own path.
|
||||
func SelfChdir() {
|
||||
if err := os.Chdir(curpath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// FileExists reports whether the named file or directory exists.
|
||||
func FileExists(name string) (existed bool) {
|
||||
existed, _ = FileExist(name)
|
||||
return
|
||||
}
|
||||
|
||||
// FileExist reports whether the named file or directory exists.
|
||||
func FileExist(name string) (existed bool, isDir bool) {
|
||||
info, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return !os.IsNotExist(err), false
|
||||
}
|
||||
return true, info.IsDir()
|
||||
}
|
||||
|
||||
// SearchFile Search a file in paths.
|
||||
// this is often used in search config file in /etc ~/
|
||||
func SearchFile(filename string, paths ...string) (fullpath string, err error) {
|
||||
for _, path := range paths {
|
||||
fullpath = filepath.Join(path, filename)
|
||||
existed, _ := FileExist(fullpath)
|
||||
if existed {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GrepFile like command grep -E
|
||||
// for example: GrepFile(`^hello`, "hello.txt")
|
||||
// \n is striped while read
|
||||
func GrepFile(patten string, filename string) (lines []string, err error) {
|
||||
re, err := regexp.Compile(patten)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lines = make([]string, 0)
|
||||
reader := bufio.NewReader(fd)
|
||||
prefix := ""
|
||||
isLongLine := false
|
||||
for {
|
||||
byteLine, isPrefix, er := reader.ReadLine()
|
||||
if er != nil && er != io.EOF {
|
||||
return nil, er
|
||||
}
|
||||
if er == io.EOF {
|
||||
break
|
||||
}
|
||||
line := string(byteLine)
|
||||
if isPrefix {
|
||||
prefix += line
|
||||
continue
|
||||
} else {
|
||||
isLongLine = true
|
||||
}
|
||||
|
||||
line = prefix + line
|
||||
if isLongLine {
|
||||
prefix = ""
|
||||
}
|
||||
if re.MatchString(line) {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
// WalkDirs traverses the directory, return to the relative path.
|
||||
// You can specify the suffix.
|
||||
func WalkDirs(targpath string, suffixes ...string) (dirlist []string) {
|
||||
if !filepath.IsAbs(targpath) {
|
||||
targpath, _ = filepath.Abs(targpath)
|
||||
}
|
||||
err := filepath.Walk(targpath, func(retpath string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if len(suffixes) == 0 {
|
||||
dirlist = append(dirlist, RelPath(retpath))
|
||||
return nil
|
||||
}
|
||||
_retpath := RelPath(retpath)
|
||||
for _, suffix := range suffixes {
|
||||
if strings.HasSuffix(_retpath, suffix) {
|
||||
dirlist = append(dirlist, _retpath)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("utils.WalkRelDirs: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FilepathSplitExt splits the filename into a pair (root, ext) such that root + ext == filename,
|
||||
// and ext is empty or begins with a period and contains at most one period.
|
||||
// Leading periods on the basename are ignored; splitext('.cshrc') returns ('', '.cshrc').
|
||||
func FilepathSplitExt(filename string, slashInsensitive ...bool) (root, ext string) {
|
||||
insensitive := false
|
||||
if len(slashInsensitive) > 0 {
|
||||
insensitive = slashInsensitive[0]
|
||||
}
|
||||
if insensitive {
|
||||
filename = FilepathSlashInsensitive(filename)
|
||||
}
|
||||
for i := len(filename) - 1; i >= 0 && !os.IsPathSeparator(filename[i]); i-- {
|
||||
if filename[i] == '.' {
|
||||
return filename[:i], filename[i:]
|
||||
}
|
||||
}
|
||||
return filename, ""
|
||||
}
|
||||
|
||||
// FilepathStem returns the stem of filename.
|
||||
// Example:
|
||||
// FilepathStem("/root/dir/sub/file.ext") // output "file"
|
||||
// NOTE:
|
||||
// If slashInsensitive is empty, default is false.
|
||||
func FilepathStem(filename string, slashInsensitive ...bool) string {
|
||||
insensitive := false
|
||||
if len(slashInsensitive) > 0 {
|
||||
insensitive = slashInsensitive[0]
|
||||
}
|
||||
if insensitive {
|
||||
filename = FilepathSlashInsensitive(filename)
|
||||
}
|
||||
base := filepath.Base(filename)
|
||||
for i := len(base) - 1; i >= 0; i-- {
|
||||
if base[i] == '.' {
|
||||
return base[:i]
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// FilepathSlashInsensitive ignore the difference between the slash and the backslash,
|
||||
// and convert to the same as the current system.
|
||||
func FilepathSlashInsensitive(path string) string {
|
||||
if filepath.Separator == '/' {
|
||||
return strings.Replace(path, "\\", "/", -1)
|
||||
}
|
||||
return strings.Replace(path, "/", "\\", -1)
|
||||
}
|
||||
|
||||
// FilepathContains checks if the basepath path contains the subpaths.
|
||||
func FilepathContains(basepath string, subpaths []string) error {
|
||||
basepath, err := filepath.Abs(basepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range subpaths {
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, err := filepath.Rel(basepath, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasPrefix(rel, "..") {
|
||||
return fmt.Errorf("%s is not include %s", basepath, p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filepathRelative(basepath, targpath string) (string, error) {
|
||||
abs, err := filepath.Abs(targpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rel, err := filepath.Rel(basepath, abs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if strings.HasPrefix(rel, "..") {
|
||||
return "", fmt.Errorf("%s is not include %s", basepath, abs)
|
||||
}
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// FilepathDistinct removes the same path and return in the original order.
|
||||
// If toAbs is true, return the result to absolute paths.
|
||||
func FilepathDistinct(paths []string, toAbs bool) ([]string, error) {
|
||||
m := make(map[string]bool, len(paths))
|
||||
ret := make([]string, 0, len(paths))
|
||||
for _, p := range paths {
|
||||
abs, err := filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if m[abs] {
|
||||
continue
|
||||
}
|
||||
m[abs] = true
|
||||
if toAbs {
|
||||
ret = append(ret, abs)
|
||||
} else {
|
||||
ret = append(ret, p)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
||||
// FilepathSame checks if the two paths are the same.
|
||||
func FilepathSame(path1, path2 string) (bool, error) {
|
||||
if path1 == path2 {
|
||||
return true, nil
|
||||
}
|
||||
p1, err := filepath.Abs(path1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
p2, err := filepath.Abs(path2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return p1 == p2, nil
|
||||
}
|
||||
|
||||
// MkdirAll creates a directory named path,
|
||||
// along with any necessary parents, and returns nil,
|
||||
// or else returns an error.
|
||||
// The permission bits perm (before umask) are used for all
|
||||
// directories that MkdirAll creates.
|
||||
// If path is already a directory, MkdirAll does nothing
|
||||
// and returns nil.
|
||||
// If perm is empty, default use 0755.
|
||||
func MkdirAll(path string, perm ...os.FileMode) error {
|
||||
var fm os.FileMode = 0755
|
||||
if len(perm) > 0 {
|
||||
fm = perm[0]
|
||||
}
|
||||
return os.MkdirAll(path, fm)
|
||||
}
|
||||
|
||||
// WriteFile writes file, and automatically creates the directory if necessary.
|
||||
// NOTE:
|
||||
// If perm is empty, automatically determine the file permissions based on extension.
|
||||
func WriteFile(filename string, data []byte, perm ...os.FileMode) error {
|
||||
filename = filepath.FromSlash(filename)
|
||||
err := MkdirAll(filepath.Dir(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(perm) > 0 {
|
||||
return ioutil.WriteFile(filename, data, perm[0])
|
||||
}
|
||||
var ext string
|
||||
if idx := strings.LastIndex(filename, "."); idx != -1 {
|
||||
ext = filename[idx:]
|
||||
}
|
||||
switch ext {
|
||||
case ".sh", ".py", ".rb", ".bat", ".com", ".vbs", ".htm", ".run", ".App", ".exe", ".reg":
|
||||
return ioutil.WriteFile(filename, data, 0755)
|
||||
default:
|
||||
return ioutil.WriteFile(filename, data, 0644)
|
||||
}
|
||||
}
|
||||
|
||||
// RewriteFile rewrites the file.
|
||||
func RewriteFile(filename string, fn func(content []byte) (newContent []byte, err error)) error {
|
||||
f, err := os.OpenFile(filename, os.O_RDWR, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
content, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newContent, err := fn(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes.Equal(content, newContent) {
|
||||
return nil
|
||||
}
|
||||
f.Seek(0, 0)
|
||||
f.Truncate(0)
|
||||
_, err = f.Write(newContent)
|
||||
return err
|
||||
}
|
||||
|
||||
// RewriteToFile rewrites the file to newfilename.
|
||||
// If newfilename already exists and is not a directory, replaces it.
|
||||
func RewriteToFile(filename, newfilename string, fn func(content []byte) (newContent []byte, err error)) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cnt, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newContent, err := fn(cnt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return WriteFile(newfilename, newContent, info.Mode())
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
|
||||
// ReadLines reads all lines of the specified file.
|
||||
func ReadLines(path string) ([]string, int, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
lineCount := 0
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
lineCount++
|
||||
}
|
||||
|
||||
if scanner.Err() == bufio.ErrTooLong {
|
||||
panic(scanner.Err())
|
||||
}
|
||||
return lines, lineCount, scanner.Err()
|
||||
}
|
||||
|
||||
// ReadLinesV2 reads all lines of the specified file.
|
||||
func ReadLinesV2(path string) ([]string, int, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
lineCount := 0
|
||||
reader := bufio.NewReader(file)
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
lines = append(lines, line)
|
||||
lineCount++
|
||||
if err == io.EOF {
|
||||
return lines, lineCount, nil
|
||||
}
|
||||
if err != nil {
|
||||
return lines, lineCount, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListDir lists all the files in the directory
|
||||
func ListDir(path string) []string {
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
filenames := make([]string, len(files))
|
||||
for idx, file := range files {
|
||||
filenames[idx] = file.Name()
|
||||
}
|
||||
return filenames
|
||||
}
|
||||
|
||||
// IsPathExist determines whether a file/dir path exists.
|
||||
// User os.Stat to get the info of target file or dir to check whether exists.
|
||||
// If os.Stat returns nil err, the target exists.
|
||||
// If os.Stat returns a os.ErrNotExist err, the target does not exist.
|
||||
// If the error returned is another type, the target is uncertain whether exists.
|
||||
func IsPathExist(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
||||
// Create creates or truncates the target file specified by path.
|
||||
// If the parent directory does not exist, it will be created with mode os.ModePerm.is cr truncated.
|
||||
// If the file does not exist, it is created with mode 0666.
|
||||
// If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR.
|
||||
func Create(filePath string) (*os.File, error) {
|
||||
if exist, err := IsPathExist(filePath); err != nil {
|
||||
return nil, err
|
||||
} else if exist {
|
||||
return os.Create(filePath)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Create(filePath)
|
||||
}
|
||||
|
||||
// FileToBytes serialize the file to bytes.
|
||||
func FileToBytes(path string) []byte {
|
||||
byteStream, _ := ioutil.ReadFile(path)
|
||||
return byteStream
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
package maputil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
@@ -95,3 +96,249 @@ func Struct2Map(obj interface{}) map[string]interface{} {
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Part 1: convert a slice or array to the specified type map set strictly.
|
||||
// Note that the the element type of slice or array need to be equal to map key type.
|
||||
// For example, []uint64{1, 2, 3} can be converted to map[uint64]struct{}{1:struct{}, 2:struct{},
|
||||
// 3:struct{}} by calling ToUint64MapSetStrict() but can't be converted to map[string]struct{}{"1":struct{},
|
||||
// "2":struct{}, "3":struct{}}.
|
||||
//
|
||||
|
||||
// ToBoolMapSetStrict converts a slice or array to map[bool]struct{} strictly.
|
||||
func ToBoolMapSetStrict(i interface{}) map[bool]struct{} {
|
||||
m, _ := ToBoolMapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToBoolMapSetStrictE converts a slice or array to map[bool]struct{} with error.
|
||||
func ToBoolMapSetStrictE(i interface{}) (map[bool]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[bool]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[bool]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToIntMapSetStrict converts a slice or array to map[int]struct{}.
|
||||
func ToIntMapSetStrict(i interface{}) map[int]struct{} {
|
||||
m, _ := ToIntMapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToIntMapSetStrictE converts a slice or array to map[int]struct{} with error.
|
||||
func ToIntMapSetStrictE(i interface{}) (map[int]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[int]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v isn't map[int]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToInt8MapSetStrict converts a slice or array to map[int8]struct{}.
|
||||
func ToInt8MapSetStrict(i interface{}) map[int8]struct{} {
|
||||
m, _ := ToInt8MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToInt8MapSetStrictE converts a slice or array to map[int8]struct{} with error.
|
||||
func ToInt8MapSetStrictE(i interface{}) (map[int8]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[int8]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int8]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToInt16MapSetStrict converts a slice or array to map[int16]struct{}.
|
||||
func ToInt16MapSetStrict(i interface{}) map[int16]struct{} {
|
||||
m, _ := ToInt16MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToInt16MapSetStrictE converts a slice or array to map[int16]struct{} with error.
|
||||
func ToInt16MapSetStrictE(i interface{}) (map[int16]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[int16]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int16]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToInt32MapSetStrict converts a slice or array to map[int32]struct{}.
|
||||
func ToInt32MapSetStrict(i interface{}) map[int32]struct{} {
|
||||
m, _ := ToInt32MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToInt32MapSetStrictE converts a slice or array to map[int32]struct{} with error.
|
||||
func ToInt32MapSetStrictE(i interface{}) (map[int32]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[int32]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int32]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToInt64MapSetStrict converts a slice or array to map[int64]struct{}.
|
||||
func ToInt64MapSetStrict(i interface{}) map[int64]struct{} {
|
||||
m, _ := ToInt64MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToInt64MapSetStrictE converts a slice or array to map[int64]struct{} with error.
|
||||
func ToInt64MapSetStrictE(i interface{}) (map[int64]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[int64]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[int64]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToUintMapSetStrict converts a slice or array to map[uint]struct{}.
|
||||
func ToUintMapSetStrict(i interface{}) map[uint]struct{} {
|
||||
m, _ := ToUintMapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToUintMapSetStrictE converts a slice or array to map[uint]struct{} with error.
|
||||
func ToUintMapSetStrictE(i interface{}) (map[uint]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[uint]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint8]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToUint8MapSetStrict converts a slice or array to map[uint8]struct{}.
|
||||
func ToUint8MapSetStrict(i interface{}) map[uint8]struct{} {
|
||||
m, _ := ToUint8MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToUint8MapSetStrictE converts a slice or array to map[uint8]struct{} with error.
|
||||
func ToUint8MapSetStrictE(i interface{}) (map[uint8]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[uint8]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint8]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToUint16MapSetStrict converts a slice or array to map[uint16]struct{}.
|
||||
func ToUint16MapSetStrict(i interface{}) map[uint16]struct{} {
|
||||
m, _ := ToUint16MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToUint16MapSetStrictE converts a slice or array to map[uint16]struct{} with error.
|
||||
func ToUint16MapSetStrictE(i interface{}) (map[uint16]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[uint16]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint16]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToUint32MapSetStrict converts a slice or array to map[uint32]struct{}.
|
||||
func ToUint32MapSetStrict(i interface{}) map[uint32]struct{} {
|
||||
m, _ := ToUint32MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToUint32MapSetStrictE converts a slice or array to map[uint32]struct{} with error.
|
||||
func ToUint32MapSetStrictE(i interface{}) (map[uint32]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[uint32]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint32]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToUint64MapSetStrict converts a slice or array to map[uint64]struct{}.
|
||||
func ToUint64MapSetStrict(i interface{}) map[uint64]struct{} {
|
||||
m, _ := ToUint64MapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToUint64MapSetStrictE converts a slice or array to map[uint64]struct{} with error.
|
||||
func ToUint64MapSetStrictE(i interface{}) (map[uint64]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[uint64]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[uint64]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToStrMapSetStrict converts a slice or array to map[string]struct{}.
|
||||
func ToStrMapSetStrict(i interface{}) map[string]struct{} {
|
||||
m, _ := ToStrMapSetStrictE(i)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToStrMapSetStrictE converts a slice or array to map[string]struct{} with error.
|
||||
func ToStrMapSetStrictE(i interface{}) (map[string]struct{}, error) {
|
||||
m, err := ToMapSetStrictE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v, ok := m.(map[string]struct{}); ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, fmt.Errorf("convert success but the type %T of result %#v of isn't map[string]struct{}", m, m)
|
||||
}
|
||||
|
||||
// ToMapSetStrictE converts a slice or array to map set with error strictly.
|
||||
// The result of map key type is equal to the type input element.
|
||||
func ToMapSetStrictE(i interface{}) (interface{}, error) {
|
||||
// check params.
|
||||
if i == nil {
|
||||
return nil, fmt.Errorf("unable to converts nil to map[interface{}]struct{}")
|
||||
}
|
||||
t := reflect.TypeOf(i)
|
||||
kind := t.Kind()
|
||||
if kind != reflect.Slice && kind != reflect.Array {
|
||||
return nil, fmt.Errorf("the type %T of input %#v isn't a slice or array", i, i)
|
||||
}
|
||||
// execute the convert.
|
||||
v := reflect.ValueOf(i)
|
||||
mT := reflect.MapOf(t.Elem(), reflect.TypeOf(struct{}{}))
|
||||
mV := reflect.MakeMapWithSize(mT, v.Len())
|
||||
for j := 0; j < v.Len(); j++ {
|
||||
mV.SetMapIndex(v.Index(j), reflect.ValueOf(struct{}{}))
|
||||
}
|
||||
return mV.Interface(), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo 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 mathutil
|
||||
|
||||
// AbsInt8 gets absolute value of int8.
|
||||
//
|
||||
// Example 1: AbsInt8(-5)
|
||||
// -5 code value as below
|
||||
// original code 1000,0101
|
||||
// inverse code 1111,1010
|
||||
// complement code 1111,1011
|
||||
// Negative numbers are represented by complement code in memory.
|
||||
// shifted = n >> 7 = (1111,1011) >> 7 = 1111,1111 = -1(10-base) (负数右移,左补1)
|
||||
// 1111,1011
|
||||
// n xor shifted = ----------- = 0000,0100 = 4(10-base)
|
||||
// 1111,1111
|
||||
// (n ^ shifted) - shifted = 4 - (-1) = 5
|
||||
//
|
||||
// Example 2: AbsInt8(5)
|
||||
// 5 code value as below
|
||||
// original code 0000,0101
|
||||
// Positive numbers are represented by original code in memory,
|
||||
// and the XOR operation between positive numbers and 0 is equal to itself.
|
||||
// shifted = n >> 7 = 0
|
||||
// 0000,0101
|
||||
// n xor shifted = ----------- = 0000,0101 = 5(10-base)
|
||||
// 0000,0000
|
||||
// (n ^ shifted) - shifted = 5 - 0 = 5
|
||||
func AbsInt8(n int8) int8 {
|
||||
shifted := n >> 7
|
||||
return (n ^ shifted) - shifted
|
||||
}
|
||||
|
||||
// AbsInt16 gets absolute value of int16.
|
||||
func AbsInt16(n int16) int16 {
|
||||
shifted := n >> 15
|
||||
return (n ^ shifted) - shifted
|
||||
}
|
||||
|
||||
// AbsInt32 gets absolute value of int32.
|
||||
func AbsInt32(n int32) int32 {
|
||||
shifted := n >> 31
|
||||
return (n ^ shifted) - shifted
|
||||
}
|
||||
|
||||
// AbsInt64 gets absolute value of int64.
|
||||
func AbsInt64(n int64) int64 {
|
||||
shifted := n >> 63
|
||||
return (n ^ shifted) - shifted
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo 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 mathutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type CMPRES int8
|
||||
|
||||
const (
|
||||
INCMP CMPRES = iota - 2
|
||||
LT
|
||||
EQ
|
||||
GT
|
||||
)
|
||||
|
||||
// Compare compare the size relationship between two numeric values or strings.
|
||||
// The result is INCMP(incomparable), LT(less than), EQ(equal) or GT(greater than).
|
||||
func Compare(lhs, rhs interface{}) CMPRES {
|
||||
if !isComparable(lhs, rhs) {
|
||||
return INCMP
|
||||
}
|
||||
|
||||
lhsVal := reflect.ValueOf(lhs)
|
||||
rhsVal := reflect.ValueOf(rhs)
|
||||
|
||||
switch lhsVal.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch {
|
||||
case lhsVal.Int() < rhsVal.Int():
|
||||
return LT
|
||||
case lhsVal.Int() == rhsVal.Int():
|
||||
return EQ
|
||||
case lhsVal.Int() > rhsVal.Int():
|
||||
return GT
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
switch {
|
||||
case lhsVal.Uint() < rhsVal.Uint():
|
||||
return LT
|
||||
case lhsVal.Uint() == rhsVal.Uint():
|
||||
return EQ
|
||||
case lhsVal.Uint() > rhsVal.Uint():
|
||||
return GT
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch {
|
||||
case lhsVal.Float() < rhsVal.Float():
|
||||
return LT
|
||||
case lhsVal.Float() == rhsVal.Float():
|
||||
return EQ
|
||||
case lhsVal.Float() > rhsVal.Float():
|
||||
return GT
|
||||
}
|
||||
case reflect.String:
|
||||
switch {
|
||||
case lhsVal.String() < rhsVal.String():
|
||||
return LT
|
||||
case lhsVal.String() == rhsVal.String():
|
||||
return EQ
|
||||
case lhsVal.String() > rhsVal.String():
|
||||
return GT
|
||||
}
|
||||
}
|
||||
return INCMP
|
||||
}
|
||||
|
||||
func isComparable(lhs, rhs interface{}) bool {
|
||||
lhsVal := reflect.ValueOf(lhs)
|
||||
rhsVal := reflect.ValueOf(rhs)
|
||||
return lhsVal.Kind() == rhsVal.Kind()
|
||||
}
|
||||
|
||||
func CompareLT(lhs, rhs interface{}) bool {
|
||||
return Compare(lhs, rhs) == LT
|
||||
}
|
||||
|
||||
func CompareLE(lhs, rhs interface{}) bool {
|
||||
res := Compare(lhs, rhs)
|
||||
return res == LT || res == EQ
|
||||
}
|
||||
|
||||
func CompareEQ(lhs, rhs interface{}) bool {
|
||||
return Compare(lhs, rhs) == EQ
|
||||
}
|
||||
|
||||
func CompareGT(lhs, rhs interface{}) bool {
|
||||
return Compare(lhs, rhs) == GT
|
||||
}
|
||||
|
||||
func CompareGE(lhs, rhs interface{}) bool {
|
||||
res := Compare(lhs, rhs)
|
||||
return Compare(lhs, rhs) == GT || res == EQ
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo 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 netutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IP ip struct info.
|
||||
type IP struct {
|
||||
Begin uint32
|
||||
End uint32
|
||||
ISPCode int
|
||||
ISP string
|
||||
CountryCode int
|
||||
Country string
|
||||
ProvinceCode int
|
||||
Province string
|
||||
CityCode int
|
||||
City string
|
||||
DistrictCode int
|
||||
District string
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
}
|
||||
|
||||
// Zone ip struct info.
|
||||
type Zone struct {
|
||||
ID int64 `json:"id"`
|
||||
Addr string `json:"addr"`
|
||||
ISP string `json:"isp"`
|
||||
Country string `json:"country"`
|
||||
Province string `json:"province"`
|
||||
City string `json:"city"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
CountryCode int `json:"country_code,omitempty"`
|
||||
}
|
||||
|
||||
// List struct info list.
|
||||
type List struct {
|
||||
IPs []*IP
|
||||
}
|
||||
|
||||
// New create Xip instance and return.
|
||||
func New(path string) (list *List, err error) {
|
||||
var (
|
||||
ip *IP
|
||||
file *os.File
|
||||
line []byte
|
||||
)
|
||||
list = new(List)
|
||||
if file, err = os.Open(path); err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
reader := bufio.NewReader(file)
|
||||
for {
|
||||
if line, _, err = reader.ReadLine(); err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
lines := strings.Fields(string(line))
|
||||
if len(lines) < 13 {
|
||||
continue
|
||||
}
|
||||
// lines[2]:country lines[3]:province lines[4]:city lines[5]:unit
|
||||
if lines[3] == "香港" || lines[3] == "澳门" || lines[3] == "台湾" {
|
||||
lines[2] = lines[3]
|
||||
lines[3] = lines[4]
|
||||
lines[4] = "*"
|
||||
}
|
||||
// ex.: from 中国 中国 * to 中国 ”“ ”“
|
||||
if lines[2] == lines[3] || lines[3] == "*" {
|
||||
lines[3] = ""
|
||||
lines[4] = ""
|
||||
} else if lines[3] == lines[4] || lines[4] == "*" {
|
||||
// ex.: from 中国 北京 北京 to 中国 北京 ”“
|
||||
lines[4] = ""
|
||||
}
|
||||
ip = &IP{
|
||||
Begin: InetAtoN(lines[0]),
|
||||
End: InetAtoN(lines[1]),
|
||||
Country: lines[2],
|
||||
Province: lines[3],
|
||||
City: lines[4],
|
||||
ISP: lines[6],
|
||||
}
|
||||
ip.Latitude, _ = strconv.ParseFloat(lines[7], 64)
|
||||
ip.Longitude, _ = strconv.ParseFloat(lines[8], 64)
|
||||
ip.CountryCode, _ = strconv.Atoi(lines[12])
|
||||
list.IPs = append(list.IPs, ip)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IP ip zone info by ip
|
||||
func (l *List) IP(ipStr string) (ip *IP) {
|
||||
addr := InetAtoN(ipStr)
|
||||
i, j := 0, len(l.IPs)
|
||||
for i < j {
|
||||
h := i + (j-i)/2 // avoid overflow when computing h
|
||||
ip = l.IPs[h]
|
||||
// i ≤ h < j
|
||||
if addr < ip.Begin {
|
||||
j = h
|
||||
} else if addr > ip.End {
|
||||
i = h + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// All return ipInfos.
|
||||
func (l *List) All() []*IP {
|
||||
return l.IPs
|
||||
}
|
||||
|
||||
// ExternalIP get external ip.
|
||||
func ExternalIP() (res []string) {
|
||||
inters, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, inter := range inters {
|
||||
if !strings.HasPrefix(inter.Name, "lo") {
|
||||
addrs, err := inter.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok {
|
||||
if ipnet.IP.IsLoopback() || ipnet.IP.IsLinkLocalMulticast() || ipnet.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
if ip4 := ipnet.IP.To4(); ip4 != nil {
|
||||
switch true {
|
||||
case ip4[0] == 10:
|
||||
continue
|
||||
case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
|
||||
continue
|
||||
case ip4[0] == 192 && ip4[1] == 168:
|
||||
continue
|
||||
default:
|
||||
res = append(res, ipnet.IP.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// InternalIP get internal ip.
|
||||
func InternalIP() string {
|
||||
inters, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, inter := range inters {
|
||||
if !strings.HasPrefix(inter.Name, "lo") {
|
||||
addrs, err := inter.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return ipnet.IP.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// InetAtoN conver ip addr to uint32.
|
||||
func InetAtoN(s string) (sum uint32) {
|
||||
ip := net.ParseIP(s)
|
||||
if ip == nil {
|
||||
return
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
return
|
||||
}
|
||||
sum += uint32(ip[0]) << 24
|
||||
sum += uint32(ip[1]) << 16
|
||||
sum += uint32(ip[2]) << 8
|
||||
sum += uint32(ip[3])
|
||||
return sum
|
||||
}
|
||||
|
||||
// InetNtoA conver uint32 to ip addr.
|
||||
func InetNtoA(sum uint32) string {
|
||||
ip := make(net.IP, net.IPv4len)
|
||||
ip[0] = byte((sum >> 24) & 0xFF)
|
||||
ip[1] = byte((sum >> 16) & 0xFF)
|
||||
ip[2] = byte((sum >> 8) & 0xFF)
|
||||
ip[3] = byte(sum & 0xFF)
|
||||
return ip.String()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var (
|
||||
nativeEndian binary.ByteOrder
|
||||
|
||||
ipv4PrivateCIDRString = []string{
|
||||
"0.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16",
|
||||
"172.16.0.0/12", "192.0.0.0/24", "192.0.2.0/24", "192.88.99.0/24", "192.168.0.0/16",
|
||||
"198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/4", "240.0.0.0/4", "255.255.255.255/32",
|
||||
}
|
||||
ipv6PrivateCIDRString = []string{
|
||||
"::1/128", "::/128", "64:ff9b::/96", "::ffff:0:0/96", "100::/64", "2001::/23",
|
||||
"2001::/32", "2001:2::/48", "2001:db8::/32", "2001:10::/28", "2002::/16", "fc00::/7", "fe80::/10",
|
||||
"2001:20::/28", "ff00::/8",
|
||||
}
|
||||
|
||||
ipv4PrivateCIDR []*net.IPNet
|
||||
ipv6PrivateCIDR []*net.IPNet
|
||||
)
|
||||
|
||||
func init() {
|
||||
if nativeEndian == nil {
|
||||
var x uint32 = 0x01020304
|
||||
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||
nativeEndian = binary.BigEndian
|
||||
} else {
|
||||
nativeEndian = binary.LittleEndian
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range ipv4PrivateCIDRString {
|
||||
_, n, _ := net.ParseCIDR(v)
|
||||
ipv4PrivateCIDR = append(ipv4PrivateCIDR, n)
|
||||
}
|
||||
|
||||
for _, v := range ipv6PrivateCIDRString {
|
||||
_, n, _ := net.ParseCIDR(v)
|
||||
ipv6PrivateCIDR = append(ipv6PrivateCIDR, n)
|
||||
}
|
||||
}
|
||||
|
||||
// IsReservedIP reports whether ip is private.
|
||||
// Support ipv4/ipv6, refer rfc6890.
|
||||
// Return <0 ip is invalid, =0 ip is public, >0 ip is private.
|
||||
func IsReservedIP(ip string) int {
|
||||
addr := net.ParseIP(ip)
|
||||
if addr == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
if addr.IsLoopback() || addr.IsMulticast() || addr.IsLinkLocalMulticast() || addr.IsLinkLocalUnicast() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if addr.To4() != nil {
|
||||
for _, v := range ipv4PrivateCIDR {
|
||||
if v.Contains(addr) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, v := range ipv6PrivateCIDR {
|
||||
if v.Contains(addr) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Swap16 swap a 16 bit value if aren't big endian
|
||||
func Swap16(i uint16) uint16 {
|
||||
return (i&0xff00)>>8 | (i&0xff)<<8
|
||||
}
|
||||
|
||||
// Swap32 swap a 32 bit value if aren't big endian
|
||||
func Swap32(i uint32) uint32 {
|
||||
return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24
|
||||
}
|
||||
|
||||
// Htons convert uint16 from host byte order to network byte order
|
||||
func Htons(i uint16) uint16 {
|
||||
if GetNativeEndian() == binary.BigEndian {
|
||||
return i
|
||||
}
|
||||
// 大端模式, 高位放在低地址
|
||||
// 0x1234
|
||||
// 0x12 0x34
|
||||
return Swap16(i)
|
||||
}
|
||||
|
||||
// Htonl convert uint32 from host byte order to network byte order.
|
||||
func Htonl(i uint32) uint32 {
|
||||
if GetNativeEndian() == binary.BigEndian {
|
||||
return i
|
||||
}
|
||||
// 大端模式, 高位放在低地址
|
||||
// 0x12345678
|
||||
// 0x12 0x34 0x56 0x78
|
||||
return Swap32(i)
|
||||
}
|
||||
|
||||
// Ntohs convert uint16 from network byte order to host byte order.
|
||||
func Ntohs(i uint16) uint16 {
|
||||
if GetNativeEndian() == binary.BigEndian {
|
||||
return i
|
||||
}
|
||||
// 小端模式, 低位放在低地址
|
||||
// 0x1234
|
||||
// 0x34 0x12
|
||||
return Swap16(i)
|
||||
}
|
||||
|
||||
// Ntohl convert uint32 from network byte order to host byte order.
|
||||
func Ntohl(i uint32) uint32 {
|
||||
if GetNativeEndian() == binary.BigEndian {
|
||||
return i
|
||||
}
|
||||
// 小端模式, 低位放在低地址
|
||||
// 0x12345678
|
||||
// 0x78 0x56 0x34 0x12
|
||||
return Swap32(i)
|
||||
}
|
||||
|
||||
// IPv4ToU32 convert ipv4(a.b.c.d) to uint32 in host byte order.
|
||||
func IPv4ToU32(ip net.IP) uint32 {
|
||||
if ip == nil {
|
||||
return 0
|
||||
}
|
||||
a := uint32(ip[12])
|
||||
b := uint32(ip[13])
|
||||
c := uint32(ip[14])
|
||||
d := uint32(ip[15])
|
||||
return uint32(a<<24 | b<<16 | c<<8 | d)
|
||||
}
|
||||
|
||||
// U32ToIPv4 convert uint32 to ipv4(a.b.c.d) in host byte order.
|
||||
func U32ToIPv4(ip uint32) net.IP {
|
||||
a := byte((ip >> 24) & 0xFF)
|
||||
b := byte((ip >> 16) & 0xFF)
|
||||
c := byte((ip >> 8) & 0xFF)
|
||||
d := byte(ip & 0xFF)
|
||||
return net.IPv4(a, b, c, d)
|
||||
}
|
||||
|
||||
// IPv4StrToU32 convert IPv4 string to uint32 in host byte order.
|
||||
func IPv4StrToU32(s string) (ip uint32) {
|
||||
r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})`
|
||||
reg, err := regexp.Compile(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ips := reg.FindStringSubmatch(s)
|
||||
if ips == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ip1, _ := strconv.Atoi(ips[1])
|
||||
ip2, _ := strconv.Atoi(ips[2])
|
||||
ip3, _ := strconv.Atoi(ips[3])
|
||||
ip4, _ := strconv.Atoi(ips[4])
|
||||
|
||||
if ip1 > 255 || ip2 > 255 || ip3 > 255 || ip4 > 255 {
|
||||
return
|
||||
}
|
||||
|
||||
ip += uint32(ip1 * 0x1000000)
|
||||
ip += uint32(ip2 * 0x10000)
|
||||
ip += uint32(ip3 * 0x100)
|
||||
ip += uint32(ip4)
|
||||
return
|
||||
}
|
||||
|
||||
// U32ToIPv4Str convert uint32 to IPv4 string in host byte order.
|
||||
func U32ToIPv4Str(ip uint32) string {
|
||||
return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
|
||||
}
|
||||
|
||||
// GetNativeEndian gets byte order for the current system.
|
||||
func GetNativeEndian() binary.ByteOrder {
|
||||
return nativeEndian
|
||||
}
|
||||
|
||||
// IsLittleEndian determines whether the host byte order is little endian.
|
||||
func IsLittleEndian() bool {
|
||||
n := 0x1234
|
||||
return *(*byte)(unsafe.Pointer(&n)) == 0x34
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package netutil
|
||||
|
||||
import "testing"
|
||||
|
||||
|
||||
func TestExternalIP(t *testing.T) {
|
||||
t.Log(ExternalIP())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo 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 cmder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var cmdArg [2]string
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "windows" {
|
||||
cmdArg[0] = "cmd"
|
||||
cmdArg[1] = "/c"
|
||||
} else {
|
||||
cmdArg[0] = "/bin/sh"
|
||||
cmdArg[1] = "-c"
|
||||
}
|
||||
}
|
||||
|
||||
// Run exec cmd and catch the result.
|
||||
// Waits for the given command to finish with a timeout.
|
||||
// If the command times out, it attempts to kill the process.
|
||||
func Run(cmdLine string, timeout ...time.Duration) *Result {
|
||||
cmd := exec.Command(cmdArg[0], cmdArg[1], cmdLine)
|
||||
var ret = new(Result)
|
||||
cmd.Stdout = &ret.buf
|
||||
cmd.Stderr = &ret.buf
|
||||
cmd.Env = os.Environ()
|
||||
ret.err = cmd.Start()
|
||||
if ret.err != nil {
|
||||
return ret
|
||||
}
|
||||
if len(timeout) == 0 || timeout[0] <= 0 {
|
||||
ret.err = cmd.Wait()
|
||||
return ret
|
||||
}
|
||||
timer := time.NewTimer(timeout[0])
|
||||
done := make(chan error)
|
||||
go func() { done <- cmd.Wait() }()
|
||||
select {
|
||||
case ret.err = <-done:
|
||||
timer.Stop()
|
||||
case <-timer.C:
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
ret.err = fmt.Errorf("command timed out and killing process fail: %s", err.Error())
|
||||
} else {
|
||||
// wait for the command to return after killing it
|
||||
<-done
|
||||
ret.err = errors.New("command timed out")
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Result cmd exec result
|
||||
type Result struct {
|
||||
buf bytes.Buffer
|
||||
err error
|
||||
str *string
|
||||
}
|
||||
|
||||
// Err returns the error log.
|
||||
func (r *Result) Err() error {
|
||||
if r.err == nil {
|
||||
return nil
|
||||
}
|
||||
r.err = errors.New(r.String())
|
||||
return r.err
|
||||
}
|
||||
|
||||
// String returns the exec log.
|
||||
func (r *Result) String() string {
|
||||
if r.str == nil {
|
||||
b := bytes.TrimSpace(r.buf.Bytes())
|
||||
if r.err != nil {
|
||||
b = append(b, ' ', '(')
|
||||
b = append(b, r.err.Error()...)
|
||||
b = append(b, ')')
|
||||
}
|
||||
r.str = (*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
return *r.str
|
||||
}
|
||||
|
||||
|
||||
// IsWin determine whether the system is windows
|
||||
func IsWin() bool {
|
||||
return runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
// IsMac determines whether the system is darwin
|
||||
func IsMac() bool {
|
||||
return runtime.GOOS == "darwin"
|
||||
}
|
||||
|
||||
// IsLinux determines whether the system is linux
|
||||
func IsLinux() bool {
|
||||
return runtime.GOOS == "linux"
|
||||
}
|
||||
|
||||
// IsSupportColor check current console whether support color.
|
||||
// Supported: linux, mac, or windows's ConEmu, Cmder, putty, git-bash.exe
|
||||
// Not support: windows cmd.exe, powerShell.exe
|
||||
func IsSupportColor() bool {
|
||||
// Support color: "TERM=xterm" "TERM=xterm-vt220" "TERM=xterm-256color" "TERM=screen-256color"
|
||||
// Don't support color: "TERM=cygwin"
|
||||
envTerm := os.Getenv("TERM")
|
||||
if strings.Contains(envTerm, "xterm") || strings.Contains(envTerm, "screen") {
|
||||
return true
|
||||
}
|
||||
|
||||
// like on ConEmu software, e.g "ConEmuANSI=ON"
|
||||
if os.Getenv("ConEmuANSI") == "ON" {
|
||||
return true
|
||||
}
|
||||
|
||||
// like on ConEmu software, e.g "ANSICON=189x2000 (189x43)"
|
||||
if os.Getenv("ANSICON") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSupport256Color check current console whether support 256 color.
|
||||
func IsSupport256Color() bool {
|
||||
// "TERM=xterm-256color" "TERM=screen-256color"
|
||||
return strings.Contains(os.Getenv("TERM"), "256color")
|
||||
}
|
||||
|
||||
// IsSupportTrueColor check current console whether support true color
|
||||
func IsSupportTrueColor() bool {
|
||||
// "COLORTERM=truecolor"
|
||||
return strings.Contains(os.Getenv("COLORTERM"), "truecolor")
|
||||
}
|
||||
@@ -65,3 +65,5 @@ func getRandSeek() int64 {
|
||||
l.Unlock()
|
||||
return time.Now().UnixNano() + randSeek
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ package stringutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -250,3 +251,886 @@ func UnicodeEmojiCode(s string) string {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
通过+号拼接字符串
|
||||
*/
|
||||
func AddStringWithOperator(str1, str2 string) string {
|
||||
|
||||
return str1 + str2
|
||||
}
|
||||
|
||||
/**
|
||||
通过strings 包的join连接字符串
|
||||
*/
|
||||
func AddStringWidthJoin(strArray []string) string {
|
||||
|
||||
return strings.Join(strArray, "")
|
||||
}
|
||||
|
||||
/**
|
||||
通过buffer 拼接字符串
|
||||
*/
|
||||
func AddStringWidthBuffer(strArray []string) string {
|
||||
var buffer bytes.Buffer
|
||||
for _, value := range strArray {
|
||||
buffer.WriteString(value)
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
/**
|
||||
反转字符串
|
||||
*/
|
||||
func ReversString(str string) string {
|
||||
count := len(str)
|
||||
bytes := make([]byte, len(str))
|
||||
for i := 0; i < len(str); i++ {
|
||||
bytes[i] = str[count-1-i]
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
https://www.cnblogs.com/linghu-java/p/9037262.html 参考这边文章
|
||||
查找给定字符串中的最长不重复子串
|
||||
返回最长不重复子串+子串的长度
|
||||
*/
|
||||
func FindMaxLenNoRepeatSubStr(s string) (string, int) {
|
||||
if len(s) == 1 {
|
||||
return s, 1
|
||||
}
|
||||
head, tail := 0, 0
|
||||
maxLenNoRepeatSubStr := ""
|
||||
for i := 0; i < len(s)-1; i++ {
|
||||
for j := i; j < len(s); j++ {
|
||||
if strings.Contains(maxLenNoRepeatSubStr, s[j:j+1]) {
|
||||
if head == 0 && tail == 0 {
|
||||
head, tail = i, j
|
||||
}
|
||||
if len(s[i:j]) > len(s[head:tail]) {
|
||||
head, tail = i, j
|
||||
}
|
||||
maxLenNoRepeatSubStr = ""
|
||||
break
|
||||
}
|
||||
maxLenNoRepeatSubStr = s[i : j+1]
|
||||
}
|
||||
if maxLenNoRepeatSubStr == s[i:] && len(s[i:]) > len(s[head:tail]) {
|
||||
head, tail = i, len(s)
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[head:tail], tail - head
|
||||
}
|
||||
|
||||
/**
|
||||
查找给定字符串中的最长不重复子串
|
||||
返回子串的长度
|
||||
*/
|
||||
func FindMaxLenNoRepeatSubStr2(s string) int {
|
||||
length := len(s)
|
||||
ans := 0
|
||||
for i := 0; i < length; i++ {
|
||||
for j := i + 1; j <= length; j++ {
|
||||
if allUnique(s, i, j) {
|
||||
if (j - i) > ans {
|
||||
ans = j - i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
func allUnique(s string, start int, end int) bool {
|
||||
set := make(map[byte]int, 0)
|
||||
for i := start; i < end; i++ {
|
||||
if _, ok := set[s[i]]; ok {
|
||||
return false
|
||||
}
|
||||
set[s[i]]++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
时间滑动窗口思想
|
||||
查找给定字符串中的最长不重复子串
|
||||
返回子串的长度
|
||||
*/
|
||||
func FindMaxLenNoRepeatSubStr3(s string) int {
|
||||
length := len(s)
|
||||
ans := 0
|
||||
m := make(map[byte]int, 0)
|
||||
i, j := 0, 0
|
||||
for i < length && j < length {
|
||||
//如果不包含
|
||||
if _, ok := m[s[j]]; !ok {
|
||||
m[s[j]]++
|
||||
j++
|
||||
if (j - i) > ans {
|
||||
ans = j - i
|
||||
}
|
||||
} else { //如果包含
|
||||
delete(m, s[i])
|
||||
i++
|
||||
}
|
||||
}
|
||||
return ans
|
||||
}
|
||||
|
||||
/**
|
||||
从两个给定字符串中找出最长公共子串
|
||||
返回最长公共子串 "gfdef", "abcdef"
|
||||
*/
|
||||
func FindMaxLenCommonSubStr(str1, str2 string) string {
|
||||
start1 := -1
|
||||
start2 := -1
|
||||
longest := 0
|
||||
for i := 0; i < len(str1); i++ {
|
||||
for j := 0; j < len(str2); j++ {
|
||||
length := 0
|
||||
m := i
|
||||
n := j
|
||||
for m < len(str1) && n < len(str2) {
|
||||
if str1[m] != str2[n] {
|
||||
break
|
||||
}
|
||||
length++
|
||||
m++
|
||||
n++
|
||||
}
|
||||
if longest < length {
|
||||
longest = length
|
||||
start1 = i
|
||||
start2 = j
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(str1) > len(str2) {
|
||||
return str1[start1 : start1+longest]
|
||||
} else {
|
||||
return str2[start2 : start2+longest]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 采用动态规划求取最长公共子串
|
||||
func FindMaxLenCommonSubStr2(str1, str2 string) string {
|
||||
l1 := len(str1)
|
||||
l2 := len(str2)
|
||||
max := 0
|
||||
end := 0
|
||||
|
||||
var twoArray [][]int
|
||||
for i := 0; i < l1+1; i++ {
|
||||
tmp := make([]int, l2+1)
|
||||
twoArray = append(twoArray, tmp)
|
||||
}
|
||||
for i := 1; i <= l1; i++ {
|
||||
for j := 1; j <= l2; j++ {
|
||||
if str1[i-1] == str2[j-1] {
|
||||
twoArray[i][j] = twoArray[i-1][j-1] + 1
|
||||
if twoArray[i][j] > max {
|
||||
max = twoArray[i][j]
|
||||
end = j
|
||||
}
|
||||
} else {
|
||||
twoArray[i][j] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
bytes := make([]byte, 0)
|
||||
for m := end - max; m < end; m++ {
|
||||
bytes = append(bytes, str2[m])
|
||||
}
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
求最长公共子序列
|
||||
*/
|
||||
func FindMaxLenCommonSubSeq(str1, str2 string) string {
|
||||
l1 := len(str1)
|
||||
l2 := len(str2)
|
||||
|
||||
bs := make([]byte, 0)
|
||||
for m := 1; m <= l2; m++ {
|
||||
for n := 1; n <= l1; n++ {
|
||||
if str1[n-1] == str2[m-1] {
|
||||
bs = append(bs, str1[n-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return Deduplicate(string(bs))
|
||||
}
|
||||
|
||||
/**
|
||||
移除字符串中的重复字符
|
||||
*/
|
||||
func RemoveRepeatStr(str string) string {
|
||||
if len(str) == 0 {
|
||||
return ""
|
||||
}
|
||||
bs := [256]byte{}
|
||||
for _, v := range str {
|
||||
bs[v] = 1
|
||||
}
|
||||
rs := make([]byte, 0)
|
||||
for index, v := range bs {
|
||||
if v == 1 {
|
||||
rs = append(rs, byte(index))
|
||||
}
|
||||
}
|
||||
return string(rs)
|
||||
}
|
||||
|
||||
/**
|
||||
去除重复的字符,返回去重后的字符
|
||||
申请了新的数组用来存储,空间复杂度o(n)
|
||||
*/
|
||||
func Deduplicate(input string) string {
|
||||
if len(input) == 0 {
|
||||
return ""
|
||||
}
|
||||
slice := make([]rune, 0, 0)
|
||||
m := make(map[rune]byte, 0)
|
||||
for _, v := range input {
|
||||
if _, ok := m[v]; ok {
|
||||
continue
|
||||
}
|
||||
slice = append(slice, v)
|
||||
m[v] = 0
|
||||
}
|
||||
return string(slice)
|
||||
}
|
||||
|
||||
/**
|
||||
去除重复的字符,返回去重后的字符
|
||||
空间复杂度o(1)
|
||||
*/
|
||||
func Deduplicate2(input string) string {
|
||||
if len(input) == 0 {
|
||||
return ""
|
||||
}
|
||||
m := make(map[rune]byte, 0)
|
||||
bs := []byte(input)
|
||||
current := 0
|
||||
for next, v := range input {
|
||||
if _, ok := m[v]; ok {
|
||||
continue
|
||||
}
|
||||
bs[current] = input[next]
|
||||
current++
|
||||
m[v] = 0
|
||||
}
|
||||
return string(bs[:current])
|
||||
}
|
||||
|
||||
/**
|
||||
你有一个单词列表 words 和一个模式 pattern,你想知道 words 中的哪些单词与模式匹配。
|
||||
如果存在字母的排列 p ,使得将模式中的每个字母 x 替换为 p(x) 之后,我们就得到了所需的单词,那么单词与模式是匹配的。
|
||||
(回想一下,字母的排列是从字母到字母的双射:每个字母映射到另一个字母,没有两个字母映射到同一个字母。)
|
||||
返回 words 中与给定模式匹配的单词列表。
|
||||
你可以按任何顺序返回答案。
|
||||
*/
|
||||
func FindAndReplacePattern(words []string, pattern string) []string {
|
||||
patternWords := make([]string, 0)
|
||||
for _, word := range words {
|
||||
flag := true
|
||||
ruleMap1 := make(map[byte]byte, len(pattern))
|
||||
ruleMap2 := make(map[byte]byte, len(pattern))
|
||||
for j := 0; j < len(pattern); j++ {
|
||||
p := pattern[j]
|
||||
w := word[j]
|
||||
if _, ok := ruleMap1[p]; ok {
|
||||
if ruleMap1[p] != w {
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
} else if _, ok := ruleMap2[w]; ok {
|
||||
flag = false
|
||||
} else {
|
||||
ruleMap1[p] = w
|
||||
ruleMap2[w] = p
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
patternWords = append(patternWords, word)
|
||||
}
|
||||
}
|
||||
return patternWords
|
||||
}
|
||||
|
||||
/**
|
||||
判断字符串是否为空
|
||||
true 为空 false 不为空
|
||||
*/
|
||||
func IsBlank(str string) bool {
|
||||
return !(len(str) > 0)
|
||||
}
|
||||
|
||||
/**
|
||||
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000
|
||||
"abab" true "aba" false
|
||||
*/
|
||||
func RepeatedSubstringPattern(s string) bool {
|
||||
length := len(s)
|
||||
if length == 0 || length == 1 {
|
||||
return false
|
||||
}
|
||||
n := 2
|
||||
for n <= length {
|
||||
mid := length / n
|
||||
step := mid
|
||||
index := 0
|
||||
flag := true
|
||||
for mid < length {
|
||||
if (mid+step) > length || s[index:mid] != s[mid:mid+step] {
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
index = index + step
|
||||
mid = mid + step
|
||||
}
|
||||
if flag {
|
||||
fmt.Println(step)
|
||||
return true
|
||||
}
|
||||
n++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000
|
||||
"abab" true "aba" false
|
||||
*/
|
||||
func RepeatedSubstringPattern2(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
size := len(s)
|
||||
ss := (s + s)[1 : size*2-1]
|
||||
return strings.Contains(ss, s)
|
||||
}
|
||||
|
||||
func GetNext(p string) []int { //ababda
|
||||
next := make([]int, len(p))
|
||||
next[0] = -1
|
||||
k := -1
|
||||
i := 0
|
||||
for i < len(p)-1 {
|
||||
if k == -1 || p[i] == p[k] {
|
||||
k++
|
||||
i++
|
||||
next[i] = k
|
||||
} else {
|
||||
k = next[k]
|
||||
}
|
||||
}
|
||||
fmt.Println(next)
|
||||
return next
|
||||
}
|
||||
|
||||
/**
|
||||
KMP 算法,字符串模式匹配算法 主要是 GetNext
|
||||
匹配 target 在 source 存在时的起始位置 (字符串搜索) "adabeabcabc", "abcabc"
|
||||
*/
|
||||
func StrMatch(source, target string) int {
|
||||
slen := len(source)
|
||||
tlen := len(target)
|
||||
next := GetNext(target)
|
||||
q := 0
|
||||
for i := 0; i < slen; i++ {
|
||||
for q > 0 && target[q] != source[i] {
|
||||
q = next[q]
|
||||
}
|
||||
if target[q] == source[i] {
|
||||
q++
|
||||
}
|
||||
if q == tlen {
|
||||
return i - tlen + 1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/**
|
||||
判断括号是否成对出现
|
||||
输入: "()[]{}"
|
||||
输出: true
|
||||
输入: "(]"
|
||||
输出: false
|
||||
*/
|
||||
func IsValid(s string) bool {
|
||||
stack := make([]rune, len(s))
|
||||
size := 0
|
||||
for _, v := range s {
|
||||
if v == '(' {
|
||||
stack[size] = ')'
|
||||
} else if v == '[' {
|
||||
stack[size] = ']'
|
||||
} else if v == '{' {
|
||||
stack[size] = '}'
|
||||
} else {
|
||||
if size == 0 && stack[size] == 0 {
|
||||
return false
|
||||
}
|
||||
if size-1 < 0 {
|
||||
return false
|
||||
}
|
||||
if v != stack[size-1] {
|
||||
return false
|
||||
}
|
||||
size--
|
||||
continue
|
||||
}
|
||||
size++
|
||||
}
|
||||
if size > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
|
||||
示例 1:
|
||||
输入: "babad"
|
||||
输出: "bab"
|
||||
注意: "aba"也是一个有效答案。
|
||||
示例 2:
|
||||
输入: "cbbd"
|
||||
输出: "bb"
|
||||
*/
|
||||
func LongestPalindrome(s string) string {
|
||||
if len(s) == 0 || len(s) == 1 {
|
||||
return s
|
||||
}
|
||||
head, tail := 0, len(s)-1
|
||||
seq := 0
|
||||
max := 1
|
||||
index := 0
|
||||
for head < len(s)-1 {
|
||||
tail = len(s) - 1
|
||||
tempHead := head
|
||||
for tempHead < tail {
|
||||
if s[tempHead] == s[tail] {
|
||||
seq++
|
||||
if tail-tempHead <= 2 {
|
||||
break
|
||||
}
|
||||
tempHead++
|
||||
tail--
|
||||
continue
|
||||
}
|
||||
tail--
|
||||
if seq > 0 {
|
||||
tempHead = tempHead - seq
|
||||
tail = tail + seq
|
||||
seq = 0
|
||||
}
|
||||
}
|
||||
if seq > 0 {
|
||||
if (seq*2 + (tail-tempHead)/2) >= max {
|
||||
max = seq*2 + (tail-tempHead)/2
|
||||
index = tempHead - seq + 1
|
||||
}
|
||||
}
|
||||
head++
|
||||
seq = 0
|
||||
}
|
||||
return s[index : index+max]
|
||||
}
|
||||
|
||||
/**
|
||||
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
|
||||
示例 1:
|
||||
输入: "abcabcbb"
|
||||
输出: 3
|
||||
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
|
||||
示例 2:
|
||||
输入: "bbbbb"
|
||||
输出: 1
|
||||
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
|
||||
示例 3:
|
||||
输入: "pwwkew"
|
||||
输出: 3
|
||||
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
|
||||
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
|
||||
*/
|
||||
func LengthOfLongestSubstring(s string) int {
|
||||
next := GetNext(s)
|
||||
m := make(map[byte]int, 0)
|
||||
temp := 0
|
||||
max := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if _, ok := m[s[i]]; ok {
|
||||
if max == 0 {
|
||||
max = temp
|
||||
}
|
||||
i = next[i] - 1
|
||||
m = make(map[byte]int, 0)
|
||||
if temp > max {
|
||||
max = temp
|
||||
}
|
||||
temp = 0
|
||||
continue
|
||||
}
|
||||
m[s[i]] = 1
|
||||
temp++
|
||||
}
|
||||
if temp > max {
|
||||
max = temp
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
/**
|
||||
最长公共前缀
|
||||
编写一个函数来查找字符串数组中的最长公共前缀。
|
||||
如果不存在公共前缀,返回空字符串 ""。
|
||||
示例 1:
|
||||
输入: ["flower","flow","flight"]
|
||||
输出: "fl"
|
||||
示例 2:
|
||||
输入: ["dog","racecar","car"]
|
||||
输出: ""
|
||||
解释: 输入不存在公共前缀。
|
||||
说明:
|
||||
所有输入只包含小写字母 a-z 。
|
||||
*/
|
||||
|
||||
func LongestCommonPrefix(strs []string) string {
|
||||
if strs == nil || len(strs) == 0 {
|
||||
return ""
|
||||
}
|
||||
bs := make([]rune, 0, 0)
|
||||
strs = sort(strs)
|
||||
fmt.Println(strs)
|
||||
min := strs[0]
|
||||
for index, v := range min {
|
||||
for _, n := range strs {
|
||||
if n[index] != byte(v) {
|
||||
return string(bs)
|
||||
}
|
||||
}
|
||||
bs = append(bs, v)
|
||||
}
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
/**
|
||||
字符串按长度排序 归并
|
||||
*/
|
||||
func sort(strs []string) []string {
|
||||
if len(strs) == 1 {
|
||||
return strs
|
||||
}
|
||||
mid := len(strs) / 2
|
||||
left := sort(strs[:mid])
|
||||
right := sort(strs[mid:])
|
||||
return merger(left, right)
|
||||
|
||||
}
|
||||
func merger(left []string, right []string) []string {
|
||||
s := make([]string, 0, 0)
|
||||
l, r := 0, 0
|
||||
for l < len(left) && r < len(right) {
|
||||
if len(left[l]) < len(right[r]) {
|
||||
s = append(s, left[l])
|
||||
l++
|
||||
} else if len(left[l]) > len(right[r]) {
|
||||
s = append(s, right[r])
|
||||
r++
|
||||
} else {
|
||||
s = append(s, left[l])
|
||||
s = append(s, right[r])
|
||||
l++
|
||||
r++
|
||||
}
|
||||
}
|
||||
for l < len(left) {
|
||||
s = append(s, left[l])
|
||||
l++
|
||||
}
|
||||
for r < len(right) {
|
||||
s = append(s, right[r])
|
||||
r++
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
字符串全排列
|
||||
题目:终端随机输入一串字符串,输出该字符串的所有排列。
|
||||
例如,输入:“abc”,输出:abc、acb、bac、bca、cab、cba
|
||||
*/
|
||||
func RecursionPermutation(str string) []string {
|
||||
arrays := make([]string, 0, 0)
|
||||
arrays = permutation([]byte(str), 0, arrays)
|
||||
return arrays
|
||||
}
|
||||
func permutation(s []byte, i int, arrays []string) []string {
|
||||
if i == len(s)-1 {
|
||||
arrays = append(arrays, string(s))
|
||||
return arrays
|
||||
}
|
||||
for temp := i; temp < len(s); temp++ {
|
||||
s[i], s[temp] = s[temp], s[i]
|
||||
arrays = permutation(s, i+1, arrays)
|
||||
s[i], s[temp] = s[temp], s[i]
|
||||
}
|
||||
return arrays
|
||||
}
|
||||
|
||||
/**
|
||||
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
|
||||
换句话说,第一个字符串的排列之一是第二个字符串的子串。
|
||||
示例1:
|
||||
输入: s1 = "ab" s2 = "eidbaooo"
|
||||
输出: True
|
||||
解释: s2 包含 s1 的排列之一 ("ba").
|
||||
示例2:
|
||||
输入: s1= "ab" s2 = "eidboaoo"
|
||||
输出: FalseRecursionPermutation
|
||||
*/
|
||||
//我们不用真的去算出s1的全排列,只要统计字符出现的次数即可。可以使用一个哈希表配上双指针来做
|
||||
func CheckInclusion(s1 string, s2 string) bool {
|
||||
if len(s1) > len(s2) {
|
||||
return false
|
||||
}
|
||||
m := make(map[rune]byte, 0)
|
||||
for index, v := range s1 {
|
||||
m[v]++
|
||||
m[rune(s2[index])]--
|
||||
}
|
||||
if allZero(m) {
|
||||
return true
|
||||
}
|
||||
for temp := len(s1); temp < len(s2); temp++ {
|
||||
m[rune(s2[temp])]--
|
||||
m[rune(s2[temp-len(s1)])]++
|
||||
if allZero(m) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func allZero(m map[rune]byte) bool {
|
||||
for _, v := range m {
|
||||
if v != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
|
||||
示例 1:
|
||||
输入: num1 = "2", num2 = "3"
|
||||
输出: "6"
|
||||
示例 2:
|
||||
输入: num1 = "123", num2 = "456"
|
||||
输出: "56088"
|
||||
说明:
|
||||
num1 和 num2 的长度小于110。
|
||||
num1 和 num2 只包含数字 0-9。
|
||||
num1 和 num2 均不以零开头,除非是数字 0 本身。
|
||||
不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
|
||||
*/
|
||||
|
||||
func Multiply(num1 string, num2 string) string {
|
||||
m := make(map[int]int, 0)
|
||||
value := 0
|
||||
// 映射0 到9 的ASCII码
|
||||
for i := 48; i < 58; i++ {
|
||||
m[i] = value
|
||||
value++
|
||||
}
|
||||
n1 := len(num1)
|
||||
n2 := len(num2)
|
||||
result := make([]int, n1+n2)
|
||||
rss := make([][]int, 0, 0)
|
||||
index := n1 + n2 - 1
|
||||
for t := len(num1) - 1; t >= 0; t-- {
|
||||
var j, y int = 0, 0
|
||||
for temp := len(num2) - 1; temp >= 0; temp-- {
|
||||
k := m[int(num1[t])] * m[int(num2[temp])]
|
||||
y = (k + j) % 10
|
||||
j = (k + j) / 10
|
||||
result[index] = y
|
||||
index--
|
||||
if temp == 0 { //如果到最高位了,就把商赋值
|
||||
result[index] = j
|
||||
}
|
||||
}
|
||||
index = index + len(num2) - 1
|
||||
rss = append(rss, result)
|
||||
result = make([]int, n1+n2)
|
||||
}
|
||||
fmt.Println(rss)
|
||||
index = n1 + n2 - 1
|
||||
var sum int = 0
|
||||
var j, y int = 0, 0
|
||||
for m := index; m >= 0; m-- {
|
||||
for i := 0; i < len(rss); i++ {
|
||||
sum = sum + rss[i][m]
|
||||
}
|
||||
y = sum % 10
|
||||
if y+j < 10 {
|
||||
result[m] = y + j
|
||||
j = sum / 10
|
||||
} else {
|
||||
result[m] = (y + j) % 10
|
||||
j = sum/10 + (y+j)/10
|
||||
}
|
||||
sum = 0
|
||||
}
|
||||
for index, v := range result {
|
||||
if index == len(result)-1 && v == 0 {
|
||||
return "0"
|
||||
}
|
||||
if v == 0 {
|
||||
continue
|
||||
}
|
||||
result = result[index:]
|
||||
break
|
||||
}
|
||||
s := make([]string, len(result), len(result))
|
||||
for index, v := range result {
|
||||
fmt.Println(v)
|
||||
s[index] = string(strconv.Itoa(int(v)))
|
||||
}
|
||||
fmt.Println(result)
|
||||
fmt.Println(s)
|
||||
return strings.Join(s, "")
|
||||
}
|
||||
|
||||
func LetterCasePermutation(S string) []string {
|
||||
if len(S) == 0 {
|
||||
return nil
|
||||
}
|
||||
temps := ""
|
||||
position := 0
|
||||
sArray := make([]string, 0, 0)
|
||||
sArray = dfs(temps, S, sArray, position)
|
||||
return sArray
|
||||
}
|
||||
|
||||
// 65-90 大写
|
||||
// 97-122 小写
|
||||
func dfs(temps string, s string, sArray []string, position int) []string {
|
||||
if position == len(s) {
|
||||
sArray = append(sArray, temps)
|
||||
return sArray
|
||||
}
|
||||
//不是字母
|
||||
if s[position] < 65 || s[position] > 122 || (s[position] > 90 && s[position] < 97) {
|
||||
sArray = dfs(temps+string(rune(s[position])), s, sArray, position+1)
|
||||
} else {
|
||||
sArray = dfs(temps+strings.ToLower(string(rune(s[position]))), s, sArray, position+1)
|
||||
sArray = dfs(temps+strings.ToUpper(string(rune(s[position]))), s, sArray, position+1)
|
||||
}
|
||||
return sArray
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Split replaces strings.Split.
|
||||
// strings.Split has a giant pit because strings.Split ("", ",") will return a slice with an empty string.
|
||||
func Split(s, sep string) []string {
|
||||
if s == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(s, sep)
|
||||
}
|
||||
|
||||
// JoinStrSkipEmpty concatenates multiple strings to a single string with the specified separator and skips the empty
|
||||
// string.
|
||||
func JoinStrSkipEmpty(sep string, s ...string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, v := range s {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteString(sep)
|
||||
}
|
||||
buf.WriteString(v)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// JoinStr concatenates multiple strings to a single string with the specified separator.
|
||||
// Note that JoinStr doesn't skip the empty string.
|
||||
func JoinStr(sep string, s ...string) string {
|
||||
var buf bytes.Buffer
|
||||
for i, v := range s {
|
||||
if i != 0 {
|
||||
buf.WriteString(sep)
|
||||
}
|
||||
buf.WriteString(v)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ReverseStr reverses the specified string without modifying the original string.
|
||||
func ReverseStr(s string) string {
|
||||
rs := []rune(s)
|
||||
var r []rune
|
||||
for i := len(rs) - 1; i >= 0; i-- {
|
||||
r = append(r, rs[i])
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// GetAlphanumericNumByASCII gets the alphanumeric number based on the ASCII code value.
|
||||
// Note that this function has a better performance than GetAlphanumericNumByRegExp, so this function is recommended.
|
||||
func GetAlphanumericNumByASCII(s string) int {
|
||||
num := int(0)
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch {
|
||||
case 48 <= s[i] && s[i] <= 57: // digits
|
||||
fallthrough
|
||||
case 65 <= s[i] && s[i] <= 90: // uppercase letters
|
||||
fallthrough
|
||||
case 97 <= s[i] && s[i] <= 122: // lowercase letters
|
||||
num++
|
||||
default:
|
||||
}
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
// GetAlphanumericNumByASCIIV2 gets the alphanumeric number based on the ASCII code value.
|
||||
// Because range by rune so the performance is worse than GetAlphanumericNumByASCII.
|
||||
func GetAlphanumericNumByASCIIV2(s string) int {
|
||||
num := int(0)
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
fallthrough
|
||||
case 'a' <= c && c <= 'z':
|
||||
fallthrough
|
||||
case 'A' <= c && c <= 'Z':
|
||||
num++
|
||||
default:
|
||||
}
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
// GetAlphanumericNumByRegExp gets the alphanumeric number based on regular expression.
|
||||
// Note that this function has a poor performance when compared to GetAlphanumericNumByASCII,
|
||||
// so the GetAlphanumericNumByASCII is recommended.
|
||||
func GetAlphanumericNumByRegExp(s string) int {
|
||||
rNum := regexp.MustCompile(`\d`)
|
||||
rLetter := regexp.MustCompile("[a-zA-Z]")
|
||||
return len(rNum.FindAllString(s, -1)) + len(rLetter.FindAllString(s, -1))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ package dtime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -88,3 +92,350 @@ func TodayNightUnix() int {
|
||||
tm1 := TodayTimeUnix() + 86400 - 1
|
||||
return tm1
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Time be used to MySql timestamp converting.
|
||||
type Time int64
|
||||
|
||||
// Scan scan time.
|
||||
func (jt *Time) Scan(src interface{}) (err error) {
|
||||
switch sc := src.(type) {
|
||||
case time.Time:
|
||||
*jt = Time(sc.Unix())
|
||||
case string:
|
||||
var i int64
|
||||
i, err = strconv.ParseInt(sc, 10, 64)
|
||||
*jt = Time(i)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Value get time value.
|
||||
func (jt Time) Value() (driver.Value, error) {
|
||||
return time.Unix(int64(jt), 0), nil
|
||||
}
|
||||
|
||||
// Time get time.
|
||||
func (jt Time) Time() time.Time {
|
||||
return time.Unix(int64(jt), 0)
|
||||
}
|
||||
|
||||
// Duration be used toml unmarshal string time, like 1s, 500ms.
|
||||
type Duration time.Duration
|
||||
|
||||
// UnmarshalText unmarshal text to duration.
|
||||
func (d *Duration) UnmarshalText(text []byte) error {
|
||||
tmp, err := time.ParseDuration(string(text))
|
||||
if err == nil {
|
||||
*d = Duration(tmp)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Shrink will decrease the duration by comparing with context's timeout duration
|
||||
// and return new timeout\context\CancelFunc.
|
||||
func (d Duration) Shrink(c context.Context) (Duration, context.Context, context.CancelFunc) {
|
||||
if deadline, ok := c.Deadline(); ok {
|
||||
if ctimeout := time.Until(deadline); ctimeout < time.Duration(d) {
|
||||
// deliver small timeout
|
||||
return Duration(ctimeout), c, func() {}
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(c, time.Duration(d))
|
||||
return d, ctx, cancel
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
const (
|
||||
YFormatNum = "2006"
|
||||
YMFormatNum = "200601"
|
||||
DateFormatNum = "20060102"
|
||||
DateHFormatNum = "2006010215"
|
||||
DateHMFormatNum = "200601021504"
|
||||
DateTimeFormatNum = "20060102150405"
|
||||
HFormatNum = "15"
|
||||
HMFormatNum = "1504"
|
||||
TimeFormatNum = "150405"
|
||||
DateFormat = "2006-01-02"
|
||||
TimeFormat = "15:04:05"
|
||||
DateTimeFormat = "2006-01-02 15:04:05"
|
||||
DateTimeFormatMilli = "2006-01-02 15:04:05.000"
|
||||
DateTimeFormatMicro = "2006-01-02 15:04:05.000000"
|
||||
DateTimeFormatNano = "2006-01-02 15:04:05.000000000"
|
||||
)
|
||||
|
||||
//
|
||||
// Part 0: Get some useful infomation about time
|
||||
//
|
||||
|
||||
// GetNowS gets unix timestamp in second
|
||||
func GetNowS() int64 {
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
// GetNowMs gets unix timestamp in millisecond
|
||||
func GetNowMs() int64 {
|
||||
return time.Now().UnixNano() / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
// GetNowUs gets unix timestamp in microsecond
|
||||
func GetNowUs() int64 {
|
||||
return time.Now().UnixNano() / int64(time.Microsecond)
|
||||
}
|
||||
|
||||
// GetNowNs gets unix timestamp in nanosecond
|
||||
func GetNowNs() int64 {
|
||||
return time.Now().UnixNano()
|
||||
}
|
||||
|
||||
// GetNowDate gets now date in YYYY-MM-DD
|
||||
func GetNowDate() string {
|
||||
return time.Now().Format(DateFormat)
|
||||
}
|
||||
|
||||
// GetNowDate gets now time in hh:mm:ss
|
||||
func GetNowTime() string {
|
||||
return time.Now().Format(TimeFormat)
|
||||
}
|
||||
|
||||
// GetNowDateTimeZ gets now datetime with zone in YYYY-MM-DD hh:mm:ss Zone
|
||||
// e.g. 2020-05-11 23:18:07 +08:00
|
||||
func GetNowDateTimeZ() string {
|
||||
return time.Now().Format("2006-01-02 15:04:05 Z07:00")
|
||||
}
|
||||
|
||||
// GetDayBeginMoment gets the starting moment of one day
|
||||
func GetDayBeginMoment(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
n := time.Date(y, m, d, 0, 0, 0, 0, time.Local)
|
||||
return n
|
||||
}
|
||||
|
||||
// GetDayBeginMoment1 gets the starting moment of one day specified by UNIX time stamp
|
||||
func GetDayBeginMoment1(uts int64) time.Time {
|
||||
y, m, d := time.Unix(uts, 0).Date()
|
||||
n := time.Date(y, m, d, 0, 0, 0, 0, time.Local)
|
||||
return n
|
||||
}
|
||||
|
||||
// GetDayEndMoment gets the ending moment of one day
|
||||
func GetDayEndMoment(t time.Time) time.Time {
|
||||
y, m, d := t.Date()
|
||||
n := time.Date(y, m, d, 23, 59, 59, 999999999, time.Local)
|
||||
return n
|
||||
}
|
||||
|
||||
// GetDayEndMoment1 gets the ending moment of one day specified by UNIX time stamp
|
||||
func GetDayEndMoment1(uts int64) time.Time {
|
||||
y, m, d := time.Unix(uts, 0).Date()
|
||||
n := time.Date(y, m, d, 23, 59, 59, 999999999, time.Local)
|
||||
return n
|
||||
}
|
||||
|
||||
// GetDayElapsedS gets the elapsed seconds since the starting moment of one day
|
||||
func GetDayElapsedS(t time.Time) int64 {
|
||||
return t.Unix() - GetDayBeginMoment(t).Unix()
|
||||
}
|
||||
|
||||
// GetDayElapsedMs gets the elapsed milliseconds since the starting moment of one day
|
||||
func GetDayElapsedMs(t time.Time) int64 {
|
||||
return (t.UnixNano() - GetDayBeginMoment(t).UnixNano()) / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
// GetDayElapsedUs gets the elapsed microseconds since the starting moment of one day
|
||||
func GetDayElapsedUs(t time.Time) int64 {
|
||||
return (t.UnixNano() - GetDayBeginMoment(t).UnixNano()) / int64(time.Microsecond)
|
||||
}
|
||||
|
||||
// GetDayElapsedNs gets the elapsed nanoseconds since the starting moment of one day
|
||||
func GetDayElapsedNs(t time.Time) int64 {
|
||||
return t.Unix() - GetDayBeginMoment(t).Unix()
|
||||
}
|
||||
|
||||
// GetDaysBtwTs gets the number of days between two timestamps and round down
|
||||
func GetDaysBtwTs(ts0, ts1 int64) int64 {
|
||||
return int64(math.Abs(float64(ts0-ts1))) / 86400
|
||||
}
|
||||
|
||||
// GetHoursBtwTs gets the number of hours between two timestamps and round down
|
||||
func GetHoursBtwTs(ts0, ts1 int64) int64 {
|
||||
return int64(math.Abs(float64(ts0-ts1))) / 3600
|
||||
}
|
||||
|
||||
// GetMinutesBtwTs gets the number of hours between two timestamps and round down
|
||||
func GetMinutesBtwTs(ts0, ts1 int64) int64 {
|
||||
return int64(math.Abs(float64(ts0-ts1))) / 60
|
||||
}
|
||||
|
||||
// GetWeekday gets the weekday time
|
||||
func GetWeekday(t time.Time, w time.Weekday) time.Time {
|
||||
if t.Weekday() == w {
|
||||
return t
|
||||
}
|
||||
d := w - t.Weekday()
|
||||
if w == time.Sunday {
|
||||
d += 7
|
||||
} else if t.Weekday() == time.Sunday {
|
||||
d -= 7
|
||||
}
|
||||
return t.AddDate(0, 0, int(d))
|
||||
}
|
||||
|
||||
// GetMonDate gets monday date in format 2006-01-02
|
||||
func GetMonDate(t time.Time) string {
|
||||
return GetWeekday(t, time.Monday).Format(DateFormat)
|
||||
}
|
||||
|
||||
// GetTuesDate gets tuesday date in format 2006-01-02
|
||||
func GetTuesDate(t time.Time) string {
|
||||
return GetWeekday(t, time.Tuesday).Format(DateFormat)
|
||||
}
|
||||
|
||||
// GetWedDate gets wednesday date in format 2006-01-02
|
||||
func GetWedDate(t time.Time) string {
|
||||
return GetWeekday(t, time.Wednesday).Format(DateFormat)
|
||||
}
|
||||
|
||||
// GetThursDate gets thursday date in format 2006-01-02
|
||||
func GetThursDate(t time.Time) string {
|
||||
return GetWeekday(t, time.Thursday).Format(DateFormat)
|
||||
}
|
||||
|
||||
// GetFriDate gets friday date in format 2006-01-02
|
||||
func GetFriDate(t time.Time) string {
|
||||
return GetWeekday(t, time.Friday).Format(DateFormat)
|
||||
}
|
||||
|
||||
// GetSatDate gets saturday date in format 2006-01-02
|
||||
func GetSatDate(t time.Time) string {
|
||||
return GetWeekday(t, time.Saturday).Format(DateFormat)
|
||||
}
|
||||
|
||||
// GetSunDate gets sunday date in format 2006-01-02
|
||||
func GetSunDate(t time.Time) string {
|
||||
return GetWeekday(t, time.Sunday).Format(DateFormat)
|
||||
}
|
||||
|
||||
// IsLeapYear checks the year whether is leap year
|
||||
func IsLeapYear(year int) bool {
|
||||
return (year%4 == 0 && year%100 != 0) || year%400 == 0
|
||||
}
|
||||
|
||||
// IsSameYear checks the unix timestamp whether is the same year
|
||||
func IsSameYear(uts1, uts2 int64) bool {
|
||||
t1 := time.Unix(uts1, 0)
|
||||
t2 := time.Unix(uts2, 0)
|
||||
return t1.Format(YFormatNum) == t2.Format(YFormatNum)
|
||||
}
|
||||
|
||||
// IsSameMonth checks the unix timestamp whether is the same month
|
||||
func IsSameMonth(uts1, uts2 int64) bool {
|
||||
t1 := time.Unix(uts1, 0)
|
||||
t2 := time.Unix(uts2, 0)
|
||||
return t1.Format(YMFormatNum) == t2.Format(YMFormatNum)
|
||||
}
|
||||
|
||||
// IsSameDay checks the unix timestamp whether is the same day
|
||||
func IsSameDay(uts1, uts2 int64) bool {
|
||||
t1 := time.Unix(uts1, 0)
|
||||
t2 := time.Unix(uts2, 0)
|
||||
return t1.Format(DateFormatNum) == t2.Format(DateFormatNum)
|
||||
}
|
||||
|
||||
// IsSameHour checks the unix timestamp whether is the same hour
|
||||
func IsSameHour(uts1, uts2 int64) bool {
|
||||
t1 := time.Unix(uts1, 0)
|
||||
t2 := time.Unix(uts2, 0)
|
||||
return t1.Format(DateHFormatNum) == t2.Format(DateHFormatNum)
|
||||
}
|
||||
|
||||
// IsSameMinute checks the unix timestamp whether is the same minute
|
||||
func IsSameMinute(uts1, uts2 int64) bool {
|
||||
t1 := time.Unix(uts1, 0)
|
||||
t2 := time.Unix(uts2, 0)
|
||||
return t1.Format(DateHMFormatNum) == t2.Format(DateHMFormatNum)
|
||||
}
|
||||
|
||||
// IsSameWeek checks the unix timestamp whether is the same week
|
||||
func IsSameWeek(uts1, uts2 int64) bool {
|
||||
t1 := time.Unix(uts1, 0)
|
||||
t2 := time.Unix(uts2, 0)
|
||||
return GetMonDate(t1) == GetMonDate(t2)
|
||||
}
|
||||
|
||||
//
|
||||
// Part 1: Common conversion about time
|
||||
//
|
||||
|
||||
// DateTime2UTs converts datetime in YYYY-MM-DD hh:mm:ss to unix timestamp
|
||||
func DateTime2UTs(dt string) int64 {
|
||||
loc, _ := time.LoadLocation("Local")
|
||||
t, err := time.ParseInLocation(DateTimeFormat, dt, loc)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return t.Unix()
|
||||
}
|
||||
|
||||
// UTs2DateTime converts unix timestamp to datetime in YYYY-MM-DD hh:mm:ss
|
||||
func UTs2DateTime(uts int64) string {
|
||||
return time.Unix(uts, 0).Format(DateTimeFormat)
|
||||
}
|
||||
|
||||
//
|
||||
// Part 2: A time counter to count time interval
|
||||
//
|
||||
|
||||
// TimeCounter is used to count time interval
|
||||
type TimeCounter struct {
|
||||
time.Time
|
||||
int64
|
||||
}
|
||||
|
||||
// NewTimeCounter create a time counter
|
||||
func NewTimeCounter() (t *TimeCounter) {
|
||||
t = new(TimeCounter)
|
||||
t.Set()
|
||||
return t
|
||||
}
|
||||
|
||||
// Set start timing
|
||||
func (t *TimeCounter) Set() {
|
||||
t.Time = time.Now()
|
||||
t.int64 = t.Time.UnixNano()
|
||||
}
|
||||
|
||||
// GetD return the time interval from the beginning to now in time.Duration
|
||||
func (t *TimeCounter) GetD() time.Duration {
|
||||
return time.Since(t.Time)
|
||||
}
|
||||
|
||||
// GetS return the time interval from the beginning to now in second
|
||||
func (t *TimeCounter) GetS() int64 {
|
||||
return (time.Now().UnixNano() - t.int64) / int64(time.Second)
|
||||
}
|
||||
|
||||
// GetMs return the time interval from the beginning to now in millisecond
|
||||
func (t *TimeCounter) GetMs() int64 {
|
||||
return (time.Now().UnixNano() - t.int64) / int64(time.Millisecond)
|
||||
}
|
||||
|
||||
// GetUs return the time interval from the beginning to now in microsecond
|
||||
func (t *TimeCounter) GetUs() int64 {
|
||||
return (time.Now().UnixNano() - t.int64) / int64(time.Microsecond)
|
||||
}
|
||||
|
||||
// GetNs return the time interval from the beginning to now in nanosecond
|
||||
func (t *TimeCounter) GetNs() int64 {
|
||||
return time.Now().UnixNano() - t.int64
|
||||
}
|
||||
|
||||
// TimeCost count time cost
|
||||
func TimeCost() func() time.Duration {
|
||||
start := time.Now()
|
||||
return func() time.Duration {
|
||||
return time.Since(start)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo 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 ziputil
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Zip compresses the specified files or dirs to zip archive.
|
||||
// If a path is a dir don't need to specify the trailing path separator.
|
||||
// For example calling Zip("archive.zip", "dir", "csv/baz.csv") will get archive.zip and the content of which is
|
||||
// dir
|
||||
// |-- foo.txt
|
||||
// |-- bar.txt
|
||||
// baz.csv
|
||||
func Zip(zipPath string, paths ...string) error {
|
||||
// create zip file
|
||||
if err := os.MkdirAll(filepath.Dir(zipPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
archive, err := os.Create(zipPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
// new zip writer
|
||||
zipWriter := zip.NewWriter(archive)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// traverse the file or directory
|
||||
for _, srcPath := range paths {
|
||||
// remove the trailing path separator if path is a directory
|
||||
srcPath = strings.TrimSuffix(srcPath, string(os.PathSeparator))
|
||||
|
||||
// visit all the files or directories in the tree
|
||||
err = filepath.Walk(srcPath, func(path string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create a local file header
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set compression
|
||||
header.Method = zip.Deflate
|
||||
|
||||
// set relative path of a file as the header name
|
||||
header.Name, err = filepath.Rel(filepath.Dir(srcPath), path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
header.Name += string(os.PathSeparator)
|
||||
}
|
||||
|
||||
// create writer for the file header and save content of the file
|
||||
headerWriter, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(headerWriter, f)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unzip decompresses a zip file to specified directory.
|
||||
// Note that the destination directory don't need to specify the trailing path separator.
|
||||
func Unzip(zipPath, dstDir string) error {
|
||||
// open zip file
|
||||
reader, err := zip.OpenReader(zipPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
for _, file := range reader.File {
|
||||
if err := unzipFile(file, dstDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unzipFile(file *zip.File, dstDir string) error {
|
||||
// create the directory of file
|
||||
filePath := path.Join(dstDir, file.Name)
|
||||
if file.FileInfo().IsDir() {
|
||||
if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// open the file
|
||||
r, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// create the file
|
||||
w, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
// save the decompressed file content
|
||||
_, err = io.Copy(w, r)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user