<commit_before>// Copyright 2014 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package storage

import (
	"container/heap"
	"sync"
	"time"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/log"
)

// A rangeItem holds a range and its priority for use with a priority queue.
type rangeItem struct {
	value    *Range
	priority float64
	// The index is needed by update and is maintained by the heap.Interface methods.
	index int // The index of the item in the heap.
}

// A priorityQueue implements heap.Interface and holds rangeItems.
type priorityQueue []*rangeItem

func (pq priorityQueue) Len() int { return len(pq) }

func (pq priorityQueue) Less(i, j int) bool {
	// We want Pop to give us the highest, not lowest, priority so we use greater than here.
	return pq[i].priority > pq[j].priority
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index, pq[j].index = i, j
}

func (pq *priorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*rangeItem)
	item.index = n
	*pq = append(*pq, item)
}

func (pq *priorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.index = -1 // for safety
	*pq = old[0 : n-1]
	return item
}

// update modifies the priority of a rangeItem in the queue.
func (pq *priorityQueue) update(item *rangeItem, priority float64) {
	item.priority = priority
	heap.Fix(pq, item.index)
}

type queueImpl interface {
	// needsLeaderLease returns whether this queue requires the leader
	// lease to operate on a range.
	needsLeaderLease() bool

	// shouldQueue accepts current time and a Range and returns whether
	// it should be queued and if so, at what priority.
	shouldQueue(proto.Timestamp, *Range) (shouldQueue bool, priority float64)

	// process accepts current time and a range and executes
	// queue-specific work on it.
	process(proto.Timestamp, *Range) error

	// timer returns a duration to wait between processing the next item
	// from the queue.
	timer() time.Duration
}

// baseQueue is the base implementation of the rangeQueue interface.
// Queue implementations should embed a baseQueue and implement queueImpl.
//
// baseQueue is not thread safe and is intended for usage only from
// the scanner's goroutine.
type baseQueue struct {
	name       string
	impl       queueImpl
	maxSize    int                  // Maximum number of ranges to queue
	incoming   chan *Range          // Channel for ranges to be queued
	sync.Mutex                      // Mutex protects priorityQ and ranges
	priorityQ  priorityQueue        // The priority queue
	ranges     map[int64]*rangeItem // Map from RaftID to rangeItem (for updating priority)
	// Some tests in this package disable queues.
	disabled bool
}

// newBaseQueue returns a new instance of baseQueue with the
// specified shouldQ function to determine which ranges to queue
// and maxSize to limit the growth of the queue. Note that
// maxSize doesn't prevent new ranges from being added, it just
// limits the total size. Higher priority ranges can still be
// added; their addition simply removes the lowest priority range.
func newBaseQueue(name string, impl queueImpl, maxSize int) *baseQueue {
	return &baseQueue{
		name:     name,
		impl:     impl,
		maxSize:  maxSize,
		incoming: make(chan *Range, 10),
		ranges:   map[int64]*rangeItem{},
	}
}

// Length returns the current size of the queue.
func (bq *baseQueue) Length() int {
	bq.Lock()
	defer bq.Unlock()
	return bq.priorityQ.Len()
}

// Start launches a goroutine to process entries in the queue. The
// provided stopper is used to finish processing.
func (bq *baseQueue) Start(clock *hlc.Clock, stopper *util.Stopper) {
	bq.processLoop(clock, stopper)
}

// MaybeAdd adds the specified range if bq.shouldQ specifies it should
// be queued. Ranges are added to the queue using the priority
// returned by bq.shouldQ. If the queue is too full, an already-queued
// range with the lowest priority may be dropped.
func (bq *baseQueue) MaybeAdd(rng *Range, now proto.Timestamp) {
	bq.Lock()
	defer bq.Unlock()

	if bq.disabled {
		return
	}
	should, priority := bq.impl.shouldQueue(now, rng)
	item, ok := bq.ranges[rng.Desc().RaftID]
	if !should {
		if ok {
			bq.remove(item.index)
		}
		return
	} else if ok {
		// Range has already been added; update priority.
		bq.priorityQ.update(item, priority)
		return
	}

	if log.V(1) {
		log.Infof("adding range %s to %s queue", rng, bq.name)
	}
	item = &rangeItem{value: rng, priority: priority}
	heap.Push(&bq.priorityQ, item)
	bq.ranges[rng.Desc().RaftID] = item

	// If adding this range has pushed the queue past its maximum size,
	// remove the lowest priority element.
	if pqLen := bq.priorityQ.Len(); pqLen > bq.maxSize {
		bq.remove(pqLen - 1)
	}
	// Signal the processLoop that a range has been added.
	bq.incoming <- rng
}

// MaybeRemove removes the specified range from the queue if enqueued.
func (bq *baseQueue) MaybeRemove(rng *Range) {
	bq.Lock()
	defer bq.Unlock()
	if item, ok := bq.ranges[rng.Desc().RaftID]; ok {
		if log.V(1) {
			log.Infof("removing range %s from %s queue", item.value, bq.name)
		}
		bq.remove(item.index)
	}
}

// processLoop processes the entries in the queue until the provided
// stopper signals exit.
//
// TODO(spencer): current load should factor into range processing timer.
func (bq *baseQueue) processLoop(clock *hlc.Clock, stopper *util.Stopper) {
	stopper.RunWorker(func() {
		// nextTime is initially nil; we don't start any timers until the queue
		// becomes non-empty.
		var nextTime <-chan time.Time

		for {
			select {
			// Incoming ranges set the next time to process in the event that
			// there were previously no ranges in the queue.
			case <-bq.incoming:
				if nextTime == nil {
					// When the first range is added, wake up immediately. This is
					// mainly to facilitate testing without unnecessary sleeps.
					nextTime = time.After(0 * time.Millisecond)
				}
			// Process ranges as the timer expires.
			case <-nextTime:
				bq.processOne(clock, stopper)
				if bq.Length() == 0 {
					nextTime = nil
				} else {
					nextTime = time.After(bq.impl.timer())
				}

			// Exit on stopper.
			case <-stopper.ShouldStop():
				bq.Lock()
				bq.ranges = map[int64]*rangeItem{}
				bq.priorityQ = nil
				bq.Unlock()
				return
			}
		}
	})
}

func (bq *baseQueue) processOne(clock *hlc.Clock, stopper *util.Stopper) {
	if !stopper.StartTask() {
		return
	}
	defer stopper.FinishTask()

	start := time.Now()
	bq.Lock()
	rng := bq.pop()
	bq.Unlock()
	if rng != nil {
		now := clock.Now()
		if log.V(1) {
			log.Infof("processing range %s from %s queue...", rng, bq.name)
		}
		// If the queue requires the leader lease to process the
		// range, check whether this replica has leader lease and
		// renew or acquire if necessary.
		if bq.impl.needsLeaderLease() {
			// Create a "fake" get request in order to invoke redirectOnOrAcquireLease.
			args := &proto.GetRequest{RequestHeader: proto.RequestHeader{Timestamp: now}}
			if err := rng.redirectOnOrAcquireLeaderLease(args.Header().Timestamp); err != nil {
				if log.V(1) {
					log.Infof("this replica of %s could not acquire leader lease; skipping...", rng)
				}
				return
			}
		}
		if err := bq.impl.process(now, rng); err != nil {
			log.Errorf("failure processing range %s from %s queue: %s", rng, bq.name, err)
		}
		if log.V(1) {
			log.Infof("processed range %s from %s queue in %s", rng, bq.name, time.Now().Sub(start))
		}
	}
}

// pop dequeues the highest priority range in the queue. Returns the
// range if not empty; otherwise, returns nil. Expects mutex to be
// locked.
func (bq *baseQueue) pop() *Range {
	if bq.priorityQ.Len() == 0 {
		return nil
	}
	item := heap.Pop(&bq.priorityQ).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
	return item.value
}

// remove removes an element from the priority queue by index. Expects
// mutex to be locked.
func (bq *baseQueue) remove(index int) {
	item := heap.Remove(&bq.priorityQ, index).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
}
<commit_msg>Increase the queue size from 10 to 50<commit_after>// Copyright 2014 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package storage

import (
	"container/heap"
	"sync"
	"time"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/log"
)

// A rangeItem holds a range and its priority for use with a priority queue.
type rangeItem struct {
	value    *Range
	priority float64
	// The index is needed by update and is maintained by the heap.Interface methods.
	index int // The index of the item in the heap.
}

// A priorityQueue implements heap.Interface and holds rangeItems.
type priorityQueue []*rangeItem

func (pq priorityQueue) Len() int { return len(pq) }

func (pq priorityQueue) Less(i, j int) bool {
	// We want Pop to give us the highest, not lowest, priority so we use greater than here.
	return pq[i].priority > pq[j].priority
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index, pq[j].index = i, j
}

func (pq *priorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*rangeItem)
	item.index = n
	*pq = append(*pq, item)
}

func (pq *priorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.index = -1 // for safety
	*pq = old[0 : n-1]
	return item
}

// update modifies the priority of a rangeItem in the queue.
func (pq *priorityQueue) update(item *rangeItem, priority float64) {
	item.priority = priority
	heap.Fix(pq, item.index)
}

type queueImpl interface {
	// needsLeaderLease returns whether this queue requires the leader
	// lease to operate on a range.
	needsLeaderLease() bool

	// shouldQueue accepts current time and a Range and returns whether
	// it should be queued and if so, at what priority.
	shouldQueue(proto.Timestamp, *Range) (shouldQueue bool, priority float64)

	// process accepts current time and a range and executes
	// queue-specific work on it.
	process(proto.Timestamp, *Range) error

	// timer returns a duration to wait between processing the next item
	// from the queue.
	timer() time.Duration
}

// baseQueue is the base implementation of the rangeQueue interface.
// Queue implementations should embed a baseQueue and implement queueImpl.
//
// baseQueue is not thread safe and is intended for usage only from
// the scanner's goroutine.
type baseQueue struct {
	name       string
	impl       queueImpl
	maxSize    int                  // Maximum number of ranges to queue
	incoming   chan *Range          // Channel for ranges to be queued
	sync.Mutex                      // Mutex protects priorityQ and ranges
	priorityQ  priorityQueue        // The priority queue
	ranges     map[int64]*rangeItem // Map from RaftID to rangeItem (for updating priority)
	// Some tests in this package disable queues.
	disabled bool
}

