From 770c2f33802ecaf9b7ffcf2c72915d46614e252e Mon Sep 17 00:00:00 2001 From: godotg Date: Fri, 16 Sep 2022 13:18:59 +0800 Subject: [PATCH] =?UTF-8?q?feat[golang]:=20=E6=B7=BB=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=BA=9Bgo=E7=9A=84=E5=B8=B8=E7=94=A8=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- net/src/test/go/util/arrayutil/array.go | 1121 +++++++++++++++++++++ net/src/test/go/util/byteutil/byte.go | 213 ++++ net/src/test/go/util/collection/bitset.go | 348 +++++++ net/src/test/go/util/fileutil/dir.go | 69 ++ net/src/test/go/util/fileutil/file.go | 448 ++++++++ net/src/test/go/util/maputil/map.go | 247 +++++ net/src/test/go/util/mathutil/abs.go | 59 ++ net/src/test/go/util/mathutil/cmd.go | 104 ++ net/src/test/go/util/netutil/netu.go | 418 ++++++++ net/src/test/go/util/netutil/netu_test.go | 9 + net/src/test/go/util/os/os.go | 153 +++ net/src/test/go/util/random/random.go | 2 + net/src/test/go/util/stringutil/string.go | 884 ++++++++++++++++ net/src/test/go/util/timeutil/time.go | 351 +++++++ net/src/test/go/util/ziputil/zip.go | 145 +++ 15 files changed, 4571 insertions(+) create mode 100644 net/src/test/go/util/collection/bitset.go create mode 100644 net/src/test/go/util/mathutil/abs.go create mode 100644 net/src/test/go/util/mathutil/cmd.go create mode 100644 net/src/test/go/util/netutil/netu.go create mode 100644 net/src/test/go/util/netutil/netu_test.go create mode 100644 net/src/test/go/util/os/os.go create mode 100644 net/src/test/go/util/ziputil/zip.go diff --git a/net/src/test/go/util/arrayutil/array.go b/net/src/test/go/util/arrayutil/array.go index 8fee2c35..8e36e46c 100644 --- a/net/src/test/go/util/arrayutil/array.go +++ b/net/src/test/go/util/arrayutil/array.go @@ -12,7 +12,9 @@ package arrayutil import ( + "fmt" "math" + "reflect" "strconv" "strings" ) @@ -375,3 +377,1122 @@ func CommaPaginator(page int, listRow int, total int) map[string]int { } +// ----------------------------------------------------------------------------------------------- +/** +求最大子序列和 (就是说子序列加起来和最大) +*/ +func FindMaxSeqSum(array []int) int { + SeqSum := make([]int, 0) // 存储子序列和 + // 初始子序列和为 数组下标为0的值 + SeqSum = append(SeqSum, array[0]) + for i := 1; i < len(array); i++ { + if array[i] > SeqSum[i-1]+array[i] { + SeqSum = append(SeqSum, array[i]) + } else { + SeqSum = append(SeqSum, SeqSum[i-1]+array[i]) + } + } + max := SeqSum[0] + for j := 1; j < len(SeqSum); j++ { + if SeqSum[j] > SeqSum[j-1] { + max = SeqSum[j] + } + } + //fmt.Println(max) + return max +} + +/** +二分查找法 +查找某个值在有序数组中是否存在 +*/ +func BinaryFindOrderArray(array []int, value int) bool { + head := 0 + tail := len(array) - 1 + for head <= tail { + mid := (head + tail) >> 1 + if array[mid] == value { + return true + } else if array[mid] > value { + tail = mid - 1 + } else { + head = mid + 1 + } + } + return false +} + +/** +数组是有序的 +在数组中查找匹配value的第一个下标位置 +*/ +func BinaryFindFirstOrderArray(array []int, value int) int { + head := 0 + height := len(array) - 1 + for head <= height { + mid := head + (height-head)>>1 + if value > array[mid] { + head = mid + 1 + } else if value < array[mid] { + height = mid - 1 + } else { + if mid == 0 || array[mid-1] != value { + return mid + } + height = mid - 1 + } + } + return -1 +} + +/** +查找有序数组中匹配目标的最后一个位置的下标 +*/ +func BinaryFindTailOrderArray(array []int, value int) int { + head := 0 + tail := len(array) - 1 + for head <= tail { + mid := head + (tail-head)>>1 + if array[mid] > value { + tail = mid - 1 + } else if array[mid] < value { + head = mid + 1 + } else { + if mid == len(array)-1 || array[mid+1] != value { + return mid + } + head = mid + 1 + } + } + + return -1 +} + +/** +给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 +说明: +初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 +你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 +示例: +输入: +nums1 = [1,2,3,0,0,0], m = 3 +nums2 = [2,5,6], n = 3 +输出: [1,2,2,3,5,6] +*/ +func MergeTwoArray(nums1 []int, m int, nums2 []int, n int) { + if n > 0 { + for i := 0; i < n; i++ { + nums1[m+i] = nums2[i] + } + } + lindex := 0 + rindex := m + for lindex < m && len(nums1) > rindex { + for lindex < m && nums1[lindex] > nums1[rindex] { + nums1[lindex], nums1[rindex] = nums1[rindex], nums1[lindex] + //使右边重新变得有序 + for (rindex + 1) < (m + n) { + if nums1[rindex] < nums1[rindex+1] { + break + } + nums1[rindex], nums1[rindex+1] = nums1[rindex+1], nums1[rindex] + rindex++ + } + rindex = m + } + lindex++ + } +} + +/** +给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 +示例: +输入: [-2,1,-3,4,-1,2,1,-5,4], +输出: 6 +解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 +进阶: +如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 +*/ +/** + * 定义状态: + * dp[i] : 表示以 nums[i] 结尾的连续子数组的最大和 + *

