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
+}