// newBaseQueue returns a new instance of baseQueue with the
// specified shouldQ function to determine which ranges to queue
// and maxSize to limit the growth of the queue. Note that
// maxSize doesn't prevent new ranges from being added, it just
// limits the total size. Higher priority ranges can still be
// added; their addition simply removes the lowest priority range.
func newBaseQueue(name string, impl queueImpl, maxSize int) *baseQueue {
	return &baseQueue{
		name:     name,
		impl:     impl,
		maxSize:  maxSize,
		incoming: make(chan *Range, 50),
		ranges:   map[int64]*rangeItem{},
	}
}

// Length returns the current size of the queue.
func (bq *baseQueue) Length() int {
	bq.Lock()
	defer bq.Unlock()
	return bq.priorityQ.Len()
}

// Start launches a goroutine to process entries in the queue. The
// provided stopper is used to finish processing.
func (bq *baseQueue) Start(clock *hlc.Clock, stopper *util.Stopper) {
	bq.processLoop(clock, stopper)
}

// MaybeAdd adds the specified range if bq.shouldQ specifies it should
// be queued. Ranges are added to the queue using the priority
// returned by bq.shouldQ. If the queue is too full, an already-queued
// range with the lowest priority may be dropped.
func (bq *baseQueue) MaybeAdd(rng *Range, now proto.Timestamp) {
	bq.Lock()
	defer bq.Unlock()

	if bq.disabled {
		return
	}
	should, priority := bq.impl.shouldQueue(now, rng)
	item, ok := bq.ranges[rng.Desc().RaftID]
	if !should {
		if ok {
			bq.remove(item.index)
		}
		return
	} else if ok {
		// Range has already been added; update priority.
		bq.priorityQ.update(item, priority)
		return
	}

	if log.V(1) {
		log.Infof("adding range %s to %s queue", rng, bq.name)
	}
	item = &rangeItem{value: rng, priority: priority}
	heap.Push(&bq.priorityQ, item)
	bq.ranges[rng.Desc().RaftID] = item

	// If adding this range has pushed the queue past its maximum size,
	// remove the lowest priority element.
	if pqLen := bq.priorityQ.Len(); pqLen > bq.maxSize {
		bq.remove(pqLen - 1)
	}
	// Signal the processLoop that a range has been added.
	bq.incoming <- rng
}

// MaybeRemove removes the specified range from the queue if enqueued.
func (bq *baseQueue) MaybeRemove(rng *Range) {
	bq.Lock()
	defer bq.Unlock()
	if item, ok := bq.ranges[rng.Desc().RaftID]; ok {
		if log.V(1) {
			log.Infof("removing range %s from %s queue", item.value, bq.name)
		}
		bq.remove(item.index)
	}
}

// processLoop processes the entries in the queue until the provided
// stopper signals exit.
//
// TODO(spencer): current load should factor into range processing timer.
func (bq *baseQueue) processLoop(clock *hlc.Clock, stopper *util.Stopper) {
	stopper.RunWorker(func() {
		// nextTime is initially nil; we don't start any timers until the queue
		// becomes non-empty.
		var nextTime <-chan time.Time

		for {
			select {
			// Incoming ranges set the next time to process in the event that
			// there were previously no ranges in the queue.
			case <-bq.incoming:
				if nextTime == nil {
					// When the first range is added, wake up immediately. This is
					// mainly to facilitate testing without unnecessary sleeps.
					nextTime = time.After(0 * time.Millisecond)
				}
			// Process ranges as the timer expires.
			case <-nextTime:
				bq.processOne(clock, stopper)
				if bq.Length() == 0 {
					nextTime = nil
				} else {
					nextTime = time.After(bq.impl.timer())
				}

			// Exit on stopper.
			case <-stopper.ShouldStop():
				bq.Lock()
				bq.ranges = map[int64]*rangeItem{}
				bq.priorityQ = nil
				bq.Unlock()
				return
			}
		}
	})
}

func (bq *baseQueue) processOne(clock *hlc.Clock, stopper *util.Stopper) {
	if !stopper.StartTask() {
		return
	}
	defer stopper.FinishTask()

	start := time.Now()
	bq.Lock()
	rng := bq.pop()
	bq.Unlock()
	if rng != nil {
		now := clock.Now()
		if log.V(1) {
			log.Infof("processing range %s from %s queue...", rng, bq.name)
		}
		// If the queue requires the leader lease to process the
		// range, check whether this replica has leader lease and
		// renew or acquire if necessary.
		if bq.impl.needsLeaderLease() {
			// Create a "fake" get request in order to invoke redirectOnOrAcquireLease.
			args := &proto.GetRequest{RequestHeader: proto.RequestHeader{Timestamp: now}}
			if err := rng.redirectOnOrAcquireLeaderLease(args.Header().Timestamp); err != nil {
				if log.V(1) {
					log.Infof("this replica of %s could not acquire leader lease; skipping...", rng)
				}
				return
			}
		}
		if err := bq.impl.process(now, rng); err != nil {
			log.Errorf("failure processing range %s from %s queue: %s", rng, bq.name, err)
		}
		if log.V(1) {
			log.Infof("processed range %s from %s queue in %s", rng, bq.name, time.Now().Sub(start))
		}
	}
}

// pop dequeues the highest priority range in the queue. Returns the
// range if not empty; otherwise, returns nil. Expects mutex to be
// locked.
func (bq *baseQueue) pop() *Range {
	if bq.priorityQ.Len() == 0 {
		return nil
	}
	item := heap.Pop(&bq.priorityQ).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
	return item.value
}

// remove removes an element from the priority queue by index. Expects
// mutex to be locked.
func (bq *baseQueue) remove(index int) {
	item := heap.Remove(&bq.priorityQ, index).(*rangeItem)
	delete(bq.ranges, item.value.Desc().RaftID)
}
<|endoftext|>


<commit_before>package cqlb

import (
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/gocql/gocql"
	"github.com/jinzhu/inflection"
	"github.com/relops/cqlr"
)

const (
	insertQueryTemplate = "insert into %s (%s) values(%s);"
	whereQueryTemplate  = "select %s from %s %s %s %s;"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

type Session struct {
	s           *gocql.Session
	query       interface{}
	args        []interface{}
	sel         []string
	limit       int
	consistency gocql.Consistency
	value       reflect.Value
	indirect    reflect.Value
	tableName   string
}

func SetSession(s *gocql.Session) *Session {
	return &Session{s: s}
}

func (s *Session) Consistency(consistency gocql.Consistency) *Session {
	c := s.clone()
	c.consistency = consistency
	return c
}

func (s *Session) Find(slice interface{}) error {
	// var fields map[string]interface{}
	// var fieldsToScan []interface{}
	// value := reflect.ValueOf(slice)
	// k := value.Kind()
	// if k != reflect.Slice {
	// 	return errors.New("value should be a slice.")
	// }
	// v := value.Index(0)
	// indirect := reflect.Indirect(v)
	// s.setModel(v)
	// query := s.query
	// vq := reflect.ValueOf(query)
	// kindQuery := vq.Kind()
	// switch kindQuery {
	// case reflect.Map:
	// 	fields = whereFieldsFromMap(query)
	// }
	// iter := s.s.Query(s.whereQuery(fields), values).Iter()
	// cols := iter.Columns()
	// values := make([]interface{}, len(cols))
	// names := f["names"].([]string)
	// for i, col := range cols {
	// 	values[i] = f["strategies"].(map[string]interface{})[col.Name]
	// }
	return nil
}

func (s *Session) Insert(v interface{}) error {
	f := fields(v)
	stmt := insertQuery(f)
	fmt.Println("fields", f)
	return s.s.Query(stmt, f["values"].([]interface{})...).Exec()
}

func (s *Session) Iter(value interface{}) *gocql.Iter {
	c := s.clone()
	var fields map[string]interface{}
	v := reflect.ValueOf(value)
	c.setModel(v)
	query := c.query
	vq := reflect.ValueOf(query)
	kindQuery := vq.Kind()
	switch kindQuery {
	case reflect.Map:
		fields = whereFieldsFromMap(query)
	}
	values := fields["values"].([]interface{})
	q := c.s.Query(c.whereQuery(fields), values...)
	if consistency := c.consistency; consistency > 0 {
		q = q.Consistency(consistency)
	}
	return q.Iter()
}

func (s *Session) Where(query interface{}, args ...interface{}) *Session {
	ns := s.clone()
	ns.query = query
	ns.args = args
	return ns
}

func (s *Session) Limit(limit int) *Session {
	c := s.clone()
	c.limit = limit
	return c
}

func (s *Session) Model(value interface{}) *Session {
	v := reflect.ValueOf(value)
	ns := s.clone()
	ns.setModel(v)
	return ns
}

func (s *Session) Scan(value interface{}) bool {
	var fields map[string]interface{}
	v := reflect.ValueOf(value)
	s.setModel(v)
	query := s.query
	vq := reflect.ValueOf(query)
	kindQuery := vq.Kind()
	switch kindQuery {
	case reflect.Map:
		fields = whereFieldsFromMap(query)
	}
	values := fields["values"].([]interface{})
	q := s.s.Query(s.whereQuery(fields), values...)
	if consistency := s.consistency; consistency > 0 {
		q = q.Consistency(consistency)
	}
	b := cqlr.BindQuery(q)
	return b.Scan(value)
}

func (s *Session) Select(sel ...string) *Session {
	c := s.clone()
	c.sel = sel
	return c
}

func (s *Session) Table(name string) *Session {
	c := s.clone()
	c.tableName = name
	return c
}

func (s *Session) limitString() string {
	if limit := s.limit; limit > 0 {
		return fmt.Sprintf("LIMIT %v", limit)
	}
	return ""
}

func (s *Session) selectString() string {
	if sel := s.sel; len(sel) > 0 {
		return strings.Join(sel, ",")
	}
	return "*"
}

func (s *Session) setModel(v reflect.Value) {
	indirect := reflect.Indirect(v)
	t := indirect.Type()
	s.value = v
	s.indirect = indirect
	if s.tableName == "" {
		s.tableName = inflection.Plural(strings.ToLower(t.Name()))
	}
}

func (s *Session) clone() *Session {
	ns := *s
	return &ns
}

func (s *Session) whereQuery(f map[string]interface{}) string {
	var conditionsString string
	sel := s.selectString()
	limit := s.limitString()
	if conditions := f["conditions"].(string); conditions != "" {
		conditionsString = fmt.Sprintf("WHERE %v", conditions)
	}
	query := fmt.Sprintf(whereQueryTemplate, sel, s.tableName, conditionsString, limit, "")
	return query
}

func insertQuery(f map[string]interface{}) string {
	query := fmt.Sprintf(insertQueryTemplate, f["table_name"], f["names"], f["slots"])
	return query
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[1] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string]interface{} {
	var names string
	var slots string
	var values []interface{}
	t1 := time.Now()
	strategies := make(map[string]interface{})
	result := make(map[string]interface{})
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	result["table_name"] = inflection.Plural(strings.ToLower(t.Name()))
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		var tagName string
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		fvIndirect := reflect.Indirect(fv)
		if fvIndirect.IsValid() == false {
			continue
		}
		inf = fvIndirect.Interface()
		isZero := isZero(inf)
		if isZero == true && tag.OmitEmpty == true {
			continue
		}
		if i != 0 {
			names += ","
			slots += ","
		}
		if tag.Name != "" {
			tagName = tag.Name
		} else {
			tagName = strings.ToLower(f.Name)
		}
		names += tagName
		slots += "?"
		strategies[tagName] = inf
		values = append(values, inf)
	}
	result["names"] = names
	result["values"] = values
	result["slots"] = slots
	fmt.Println("duration cqlb", time.Since(t1))
	return result
}