+ * 状态转移方程: + * dp[i] = max{num[i],dp[i-1] + num[i]} + * + * @param nums + * @return + */ +func maxSubArray(nums []int) int { + if len(nums) == 0 { + return 0 + } + dp := make([]int, len(nums)) + for index, v := range nums { + if index == 0 { + dp[index] = v + } else { + if dp[index-1]+v > v { + dp[index] = dp[index-1] + v + } else { + dp[index] = v + } + } + } + max := dp[0] + for _, v := range dp { + if v > max { + max = v + } + } + return max +} + +/** +给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 +设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 +注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 +示例 1: +输入: [7,1,5,3,6,4] +输出: 7 +解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 +示例 2: +输入: [1,2,3,4,5] +输出: 4 +解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 + 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 +示例 3: +输入: [7,6,4,3,1] +输出: 0 +解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 +*/ +func maxProfit(prices []int) int { + + return 0 +} + +/** +给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。 +注意:答案中不可以包含重复的三元组。 +例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], +满足要求的三元组集合为: +[ + [-1, 0, 1], + [-1, -1, 2] +] +*/ + +/** +首先对数组从小到大排序,从一个数开始遍历,若该数大于0,后面的数不可能与其相加和为0,所以跳过;否则该数可能是满足要求的第一个数,这样可以转化为求后面数组中两数之和为该数的相反数的问题。 +定义两个指针一前一后,若找到两数之和满足条件则加入到解集中;若大于和则后指针向前移动,反之则前指针向后移动,直到前指针大于等于后指针。这样遍历第一个数直到数组的倒数第3位。 +注意再求和过程中首先判断该数字是否与前面数字重复,保证解集中没有重复解。 +*/ +func ThreeSum(nums []int) [][]int { + result := make([][]int, 0, 0) + if len(nums) < 3 { + return result + } + bigHeapSort(nums) + if nums[0] == 0 && nums[len(nums)-1] == 0 { + result = append(result, []int{0, 0, 0}) + return result + } + for index, v := range nums { + if v > 0 && index == 0 { + return result + } + if index != 0 && v == nums[index-1] { + continue + } + next, pre := index+1, len(nums)-1 + temp := make([]int, 3, 3) + for next < pre { + if nums[next]+nums[pre] > -v { + pre-- + } else if nums[next]+nums[pre] < -v { + next++ + } else { + temp[0] = nums[index] + temp[1] = nums[next] + temp[2] = nums[pre] + result = append(result, temp) + temp = make([]int, 3, 3) + t := next + next++ + for next < pre && nums[next] == nums[t] { + next++ + } + } + } + } + return result +} +func bigHeapSort(nums []int) { + for i := len(nums)/2 - 1; i >= 0; i-- { + createHeap(nums, i, len(nums)) + } + for j := len(nums) - 1; j >= 0; j-- { + nums[0], nums[j] = nums[j], nums[0] + createHeap(nums, 0, j) + } +} +func createHeap(nums []int, i int, length int) { + left := i*2 + 1 + for left < length { + if left+1 < length && nums[left+1] > nums[left] { + left++ + } + if nums[i] < nums[left] { + nums[i], nums[left] = nums[left], nums[i] + } + i = left + left = i*2 + 1 + } +} + +/** + 给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。 +按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下: +"123" +"132" +"213" +"231" +"312" +"321" + 求n的全排列 +*/ +func GetAllPermutation(n int) [][]int { + nums := make([]int, n) + for i := 1; i <= n; i++ { + nums[i-1] = i + } + result := make([][]int, 0, 0) + result = permutation(nums, 0, result) + return result +} +func permutation(nums []int, index int, result [][]int) [][]int { + if index == len(nums)-1 { + tempArray := make([]int, len(nums), len(nums)) + copy(tempArray, nums) + result = append(result, tempArray) + return result + } + for temp := index; temp < len(nums); temp++ { + nums[index], nums[temp] = nums[temp], nums[index] + result = permutation(nums, index+1, result) + nums[index], nums[temp] = nums[temp], nums[index] + } + return result +} + +func CombinationSum(candidates []int, target int) [][]int { + result := make([][]int, 0) + temp := make([]int, 0) + backtrack(candidates, temp, 0, target, &result) + return result +} + +func backtrack(candidates, temp []int, start, target int, result *[][]int) { + if target == 0 { + t := make([]int, len(temp)) + copy(t, temp) + *result = append(*result, t) + return + } + for i := start; i < len(candidates); i++ { + temp = append(temp, candidates[i]) + target = target - candidates[i] + backtrack(candidates, temp, start+1, target, result) + target = target + temp[len(temp)-1] + temp = temp[:len(temp)-1] + } + +} + + +// ---------------------------------------------------------------------------------------------------------- + +// +// Part 1: unique a slice, e.g. input []int32{1, 2, 2, 3} and output is []int32{1, 2, 3}. +// + +func UniqueIntSlice(src []int) []int { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]int) + return v +} + +func UniqueInt8Slice(src []int8) []int8 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]int8) + return v +} + +func UniqueInt16Slice(src []int16) []int16 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]int16) + return v +} + +func UniqueInt32Slice(src []int32) []int32 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]int32) + return v +} + +func UniqueInt64Slice(src []int64) []int64 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]int64) + return v +} + +func UniqueUintSlice(src []uint) []uint { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]uint) + return v +} + +func UniqueUint8Slice(src []uint8) []uint8 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]uint8) + return v +} + +func UniqueUint16Slice(src []uint16) []uint16 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]uint16) + return v +} + +func UniqueUint32Slice(src []uint32) []uint32 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]uint32) + return v +} + +func UniqueUint64Slice(src []uint64) []uint64 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]uint64) + return v +} + +func UniqueFloat32Slice(src []float32) []float32 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]float32) + return v +} + +func UniqueFloat64Slice(src []float64) []float64 { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]float64) + return v +} + +func UniqueStrSlice(src []string) []string { + dst, _ := UniqueSliceE(src) + v, _ := dst.([]string) + return v +} + +// +// Part 2: reverse a slice, e.g. input []int32{1, 2, 3} and output is []int32{3, 2, 1}. +// + +func ReverseIntSlice(src []int) []int { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]int) + return v +} + +func ReverseInt8Slice(src []int8) []int8 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]int8) + return v +} + +func ReverseInt16Slice(src []int16) []int16 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]int16) + return v +} + +func ReverseInt32Slice(src []int32) []int32 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]int32) + return v +} + +func ReverseInt64Slice(src []int64) []int64 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]int64) + return v +} + +func ReverseUintSlice(src []uint) []uint { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]uint) + return v +} + +func ReverseUint8Slice(src []uint8) []uint8 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]uint8) + return v +} + +func ReverseUint16Slice(src []uint16) []uint16 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]uint16) + return v +} + +func ReverseUint32Slice(src []uint32) []uint32 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]uint32) + return v +} + +func ReverseUint64Slice(src []uint64) []uint64 { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]uint64) + return v +} + +func ReverseStrSlice(src []string) []string { + dst, _ := ReverseSliceE(src) + v, _ := dst.([]string) + return v +} + +// +// Part 3: sum a slice, e.g. input []int32{1, 2, 3} and output is 6. +// + +// SumSlice calculates the sum of slice elements +func SumSlice(slice interface{}) float64 { + v, _ := SumSliceE(slice) + return v +} + +// +// Part 4: determine whether the slice contains an element. +// + +// IsContains checks whether slice or array contains the target element. +// Note that if the target element is a numeric literal, please specify its type explicitly, otherwise it defaults to int. +// For example you might call like IsContains([]int32{1,2,3}, int32(1)). +func IsContains(i interface{}, target interface{}) bool { + if i == nil { + return false + } + t := reflect.TypeOf(i) + if t.Kind() != reflect.Slice && t.Kind() != reflect.Array { + return false + } + v := reflect.ValueOf(i) + for i := 0; i < v.Len(); i++ { + if target == v.Index(i).Interface() { + return true + } + } + return false +} + + +// +// Part 6: CRUD(Create Read Update Delete) on slice by index. +// + +func InsertIntSlice(src []int, index, value int) []int { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]int) + return v +} + +func InsertInt8Slice(src []int8, index int, value int8) []int8 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]int8) + return v +} + +func InsertInt16Slice(src []int, index int, value int16) []int16 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]int16) + return v +} + +func InsertInt32Slice(src []int, index int, value int32) []int32 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]int32) + return v +} + +func InsertInt64Slice(src []int, index int, value int64) []int64 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]int64) + return v +} + +func InsertUintSlice(src []int, index int, value uint) []uint { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]uint) + return v +} + +func InsertUint8Slice(src []int8, index int, value uint8) []uint8 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]uint8) + return v +} + +func InsertUint16Slice(src []int, index int, value uint16) []uint16 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]uint16) + return v +} + +func InsertUint32Slice(src []int, index int, value uint32) []uint32 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]uint32) + return v +} + +func InsertUint64Slice(src []int, index int, value uint64) []uint64 { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]uint64) + return v +} + +func InsertStrSlice(src []int, index int, value string) []string { + tmp, _ := InsertSliceE(src, index, value) + v, _ := tmp.([]string) + return v +} + +func UpdateIntSlice(src []int, index, value int) []int { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]int) + return v +} + +func UpdateInt8Slice(src []int8, index int, value int8) []int8 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]int8) + return v +} + +func UpdateInt16Slice(src []int, index int, value int16) []int16 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]int16) + return v +} + +func UpdateInt32Slice(src []int, index int, value int32) []int32 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]int32) + return v +} + +func UpdateInt64Slice(src []int, index int, value int64) []int64 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]int64) + return v +} + +func UpdateUintSlice(src []int, index int, value uint) []uint { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]uint) + return v +} + +func UpdateUint8Slice(src []int8, index int, value uint8) []uint8 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]uint8) + return v +} + +func UpdateUint16Slice(src []int, index int, value uint16) []uint16 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]uint16) + return v +} + +func UpdateUint32Slice(src []int, index int, value uint32) []uint32 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]uint32) + return v +} + +func UpdateUint64Slice(src []int, index int, value uint64) []uint64 { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]uint64) + return v +} + +func UpdateStrSlice(src []int, index int, value string) []string { + tmp, _ := UpdateSliceE(src, index, value) + v, _ := tmp.([]string) + return v +} + +func GetEleIndexesSlice(slice interface{}, value interface{}) []int { + indexes, _ := GetEleIndexesSliceE(slice, value) + return indexes +} + +// +// Part 7: get the min or max element of a slice. +// + +func MinIntSlice(sl []int) int { + min, _ := MinSliceE(sl) + v, _ := min.(int) + return v +} + +func MinInt8Slice(sl []int8) int8 { + min, _ := MinSliceE(sl) + v, _ := min.(int8) + return v +} + +func MinInt16Slice(sl []int16) int16 { + min, _ := MinSliceE(sl) + v, _ := min.(int16) + return v +} + +func MinInt32Slice(sl []int32) int32 { + min, _ := MinSliceE(sl) + v, _ := min.(int32) + return v +} + +func MinInt64Slice(sl []int64) int64 { + min, _ := MinSliceE(sl) + v, _ := min.(int64) + return v +} + +func MinUintSlice(sl []uint) uint { + min, _ := MinSliceE(sl) + v, _ := min.(uint) + return v +} + +func MinUint8Slice(sl []uint8) uint8 { + min, _ := MinSliceE(sl) + v, _ := min.(uint8) + return v +} + +func MinUint16Slice(sl []uint16) uint16 { + min, _ := MinSliceE(sl) + v, _ := min.(uint16) + return v +} + +func MinUint32Slice(sl []uint32) uint32 { + min, _ := MinSliceE(sl) + v, _ := min.(uint32) + return v +} + +func MinUint64Slice(sl []uint64) uint64 { + min, _ := MinSliceE(sl) + v, _ := min.(uint64) + return v +} + +func MinFloat32Slice(sl []float32) float32 { + min, _ := MinSliceE(sl) + v, _ := min.(float32) + return v +} + +func MinFloat64Slice(sl []float64) float64 { + min, _ := MinSliceE(sl) + v, _ := min.(float64) + return v +} + +func MaxIntSlice(sl []int) int { + max, _ := MaxSliceE(sl) + v, _ := max.(int) + return v +} + +func MaxInt8Slice(sl []int8) int8 { + max, _ := MaxSliceE(sl) + v, _ := max.(int8) + return v +} + +func MaxInt16Slice(sl []int16) int16 { + max, _ := MaxSliceE(sl) + v, _ := max.(int16) + return v +} + +func MaxInt32Slice(sl []int32) int32 { + max, _ := MaxSliceE(sl) + v, _ := max.(int32) + return v +} + +func MaxInt64Slice(sl []int64) int64 { + max, _ := MaxSliceE(sl) + v, _ := max.(int64) + return v +} + +func MaxUintSl(sl []uint) uint { + max, _ := MaxSliceE(sl) + v, _ := max.(uint) + return v +} + +func MaxUint8Slice(sl []uint8) uint8 { + max, _ := MaxSliceE(sl) + v, _ := max.(uint8) + return v +} + +func MaxUint16Slice(sl []uint16) uint16 { + max, _ := MaxSliceE(sl) + v, _ := max.(uint16) + return v +} + +func MaxUint32Slice(sl []uint32) uint32 { + max, _ := MaxSliceE(sl) + v, _ := max.(uint32) + return v +} + +func MaxUint64Slice(sl []uint64) uint64 { + max, _ := MaxSliceE(sl) + v, _ := max.(uint64) + return v +} + +func MaxFloat32Slice(sl []float32) float32 { + max, _ := MaxSliceE(sl) + v, _ := max.(float32) + return v +} + +func MaxFloat64Slice(sl []float64) float64 { + max, _ := MaxSliceE(sl) + v, _ := max.(float64) + return v +} + +// +// Part 8: get a random element from a slice or array. +// + +// GetRandomSliceElem get a random element from a slice or array. +// If the length of slice or array is zero it will panic. + +// +// Part x: basic operating functions of slice. +// + +// UniqueSliceE deletes repeated elements in a slice with error. +// Note that the original slice will not be modified. +func UniqueSliceE(slice interface{}) (interface{}, error) { + // check params + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + // unique the slice + dst := reflect.MakeSlice(reflect.TypeOf(slice), 0, v.Len()) + m := make(map[interface{}]struct{}) + for i := 0; i < v.Len(); i++ { + if _, ok := m[v.Index(i).Interface()]; !ok { + dst = reflect.Append(dst, v.Index(i)) + m[v.Index(i).Interface()] = struct{}{} + } + } + return dst.Interface(), nil +} + +// ReverseSliceE reverses the specified slice without modifying the original slice. +func ReverseSliceE(slice interface{}) (interface{}, error) { + // check params + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + // reverse the slice + dst := reflect.MakeSlice(reflect.TypeOf(slice), 0, v.Len()) + for i := v.Len() - 1; i >= 0; i-- { + dst = reflect.Append(dst, v.Index(i)) + } + return dst.Interface(), nil +} + +// SumSliceE returns the sum of slice elements and an error if occurred. +func SumSliceE(slice interface{}) (float64, error) { + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return 0.0, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + + var sum float64 + for i := 0; i < v.Len(); i++ { + switch v := v.Index(i).Interface().(type) { + case int: + sum += float64(v) + case int8: + sum += float64(v) + case int16: + sum += float64(v) + case int32: + sum += float64(v) + case int64: + sum += float64(v) + case uint: + sum += float64(v) + case uint8: + sum += float64(v) + case uint16: + sum += float64(v) + case uint32: + sum += float64(v) + case uint64: + sum += float64(v) + case float32: + sum += float64(v) + case float64: + sum += v + default: + return 0.0, fmt.Errorf("the element %#v of slice type %T isn't numerical type", v, v) + } + } + return sum, nil +} + +// MinSliceE returns the smallest element of the slice and an error if occurred. +// If slice length is zero return the zero value of the element type. +func MinSliceE(slice interface{}) (interface{}, error) { + // check params + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + if v.Len() == 0 { + return nil, nil + } + // get the min element + min := v.Index(0).Interface() + for i := 1; i < v.Len(); i++ { + switch v := v.Index(i).Interface().(type) { + case int: + if v < min.(int) { + min = v + } + case int8: + if v < min.(int8) { + min = v + } + case int16: + if v < min.(int16) { + min = v + } + case int32: + if v < min.(int32) { + min = v + } + case int64: + if v < min.(int64) { + min = v + } + case uint: + if v < min.(uint) { + min = v + } + case uint8: + if v < min.(uint8) { + min = v + } + case uint16: + if v < min.(uint16) { + min = v + } + case uint32: + if v < min.(uint32) { + min = v + } + case uint64: + if v < min.(uint64) { + min = v + } + case float32: + if v < min.(float32) { + min = v + } + case float64: + if v < min.(float64) { + min = v + } + default: + return nil, fmt.Errorf("the element %#v of slice type %T isn't numerical type", v, v) + } + } + return min, nil +} + +// MaxSliceE returns the largest element of the slice and an error if occurred. +// If slice length is zero return the zero value of the element type. +func MaxSliceE(slice interface{}) (interface{}, error) { + // check params + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + if v.Len() == 0 { + return nil, nil + } + // get the max element. + max := v.Index(0).Interface() + for i := 1; i < v.Len(); i++ { + switch v := v.Index(i).Interface().(type) { + case int: + if v > max.(int) { + max = v + } + case int8: + if v > max.(int8) { + max = v + } + case int16: + if v > max.(int16) { + max = v + } + case int32: + if v > max.(int32) { + max = v + } + case int64: + if v > max.(int64) { + max = v + } + case uint: + if v > max.(uint) { + max = v + } + case uint8: + if v > max.(uint8) { + max = v + } + case uint16: + if v > max.(uint16) { + max = v + } + case uint32: + if v > max.(uint32) { + max = v + } + case uint64: + if v > max.(uint64) { + max = v + } + case float32: + if v > max.(float32) { + max = v + } + case float64: + if v > max.(float64) { + max = v + } + default: + return nil, fmt.Errorf("the element %#v of slice type %T isn't numerical type", v, v) + } + } + return max, nil +} + + +// InsertSliceE inserts a element to slice in the specified index. +// Note that the original slice will not be modified. +func InsertSliceE(slice interface{}, index int, value interface{}) (interface{}, error) { + // check params + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + t := reflect.TypeOf(slice) + if index < 0 || index > v.Len() || t.Elem() != reflect.TypeOf(value) { + return nil, errors.New("param is invalid") + } + + dst := reflect.MakeSlice(t, 0, v.Len()+1) + + // add the element to the end of slice + if index == v.Len() { + dst = reflect.AppendSlice(dst, v) + dst = reflect.Append(dst, reflect.ValueOf(value)) + return dst.Interface(), nil + } + + dst = reflect.AppendSlice(dst, v.Slice(0, index+1)) + dst = reflect.AppendSlice(dst, v.Slice(index, v.Len())) + dst.Index(index).Set(reflect.ValueOf(value)) + return dst.Interface(), nil +} + +// New returns an error that formats as the given text. +// Each call to New returns a distinct error value even if the text is identical. +func NewArrayError(text string) error { + return &ArrayErrorString{text} +} + +// errorString is a trivial implementation of error. +type ArrayErrorString struct { + s string +} + +func (e *ArrayErrorString) Error() string { + return e.s +} + +// UpdateSliceE modifies the specified index element of slice. +// Note that the original slice will not be modified. +func UpdateSliceE(slice interface{}, index int, value interface{}) (interface{}, error) { + // check params + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + if index > v.Len()-1 || reflect.TypeOf(slice).Elem() != reflect.TypeOf(value) { + return nil, NewArrayError("param is invalid") + } + + t := reflect.MakeSlice(reflect.TypeOf(slice), 0, 0) + t = reflect.AppendSlice(t, v.Slice(0, v.Len())) + t.Index(index).Set(reflect.ValueOf(value)) + return t.Interface(), nil +} + +// GetEleIndexesSliceE finds all indexes of the specified element in a slice. +func GetEleIndexesSliceE(slice interface{}, value interface{}) ([]int, error) { + // check params + v := reflect.ValueOf(slice) + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("the input %#v of type %T isn't a slice", slice, slice) + } + // get indexes + var indexes []int + for i := 0; i < v.Len(); i++ { + if v.Index(i).Interface() == value { + indexes = append(indexes, i) + } + } + return indexes, nil +} + diff --git a/net/src/test/go/util/byteutil/byte.go b/net/src/test/go/util/byteutil/byte.go index f49a4da5..41612b6d 100644 --- a/net/src/test/go/util/byteutil/byte.go +++ b/net/src/test/go/util/byteutil/byte.go @@ -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) +} diff --git a/net/src/test/go/util/collection/bitset.go b/net/src/test/go/util/collection/bitset.go new file mode 100644 index 00000000..7b524fc0 --- /dev/null +++ b/net/src/test/go/util/collection/bitset.go @@ -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< 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 +} diff --git a/net/src/test/go/util/fileutil/file.go b/net/src/test/go/util/fileutil/file.go index 8ac2558f..bafc1b33 100644 --- a/net/src/test/go/util/fileutil/file.go +++ b/net/src/test/go/util/fileutil/file.go @@ -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 +} + diff --git a/net/src/test/go/util/maputil/map.go b/net/src/test/go/util/maputil/map.go index c20fc9e0..8a434b74 100644 --- a/net/src/test/go/util/maputil/map.go +++ b/net/src/test/go/util/maputil/map.go @@ -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 +} diff --git a/net/src/test/go/util/mathutil/abs.go b/net/src/test/go/util/mathutil/abs.go new file mode 100644 index 00000000..68c7dbf4 --- /dev/null +++ b/net/src/test/go/util/mathutil/abs.go @@ -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 +} diff --git a/net/src/test/go/util/mathutil/cmd.go b/net/src/test/go/util/mathutil/cmd.go new file mode 100644 index 00000000..744202b4 --- /dev/null +++ b/net/src/test/go/util/mathutil/cmd.go @@ -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 +} diff --git a/net/src/test/go/util/netutil/netu.go b/net/src/test/go/util/netutil/netu.go new file mode 100644 index 00000000..19bb1f37 --- /dev/null +++ b/net/src/test/go/util/netutil/netu.go @@ -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 +} diff --git a/net/src/test/go/util/netutil/netu_test.go b/net/src/test/go/util/netutil/netu_test.go new file mode 100644 index 00000000..c98a9c94 --- /dev/null +++ b/net/src/test/go/util/netutil/netu_test.go @@ -0,0 +1,9 @@ +package netutil + +import "testing" + + +func TestExternalIP(t *testing.T) { + t.Log(ExternalIP()) +} + diff --git a/net/src/test/go/util/os/os.go b/net/src/test/go/util/os/os.go new file mode 100644 index 00000000..40ee268e --- /dev/null +++ b/net/src/test/go/util/os/os.go @@ -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") +} diff --git a/net/src/test/go/util/random/random.go b/net/src/test/go/util/random/random.go index 5224a2e7..1d89ae82 100644 --- a/net/src/test/go/util/random/random.go +++ b/net/src/test/go/util/random/random.go @@ -65,3 +65,5 @@ func getRandSeek() int64 { l.Unlock() return time.Now().UnixNano() + randSeek } + + diff --git a/net/src/test/go/util/stringutil/string.go b/net/src/test/go/util/stringutil/string.go index 81d13152..4f16b165 100644 --- a/net/src/test/go/util/stringutil/string.go +++ b/net/src/test/go/util/stringutil/string.go @@ -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)) +} diff --git a/net/src/test/go/util/timeutil/time.go b/net/src/test/go/util/timeutil/time.go index f4cf32d2..da001a9f 100644 --- a/net/src/test/go/util/timeutil/time.go +++ b/net/src/test/go/util/timeutil/time.go @@ -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) + } +} diff --git a/net/src/test/go/util/ziputil/zip.go b/net/src/test/go/util/ziputil/zip.go new file mode 100644 index 00000000..8482dc1f --- /dev/null +++ b/net/src/test/go/util/ziputil/zip.go @@ -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 +}