diff --git a/util/deepcopy.go b/util/deepcopy.go new file mode 100644 index 0000000..570718a --- /dev/null +++ b/util/deepcopy.go @@ -0,0 +1,76 @@ +package util + +// reference: https://github.com/mohae/deepcopy +import ( + "reflect" +) + +func deepCopy(dst, src reflect.Value) { + switch src.Kind() { + case reflect.Interface: + value := src.Elem() + if !value.IsValid() { + return + } + newValue := reflect.New(value.Type()).Elem() + deepCopy(newValue, value) + dst.Set(newValue) + case reflect.Ptr: + value := src.Elem() + if !value.IsValid() { + return + } + dst.Set(reflect.New(value.Type())) + deepCopy(dst.Elem(), value) + case reflect.Map: + dst.Set(reflect.MakeMap(src.Type())) + keys := src.MapKeys() + for _, key := range keys { + value := src.MapIndex(key) + newValue := reflect.New(value.Type()).Elem() + deepCopy(newValue, value) + dst.SetMapIndex(key, newValue) + } + case reflect.Slice: + dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap())) + for i := 0; i < src.Len(); i++ { + deepCopy(dst.Index(i), src.Index(i)) + } + case reflect.Struct: + typeSrc := src.Type() + for i := 0; i < src.NumField(); i++ { + value := src.Field(i) + tag := typeSrc.Field(i).Tag + if value.CanSet() && tag.Get("deepcopy") != "-" { + deepCopy(dst.Field(i), value) + } + } + default: + dst.Set(src) + } +} + +func DeepCopy(dst, src interface{}) { + typeDst := reflect.TypeOf(dst) + typeSrc := reflect.TypeOf(src) + if typeDst != typeSrc { + panic("DeepCopy: " + typeDst.String() + " != " + typeSrc.String()) + } + if typeSrc.Kind() != reflect.Ptr { + panic("DeepCopy: pass arguments by address") + } + + valueDst := reflect.ValueOf(dst).Elem() + valueSrc := reflect.ValueOf(src).Elem() + if !valueDst.IsValid() || !valueSrc.IsValid() { + panic("DeepCopy: invalid arguments") + } + + deepCopy(valueDst, valueSrc) +} + +func DeepClone(v interface{}) interface{} { + dst := reflect.New(reflect.TypeOf(v)).Elem() + deepCopy(dst, reflect.ValueOf(v)) + return dst.Interface() +} diff --git a/util/example_test.go b/util/example_test.go new file mode 100644 index 0000000..db4cbbe --- /dev/null +++ b/util/example_test.go @@ -0,0 +1,92 @@ +package util_test + +import ( + "fmt" + "github.com/name5566/leaf/util" +) + +func ExampleMap() { + m := new(util.Map) + + fmt.Println(m.Get("key")) + m.Set("key", "value") + fmt.Println(m.Get("key")) + m.Del("key") + fmt.Println(m.Get("key")) + + m.Set(1, "1") + m.Set(2, 2) + m.Set("3", 3) + + fmt.Println(m.Len()) + + // Output: + // + // value + // + // 3 +} + +func ExampleRandGroup() { + i := util.RandGroup(0, 0, 50, 50) + switch i { + case 2, 3: + fmt.Println("ok") + } + + // Output: + // ok +} + +func ExampleRandInterval() { + v := util.RandInterval(-1, 1) + switch v { + case -1, 0, 1: + fmt.Println("ok") + } + + // Output: + // ok +} + +func ExampleRandIntervalN() { + r := util.RandIntervalN(-1, 0, 2) + if r[0] == -1 && r[1] == 0 || + r[0] == 0 && r[1] == -1 { + fmt.Println("ok") + } + + // Output: + // ok +} + +func ExampleDeepCopy() { + src := []int{1, 2, 3} + + var dst []int + util.DeepCopy(&dst, &src) + + for _, v := range dst { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 3 +} + +func ExampleDeepClone() { + src := []int{1, 2, 3} + + dst := util.DeepClone(src).([]int) + + for _, v := range dst { + fmt.Println(v) + } + + // Output: + // 1 + // 2 + // 3 +} diff --git a/util/map.go b/util/map.go new file mode 100644 index 0000000..dfba240 --- /dev/null +++ b/util/map.go @@ -0,0 +1,101 @@ +package util + +import ( + "sync" +) + +type Map struct { + sync.RWMutex + m map[interface{}]interface{} +} + +func (m *Map) init() { + if m.m == nil { + m.m = make(map[interface{}]interface{}) + } +} + +func (m *Map) UnsafeGet(key interface{}) interface{} { + if m.m == nil { + return nil + } else { + return m.m[key] + } +} + +func (m *Map) Get(key interface{}) interface{} { + m.RLock() + defer m.RUnlock() + return m.UnsafeGet(key) +} + +func (m *Map) UnsafeSet(key interface{}, value interface{}) { + m.init() + m.m[key] = value +} + +func (m *Map) Set(key interface{}, value interface{}) { + m.Lock() + defer m.Unlock() + m.UnsafeSet(key, value) +} + +func (m *Map) TestAndSet(key interface{}, value interface{}) interface{} { + m.Lock() + defer m.Unlock() + + m.init() + + if v, ok := m.m[key]; ok { + return v + } else { + m.m[key] = value + return nil + } +} + +func (m *Map) UnsafeDel(key interface{}) { + m.init() + delete(m.m, key) +} + +func (m *Map) Del(key interface{}) { + m.Lock() + defer m.Unlock() + m.UnsafeDel(key) +} + +func (m *Map) UnsafeLen() int { + if m.m == nil { + return 0 + } else { + return len(m.m) + } +} + +func (m *Map) Len() int { + m.RLock() + defer m.RUnlock() + return m.UnsafeLen() +} + +func (m *Map) UnsafeRange(f func(interface{}, interface{})) { + if m.m == nil { + return + } + for k, v := range m.m { + f(k, v) + } +} + +func (m *Map) RLockRange(f func(interface{}, interface{})) { + m.RLock() + defer m.RUnlock() + m.UnsafeRange(f) +} + +func (m *Map) LockRange(f func(interface{}, interface{})) { + m.Lock() + defer m.Unlock() + m.UnsafeRange(f) +} diff --git a/util/rand.go b/util/rand.go new file mode 100644 index 0000000..557c471 --- /dev/null +++ b/util/rand.go @@ -0,0 +1,91 @@ +package util + +import ( + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func RandGroup(p ...uint32) int { + if p == nil { + panic("args not found") + } + + r := make([]uint32, len(p)) + for i := 0; i < len(p); i++ { + if i == 0 { + r[0] = p[0] + } else { + r[i] = r[i-1] + p[i] + } + } + + rl := r[len(r)-1] + if rl == 0 { + return 0 + } + + rn := uint32(rand.Int63n(int64(rl))) + for i := 0; i < len(r); i++ { + if rn < r[i] { + return i + } + } + + panic("bug") +} + +func RandInterval(b1, b2 int32) int32 { + if b1 == b2 { + return b1 + } + + min, max := int64(b1), int64(b2) + if min > max { + min, max = max, min + } + return int32(rand.Int63n(max-min+1) + min) +} + +func RandIntervalN(b1, b2 int32, n uint32) []int32 { + if b1 == b2 { + return []int32{b1} + } + + min, max := int64(b1), int64(b2) + if min > max { + min, max = max, min + } + l := max - min + 1 + if int64(n) > l { + n = uint32(l) + } + + r := make([]int32, n) + m := make(map[int32]int32) + for i := uint32(0); i < n; i++ { + v := int32(rand.Int63n(l) + min) + + if mv, ok := m[v]; ok { + r[i] = mv + } else { + r[i] = v + } + + lv := int32(l - 1 + min) + if v != lv { + if mv, ok := m[lv]; ok { + m[v] = mv + } else { + m[v] = lv + } + } + + l-- + } + + return r +} diff --git a/util/semaphore.go b/util/semaphore.go new file mode 100644 index 0000000..16fd136 --- /dev/null +++ b/util/semaphore.go @@ -0,0 +1,15 @@ +package util + +type Semaphore chan struct{} + +func MakeSemaphore(n int) Semaphore { + return make(Semaphore, n) +} + +func (s Semaphore) Acquire() { + s <- struct{}{} +} + +func (s Semaphore) Release() { + <-s +}