func whereFieldsFromMap(value interface{}) map[string]interface{} {
	var conditions string
	var values []interface{}
	var names []string
	t1 := time.Now()
	result := make(map[string]interface{})
	v := reflect.ValueOf(value)
	keys := v.MapKeys()
	for i := 0; i < len(keys); i++ {
		key := keys[i]
		keyString := key.String()
		value := v.MapIndex(key).Interface()
		if i != 0 {
			conditions += " AND "
		}
		conditions += fmt.Sprintf("%s = ?", keyString)
		names = append(names, keyString)
		values = append(values, value)
	}
	result["conditions"] = conditions
	result["values"] = values
	result["names"] = names
	fmt.Println("duration whereFieldsFromMap", time.Since(t1))
	return result
}

func isZero(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<commit_msg>Added support to allow filtering.<commit_after>package cqlb

import (
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/gocql/gocql"
	"github.com/jinzhu/inflection"
	"github.com/relops/cqlr"
)

const (
	insertQueryTemplate = "insert into %s (%s) values(%s);"
	whereQueryTemplate  = "select %s from %s %s %s %s;"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

type Session struct {
	s              *gocql.Session
	query          interface{}
	args           []interface{}
	sel            []string
	limit          int
	allowFiltering bool
	consistency    gocql.Consistency
	value          reflect.Value
	indirect       reflect.Value
	tableName      string
}

func SetSession(s *gocql.Session) *Session {
	return &Session{s: s}
}

func (s *Session) AllowFiltering(b bool) *Session {
	c := s.clone()
	c.allowFiltering = b
	return c
}

func (s *Session) Consistency(consistency gocql.Consistency) *Session {
	c := s.clone()
	c.consistency = consistency
	return c
}

func (s *Session) Find(slice interface{}) error {
	// var fields map[string]interface{}
	// var fieldsToScan []interface{}
	// value := reflect.ValueOf(slice)
	// k := value.Kind()
	// if k != reflect.Slice {
	// 	return errors.New("value should be a slice.")
	// }
	// v := value.Index(0)
	// indirect := reflect.Indirect(v)
	// s.setModel(v)
	// query := s.query
	// vq := reflect.ValueOf(query)
	// kindQuery := vq.Kind()
	// switch kindQuery {
	// case reflect.Map:
	// 	fields = whereFieldsFromMap(query)
	// }
	// iter := s.s.Query(s.whereQuery(fields), values).Iter()
	// cols := iter.Columns()
	// values := make([]interface{}, len(cols))
	// names := f["names"].([]string)
	// for i, col := range cols {
	// 	values[i] = f["strategies"].(map[string]interface{})[col.Name]
	// }
	return nil
}

func (s *Session) Insert(v interface{}) error {
	f := fields(v)
	stmt := insertQuery(f)
	fmt.Println("fields", f)
	return s.s.Query(stmt, f["values"].([]interface{})...).Exec()
}

func (s *Session) Iter(value interface{}) *gocql.Iter {
	c := s.clone()
	var fields map[string]interface{}
	v := reflect.ValueOf(value)
	c.setModel(v)
	query := c.query
	vq := reflect.ValueOf(query)
	kindQuery := vq.Kind()
	switch kindQuery {
	case reflect.Map:
		fields = whereFieldsFromMap(query)
	}
	values := fields["values"].([]interface{})
	q := c.s.Query(c.whereQuery(fields), values...)
	if consistency := c.consistency; consistency > 0 {
		q = q.Consistency(consistency)
	}
	return q.Iter()
}

func (s *Session) Where(query interface{}, args ...interface{}) *Session {
	ns := s.clone()
	ns.query = query
	ns.args = args
	return ns
}

func (s *Session) Limit(limit int) *Session {
	c := s.clone()
	c.limit = limit
	return c
}

func (s *Session) Model(value interface{}) *Session {
	v := reflect.ValueOf(value)
	ns := s.clone()
	ns.setModel(v)
	return ns
}

func (s *Session) Scan(value interface{}) bool {
	var fields map[string]interface{}
	v := reflect.ValueOf(value)
	s.setModel(v)
	query := s.query
	vq := reflect.ValueOf(query)
	kindQuery := vq.Kind()
	switch kindQuery {
	case reflect.Map:
		fields = whereFieldsFromMap(query)
	}
	values := fields["values"].([]interface{})
	q := s.s.Query(s.whereQuery(fields), values...)
	if consistency := s.consistency; consistency > 0 {
		q = q.Consistency(consistency)
	}
	b := cqlr.BindQuery(q)
	return b.Scan(value)
}

func (s *Session) Select(sel ...string) *Session {
	c := s.clone()
	c.sel = sel
	return c
}

func (s *Session) Table(name string) *Session {
	c := s.clone()
	c.tableName = name
	return c
}

func (s *Session) allowFilteringString() string {
	if s.allowFiltering == true {
		return "ALLOW FILTERING"
	}
	return ""
}

func (s *Session) limitString() string {
	if limit := s.limit; limit > 0 {
		return fmt.Sprintf("LIMIT %v", limit)
	}
	return ""
}

func (s *Session) selectString() string {
	if sel := s.sel; len(sel) > 0 {
		return strings.Join(sel, ",")
	}
	return "*"
}

func (s *Session) setModel(v reflect.Value) {
	indirect := reflect.Indirect(v)
	t := indirect.Type()
	s.value = v
	s.indirect = indirect
	if s.tableName == "" {
		s.tableName = inflection.Plural(strings.ToLower(t.Name()))
	}
}

func (s *Session) clone() *Session {
	ns := *s
	return &ns
}

func (s *Session) whereQuery(f map[string]interface{}) string {
	var conditionsString string
	sel := s.selectString()
	limit := s.limitString()
	allowFiltering := s.allowFilteringString()
	if conditions := f["conditions"].(string); conditions != "" {
		conditionsString = fmt.Sprintf("WHERE %v", conditions)
	}
	query := fmt.Sprintf(whereQueryTemplate, sel, s.tableName, conditionsString, limit, allowFiltering)
	return query
}

func insertQuery(f map[string]interface{}) string {
	query := fmt.Sprintf(insertQueryTemplate, f["table_name"], f["names"], f["slots"])
	return query
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[1] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string]interface{} {
	var names string
	var slots string
	var values []interface{}
	t1 := time.Now()
	strategies := make(map[string]interface{})
	result := make(map[string]interface{})
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	result["table_name"] = inflection.Plural(strings.ToLower(t.Name()))
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		var tagName string
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		fvIndirect := reflect.Indirect(fv)
		if fvIndirect.IsValid() == false {
			continue
		}
		inf = fvIndirect.Interface()
		isZero := isZero(inf)
		if isZero == true && tag.OmitEmpty == true {
			continue
		}
		if i != 0 {
			names += ","
			slots += ","
		}
		if tag.Name != "" {
			tagName = tag.Name
		} else {
			tagName = strings.ToLower(f.Name)
		}
		names += tagName
		slots += "?"
		strategies[tagName] = inf
		values = append(values, inf)
	}
	result["names"] = names
	result["values"] = values
	result["slots"] = slots
	fmt.Println("duration cqlb", time.Since(t1))
	return result
}

func whereFieldsFromMap(value interface{}) map[string]interface{} {
	var conditions string
	var values []interface{}
	var names []string
	t1 := time.Now()
	result := make(map[string]interface{})
	v := reflect.ValueOf(value)
	keys := v.MapKeys()
	for i := 0; i < len(keys); i++ {
		key := keys[i]
		keyString := key.String()
		value := v.MapIndex(key).Interface()
		if i != 0 {
			conditions += " AND "
		}
		conditions += fmt.Sprintf("%s = ?", keyString)
		names = append(names, keyString)
		values = append(values, value)
	}
	result["conditions"] = conditions
	result["values"] = values
	result["names"] = names
	fmt.Println("duration whereFieldsFromMap", time.Since(t1))
	return result
}

func isZero(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<|endoftext|>


<commit_before>package cqlb

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/gocql/gocql"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[0] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string]interface{} {
	result := map[string]interface{}{}
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		fmt.Println(tag)
		if fv.IsValid() == false && tag.OmitEmpty == true {
			continue
		}
		fvIndirect := reflect.Indirect(fv)
		inf = fvIndirect.Interface()
		if tag.Name != "" {
			result[tag.Name] = inf
		} else {
			//fmt.Println(f.Name, indirect.Field(f.Index[0]))
			result[strings.ToLower(f.Name)] = inf
			//b.fieldMap[strings.ToLower(f.Name)] = f.Index
		}
	}
	return result
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<commit_msg>Set Session.<commit_after>package cqlb

import (
	"reflect"
	"strings"

	"github.com/gocql/gocql"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

type Session struct{}

func SetSession(*gocql.Session) *Session {
	return &Session{}
}

func (s *Session) Insert(v interface{}) {
	f := fields(v)
	insertQuery(f)
}

func insertQuery(f map[string][]interface{}) string {
	return ""
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[0] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string][]interface{} {
	var names []interface{}
	var values []interface{}
	result := make(map[string][]interface{}, 2)
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		if fv.IsValid() == false && tag.OmitEmpty == true {
			continue
		}
		fvIndirect := reflect.Indirect(fv)
		inf = fvIndirect.Interface()
		if tag.Name != "" {
			names = append(names, tag.Name)
		} else {
			names = append(names, strings.ToLower(f.Name))
		}
		values = append(values, inf)
	}
	result["names"] = names
	result["values"] = values
	return result
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<|endoftext|>


<commit_before>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	stop     chan struct{}
	add      chan *Entry
	snapshot chan []*Entry
	running  bool
	count    int
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// The identifier to reference the job instance.
	Id int

	// 0: normal, 1: paused
	Status int
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int      { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		count:    0,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) (int, error) {
	return c.AddJob(spec, FuncJob(cmd))
}

// RemoveFunc removes a func from the Cron referenced by the id.
func (c *Cron) RemoveFunc(id int) {
	w := 0 // write index
	for _, x := range c.entries {
		if id == x.Id {
			continue
		}
		c.entries[w] = x
		w++
	}
	c.entries = c.entries[:w]
}

func (c *Cron) PauseFunc(id int) {
	for _, x := range c.entries {
		if id == x.Id {
			x.Status = 1
			break
		}
	}
}

func (c *Cron) ResumeFunc(id int) {
	for _, x := range c.entries {
		if id == x.Id {
			x.Status = 0
			break
		}
	}
}

// Status inquires the status of a job, 0: running, 1: paused, -1: not started.
func (c *Cron) Status(id int) int {
	for _, x := range c.entries {
		if id == x.Id {
			return x.Status
		}
	}
	return -1
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) (int, error) {
	schedule, err := Parse(spec)
	if err != nil {
		return -1, err
	}
	c.count++
	c.Schedule(schedule, cmd, c.count)
	return c.count, nil
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job, id int) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
		Id:       id,
		Status:   0,
	}
	if !c.running {
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	for {
		// Figure out the next activation times for each entry.
		now := time.Now().Local()
		for _, entry := range c.entries {
			entry.Next = entry.Schedule.Next(now)
		}
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))
		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}
		select {
		case now = <-time.After(effective.Sub(now)):
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				if e.Status == 0 {
					go e.Job.Run()
				}
				e.Prev = e.Next
				e.Next = e.Schedule.Next(effective)
			}
			continue

		case newEntry := <-c.add:
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(now)

		case <-c.snapshot:
			c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		//		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
			Schedule: e.Schedule,
			Next:     e.Next,
			Prev:     e.Prev,
			Job:      e.Job,
			Id:       e.Id,
			Status:   e.Status,
		})
	}
	return entries
}
<commit_msg>Uupdate.<commit_after>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	stop     chan struct{}
	add      chan *Entry
	snapshot chan []*Entry
	running  bool
	count    int
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// The identifier to reference the job instance.
	Id int

	// 0: normal, 1: paused
	Status int
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int      { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		count:    0,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) (int, error) {
	return c.AddJob(spec, FuncJob(cmd))
}

