From 6f299f87e7766e7de7e1770c5be20883c4d7deb3 Mon Sep 17 00:00:00 2001 From: huangsimin Date: Thu, 6 Dec 2018 10:56:51 +0800 Subject: [PATCH] fix trie search bug; and rename trie method SearchMostPrefix to SearchDepth --- crontab.go | 410 ------------------------------------------------ crontab_test.go | 75 --------- option.go | 2 +- structure.go | 4 +- trie_year.go | 269 ------------------------------- 5 files changed, 4 insertions(+), 756 deletions(-) delete mode 100644 crontab.go delete mode 100644 crontab_test.go delete mode 100644 trie_year.go diff --git a/crontab.go b/crontab.go deleted file mode 100644 index 4c1e6f2..0000000 --- a/crontab.go +++ /dev/null @@ -1,410 +0,0 @@ -package curl2info - -import ( - "errors" - "fmt" - "log" - "regexp" - "strconv" - "strings" - "time" - - "github.com/Pallinder/go-randomdata" - - "github.com/davecgh/go-spew/spew" -) - -type randLR struct { - left, right int -} - -type hInterval struct { - PlanFail []randLR - PlanNormal []randLR - - Count int - ConstCount int -} - -func (interval *hInterval) reset() { - interval.Count = interval.ConstCount -} - -type timePointer struct { - left, right int - leftlimit, rightlimit int - per int - isAll bool -} - -func (tp *timePointer) String() string { - return fmt.Sprintf("left: %d, right: %d, leftlimit: %d, rightlimit: %d, per: %d", tp.left, tp.right, tp.leftlimit, tp.rightlimit, tp.per) -} - -// Crontab 的string解析 -type Crontab struct { - min []timePointer - hour []timePointer - day []timePointer - month []timePointer - week []timePointer - - WillPlans []time.Time - SkipPlans []time.Time - - YearPlan *trieYear - - interval *CircularLinked - lastStatus bool - nextTime time.Time -} - -// NewCrontab create 一个crontab -func NewCrontab(crontab string) *Crontab { - cron := &Crontab{} - cron.FromString(crontab) - return cron -} - -// SetStatus 设置状态 接口定义 -func (cron *Crontab) SetStatus(status interface{}) { - if cron.interval != nil { - cron.lastStatus = status.(bool) - } -} - -// GetStatus 设置状态 接口定义 -func (cron *Crontab) GetStatus() (status interface{}) { - if cron.interval != nil { - return cron.lastStatus - } - return nil -} - -// TimeUp 是否时间快到 -func (cron *Crontab) TimeUp() bool { - - if cron.interval != nil { - return cron.intervalTimeUp() - } - - return cron.linuxTimeUp() -} - -// NextTime 返回下次任务的时间 -func (cron *Crontab) NextTime() *time.Time { - if cron.interval != nil { - return &cron.nextTime - } - - if len(cron.WillPlans) > 0 { - return &cron.WillPlans[0] - } - - return nil -} - -func (cron *Crontab) String() string { - return fmt.Sprintf("min:%s\nhour:%s\nday:%s\nmonth:%s\nweek:%s\n", spew.Sdump(cron.min), spew.Sdump(cron.hour), spew.Sdump(cron.day), spew.Sdump(cron.month), spew.Sdump(cron.week)) -} - -// FromString 解析crontab 的 表达式 -func (cron *Crontab) FromString(crontab string) error { - crontab = strings.TrimSpace(crontab) - - matches := regexp.MustCompile("[^ ]+").FindAllString(crontab, -1) - mlen := len(matches) - switch mlen { - case 1: - // "f1-2|5-10x5,f1|10m,10-15,f1" - cron.nextTime = time.Now() - cron.lastStatus = true - cron.interval = NewCircularLinked() - var intervalList []interface{} - intervalList = parseIntervalString(matches[0]) - cron.interval.Append(intervalList...) - - case 5: - cron.min = createTimePointer(matches[0], 0, 59, true) - cron.hour = createTimePointer(matches[1], 0, 23, true) - cron.day = createTimePointer(matches[2], 1, 31, false) - cron.month = createTimePointer(matches[3], 1, 12, true) - cron.week = createTimePointer(matches[4], 0, 6, true) - - cron.createYearPlan() - default: - return errors.New("mathches len != want, check crontab string") - } - - return nil -} - -// createYearPlan 创建年度计划 -func (cron *Crontab) createYearPlan() { - cron.YearPlan = newTrieYear() - cron.YearPlan.FromCrontab(cron) -} - -func (cron *Crontab) linuxTimeUp() bool { - now := time.Now() - maxlen := 1000 - createlen := 500 - - plen := len(cron.WillPlans) - if plen <= createlen { - var lastplan time.Time - if plen == 0 { - lastplan = now - } else { - lastplan = cron.WillPlans[plen-1].Add(time.Minute) - } - if !cron.YearPlan.CheckYear() { - cron.createYearPlan() - } - - timeplans := cron.YearPlan.GetPlanTime(cron, lastplan, uint(maxlen-plen)) - cron.WillPlans = append(cron.WillPlans, timeplans...) - } - - if len(cron.WillPlans) > 0 { - istimeup := false - for i := 0; i < maxlen; i++ { - if now.Unix() >= cron.WillPlans[i].Unix() { - istimeup = true - } else { - if istimeup { - if i-1 > 0 { - cron.SkipPlans = append(cron.SkipPlans, cron.WillPlans[0:i-1]...) - if len(cron.SkipPlans) >= maxlen+200 { - cron.SkipPlans = cron.SkipPlans[200:] - } - } - cron.WillPlans = cron.WillPlans[i:] - return istimeup - } - - return istimeup - } - } - - cron.SkipPlans = append(cron.SkipPlans, cron.WillPlans...) - cron.WillPlans = nil - return istimeup - } - - log.Panicln("error willplans range") - return false -} - -func (cron *Crontab) intervalTimeUp() bool { - - now := time.Now() - if now.Unix() >= cron.nextTime.Unix() { - - iv := cron.interval.Cursor().GetValue().(hInterval) - isecond := 0 - if cron.lastStatus == false && len(iv.PlanFail) > 0 { - idx := randomdata.Number(len(iv.PlanFail)) - lr := iv.PlanFail[idx] - isecond = randomdata.Number(lr.left, lr.right+1) - } else { - idx := randomdata.Number(len(iv.PlanNormal)) - lr := iv.PlanNormal[idx] - isecond = randomdata.Number(lr.left, lr.right+1) - } - - iv.Count-- - if iv.Count <= 0 { - iv.reset() - cron.interval.MoveNext() - } - - cron.nextTime = now.Add(time.Duration(isecond) * time.Second) - return true - } - - return false -} - -func createTimePointer(min string, llimit, rlimit int, fixedLeftRight bool) []timePointer { - - var result []timePointer - - exelist := strings.Split(min, ",") - for _, exe := range exelist { - tp := timePointer{} - - takeper := strings.Split(exe, "/") // per - var rangevalue, per string - if len(takeper) == 1 { - rangevalue = exe - per = "1" - } else { - rangevalue = takeper[0] - per = takeper[1] - } - // takeRange - be := strings.Split(rangevalue, "-") - var left, rigth string - switch len(be) { - case 1: - left = be[0] - rigth = be[0] - case 2: - left = be[0] - rigth = be[1] - default: - panic(errors.New("range value is > 2")) - } - - if left == "*" { - tp.left = llimit - } else { - ileft, err := strconv.Atoi(strings.Replace(left, "^", "-", -1)) - if err != nil { - panic(err) - } - tp.left = ileft - } - - if rigth == "*" { - tp.right = rlimit - } else { - iright, err := strconv.Atoi(strings.Replace(rigth, "^", "-", -1)) - if err != nil { - panic(err) - } - tp.right = iright - } - - iper, err := strconv.Atoi(per) - if err != nil { - panic(err) - } - - tp.per = iper - tp.leftlimit = llimit - tp.rightlimit = rlimit - - // 修正左值 - leftfixed := tp.left - if leftfixed < 0 { - leftfixed += tp.rightlimit + 1 - if fixedLeftRight { - tp.left = leftfixed - } - } - - rightfixed := tp.right - if rightfixed < 0 { - rightfixed += tp.rightlimit + 1 - if fixedLeftRight { - tp.right = rightfixed - } - } - - // 全部符合 当左等于左 且 右等于右最大 并且 per == 1 TODO: 如果加入时间间隔 就需要 加多一个 附加值 - if leftfixed == tp.leftlimit && rightfixed == tp.rightlimit && tp.per == 1 { - tp.isAll = true - } - - result = append(result, tp) - } - - return result -} - -func parseIntervalString(crontab string) []interface{} { - var result []interface{} - - values := strings.Split(crontab, ",") - for _, value := range values { - interval := hInterval{} - // 次数 - valuesCounts := strings.Split(value, "x") - switch len(valuesCounts) { - case 1: - interval.ConstCount = 1 - - case 2: - count, err := strconv.Atoi(valuesCounts[1]) - if err != nil { - panic(err) - } - interval.ConstCount = count - default: - panic("valuesCounts error, the len is not in range") - } - - // 统计失败与普通间隔值的数组 - failAndNormal := valuesCounts[0] - - valuesFN := strings.Split(failAndNormal, "|") - - for _, FN := range valuesFN { - if FN == "" { - continue - } - - switch FN[0] { - case 'f', 'F': - fvalue := FN[1:] - interval.PlanFail = append(interval.PlanFail, parseRandLR(fvalue)) - case 'n': - value := FN[1:] - interval.PlanNormal = append(interval.PlanNormal, parseRandLR(value)) - default: - interval.PlanNormal = append(interval.PlanNormal, parseRandLR(FN)) - } - } - - interval.reset() - result = append(result, interval) - } - - return result -} - -func parseRandLR(value string) randLR { - vlen := len(value) - lastchar := value[vlen-1] - - lr := strings.Split(value, "-") - switch len(lr) { - case 1: - lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[0], lastchar)} - return lr - case 2: - - lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[1], lastchar)} - return lr - default: - panic("lr is error") - } -} - -func getInt(v string) int { - vint, err := strconv.Atoi(v) - if err != nil { - panic(err) - } - return vint -} - -func parseTimeValue(v string, lastchar byte) int { - - vlen := len(v) - - switch lastchar { - case 's': - return getInt(v[:vlen-1]) - case 'm': - return getInt(v[:vlen-1]) * 60 - case 'h': - return getInt(v[:vlen-1]) * 3600 - case 'd': - return getInt(v[:vlen-1]) * 3600 * 24 - default: - return getInt(v) - } -} diff --git a/crontab_test.go b/crontab_test.go deleted file mode 100644 index a9368c6..0000000 --- a/crontab_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package curl2info - -import ( - "fmt" - "runtime" - "testing" - "time" -) - -// type LRValue struct { -// left, right int -// } - -func TestParseCrontab(t *testing.T) { - // crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围 - crontab := "* * * * *" - - PrintMemUsage() - - ty := newTrieYear() - cron := NewCrontab(crontab) - ty.FromCrontab(cron) - - if len(ty.GetPlanTime(cron, time.Now(), 10)) != 10 { - t.Error("GetPlanTime error len != 10") - } - - cron.createYearPlan() - if !cron.TimeUp() { - t.Error("timeup error") - } - - PrintMemUsage() - -} - -func TestParseInterval(t *testing.T) { - // crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围 - // t.Error("") - // crontab := "f1-2|1-3|5-8x5,f1|10m,10-15,f1" - - // PrintMemUsage() - // cron := NewCrontab(crontab) - - // now := time.Now() - // for i := 0; i <= 15; i++ { - // if cron.TimeUp() { - // log.Println(time.Since(now)) - // now = time.Now() - // } - // time.Sleep(time.Second) - // } - - // PrintMemUsage() -} - -func TestCrontabPlus(t *testing.T) { - // crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围 - // crondata := NewCrontab("*22 * * * *") - -} - -func PrintMemUsage() { - var m runtime.MemStats - runtime.ReadMemStats(&m) - // For info on each, see: https://golang.org/pkg/runtime/#MemStats - fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc)) - fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc)) - fmt.Printf("\tSys = %v MiB", bToMb(m.Sys)) - fmt.Printf("\tNumGC = %v\n", m.NumGC) -} - -func bToMb(b uint64) uint64 { - return b / 1024 / 1024 -} diff --git a/option.go b/option.go index 2ca480b..cb1c1af 100644 --- a/option.go +++ b/option.go @@ -81,7 +81,7 @@ func (oe *optionExecute) BuildFunction(curl *CURL, soption string) *parseFunctio func judgeOptions(u *CURL, soption string) *parseFunction { word := trieStrWord(soption) - if ioe := optionTrie.SearchMostPrefix(&word); ioe != nil { + if ioe := optionTrie.SearchDepth(&word); ioe != nil { oe := ioe.(*optionExecute) return oe.BuildFunction(u, soption) } diff --git a/structure.go b/structure.go index 8415f0a..44cf141 100644 --- a/structure.go +++ b/structure.go @@ -110,7 +110,7 @@ func (trie *hTrie) Remove(word string) { } // SearchMostPrefix Returns if the word is in the trie. -func (trie *hTrie) SearchMostPrefix(iword trieWord) interface{} { +func (trie *hTrie) SearchDepth(iword trieWord) interface{} { cur := trie word := iword.GetWord() @@ -123,6 +123,8 @@ func (trie *hTrie) SearchMostPrefix(iword trieWord) interface{} { cur = next if cur.isWord { result = cur.value + } else { + result = nil } } else { return result diff --git a/trie_year.go b/trie_year.go deleted file mode 100644 index 3fd3bad..0000000 --- a/trie_year.go +++ /dev/null @@ -1,269 +0,0 @@ -package curl2info - -import ( - "time" -) - -type minuteNode struct { -} - -func newMinuteNode() *minuteNode { - return &minuteNode{} -} - -type hourNode struct { - Minute [60]*minuteNode -} - -func (hour *hourNode) CreateMinute(nminute int) { - min := &minuteNode{} - hour.Minute[nminute] = min -} - -func newHourNode() *hourNode { - return &hourNode{} -} - -type dayNode struct { - IsClear bool - Week time.Weekday - Hour [24]*hourNode -} - -func (day *dayNode) CreateHour(nhour int) { - hour := &hourNode{} - day.Hour[nhour] = hour -} - -func newDayNode(curday *time.Time) *dayNode { - day := &dayNode{} - - week := curday.Weekday() - day.Week = week - day.IsClear = true - - return day -} - -type monthNode struct { - MaxDay int - First *time.Time - Day [32]*dayNode -} - -func (month *monthNode) CreateDay(nday int) { - day := month.First.AddDate(0, 0, nday-1) - month.Day[nday] = newDayNode(&day) -} - -func newMonthNode(year, month int) *monthNode { - Month := &monthNode{} - First := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Local) - Month.First = &First - Month.MaxDay = Month.First.AddDate(0, 1, -1).Day() - return Month -} - -type trieYear struct { - Year int - Month [13]*monthNode -} - -// CheckYear 跨年判断 -func (ty *trieYear) CheckYear() bool { - year := time.Now().Year() - return ty.Year == year -} - -func (ty *trieYear) clearHour() { - - for _, month := range ty.Month { - if month != nil { - for _, day := range month.Day { - if day != nil { - for i := 0; i <= 23; i++ { - day.Hour[i] = nil - day.IsClear = true - } - } - - } - } - - } - -} - -func newTrieYear() *trieYear { - ty := trieYear{} - ty.Year = time.Now().Year() - return &ty -} - -// GetPlanTime 获取计划表 -func (ty *trieYear) GetPlanTime(cron *Crontab, aftertime time.Time, count uint) []time.Time { - - now := aftertime - nmonth := int(now.Month()) - nday := now.Day() - nhour := now.Hour() - nminute := now.Minute() - - var result []time.Time - - for i := 1; i <= 12; i++ { - - if i < nmonth { - continue - } - - month := ty.Month[i] - if month != nil { - - for j := 1; j <= 31; j++ { - - if nmonth == i { - if j < nday { - continue - } - } - - day := month.Day[j] - if day != nil { - - if day.IsClear { - insertHour(cron, day) - } - - for k := 0; k <= 23; k++ { - - if nmonth == i && nday == j { - if k < nhour { - continue - } - } - - hour := day.Hour[k] - - if hour != nil { - - for n := 0; n <= 59; n++ { - if nmonth == i && nday == j && nhour == k { - if n < nminute { - continue - } - } - - min := hour.Minute[n] - if min != nil { - - curTime := time.Date(ty.Year, time.Month(i), j, k, n, 0, 0, time.Local) - result = append(result, curTime) - count-- - if count <= 0 { - ty.clearHour() - return result - } - } - } - - } - } - - } - } - } - } - - ty.clearHour() - return result -} - -// FromCrontab 从Crontab生成树 -func (ty *trieYear) FromCrontab(cron *Crontab) { - // 月的填充 - for _, month := range cron.month { - - left := month.left - right := month.right - - for i := left; i <= right; i += month.per { - curMonth := newMonthNode(ty.Year, i) - ty.Month[i] = curMonth - // 天的填充 - insertDay(cron, curMonth) - } - } -} - -func filterDay(cron *Crontab, curday *dayNode) bool { - - for _, w := range cron.week { - if w.isAll { - return false - } - - for n := w.left; n <= w.right; n += w.per { - if n == int(curday.Week) { - return false - } - } - } - return true -} - -func insertDay(cron *Crontab, curMonth *monthNode) { - for _, day := range cron.day { - - left := day.left - if left < 0 { - left += curMonth.MaxDay + 1 - } - - right := day.right - if right < 0 { - right += curMonth.MaxDay + 1 - } - - for j := left; j <= right; j += day.per { - curMonth.CreateDay(j) - curDay := curMonth.Day[j] - - if filterDay(cron, curDay) { - curMonth.Day[j] = nil - } else { - // insertHour(cron, curDay) - } - } - - } -} - -func insertHour(cron *Crontab, curDay *dayNode) { - curDay.IsClear = false - // 时的填充 - for _, hour := range cron.hour { - - left := hour.left - right := hour.right - - for k := left; k <= right; k += hour.per { - curDay.CreateHour(k) - curHour := curDay.Hour[k] - insertMinute(cron, curHour) - } - } -} - -func insertMinute(cron *Crontab, curHour *hourNode) { - for _, min := range cron.min { - - left := min.left - right := min.right - - for l := left; l <= right; l += min.per { - curHour.CreateMinute(l) - } - - } -}