// RemoveFunc removes a func from the Cron referenced by the id.
func (c *Cron) RemoveFunc(id int) {
	w := 0 // write index
	for _, x := range c.entries {
		if id == x.Id {
			continue
		}
		c.entries[w] = x
		w++
	}
	c.entries = c.entries[:w]
}

func (c *Cron) PauseFunc(id int) {
	for _, x := range c.entries {
		if id == x.Id {
			x.Status = 1
			break
		}
	}
}

func (c *Cron) ResumeFunc(id int) {
	for _, x := range c.entries {
		if id == x.Id {
			x.Status = 0
			break
		}
	}
}

// Status inquires the status of a job, 0: running, 1: paused, -1: not started.
func (c *Cron) Status(id int) int {
	for _, x := range c.entries {
		if id == x.Id {
			return x.Status
		}
	}
	return -1
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) (int, error) {
	schedule, err := Parse(spec)
	if err != nil {
		return -1, err
	}
	c.count++
	c.Schedule(schedule, cmd, c.count)
	return c.count, nil
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job, id int) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
		Id:       id,
		Status:   0,
	}
	if !c.running {
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	for {
		// Figure out the next activation times for each entry.
		now := time.Now().Local()
		for _, entry := range c.entries {
			entry.Next = entry.Schedule.Next(now)
		}
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))
		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}
		select {
		case now = <-time.After(effective.Sub(now)):
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				if e.Status == 0 {
					go e.Job.Run()
				}
				e.Prev = e.Next
				e.Next = e.Schedule.Next(effective)
			}
			continue

		case newEntry := <-c.add:
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(now)

		case <-c.snapshot:
			c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		//		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	if !c.running {
		return
	}
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
			Schedule: e.Schedule,
			Next:     e.Next,
			Prev:     e.Prev,
			Job:      e.Job,
			Id:       e.Id,
			Status:   e.Status,
		})
	}
	return entries
}
<|endoftext|>


<commit_before>package str

import (
	"strings"
	"unicode"

	"golang.org/x/text/runes"

	"golang.org/x/text/transform"
	"golang.org/x/text/unicode/norm"
)

//RemoveWhiteSpace removes whitespace {'\n' '\t' ' ' '\r`} from a string.
//Note that this converts to runes and back to UTF-8, so RemoveWhiteSpace(s) == s
//for a non-whitespace string does not necessarially hold, since the code points may differ.
func RemoveWhiteSpace(s string) string {
	return RemoveRunes(s, '\n', ' ', '\r', '\t')
}

//NormEqual returns true if the NKFC normalized forms of both strings are equal.
func NormEqual(s, q string) bool {
	return NKFC(s) == NKFC(q)
}

//NormFoldEqual returns true if the casefolded, NKFC normalized forms of both strings are equal.
func NormFoldEqual(s, q string) bool {
	return strings.EqualFold(NKFC(s), NKFC(q))
}

//NKFC normalizes a string to it's NKFC form
func NKFC(s string) string {
	return norm.NFKC.String(s)
}

//NFKD normalizes a string to it's NFKD form
func NFKD(s string) string {
	return norm.NFKD.String(s)
}

//NFD normalizes a string to it's NFD form
func NFD(s string) string {
	return norm.NFD.String(s)
}

//NFC normalizes a string to it's NFC form
func NFC(s string) string {
	return norm.NFC.String(s)
}

func isNonSpacingMark(r rune) bool {
	return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
}

var removeNonSpacingMarks = runes.Remove(runes.In(unicode.Mn))
var diacriticRemover = transform.Chain(norm.NFD, transform.RemoveFunc(isNonSpacingMark), norm.NFC)

//RemoveDiacriticsNFC creates a copy of s with the diacritics removed. It also transforms it to NFC.
//Thread Safe
func RemoveDiacriticsNFC(s string) string {
	b := []byte(s)
	b, _, _ = transform.Bytes(norm.NFD, b)
	b, _, _ = transform.Bytes(removeNonSpacingMarks, b)
	b, _, _ = transform.Bytes(diacriticRemover, b)
	return string(b)
}
<commit_msg>still trying to make threadsafe<commit_after>package str

import (
	"strings"
	"unicode"

	"golang.org/x/text/runes"

	"golang.org/x/text/transform"
	"golang.org/x/text/unicode/norm"
)

//RemoveWhiteSpace removes whitespace {'\n' '\t' ' ' '\r`} from a string.
//Note that this converts to runes and back to UTF-8, so RemoveWhiteSpace(s) == s
//for a non-whitespace string does not necessarially hold, since the code points may differ.
func RemoveWhiteSpace(s string) string {
	return RemoveRunes(s, '\n', ' ', '\r', '\t')
}

//NormEqual returns true if the NKFC normalized forms of both strings are equal.
func NormEqual(s, q string) bool {
	return NKFC(s) == NKFC(q)
}

//NormFoldEqual returns true if the casefolded, NKFC normalized forms of both strings are equal.
func NormFoldEqual(s, q string) bool {
	return strings.EqualFold(NKFC(s), NKFC(q))
}

//NKFC normalizes a string to it's NKFC form
func NKFC(s string) string {
	return norm.NFKC.String(s)
}

//NFKD normalizes a string to it's NFKD form
func NFKD(s string) string {
	return norm.NFKD.String(s)
}

//NFD normalizes a string to it's NFD form
func NFD(s string) string {
	return norm.NFD.String(s)
}

//NFC normalizes a string to it's NFC form
func NFC(s string) string {
	return norm.NFC.String(s)
}

func isNonSpacingMark(r rune) bool {
	return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
}

var removeNonSpacingMarks = runes.Remove(runes.In(unicode.Mn))
var diacriticRemover = transform.Chain(norm.NFD, removeNonSpacingMarks, norm.NFC)

//RemoveDiacriticsNFC creates a copy of s with the diacritics removed. It also transforms it to NFC.
//Thread Safe
func RemoveDiacriticsNFC(s string) string {
	var diacriticRemover = transform.Chain(norm.NFD, removeNonSpacingMarks, norm.NFC)
	out, _, _ := transform.String(diacriticRemover, s)
	return out
}
<|endoftext|>


<commit_before>package smartcrop

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	_ "image/png"
	"math"
	"os"
	"time"

	"github.com/nfnt/resize"
)

var (
	aspect                  = 0
	cropWidth               = 0.0
	cropHeight              = 0.0
	detailWeight            = 0.2
	skinColor               = [3]float64{0.78, 0.57, 0.44}
	skinBias                = 0.01
	skinBrightnessMin       = 0.2
	skinBrightnessMax       = 1.0
	skinThreshold           = 0.8
	skinWeight              = 1.8
	saturationBrightnessMin = 0.25
	saturationBrightnessMax = 0.9
	saturationThreshold     = 0.4
	saturationBias          = 0.2
	saturationWeight        = 0.3
	// step * minscale rounded down to the next power of two should be good
	scoreDownSample   = 8
	step              = 8
	scaleStep         = 0.1
	minScale          = 0.9
	maxScale          = 1.0
	edgeRadius        = 0.4
	edgeWeight        = -20.0
	outsideImportance = -0.5
	ruleOfThirds      = true
	prescale          = true
	prescalefactor    = 1.0
	debug             = false
)

type Score struct {
	Detail     float64
	Saturation float64
	Skin       float64
	Total      float64
}

type Crop struct {
	X      int
	Y      int
	Width  int
	Height int
	Score  Score
}

func SmartCrop(img *image.Image, width, height int) (Crop, image.Image, error) {
	if width == 0 && height == 0 {
		return Crop{}, nil, errors.New("Expect either a height or width")
	}

	scale := math.Min(float64((*img).Bounds().Size().X)/float64(width), float64((*img).Bounds().Size().Y)/float64(height))

	// resize image for faster processing
	var lowimg image.Image

	if prescale {

		if f := 1 / scale / minScale; f < 1 {
			prescalefactor = f
		}
		fmt.Println(prescalefactor)

		lowimg = resize.Resize(
			uint(float64((*img).Bounds().Size().X)*prescalefactor),
			0,
			*img,
			resize.NearestNeighbor)
		WriteImageToJpeg(&lowimg, "/tmp/prescale.jpg")

	} else {
		lowimg = *img
	}

	cropWidth, cropHeight = math.Floor(float64(width)*scale*prescalefactor), math.Floor(float64(height)*scale*prescalefactor)
	minScale = math.Min(maxScale, math.Max(1.0/scale, minScale))

	fmt.Printf("original resolution: %dx%d\n", (*img).Bounds().Size().X, (*img).Bounds().Size().Y)
	fmt.Printf("scale: %f, cropw: %f, croph: %f, minscale: %f\n", scale, cropWidth, cropHeight, minScale)

	//topCrop := analyse(img)
	topCrop := analyse(&lowimg)
	return topCrop, lowimg, nil
}

func thirds(x float64) float64 {
	x1 := int(x - (1.0 / 3.0) + 1.0)
	res := (float64(x1%2.0) * 0.5) - 0.5
	return res * 16.0
}

func importance(crop *Crop, x, y int) float64 {
	if crop.X > x || x >= crop.X+crop.Width || crop.Y > y || y >= crop.Y+crop.Height {
		return outsideImportance
	}

	xf := float64(x-crop.X) / float64(crop.Width)
	yf := float64(y-crop.Y) / float64(crop.Height)

	px := math.Abs(0.5-xf) * 2.0
	py := math.Abs(0.5-yf) * 2.0

	dx := math.Max(px-1.0+edgeRadius, 0.0)
	dy := math.Max(py-1.0+edgeRadius, 0.0)
	d := (math.Pow(dx, 2) + math.Pow(dy, 2)) * edgeWeight

	s := 1.41 - math.Sqrt(math.Pow(px, 2)+math.Pow(py, 2))
	if ruleOfThirds {
		s += (math.Max(0.0, s+d+0.5) * 1.2) * (thirds(px) + thirds(py))
	}

	return s + d
}

func score(output *image.Image, crop *Crop) Score {
	o := (*output).(*image.RGBA)
	height := (*output).Bounds().Size().Y
	width := (*output).Bounds().Size().X
	score := Score{}

	for y := 0; y < height; y++ {
		yoffset := y * width
		ydownSample := y * scoreDownSample
		for x := 0; x < width; x++ {
			//			now := time.Now()
			imp := importance(crop, x*scoreDownSample, ydownSample)
			//			fmt.Println("Time elapsed single-imp:", time.Since(now))

			p := yoffset + x*4

			r8 := float64(o.Pix[p]) / 255.0
			g8 := float64(o.Pix[p+1]) / 255.0
			b8 := float64(o.Pix[p+2]) / 255.0

			score.Skin += r8 * (g8 + skinBias) * imp
			score.Detail += g8 * imp
			score.Saturation += b8 * (g8 + saturationBias) * imp
		}
	}

	score.Total = (score.Detail*detailWeight + score.Skin*skinWeight + score.Saturation*saturationWeight) / float64(crop.Width) / float64(crop.Height)
	return score
}

func WriteImageToJpeg(img *image.Image, name string) {
	fso, err := os.Create(name)
	if err != nil {
		panic(err)
	}
	defer fso.Close()

	jpeg.Encode(fso, (*img), &jpeg.Options{Quality: 90})
}

func analyse(img *image.Image) Crop {
	o := image.Image(image.NewRGBA((*img).Bounds()))

	now := time.Now()
	edgeDetect(img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
	//WriteImageToJpeg(&o, "/tmp/smartcrop_edge.jpg")

	now = time.Now()
	skinDetect(img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
	//WriteImageToJpeg(&o, "/tmp/smartcrop_skin.jpg")

	now = time.Now()
	saturationDetect(img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
	//WriteImageToJpeg(&o, "/tmp/smartcrop_sat.jpg")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Println("Time elapsed score:", time.Since(now))

	return topCrop
}

func saturation(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	maximum := math.Max(math.Max(r8/255.0, g8/255.0), b8/255.0)
	minimum := math.Min(math.Min(r8/255.0, g8/255.0), b8/255.0)

	if maximum == minimum {
		return 0
	}

	l := (maximum + minimum) / 2.0
	d := maximum - minimum

	if l > 0.5 {
		return d / (2.0 - maximum - minimum)
	} else {
		return d / (maximum + minimum)
	}
}

func cie(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	return 0.5126*b8 + 0.7152*g8 + 0.0722*r8
}

func skinCol(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	mag := math.Sqrt(math.Pow(r8, 2) + math.Pow(g8, 2) + math.Pow(b8, 2))
	rd := r8/mag - skinColor[0]
	gd := g8/mag - skinColor[1]
	bd := b8/mag - skinColor[2]

	d := math.Sqrt(math.Pow(rd, 2) + math.Pow(gd, 2) + math.Pow(bd, 2))
	return 1.0 - d
}

func edgeDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			var lightness float64

			if x == 0 || x >= w-1 || y == 0 || y >= h-1 {
				lightness = cie((*i).At(x, y))
			} else {
				lightness = cie((*i).At(x, y))*4.0 -
					cie((*i).At(x, y-1)) -
					cie((*i).At(x-1, y)) -
					cie((*i).At(x+1, y)) -
					cie((*i).At(x, y+1))
			}

			if lightness < 0 {
				continue
			}

			nc := color.RGBA{uint8(lightness), uint8(lightness), uint8(lightness), 255}
			(*o).(*image.RGBA).Set(x, y, nc)
		}
	}
}

func skinDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			skin := skinCol((*i).At(x, y))

			if skin > skinThreshold && lightness >= skinBrightnessMin && lightness <= skinBrightnessMax {
				r := (skin - skinThreshold) * (255.0 / (1.0 - skinThreshold))
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r), uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{0, uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func saturationDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			saturation := saturation((*i).At(x, y))

			if saturation > saturationThreshold && lightness >= saturationBrightnessMin && lightness <= saturationBrightnessMax {
				b := (saturation - saturationThreshold) * (255.0 / (1.0 - saturationThreshold))
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), 0, 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func crops(i *image.Image) []Crop {
	res := []Crop{}
	width := (*i).Bounds().Size().X
	height := (*i).Bounds().Size().Y

	//minDimension := math.Min(float64(width), float64(height))
	cropW := cropWidth  //|| minDimension
	cropH := cropHeight //|| minDimension

	for scale := maxScale; scale >= minScale; scale -= scaleStep {
		for y := 0; float64(y)+cropH*scale <= float64(height); y += step {
			for x := 0; float64(x)+cropW*scale <= float64(width); x += step {
				res = append(res, Crop{
					X:      x,
					Y:      y,
					Width:  int(cropW * scale),
					Height: int(cropH * scale),
				})
			}
		}
	}

	return res
}
<commit_msg>chop instead of floor<commit_after>package smartcrop

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/jpeg"
	_ "image/png"
	"math"
	"os"
	"time"

	"github.com/nfnt/resize"
)

var (
	aspect                  = 0
	cropWidth               = 0.0
	cropHeight              = 0.0
	detailWeight            = 0.2
	skinColor               = [3]float64{0.78, 0.57, 0.44}
	skinBias                = 0.01
	skinBrightnessMin       = 0.2
	skinBrightnessMax       = 1.0
	skinThreshold           = 0.8
	skinWeight              = 1.8
	saturationBrightnessMin = 0.25
	saturationBrightnessMax = 0.9
	saturationThreshold     = 0.4
	saturationBias          = 0.2
	saturationWeight        = 0.3
	// step * minscale rounded down to the next power of two should be good
	scoreDownSample   = 8
	step              = 8
	scaleStep         = 0.1
	minScale          = 0.9
	maxScale          = 1.0
	edgeRadius        = 0.4
	edgeWeight        = -20.0
	outsideImportance = -0.5
	ruleOfThirds      = true
	prescale          = true
	prescalefactor    = 1.0
	debug             = false
)

type Score struct {
	Detail     float64
	Saturation float64
	Skin       float64
	Total      float64
}

type Crop struct {
	X      int
	Y      int
	Width  int
	Height int
	Score  Score
}

func SmartCrop(img *image.Image, width, height int) (Crop, image.Image, error) {
	if width == 0 && height == 0 {
		return Crop{}, nil, errors.New("Expect either a height or width")
	}

	scale := math.Min(float64((*img).Bounds().Size().X)/float64(width), float64((*img).Bounds().Size().Y)/float64(height))

	// resize image for faster processing
	var lowimg image.Image

	if prescale {

		if f := 1 / scale / minScale; f < 1 {
			prescalefactor = f
		}
		fmt.Println(prescalefactor)

		lowimg = resize.Resize(
			uint(float64((*img).Bounds().Size().X)*prescalefactor),
			0,
			*img,
			resize.NearestNeighbor)
		WriteImageToJpeg(&lowimg, "/tmp/prescale.jpg")

	} else {
		lowimg = *img
	}

	cropWidth, cropHeight = chop(float64(width)*scale*prescalefactor), chop(float64(height)*scale*prescalefactor)
	minScale = math.Min(maxScale, math.Max(1.0/scale, minScale))

	fmt.Printf("original resolution: %dx%d\n", (*img).Bounds().Size().X, (*img).Bounds().Size().Y)
	fmt.Printf("scale: %f, cropw: %f, croph: %f, minscale: %f\n", scale, cropWidth, cropHeight, minScale)

	//topCrop := analyse(img)
	topCrop := analyse(&lowimg)
	return topCrop, lowimg, nil
}

func chop(x float64) float64 {
	if x < 0 {
		return math.Ceil(x)
	}
	return math.Floor(x)
}

func thirds(x float64) float64 {
	x1 := int(x - (1.0 / 3.0) + 1.0)
	res := (float64(x1%2.0) * 0.5) - 0.5
	return res * 16.0
}

func importance(crop *Crop, x, y int) float64 {
	if crop.X > x || x >= crop.X+crop.Width || crop.Y > y || y >= crop.Y+crop.Height {
		return outsideImportance
	}

	xf := float64(x-crop.X) / float64(crop.Width)
	yf := float64(y-crop.Y) / float64(crop.Height)

	px := math.Abs(0.5-xf) * 2.0
	py := math.Abs(0.5-yf) * 2.0

	dx := math.Max(px-1.0+edgeRadius, 0.0)
	dy := math.Max(py-1.0+edgeRadius, 0.0)
	d := (math.Pow(dx, 2) + math.Pow(dy, 2)) * edgeWeight

	s := 1.41 - math.Sqrt(math.Pow(px, 2)+math.Pow(py, 2))
	if ruleOfThirds {
		s += (math.Max(0.0, s+d+0.5) * 1.2) * (thirds(px) + thirds(py))
	}

	return s + d
}

func score(output *image.Image, crop *Crop) Score {
	o := (*output).(*image.RGBA)
	height := (*output).Bounds().Size().Y
	width := (*output).Bounds().Size().X
	score := Score{}

	for y := 0; y < height; y++ {
		yoffset := y * width
		ydownSample := y * scoreDownSample
		for x := 0; x < width; x++ {
			//			now := time.Now()
			imp := importance(crop, x*scoreDownSample, ydownSample)
			//			fmt.Println("Time elapsed single-imp:", time.Since(now))

			p := yoffset + x*4

			r8 := float64(o.Pix[p]) / 255.0
			g8 := float64(o.Pix[p+1]) / 255.0
			b8 := float64(o.Pix[p+2]) / 255.0

			score.Skin += r8 * (g8 + skinBias) * imp
			score.Detail += g8 * imp
			score.Saturation += b8 * (g8 + saturationBias) * imp
		}
	}

	score.Total = (score.Detail*detailWeight + score.Skin*skinWeight + score.Saturation*saturationWeight) / float64(crop.Width) / float64(crop.Height)
	return score
}

func WriteImageToJpeg(img *image.Image, name string) {
	fso, err := os.Create(name)
	if err != nil {
		panic(err)
	}
	defer fso.Close()

	jpeg.Encode(fso, (*img), &jpeg.Options{Quality: 90})
}

func analyse(img *image.Image) Crop {
	o := image.Image(image.NewRGBA((*img).Bounds()))

	now := time.Now()
	edgeDetect(img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
	//WriteImageToJpeg(&o, "/tmp/smartcrop_edge.jpg")

	now = time.Now()
	skinDetect(img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
	//WriteImageToJpeg(&o, "/tmp/smartcrop_skin.jpg")

	now = time.Now()
	saturationDetect(img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
	//WriteImageToJpeg(&o, "/tmp/smartcrop_sat.jpg")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Println("Time elapsed score:", time.Since(now))

	return topCrop
}

func saturation(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	maximum := math.Max(math.Max(r8/255.0, g8/255.0), b8/255.0)
	minimum := math.Min(math.Min(r8/255.0, g8/255.0), b8/255.0)

	if maximum == minimum {
		return 0
	}

	l := (maximum + minimum) / 2.0
	d := maximum - minimum

	if l > 0.5 {
		return d / (2.0 - maximum - minimum)
	} else {
		return d / (maximum + minimum)
	}
}

func cie(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	return 0.5126*b8 + 0.7152*g8 + 0.0722*r8
}

func skinCol(c color.Color) float64 {
	r, g, b, _ := c.RGBA()
	r8 := float64(r >> 8)
	g8 := float64(g >> 8)
	b8 := float64(b >> 8)

	mag := math.Sqrt(math.Pow(r8, 2) + math.Pow(g8, 2) + math.Pow(b8, 2))
	rd := r8/mag - skinColor[0]
	gd := g8/mag - skinColor[1]
	bd := b8/mag - skinColor[2]

	d := math.Sqrt(math.Pow(rd, 2) + math.Pow(gd, 2) + math.Pow(bd, 2))
	return 1.0 - d
}

func edgeDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			var lightness float64

			if x == 0 || x >= w-1 || y == 0 || y >= h-1 {
				lightness = cie((*i).At(x, y))
			} else {
				lightness = cie((*i).At(x, y))*4.0 -
					cie((*i).At(x, y-1)) -
					cie((*i).At(x-1, y)) -
					cie((*i).At(x+1, y)) -
					cie((*i).At(x, y+1))
			}

			if lightness < 0 {
				continue
			}

			nc := color.RGBA{uint8(lightness), uint8(lightness), uint8(lightness), 255}
			(*o).(*image.RGBA).Set(x, y, nc)
		}
	}
}

func skinDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			skin := skinCol((*i).At(x, y))

			if skin > skinThreshold && lightness >= skinBrightnessMin && lightness <= skinBrightnessMax {
				r := (skin - skinThreshold) * (255.0 / (1.0 - skinThreshold))
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r), uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				_, g, b, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{0, uint8(g >> 8), uint8(b >> 8), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func saturationDetect(i *image.Image, o *image.Image) {
	w := (*i).Bounds().Size().X
	h := (*i).Bounds().Size().Y

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			lightness := cie((*i).At(x, y)) / 255.0
			saturation := saturation((*i).At(x, y))

			if saturation > saturationThreshold && lightness >= saturationBrightnessMin && lightness <= saturationBrightnessMax {
				b := (saturation - saturationThreshold) * (255.0 / (1.0 - saturationThreshold))
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b), 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			} else {
				r, g, _, _ := (*o).At(x, y).RGBA()
				nc := color.RGBA{uint8(r >> 8), uint8(g >> 8), 0, 255}
				(*o).(*image.RGBA).Set(x, y, nc)
			}
		}
	}
}

func crops(i *image.Image) []Crop {
	res := []Crop{}
	width := (*i).Bounds().Size().X
	height := (*i).Bounds().Size().Y

	//minDimension := math.Min(float64(width), float64(height))
	cropW := cropWidth  //|| minDimension
	cropH := cropHeight //|| minDimension

	for scale := maxScale; scale >= minScale; scale -= scaleStep {
		for y := 0; float64(y)+cropH*scale <= float64(height); y += step {
			for x := 0; float64(x)+cropW*scale <= float64(width); x += step {
				res = append(res, Crop{
					X:      x,
					Y:      y,
					Width:  int(cropW * scale),
					Height: int(cropH * scale),
				})
			}
		}
	}

	return res
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.

// Package subtle implements some function that are often useful
// in cryptographic code. All functions in subtle take constant time.
package subtle

import csubtle "crypto/subtle"

// Equal returns true if and only if the two slices, x
// and y, have equal contents.
func Equal(x, y []byte) bool {
	return csubtle.ConstantTimeCompare(x, y) == 1
}

// Increment increments the slice val encoded as little
// endian number.
func Increment(val []byte) {
	t := uint16(1)
	for i := range val {
		t += uint16(val[i])
		val[i] = byte(t)
		t >>= 8
	}
}
<commit_msg>subtle: fix typo in doc<commit_after>// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.

// Package subtle implements some functions that are often useful
// in cryptographic code. All functions in subtle take constant time.
package subtle

import csubtle "crypto/subtle"

// Equal returns true if and only if the two slices, x
// and y, have equal contents.
func Equal(x, y []byte) bool {
	return csubtle.ConstantTimeCompare(x, y) == 1
}

// Increment increments the slice val encoded as little
// endian number.
func Increment(val []byte) {
	t := uint16(1)
	for i := range val {
		t += uint16(val[i])
		val[i] = byte(t)
		t >>= 8
	}
}
<|endoftext|>


<commit_before>package suit

import (
	"encoding/json"
	"fmt"
	"reflect"
	"unicode"
	"unicode/utf8"
)

func (o *ConfigurationScreen) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

func (o *ConfigurationScreen) UnmarshalJSON(bytes []byte) error {
	aMap := make(map[string]interface{})
	if err := json.Unmarshal(bytes, &aMap); err != nil {
		return err
	}
	return hydrate(aMap, o)
}

func makeTyped(typeName string) (interface{}, error) {
	switch typeName {
	case "actionList":
		return &ActionList{}, nil
	case "alert":
		return &Alert{}, nil
	case "auto":
		return &AutomaticAction{}, nil
	case "close":
		return &CloseAction{}, nil
	case "inputHidden":
		return &InputHidden{}, nil
	case "inputText":
		return &InputText{}, nil
	case "inputTime":
		return &InputTime{}, nil
	case "inputTimeRange":
		return &InputTimeRange{}, nil
	case "optionGroup":
		return &OptionGroup{}, nil
	case "progressBar":
		return &ProgressBar{}, nil
	case "radioGroup":
		return &RadioGroup{}, nil
	case "reply":
		return &ReplyAction{}, nil
	case "separator":
		return &Separator{}, nil
	case "staticText":
		return &StaticText{}, nil
	default:
		return nil, fmt.Errorf("can't make object for type: %s", typeName)
	}
}

func walk(o interface{}) map[string]interface{} {

	m := make(map[string]interface{})

	if t, ok := o.(Typed); ok {
		m["type"] = t.getType()
	}

	val := reflect.ValueOf(o)

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		val := valueField.Interface()

		if val == nil {
			continue
		}

		valueField = reflect.ValueOf(val)

		if valueField.Kind() == reflect.Ptr && !isZero(valueField) {
			valueField = valueField.Elem()
			val = valueField.Interface()
		}

		switch valueField.Kind() {
		case reflect.Struct:
			val = walk(val)
		case reflect.Slice:
			vals := []interface{}{}
			for i := 0; i < valueField.Len(); i++ {
				if valueField.Index(i).Kind() == reflect.Interface || valueField.Index(i).Kind() == reflect.Struct {
					vals = append(vals, walk(valueField.Index(i).Interface()))
				} else {
					vals = append(vals, valueField.Index(i).Interface())
				}
				val = vals
			}
		default:
			if isZero(valueField) {
				val = nil
			}
		}

		if val != nil {
			m[lF(typeField.Name)] = val
		}
	}

	return m
}

func isZero(valueField reflect.Value) bool {
	return valueField.Interface() == reflect.Zero(valueField.Type()).Interface()
}

func lF(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}

func hydrate(s map[string]interface{}, o interface{}) error {
	v := reflect.ValueOf(o).Elem()
	switch v.Kind() {
	case reflect.Struct:
		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			var mfv reflect.Value
			fv := v.Field(i)
			ft := t.Field(i)

			if sv, ok := s[lF(ft.Name)]; ok && sv != nil {
				mfv = reflect.ValueOf(sv)
				switch ft.Type.Kind() {
				case reflect.Struct:
					if svMap, ok := sv.(map[string]interface{}); !ok {
						return fmt.Errorf("failed to convert value to map")
					} else {
						if err := hydrate(svMap, fv.Addr().Interface()); err != nil {
							return fmt.Errorf("failed to hydrate %+v", ft)
						}
						continue
					}
				case reflect.Slice:
					if mfv.Kind() != reflect.Slice {
						return fmt.Errorf("hydrate: while processing '%s': failed to map '%v' to slice: value=%v", ft.Name, mfv.Kind(), fv)
					} else {
						if mfv.Type() != ft.Type {
							tmp := reflect.MakeSlice(ft.Type, mfv.Len(), mfv.Len())
							for j := 0; j < mfv.Len(); j++ {
								p := mfv.Index(j)
								vp := reflect.Indirect(p)
								vpMap := vp.Interface().(map[string]interface{})
								if ft.Type.Elem().Kind() == reflect.Interface {
									if typeName, ok := vpMap["type"].(string); ok {
										if typed, err := makeTyped(typeName); err != nil {
											return err
										} else {
											if err := hydrate(vpMap, typed); err != nil {
												return err
											}
											tmp.Index(j).Set(reflect.ValueOf(typed).Elem())
										}
									} else {
										return fmt.Errorf("trying to unmarshall interface, but no type available")
									}
								} else {
									if err := hydrate(vpMap, tmp.Index(j).Addr().Interface()); err != nil {
										return err
									}
								}
							}
							mfv = tmp
						}
					}
				case reflect.Ptr:
					mfv = mfv.Addr()
				}
			} else {
				mfv = reflect.Zero(ft.Type)
			}

			fv.Set(mfv.Convert(ft.Type))
		}
		return nil
	default:
		return fmt.Errorf("hydrate: unhandled kind: %v", v.Kind())
	}
}
<commit_msg>fix: assign to integer pointers.<commit_after>package suit

import (
	"encoding/json"
	"fmt"
	"reflect"
	"unicode"
	"unicode/utf8"
)

func (o *ConfigurationScreen) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

func (o *ConfigurationScreen) UnmarshalJSON(bytes []byte) error {
	aMap := make(map[string]interface{})
	if err := json.Unmarshal(bytes, &aMap); err != nil {
		return err
	}
	return hydrate(aMap, o)
}

func makeTyped(typeName string) (interface{}, error) {
	switch typeName {
	case "actionList":
		return &ActionList{}, nil
	case "alert":
		return &Alert{}, nil
	case "auto":
		return &AutomaticAction{}, nil
	case "close":
		return &CloseAction{}, nil
	case "inputHidden":
		return &InputHidden{}, nil
	case "inputText":
		return &InputText{}, nil
	case "inputTime":
		return &InputTime{}, nil
	case "inputTimeRange":
		return &InputTimeRange{}, nil
	case "optionGroup":
		return &OptionGroup{}, nil
	case "progressBar":
		return &ProgressBar{}, nil
	case "radioGroup":
		return &RadioGroup{}, nil
	case "reply":
		return &ReplyAction{}, nil
	case "separator":
		return &Separator{}, nil
	case "staticText":
		return &StaticText{}, nil
	default:
		return nil, fmt.Errorf("can't make object for type: %s", typeName)
	}
}

func walk(o interface{}) map[string]interface{} {

	m := make(map[string]interface{})

	if t, ok := o.(Typed); ok {
		m["type"] = t.getType()
	}

	val := reflect.ValueOf(o)

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		val := valueField.Interface()

		if val == nil {
			continue
		}

		valueField = reflect.ValueOf(val)

		if valueField.Kind() == reflect.Ptr && !isZero(valueField) {
			valueField = valueField.Elem()
			val = valueField.Interface()
		}

		switch valueField.Kind() {
		case reflect.Struct:
			val = walk(val)
		case reflect.Slice:
			vals := []interface{}{}
			for i := 0; i < valueField.Len(); i++ {
				if valueField.Index(i).Kind() == reflect.Interface || valueField.Index(i).Kind() == reflect.Struct {
					vals = append(vals, walk(valueField.Index(i).Interface()))
				} else {
					vals = append(vals, valueField.Index(i).Interface())
				}
				val = vals
			}
		default:
			if isZero(valueField) {
				val = nil
			}
		}

		if val != nil {
			m[lF(typeField.Name)] = val
		}
	}

	return m
}

func isZero(valueField reflect.Value) bool {
	return valueField.Interface() == reflect.Zero(valueField.Type()).Interface()
}

func lF(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}

func hydrate(s map[string]interface{}, o interface{}) error {
	v := reflect.ValueOf(o).Elem()
	switch v.Kind() {
	case reflect.Struct:
		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			var mfv reflect.Value
			fv := v.Field(i)
			ft := t.Field(i)

			if sv, ok := s[lF(ft.Name)]; ok && sv != nil {
				mfv = reflect.ValueOf(sv)
				switch ft.Type.Kind() {
				case reflect.Struct:
					if svMap, ok := sv.(map[string]interface{}); !ok {
						return fmt.Errorf("failed to convert value to map")
					} else {
						if err := hydrate(svMap, fv.Addr().Interface()); err != nil {
							return fmt.Errorf("failed to hydrate %+v", ft)
						}
						continue
					}
				case reflect.Slice:
					if mfv.Kind() != reflect.Slice {
						return fmt.Errorf("hydrate: while processing '%s': failed to map '%v' to slice: value=%v", ft.Name, mfv.Kind(), fv)
					} else {
						if mfv.Type() != ft.Type {
							tmp := reflect.MakeSlice(ft.Type, mfv.Len(), mfv.Len())
							for j := 0; j < mfv.Len(); j++ {
								p := mfv.Index(j)
								vp := reflect.Indirect(p)
								vpMap := vp.Interface().(map[string]interface{})
								if ft.Type.Elem().Kind() == reflect.Interface {
									if typeName, ok := vpMap["type"].(string); ok {
										if typed, err := makeTyped(typeName); err != nil {
											return err
										} else {
											if err := hydrate(vpMap, typed); err != nil {
												return err
											}
											tmp.Index(j).Set(reflect.ValueOf(typed).Elem())
										}
									} else {
										return fmt.Errorf("trying to unmarshall interface, but no type available")
									}
								} else {
									if err := hydrate(vpMap, tmp.Index(j).Addr().Interface()); err != nil {
										return err
									}
								}
							}
							mfv = tmp
						}
					}
				case reflect.Ptr:
					nfv := reflect.New(ft.Type.Elem())
					reflect.Indirect(nfv).Set(mfv.Convert(ft.Type.Elem()))
					mfv = nfv
				}
			} else {
				mfv = reflect.Zero(ft.Type)
			}

			fv.Set(mfv.Convert(ft.Type))
		}
		return nil
	default:
		return fmt.Errorf("hydrate: unhandled kind: %v", v.Kind())
	}
}
<|endoftext|>


<commit_before>package operations

import (
	"fmt"
	"github.com/qiniu/qshell/v2/iqshell"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/common/export"
	"github.com/qiniu/qshell/v2/iqshell/common/flow"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/common/utils"
	"github.com/qiniu/qshell/v2/iqshell/storage/object"
	"github.com/qiniu/qshell/v2/iqshell/storage/object/batch"
	"path/filepath"
	"time"
)

type StatusInfo object.StatusApiInfo

func (info *StatusInfo) Check() *data.CodeError {
	if len(info.Bucket) == 0 {
		return alert.CannotEmptyError("Bucket", "")
	}
	if len(info.Key) == 0 {
		return alert.CannotEmptyError("Key", "")
	}
	return nil
}

func Status(cfg *iqshell.Config, info StatusInfo) {
	if shouldContinue := iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{
		Checker: &info,
	}); !shouldContinue {
		return
	}

	result, err := object.Status(object.StatusApiInfo(info))
	if err != nil {
		log.ErrorF("Status Failed, [%s:%s], Error:%v",
			info.Bucket, info.Key, err)
		return
	}

	if result.IsSuccess() {
		log.InfoF("Status Success, [%s:%s]", info.Bucket, info.Key)
		log.Alert(getResultInfo(info.Bucket, info.Key, result))
	}
}

type BatchStatusInfo struct {
	BatchInfo batch.Info
	Bucket    string
}

func (info *BatchStatusInfo) Check() *data.CodeError {
	if err := info.BatchInfo.Check(); err != nil {
		return err
	}

	if len(info.Bucket) == 0 {
		return alert.CannotEmptyError("Bucket", "")
	}
	return nil
}

func BatchStatus(cfg *iqshell.Config, info BatchStatusInfo) {
	cfg.JobPathBuilder = func(cmdPath string) string {
		jobId := utils.Md5Hex(fmt.Sprintf("%s:%s:%s", cfg.CmdCfg.CmdId, info.Bucket, info.BatchInfo.InputFile))
		return filepath.Join(cmdPath, jobId)
	}
	if shouldContinue := iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{
		Checker: &info,
	}); !shouldContinue {
		return
	}

	exporter, err := export.NewFileExport(info.BatchInfo.FileExporterConfig)
	if err != nil {
		log.Error(err)
		return
	}

	batch.NewHandler(info.BatchInfo).EmptyOperation(func() flow.Work {
		return &object.StatusApiInfo{}
	}).ItemsToOperation(func(items []string) (operation batch.Operation, err *data.CodeError) {
		key := items[0]
		if key != "" {
			return &object.StatusApiInfo{
				Bucket: info.Bucket,
				Key:    key,
			}, nil
		}
		return nil, alert.Error("key invalid", "")
	}).OnResult(func(operationInfo string, operation batch.Operation, result *batch.OperationResult) {
		apiInfo, ok := (operation).(*object.StatusApiInfo)
		if !ok {
			exporter.Fail().ExportF("%s%s%d-%s", operationInfo, flow.ErrorSeparate, result.Code, result.Error)
			log.ErrorF("Status Failed, %s, Code: %d, Error: %s", operationInfo, result.Code, result.Error)
			return
		}
		in := (*StatusInfo)(apiInfo)
		if result.IsSuccess() {
			exporter.Success().Export(operationInfo)
			log.AlertF("%s\t%d\t%s\t%s\t%d\t%d",
				in.Key, result.FSize, result.Hash, result.MimeType, result.PutTime, result.Type)
		} else {
			exporter.Fail().ExportF("%s%s%d-%s", operationInfo, flow.ErrorSeparate, result.Code, result.Error)
			log.ErrorF("Status Failed, [%s:%s], Code: %d, Error: %s", in.Bucket, in.Key, result.Code, result.Error)
		}
	}).OnError(func(err *data.CodeError) {
		log.ErrorF("Batch Status error:%v:", err)
	}).Start()
}

func getResultInfo(bucket, key string, status object.StatusResult) string {
	statInfo := fmt.Sprintf("%-20s%s\r\n", "Bucket:", bucket)
	statInfo += fmt.Sprintf("%-20s%s\r\n", "Key:", key)
	statInfo += fmt.Sprintf("%-20s%s\r\n", "FileHash:", status.Hash)
	statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "Fsize:", status.FSize, utils.FormatFileSize(status.FSize))

	putTime := time.Unix(0, status.PutTime*100)
	statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "PutTime:", status.PutTime, putTime.String())
	statInfo += fmt.Sprintf("%-20s%s\r\n", "MimeType:", status.MimeType)

	resoreStatus := ""
	if status.RestoreStatus > 0 {
		if status.RestoreStatus == 1 {
			resoreStatus = "解冻中"
		} else if status.RestoreStatus == 2 {
			resoreStatus = "解冻完成"
		}
	}
	if len(resoreStatus) > 0 {
		statInfo += fmt.Sprintf("%-20s%d(%s)\r\n", "RestoreStatus:", status.RestoreStatus, resoreStatus)
	}

	if status.Expiration > 0 {
		expiration := time.Unix(status.Expiration, 0)
		statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "Expiration:", status.Expiration, expiration.String())
	}

	statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "FileType:", status.Type, getStorageTypeDescription(status.Type))

	return statInfo
}

var objectTypes = []string{"标准存储", "低频存储", "归档存储", "深度归档存储"}

func getStorageTypeDescription(storageType int) string {
	typeString := "未知类型"
	if storageType >= 0 && storageType < len(objectTypes) {
		typeString = objectTypes[storageType]
	}
	return typeString
}
<commit_msg>optimize stat log<commit_after>package operations

import (
	"fmt"
	"github.com/qiniu/qshell/v2/iqshell"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/common/export"
	"github.com/qiniu/qshell/v2/iqshell/common/flow"
	"github.com/qiniu/qshell/v2/iqshell/common/log"
	"github.com/qiniu/qshell/v2/iqshell/common/utils"
	"github.com/qiniu/qshell/v2/iqshell/storage/object"
	"github.com/qiniu/qshell/v2/iqshell/storage/object/batch"
	"path/filepath"
	"time"
)

type StatusInfo object.StatusApiInfo

func (info *StatusInfo) Check() *data.CodeError {
	if len(info.Bucket) == 0 {
		return alert.CannotEmptyError("Bucket", "")
	}
	if len(info.Key) == 0 {
		return alert.CannotEmptyError("Key", "")
	}
	return nil
}

func Status(cfg *iqshell.Config, info StatusInfo) {
	if shouldContinue := iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{
		Checker: &info,
	}); !shouldContinue {
		return
	}

	result, err := object.Status(object.StatusApiInfo(info))
	if err != nil {
		log.ErrorF("Status Failed, [%s:%s], Error:%v",
			info.Bucket, info.Key, err)
		return
	}

	if result.IsSuccess() {
		log.Alert(getResultInfo(info.Bucket, info.Key, result))
	}
}

type BatchStatusInfo struct {
	BatchInfo batch.Info
	Bucket    string
}

func (info *BatchStatusInfo) Check() *data.CodeError {
	if err := info.BatchInfo.Check(); err != nil {
		return err
	}

	if len(info.Bucket) == 0 {
		return alert.CannotEmptyError("Bucket", "")
	}
	return nil
}

func BatchStatus(cfg *iqshell.Config, info BatchStatusInfo) {
	cfg.JobPathBuilder = func(cmdPath string) string {
		jobId := utils.Md5Hex(fmt.Sprintf("%s:%s:%s", cfg.CmdCfg.CmdId, info.Bucket, info.BatchInfo.InputFile))
		return filepath.Join(cmdPath, jobId)
	}
	if shouldContinue := iqshell.CheckAndLoad(cfg, iqshell.CheckAndLoadInfo{
		Checker: &info,
	}); !shouldContinue {
		return
	}

	exporter, err := export.NewFileExport(info.BatchInfo.FileExporterConfig)
	if err != nil {
		log.Error(err)
		return
	}

	batch.NewHandler(info.BatchInfo).EmptyOperation(func() flow.Work {
		return &object.StatusApiInfo{}
	}).ItemsToOperation(func(items []string) (operation batch.Operation, err *data.CodeError) {
		key := items[0]
		if key != "" {
			return &object.StatusApiInfo{
				Bucket: info.Bucket,
				Key:    key,
			}, nil
		}
		return nil, alert.Error("key invalid", "")
	}).OnResult(func(operationInfo string, operation batch.Operation, result *batch.OperationResult) {
		apiInfo, ok := (operation).(*object.StatusApiInfo)
		if !ok {
			exporter.Fail().ExportF("%s%s%d-%s", operationInfo, flow.ErrorSeparate, result.Code, result.Error)
			log.ErrorF("Status Failed, %s, Code: %d, Error: %s", operationInfo, result.Code, result.Error)
			return
		}
		in := (*StatusInfo)(apiInfo)
		if result.IsSuccess() {
			exporter.Success().Export(operationInfo)
			log.AlertF("%s\t%d\t%s\t%s\t%d\t%d",
				in.Key, result.FSize, result.Hash, result.MimeType, result.PutTime, result.Type)
		} else {
			exporter.Fail().ExportF("%s%s%d-%s", operationInfo, flow.ErrorSeparate, result.Code, result.Error)
			log.ErrorF("Status Failed, [%s:%s], Code: %d, Error: %s", in.Bucket, in.Key, result.Code, result.Error)
		}
	}).OnError(func(err *data.CodeError) {
		log.ErrorF("Batch Status error:%v:", err)
	}).Start()
}

func getResultInfo(bucket, key string, status object.StatusResult) string {
	statInfo := fmt.Sprintf("%-20s%s\r\n", "Bucket:", bucket)
	statInfo += fmt.Sprintf("%-20s%s\r\n", "Key:", key)
	statInfo += fmt.Sprintf("%-20s%s\r\n", "FileHash:", status.Hash)
	statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "Fsize:", status.FSize, utils.FormatFileSize(status.FSize))

	putTime := time.Unix(0, status.PutTime*100)
	statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "PutTime:", status.PutTime, putTime.String())
	statInfo += fmt.Sprintf("%-20s%s\r\n", "MimeType:", status.MimeType)

	resoreStatus := ""
	if status.RestoreStatus > 0 {
		if status.RestoreStatus == 1 {
			resoreStatus = "解冻中"
		} else if status.RestoreStatus == 2 {
			resoreStatus = "解冻完成"
		}
	}
	if len(resoreStatus) > 0 {
		statInfo += fmt.Sprintf("%-20s%d(%s)\r\n", "RestoreStatus:", status.RestoreStatus, resoreStatus)
	}

	if status.Expiration > 0 {
		expiration := time.Unix(status.Expiration, 0)
		statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "Expiration:", status.Expiration, expiration.String())
	}

	statInfo += fmt.Sprintf("%-20s%d -> %s\r\n", "FileType:", status.Type, getStorageTypeDescription(status.Type))

	return statInfo
}

var objectTypes = []string{"标准存储", "低频存储", "归档存储", "深度归档存储"}

func getStorageTypeDescription(storageType int) string {
	typeString := "未知类型"
	if storageType >= 0 && storageType < len(objectTypes) {
		typeString = objectTypes[storageType]
	}
	return typeString
}
<|endoftext|>


<commit_before>package data

import (
	"github.com/go-martini/martini"
	"net/http"
	"strconv"
	"strings"
)

type Data map[string]string

func Parser() func(c martini.Context, req *http.Request) {
	return func(c martini.Context, req *http.Request) {
		vals := Data{}
		for key, val := range req.URL.Query() {
			vals[key] = val[0]
		}
		contentType := req.Header.Get("Content-Type")
		if strings.Contains(contentType, "multipart/form-data") {
			if err := req.ParseMultipartForm(2048); err != nil {
				panic(err)
			}
			// convert from map[string][]string to map[string]string
			// the first value for each key is considered the right value
			for key, val := range req.MultipartForm.Value {
				vals[key] = val[0]
			}
		} else if strings.Contains(contentType, "form-urlencoded") {
			if err := req.ParseForm(); err != nil {
				panic(err)
			}
			// convert from map[string][]string to map[string]string
			// the first value for each key is considered the right value
			for key, val := range req.PostForm {
				vals[key] = val[0]
			}
		}
		c.Map(vals)
	}
}

func (d Data) KeyExists(key string) bool {
	_, found := d[key]
	return found
}

func (d Data) Get(key string) string {
	return d[key]
}

func (d Data) GetInt(key string) int {
	str, found := d[key]
	if !found {
		return 0
	} else {
		if result, err := strconv.Atoi(str); err != nil {
			panic(err)
		} else {
			return result
		}
	}
}

func (d Data) GetStrings(key string) []string {
	return strings.Split(d[key], ",")
}

func (d Data) Validator() *Validator {
	return &Validator{
		data: d,
	}
}
<commit_msg>Implement GetBool method for data.Data<commit_after>package data

import (
	"github.com/go-martini/martini"
	"net/http"
	"strconv"
	"strings"
)

type Data map[string]string

func Parser() func(c martini.Context, req *http.Request) {
	return func(c martini.Context, req *http.Request) {
		vals := Data{}
		for key, val := range req.URL.Query() {
			vals[key] = val[0]
		}
		contentType := req.Header.Get("Content-Type")
		if strings.Contains(contentType, "multipart/form-data") {
			if err := req.ParseMultipartForm(2048); err != nil {
				panic(err)
			}
			// convert from map[string][]string to map[string]string
			// the first value for each key is considered the right value
			for key, val := range req.MultipartForm.Value {
				vals[key] = val[0]
			}
		} else if strings.Contains(contentType, "form-urlencoded") {
			if err := req.ParseForm(); err != nil {
				panic(err)
			}
			// convert from map[string][]string to map[string]string
			// the first value for each key is considered the right value
			for key, val := range req.PostForm {
				vals[key] = val[0]
			}
		}
		c.Map(vals)
	}
}

func (d Data) KeyExists(key string) bool {
	_, found := d[key]
	return found
}

func (d Data) Get(key string) string {
	return d[key]
}

func (d Data) GetInt(key string) int {
	str, found := d[key]
	if !found {
		return 0
	} else {
		if result, err := strconv.Atoi(str); err != nil {
			panic(err)
		} else {
			return result
		}
	}
}

func (d Data) GetStrings(key string) []string {
	return strings.Split(d[key], ",")
}

func (d Data) GetBool(key string) bool {
	str, found := d[key]
	if !found {
		return false
	} else {
		if result, err := strconv.ParseBool(str); err != nil {
			panic(err)
		} else {
			return result
		}
	}
}

func (d Data) Validator() *Validator {
	return &Validator{
		data: d,
	}
}
<|endoftext|>