<commit_before>package sched

import (
    "log"
    "net"
    "time"
    "sync"
    "strings"
    "container/list"
    "container/heap"
)

type Sched struct {
    timer      *time.Timer
    grabQueue  *list.List
    jobQueue   *list.List
    entryPoint string
    JobLocker  *sync.Mutex
    Funcs      map[string]*FuncStat
    store      Storer
    jobPQ      map[string]*PriorityQueue
}

type Counter uint

func (c *Counter) Incr() {
    *c = *c + 1
}

func (c *Counter) Decr() {
    *c = *c - 1
}

type FuncStat struct {
    Worker     Counter `json:"worker_count"`
    Job        Counter `json:"job_count"`
    Processing Counter `json:"processing"`
}

func NewSched(entryPoint string, store Storer) *Sched {
    sched := new(Sched)
    sched.timer = time.NewTimer(1 * time.Hour)
    sched.grabQueue = list.New()
    sched.jobQueue = list.New()
    sched.entryPoint = entryPoint
    sched.JobLocker = new(sync.Mutex)
    sched.Funcs = make(map[string]*FuncStat)
    sched.store = store
    sched.jobPQ = make(map[string]*PriorityQueue)
    return sched
}

func (sched *Sched) Serve() {
    parts := strings.SplitN(sched.entryPoint, "://", 2)
    if parts[0] == "unix" {
        sockCheck(parts[1])
    }
    sched.checkJobQueue()
    go sched.handle()
    listen, err := net.Listen(parts[0], parts[1])
    if err != nil {
        log.Fatal(err)
    }
    defer listen.Close()
    log.Printf("huabot-sched started on %s\n", sched.entryPoint)
    for {
        conn, err := listen.Accept()
        if err != nil {
            log.Fatal(err)
        }
        sched.HandleConnection(conn)
    }
}

func (sched *Sched) Notify() {
    sched.timer.Reset(time.Millisecond)
}

func (sched *Sched) DieWorker(worker *Worker) {
    defer sched.Notify()
    sched.removeGrabQueue(worker)
    worker.Close()
}

func (sched *Sched) HandleConnection(conn net.Conn) {
    c := Conn{Conn: conn}
    payload, err := c.Receive()
    if err != nil {
        return
    }
    switch payload[0] {
    case TYPE_CLIENT:
        client := NewClient(sched, c)
        go client.Handle()
        break
    case TYPE_WORKER:
        worker := NewWorker(sched, c)
        go worker.Handle()
        break
    default:
        log.Printf("Unsupport client %d\n", payload[0])
        c.Close()
        break
    }
}

func (sched *Sched) Done(jobId int64) {
    defer sched.Notify()
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    removeListJob(sched.jobQueue, jobId)
    job, err := sched.store.Get(jobId)
    if err == nil {
        sched.store.Delete(jobId)
        sched.DecrStatJob(job)
        sched.DecrStatProc(job)
    }
    return
}

func (sched *Sched) isDoJob(job Job) bool {
    now := time.Now()
    current := int64(now.Unix())
    ret := false
    for e := sched.jobQueue.Front(); e != nil; e = e.Next() {
        chk := e.Value.(Job)
        runAt := chk.RunAt
        if runAt < chk.SchedAt {
            runAt = chk.SchedAt
        }
        if chk.Timeout > 0 && runAt + chk.Timeout < current {
            newJob, _ := sched.store.Get(chk.Id)
            if newJob.Status == JOB_STATUS_PROC {
                sched.DecrStatProc(newJob)
                newJob.Status = JOB_STATUS_READY
                sched.store.Save(newJob)
                sched.pushJobPQ(newJob)
            }
            sched.jobQueue.Remove(e)
            continue
        }
        if chk.Id == job.Id {
            old := e.Value.(Job)
            runAt := old.RunAt
            if runAt < old.SchedAt {
                runAt = old.SchedAt
            }
            if old.Timeout > 0 && runAt + old.Timeout < current {
                ret = false
            } else {
                ret = true
            }
        }
    }
    return ret
}

func (sched *Sched) SubmitJob(worker *Worker, job Job) {
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    if job.Name == "" {
        sched.store.Delete(job.Id)
        return
    }
    if sched.isDoJob(job) {
        return
    }
    if !worker.alive {
        return
    }
    if err := worker.HandleDo(job); err != nil {
        worker.alive = false
        go sched.DieWorker(worker)
        return
    }
    now := time.Now()
    current := int64(now.Unix())
    job.Status = JOB_STATUS_PROC
    job.RunAt = current
    sched.store.Save(job)
    sched.IncrStatProc(job)
    sched.jobQueue.PushBack(job)
    sched.removeGrabQueue(worker)
}

func (sched *Sched) handle() {
    var current time.Time
    var timestamp int64
    for {
        if sched.grabQueue.Len() == 0 {
            sched.timer.Reset(time.Minute)
            current =<-sched.timer.C
            continue
        }

        maybeItem := make(map[string]*Item)
        for Func, pq := range sched.jobPQ {
            if pq.Len() == 0 {
                continue
            }

            item := heap.Pop(pq).(*Item)

            maybeItem[Func] = item

        }

        if len(maybeItem) == 0 {
            sched.timer.Reset(time.Minute)
            current =<-sched.timer.C
            continue
        }

        var lessItem *Item
        var lessFunc string

        for Func, item := range maybeItem {
            if lessItem == nil {
                lessItem = item
                lessFunc = Func
                continue
            }
            if lessItem.priority > item.priority {
                lessItem = item
                lessFunc = Func
            }
        }

        for Func, item := range maybeItem {
            if Func == lessFunc {
                continue
            }
            pq := sched.jobPQ[Func]
            heap.Push(pq, item)
        }

        schedJob, err := sched.store.Get(lessItem.value)

        if err != nil {
            continue
        }

        pq := sched.jobPQ[schedJob.Func]
        timestamp = int64(time.Now().Unix())

        if schedJob.SchedAt > timestamp {
            sched.timer.Reset(time.Second * time.Duration(schedJob.SchedAt - timestamp))
            current =<-sched.timer.C
            timestamp = int64(current.Unix())
            if schedJob.SchedAt > timestamp {
                heap.Push(pq, lessItem)
                continue
            }
        }

        isSubmited := false
        for e := sched.grabQueue.Front(); e != nil; e = e.Next() {
            worker := e.Value.(*Worker)
            for _, Func := range worker.Funcs {
                if schedJob.Func == Func {
                    sched.SubmitJob(worker, schedJob)
                    isSubmited = true
                    break
                }
            }
            if isSubmited {
                break
            }
        }

        if !isSubmited {
            sched.DecrStatFunc(schedJob.Func)
        }
    }
}

func (sched *Sched) Fail(jobId int64) {
    defer sched.Notify()
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    removeListJob(sched.jobQueue, jobId)
    job, _ := sched.store.Get(jobId)
    sched.DecrStatProc(job)
    job.Status = JOB_STATUS_READY
    sched.store.Save(job)
    sched.pushJobPQ(job)
    return
}

func (sched *Sched) IncrStatFunc(Func string) {
    stat, ok := sched.Funcs[Func]
    if !ok {
        stat = new(FuncStat)
        sched.Funcs[Func] = stat
    }
    stat.Worker.Incr()
}

func (sched *Sched) DecrStatFunc(Func string) {
    stat, ok := sched.Funcs[Func]
    if ok {
        stat.Worker.Decr()
    }
}

func (sched *Sched) IncrStatJob(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if !ok {
        stat = new(FuncStat)
        sched.Funcs[job.Func] = stat
    }
    stat.Job.Incr()
}

func (sched *Sched) DecrStatJob(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if ok {
        stat.Job.Decr()
    }
}

func (sched *Sched) IncrStatProc(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if !ok {
        stat = new(FuncStat)
        sched.Funcs[job.Func] = stat
    }
    if job.Status == JOB_STATUS_PROC {
        stat.Processing.Incr()
    }
}

func (sched *Sched) DecrStatProc(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if ok && job.Status == JOB_STATUS_PROC {
        stat.Processing.Decr()
    }
}

func (sched *Sched) SchedLater(jobId int64, delay int64) {
    defer sched.Notify()
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    removeListJob(sched.jobQueue, jobId)
    job, _ := sched.store.Get(jobId)
    sched.DecrStatProc(job)
    job.Status = JOB_STATUS_READY
    var now = time.Now()
    job.SchedAt = int64(now.Unix()) + delay
    sched.store.Save(job)
    sched.pushJobPQ(job)
    return
}

func (sched *Sched) removeGrabQueue(worker *Worker) {
    for e := sched.grabQueue.Front(); e != nil; e = e.Next() {
        if e.Value.(*Worker) == worker {
            sched.grabQueue.Remove(e)
        }
    }
}

func (sched *Sched) pushJobPQ(job Job) bool {
    if job.Status == JOB_STATUS_READY {
        pq, ok := sched.jobPQ[job.Func]
        if !ok {
            pq1 := make(PriorityQueue, 0)
            pq = &pq1
            sched.jobPQ[job.Func] = pq
            heap.Init(pq)
        }
        item := &Item{
            value: job.Id,
            priority: job.SchedAt,
        }
        heap.Push(pq, item)
        return true
    }
    return false
}

func (sched *Sched) checkJobQueue() {
    updateQueue := make([]Job, 0)
    removeQueue := make([]Job, 0)
    var now = time.Now()
    current := int64(now.Unix())

    iter := sched.store.NewIterator(nil)
    for {
        if !iter.Next() {
            break
        }
        job := iter.Value()
        if job.Name == "" {
            removeQueue = append(removeQueue, job)
            continue
        }
        sched.IncrStatJob(job)
        sched.pushJobPQ(job)
        runAt := job.RunAt
        if runAt < job.SchedAt {
            runAt = job.SchedAt
        }
        if runAt + job.Timeout < current {
            updateQueue = append(updateQueue, job)
        } else {
            sched.jobQueue.PushBack(job)
            sched.IncrStatProc(job)
        }
    }

    iter.Close()

    for _, job := range updateQueue {
        job.Status = JOB_STATUS_READY
        sched.store.Save(job)
    }

    for _, job := range removeQueue {
        sched.store.Delete(job.Id)
    }
}

func (sched *Sched) Close() {
}
<commit_msg>also pushJobPQ when the job is unsubmit<commit_after>package sched

import (
    "log"
    "net"
    "time"
    "sync"
    "strings"
    "container/list"
    "container/heap"
)

type Sched struct {
    timer      *time.Timer
    grabQueue  *list.List
    jobQueue   *list.List
    entryPoint string
    JobLocker  *sync.Mutex
    Funcs      map[string]*FuncStat
    store      Storer
    jobPQ      map[string]*PriorityQueue
}

type Counter uint

func (c *Counter) Incr() {
    *c = *c + 1
}

func (c *Counter) Decr() {
    *c = *c - 1
}

type FuncStat struct {
    Worker     Counter `json:"worker_count"`
    Job        Counter `json:"job_count"`
    Processing Counter `json:"processing"`
}

func NewSched(entryPoint string, store Storer) *Sched {
    sched := new(Sched)
    sched.timer = time.NewTimer(1 * time.Hour)
    sched.grabQueue = list.New()
    sched.jobQueue = list.New()
    sched.entryPoint = entryPoint
    sched.JobLocker = new(sync.Mutex)
    sched.Funcs = make(map[string]*FuncStat)
    sched.store = store
    sched.jobPQ = make(map[string]*PriorityQueue)
    return sched
}

func (sched *Sched) Serve() {
    parts := strings.SplitN(sched.entryPoint, "://", 2)
    if parts[0] == "unix" {
        sockCheck(parts[1])
    }
    sched.checkJobQueue()
    go sched.handle()
    listen, err := net.Listen(parts[0], parts[1])
    if err != nil {
        log.Fatal(err)
    }
    defer listen.Close()
    log.Printf("huabot-sched started on %s\n", sched.entryPoint)
    for {
        conn, err := listen.Accept()
        if err != nil {
            log.Fatal(err)
        }
        sched.HandleConnection(conn)
    }
}

func (sched *Sched) Notify() {
    sched.timer.Reset(time.Millisecond)
}

func (sched *Sched) DieWorker(worker *Worker) {
    defer sched.Notify()
    sched.removeGrabQueue(worker)
    worker.Close()
}

func (sched *Sched) HandleConnection(conn net.Conn) {
    c := Conn{Conn: conn}
    payload, err := c.Receive()
    if err != nil {
        return
    }
    switch payload[0] {
    case TYPE_CLIENT:
        client := NewClient(sched, c)
        go client.Handle()
        break
    case TYPE_WORKER:
        worker := NewWorker(sched, c)
        go worker.Handle()
        break
    default:
        log.Printf("Unsupport client %d\n", payload[0])
        c.Close()
        break
    }
}

func (sched *Sched) Done(jobId int64) {
    defer sched.Notify()
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    removeListJob(sched.jobQueue, jobId)
    job, err := sched.store.Get(jobId)
    if err == nil {
        sched.store.Delete(jobId)
        sched.DecrStatJob(job)
        sched.DecrStatProc(job)
    }
    return
}

func (sched *Sched) isDoJob(job Job) bool {
    now := time.Now()
    current := int64(now.Unix())
    ret := false
    for e := sched.jobQueue.Front(); e != nil; e = e.Next() {
        chk := e.Value.(Job)
        runAt := chk.RunAt
        if runAt < chk.SchedAt {
            runAt = chk.SchedAt
        }
        if chk.Timeout > 0 && runAt + chk.Timeout < current {
            newJob, _ := sched.store.Get(chk.Id)
            if newJob.Status == JOB_STATUS_PROC {
                sched.DecrStatProc(newJob)
                newJob.Status = JOB_STATUS_READY
                sched.store.Save(newJob)
                sched.pushJobPQ(newJob)
            }
            sched.jobQueue.Remove(e)
            continue
        }
        if chk.Id == job.Id {
            old := e.Value.(Job)
            runAt := old.RunAt
            if runAt < old.SchedAt {
                runAt = old.SchedAt
            }
            if old.Timeout > 0 && runAt + old.Timeout < current {
                ret = false
            } else {
                ret = true
            }
        }
    }
    return ret
}

func (sched *Sched) SubmitJob(worker *Worker, job Job) {
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    if job.Name == "" {
        sched.store.Delete(job.Id)
        return
    }
    if sched.isDoJob(job) {
        return
    }
    if !worker.alive {
        return
    }
    if err := worker.HandleDo(job); err != nil {
        worker.alive = false
        go sched.DieWorker(worker)
        return
    }
    now := time.Now()
    current := int64(now.Unix())
    job.Status = JOB_STATUS_PROC
    job.RunAt = current
    sched.store.Save(job)
    sched.IncrStatProc(job)
    sched.jobQueue.PushBack(job)
    sched.removeGrabQueue(worker)
}

func (sched *Sched) handle() {
    var current time.Time
    var timestamp int64
    for {
        if sched.grabQueue.Len() == 0 {
            sched.timer.Reset(time.Minute)
            current =<-sched.timer.C
            continue
        }

        maybeItem := make(map[string]*Item)
        for Func, pq := range sched.jobPQ {
            if pq.Len() == 0 {
                continue
            }

            item := heap.Pop(pq).(*Item)

            maybeItem[Func] = item

        }

        if len(maybeItem) == 0 {
            sched.timer.Reset(time.Minute)
            current =<-sched.timer.C
            continue
        }

        var lessItem *Item
        var lessFunc string

        for Func, item := range maybeItem {
            if lessItem == nil {
                lessItem = item
                lessFunc = Func
                continue
            }
            if lessItem.priority > item.priority {
                lessItem = item
                lessFunc = Func
            }
        }

        for Func, item := range maybeItem {
            if Func == lessFunc {
                continue
            }
            pq := sched.jobPQ[Func]
            heap.Push(pq, item)
        }

        schedJob, err := sched.store.Get(lessItem.value)

        if err != nil {
            continue
        }

        timestamp = int64(time.Now().Unix())

        if schedJob.SchedAt > timestamp {
            sched.timer.Reset(time.Second * time.Duration(schedJob.SchedAt - timestamp))
            current =<-sched.timer.C
            timestamp = int64(current.Unix())
            if schedJob.SchedAt > timestamp {
                sched.pushJobPQ(schedJob)
                continue
            }
        }

        isSubmited := false
        for e := sched.grabQueue.Front(); e != nil; e = e.Next() {
            worker := e.Value.(*Worker)
            for _, Func := range worker.Funcs {
                if schedJob.Func == Func {
                    sched.SubmitJob(worker, schedJob)
                    isSubmited = true
                    break
                }
            }
            if isSubmited {
                break
            }
        }

        if !isSubmited {
            sched.DecrStatFunc(schedJob.Func)
            sched.pushJobPQ(schedJob)
        }
    }
}

func (sched *Sched) Fail(jobId int64) {
    defer sched.Notify()
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    removeListJob(sched.jobQueue, jobId)
    job, _ := sched.store.Get(jobId)
    sched.DecrStatProc(job)
    job.Status = JOB_STATUS_READY
    sched.store.Save(job)
    sched.pushJobPQ(job)
    return
}

func (sched *Sched) IncrStatFunc(Func string) {
    stat, ok := sched.Funcs[Func]
    if !ok {
        stat = new(FuncStat)
        sched.Funcs[Func] = stat
    }
    stat.Worker.Incr()
}

func (sched *Sched) DecrStatFunc(Func string) {
    stat, ok := sched.Funcs[Func]
    if ok {
        stat.Worker.Decr()
    }
}

func (sched *Sched) IncrStatJob(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if !ok {
        stat = new(FuncStat)
        sched.Funcs[job.Func] = stat
    }
    stat.Job.Incr()
}

func (sched *Sched) DecrStatJob(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if ok {
        stat.Job.Decr()
    }
}

func (sched *Sched) IncrStatProc(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if !ok {
        stat = new(FuncStat)
        sched.Funcs[job.Func] = stat
    }
    if job.Status == JOB_STATUS_PROC {
        stat.Processing.Incr()
    }
}

func (sched *Sched) DecrStatProc(job Job) {
    stat, ok := sched.Funcs[job.Func]
    if ok && job.Status == JOB_STATUS_PROC {
        stat.Processing.Decr()
    }
}

func (sched *Sched) SchedLater(jobId int64, delay int64) {
    defer sched.Notify()
    defer sched.JobLocker.Unlock()
    sched.JobLocker.Lock()
    removeListJob(sched.jobQueue, jobId)
    job, _ := sched.store.Get(jobId)
    sched.DecrStatProc(job)
    job.Status = JOB_STATUS_READY
    var now = time.Now()
    job.SchedAt = int64(now.Unix()) + delay
    sched.store.Save(job)
    sched.pushJobPQ(job)
    return
}

func (sched *Sched) removeGrabQueue(worker *Worker) {
    for e := sched.grabQueue.Front(); e != nil; e = e.Next() {
        if e.Value.(*Worker) == worker {
            sched.grabQueue.Remove(e)
        }
    }
}

func (sched *Sched) pushJobPQ(job Job) bool {
    if job.Status == JOB_STATUS_READY {
        pq, ok := sched.jobPQ[job.Func]
        if !ok {
            pq1 := make(PriorityQueue, 0)
            pq = &pq1
            sched.jobPQ[job.Func] = pq
            heap.Init(pq)
        }
        item := &Item{
            value: job.Id,
            priority: job.SchedAt,
        }
        heap.Push(pq, item)
        return true
    }
    return false
}

func (sched *Sched) checkJobQueue() {
    updateQueue := make([]Job, 0)
    removeQueue := make([]Job, 0)
    var now = time.Now()
    current := int64(now.Unix())

    iter := sched.store.NewIterator(nil)
    for {
        if !iter.Next() {
            break
        }
        job := iter.Value()
        if job.Name == "" {
            removeQueue = append(removeQueue, job)
            continue
        }
        sched.IncrStatJob(job)
        sched.pushJobPQ(job)
        runAt := job.RunAt
        if runAt < job.SchedAt {
            runAt = job.SchedAt
        }
        if runAt + job.Timeout < current {
            updateQueue = append(updateQueue, job)
        } else {
            sched.jobQueue.PushBack(job)
            sched.IncrStatProc(job)
        }
    }

    iter.Close()

    for _, job := range updateQueue {
        job.Status = JOB_STATUS_READY
        sched.store.Save(job)
    }

    for _, job := range removeQueue {
        sched.store.Delete(job.Id)
    }
}

func (sched *Sched) Close() {
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/keybase/go-libkb"
)

type CmdId struct {
	user      string
	assertion string
}

func (v *CmdId) ParseArgv(ctx *cli.Context) error {
	nargs := len(ctx.Args())
	var err error
	if nargs == 1 {
		v.user = ctx.Args()[0]
	} else {
		err = fmt.Errorf("id takes one arg -- the user to lookup")
	}
	return err
}

func (v *CmdId) Run() error {
	u, err := libkb.LoadUser(libkb.LoadUserArg{
		Name:             v.user,
		RequirePublicKey: true,
		Self:             (len(v.user) == 0),
		LoadSecrets:      false,
		ForceReload:      false,
		SkipVerify:       false,
	})

	if err == nil {
		err = u.Identify()
	}
	return err
}

func NewCmdId(cl *CommandLine) cli.Command {
	return cli.Command{
		Name:        "id",
		Usage:       "keybase id <username>",
		Description: "identify a user and check their proofs",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "assert, a",
				Usage: "a boolean expression on this identity",
			},
		},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdId{}, "id", c)
		},
	}
}

func (v *CmdId) UseConfig() bool   { return true }
func (v *CmdId) UseKeyring() bool  { return true }
func (v *CmdId) UseAPI() bool      { return true }
func (v *CmdId) UseTerminal() bool { return false }
<commit_msg>new debugging stuff for generating a tracking statement<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/keybase/go-libkb"
)

type CmdId struct {
	user      string
	assertion string
	track     bool
}

func (v *CmdId) ParseArgv(ctx *cli.Context) error {
	nargs := len(ctx.Args())
	var err error
	v.track = ctx.Bool("track-statement")
	if nargs == 1 {
		v.user = ctx.Args()[0]
	} else {
		err = fmt.Errorf("id takes one arg -- the user to lookup")
	}
	return err
}

func (v *CmdId) Run() error {
	u, err := libkb.LoadUser(libkb.LoadUserArg{
		Name:             v.user,
		RequirePublicKey: true,
		Self:             (len(v.user) == 0),
		LoadSecrets:      false,
		ForceReload:      false,
		SkipVerify:       false,
	})

	if err == nil {
		err = u.Identify()
	}

	if v.track {
		fmt.Println(u.IdTable.ToTrackingStatement().MarshalPretty())
	}

	return err
}

func NewCmdId(cl *CommandLine) cli.Command {
	return cli.Command{
		Name:        "id",
		Usage:       "keybase id <username>",
		Description: "identify a user and check their proofs",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "assert, a",
				Usage: "a boolean expression on this identity",
			},
			cli.BoolFlag{
				Name:  "t, track-statement",
				Usage: "output a JSON a track statement for this user",
			},
		},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdId{}, "id", c)
		},
	}
}

func (v *CmdId) UseConfig() bool   { return true }
func (v *CmdId) UseKeyring() bool  { return true }
func (v *CmdId) UseAPI() bool      { return true }
func (v *CmdId) UseTerminal() bool { return false }
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/nsf/termbox-go"
)

const gAnsiColorResetMask = termbox.AttrBold | termbox.AttrUnderline | termbox.AttrReverse

var gAnsiCodes = map[int]termbox.Attribute{
	0:  termbox.ColorDefault,
	1:  termbox.AttrBold,
	4:  termbox.AttrUnderline,
	7:  termbox.AttrReverse,
	30: termbox.ColorBlack,
	31: termbox.ColorRed,
	32: termbox.ColorGreen,
	33: termbox.ColorYellow,
	34: termbox.ColorBlue,
	35: termbox.ColorMagenta,
	36: termbox.ColorCyan,
	37: termbox.ColorWhite,
	40: termbox.ColorBlack,
	41: termbox.ColorRed,
	42: termbox.ColorGreen,
	43: termbox.ColorYellow,
	44: termbox.ColorBlue,
	45: termbox.ColorMagenta,
	46: termbox.ColorCyan,
	47: termbox.ColorWhite,
}

type colorEntry struct {
	fg termbox.Attribute
	bg termbox.Attribute
}

type colorMap map[string]colorEntry

func parseColors() colorMap {
	if env := os.Getenv("LS_COLORS"); env != "" {
		return parseColorsGNU(env)
	}

	if env := os.Getenv("LSCOLORS"); env != "" {
		return parseColorsBSD(env)
	}

	// default values from dircolors with removed background colors
	defaultColors := []string{
		// "rs=0",
		"di=01;34",
		"ln=01;36",
		// "mh=00",
		"pi=33", // "pi=40;33",
		"so=01;35",
		"do=01;35",
		"bd=33;01", // "bd=40;33;01",
		"cd=33;01", // "cd=40;33;01",
		"or=31;01", // "or=40;31;01",
		// "mi=00",
		"su=01;32", // "su=37;41",
		"sg=01;32", // "sg=30;43",
		// "ca=30;41",
		"tw=01;34", // "tw=30;42",
		"ow=01;34", // "ow=34;42",
		"st=01;34", // "st=37;44",
		"ex=01;32",
	}

	return parseColorsGNU(strings.Join(defaultColors, ":"))
}

func applyAnsiCodes(s string, fg, bg termbox.Attribute) (termbox.Attribute, termbox.Attribute) {
	toks := strings.Split(s, ";")

	var nums []int
	for _, tok := range toks {
		if tok == "" {
			nums = append(nums, 0)
			continue
		}
		n, err := strconv.Atoi(tok)
		if err != nil {
			log.Printf("converting escape code: %s", err)
			continue
		}
		nums = append(nums, n)
	}

	for i := 0; i < len(nums); i++ {
		n := nums[i]
		switch {
		case n == 38 && i+2 < len(nums) && nums[i+1] == 5:
			fg &= gAnsiColorResetMask
			fg |= termbox.Attribute(nums[i+2] + 1)
			i += 2
			continue
		case n == 48 && i+2 < len(nums) && nums[i+1] == 5:
			bg = termbox.Attribute(nums[i+2] + 1)
			i += 2
			continue
		}
		attr, ok := gAnsiCodes[n]
		if !ok {
			log.Printf("unknown ansi code: %d", n)
			continue
		}
		switch {
		case n == 0:
			fg, bg = attr, attr
		case n == 1 || n == 4 || n == 7:
			fg |= attr
		case 30 <= n && n <= 37:
			fg &= gAnsiColorResetMask
			fg |= attr
		case 40 <= n && n <= 47:
			bg = attr
		}
	}

	return fg, bg
}

// This function parses $LS_COLORS environment variable.
func parseColorsGNU(env string) colorMap {
	colors := make(colorMap)

	entries := strings.Split(env, ":")
	for _, entry := range entries {
		if entry == "" {
			continue
		}
		pair := strings.Split(entry, "=")
		if len(pair) != 2 {
			log.Printf("invalid $LS_COLORS entry: %s", entry)
			return colors
		}
		key, val := pair[0], pair[1]
		fg, bg := applyAnsiCodes(val, termbox.ColorDefault, termbox.ColorDefault)
		colors[key] = colorEntry{fg: fg, bg: bg}
	}

	return colors
}

// This function parses $LSCOLORS environment variable.
func parseColorsBSD(env string) colorMap {
	colors := make(colorMap)

	if len(env) != 22 {
		log.Printf("invalid $LSCOLORS variable: %s", env)
		return colors
	}

	colorNames := []string{"di", "ln", "so", "pi", "ex", "bd", "cd", "su", "sg", "tw", "ow"}
	colorCodes := map[byte]termbox.Attribute{
		'a': termbox.ColorBlack,
		'b': termbox.ColorRed,
		'c': termbox.ColorGreen,
		'd': termbox.ColorYellow, // brown
		'e': termbox.ColorBlue,
		'f': termbox.ColorMagenta,
		'g': termbox.ColorCyan,
		'h': termbox.ColorWhite, // light grey
		'A': termbox.AttrBold | termbox.ColorBlack,
		'B': termbox.AttrBold | termbox.ColorRed,
		'C': termbox.AttrBold | termbox.ColorGreen,
		'D': termbox.AttrBold | termbox.ColorYellow, // brown
		'E': termbox.AttrBold | termbox.ColorBlue,
		'F': termbox.AttrBold | termbox.ColorMagenta,
		'G': termbox.AttrBold | termbox.ColorCyan,
		'H': termbox.AttrBold | termbox.ColorWhite, // light grey
		'x': termbox.ColorDefault,
	}

	getColor := func(r byte) termbox.Attribute {
		if color, ok := colorCodes[r]; ok {
			return color
		}

		log.Printf("invalid $LSCOLORS entry: %c", r)
		return termbox.ColorDefault
	}

	for i, key := range colorNames {
		colors[key] = colorEntry{fg: getColor(env[i*2]), bg: getColor(env[i*2+1])}
	}

	return colors
}

func (cm colorMap) get(f *file) (termbox.Attribute, termbox.Attribute) {
	var key string

	switch {
	case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0:
		key = "tw"
	case f.IsDir() && f.Mode()&os.ModeSticky != 0:
		key = "st"
	case f.IsDir() && f.Mode()&0002 != 0:
		key = "ow"
	case f.IsDir():
		key = "di"
	case f.linkState == working:
		key = "ln"
	case f.linkState == broken:
		key = "or"
	case f.Mode()&os.ModeNamedPipe != 0:
		key = "pi"
	case f.Mode()&os.ModeSocket != 0:
		key = "so"
	case f.Mode()&os.ModeCharDevice != 0:
		key = "cd"
	case f.Mode()&os.ModeDevice != 0:
		key = "bd"
	case f.Mode()&os.ModeSetuid != 0:
		key = "su"
	case f.Mode()&os.ModeSetgid != 0:
		key = "sg"
	case f.Mode().IsRegular() && f.Mode()&0111 != 0:
		key = "ex"
	default:
		key = "*" + filepath.Ext(f.Name())
	}

	if val, ok := cm[key]; ok {
		return val.fg, val.bg
	}

	if val, ok := cm["fi"]; ok {
		return val.fg, val.bg
	}

	return termbox.ColorDefault, termbox.ColorDefault
}
<commit_msg>Use symlink color for directories (#195)<commit_after>package main

import (
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/nsf/termbox-go"
)

const gAnsiColorResetMask = termbox.AttrBold | termbox.AttrUnderline | termbox.AttrReverse

var gAnsiCodes = map[int]termbox.Attribute{
	0:  termbox.ColorDefault,
	1:  termbox.AttrBold,
	4:  termbox.AttrUnderline,
	7:  termbox.AttrReverse,
	30: termbox.ColorBlack,
	31: termbox.ColorRed,
	32: termbox.ColorGreen,
	33: termbox.ColorYellow,
	34: termbox.ColorBlue,
	35: termbox.ColorMagenta,
	36: termbox.ColorCyan,
	37: termbox.ColorWhite,
	40: termbox.ColorBlack,
	41: termbox.ColorRed,
	42: termbox.ColorGreen,
	43: termbox.ColorYellow,
	44: termbox.ColorBlue,
	45: termbox.ColorMagenta,
	46: termbox.ColorCyan,
	47: termbox.ColorWhite,
}

type colorEntry struct {
	fg termbox.Attribute
	bg termbox.Attribute
}

type colorMap map[string]colorEntry

func parseColors() colorMap {
	if env := os.Getenv("LS_COLORS"); env != "" {
		return parseColorsGNU(env)
	}

	if env := os.Getenv("LSCOLORS"); env != "" {
		return parseColorsBSD(env)
	}

	// default values from dircolors with removed background colors
	defaultColors := []string{
		// "rs=0",
		"di=01;34",
		"ln=01;36",
		// "mh=00",
		"pi=33", // "pi=40;33",
		"so=01;35",
		"do=01;35",
		"bd=33;01", // "bd=40;33;01",
		"cd=33;01", // "cd=40;33;01",
		"or=31;01", // "or=40;31;01",
		// "mi=00",
		"su=01;32", // "su=37;41",
		"sg=01;32", // "sg=30;43",
		// "ca=30;41",
		"tw=01;34", // "tw=30;42",
		"ow=01;34", // "ow=34;42",
		"st=01;34", // "st=37;44",
		"ex=01;32",
	}

	return parseColorsGNU(strings.Join(defaultColors, ":"))
}

func applyAnsiCodes(s string, fg, bg termbox.Attribute) (termbox.Attribute, termbox.Attribute) {
	toks := strings.Split(s, ";")

	var nums []int
	for _, tok := range toks {
		if tok == "" {
			nums = append(nums, 0)
			continue
		}
		n, err := strconv.Atoi(tok)
		if err != nil {
			log.Printf("converting escape code: %s", err)
			continue
		}
		nums = append(nums, n)
	}

	for i := 0; i < len(nums); i++ {
		n := nums[i]
		switch {
		case n == 38 && i+2 < len(nums) && nums[i+1] == 5:
			fg &= gAnsiColorResetMask
			fg |= termbox.Attribute(nums[i+2] + 1)
			i += 2
			continue
		case n == 48 && i+2 < len(nums) && nums[i+1] == 5:
			bg = termbox.Attribute(nums[i+2] + 1)
			i += 2
			continue
		}
		attr, ok := gAnsiCodes[n]
		if !ok {
			log.Printf("unknown ansi code: %d", n)
			continue
		}
		switch {
		case n == 0:
			fg, bg = attr, attr
		case n == 1 || n == 4 || n == 7:
			fg |= attr
		case 30 <= n && n <= 37:
			fg &= gAnsiColorResetMask
			fg |= attr
		case 40 <= n && n <= 47:
			bg = attr
		}
	}

	return fg, bg
}

// This function parses $LS_COLORS environment variable.
func parseColorsGNU(env string) colorMap {
	colors := make(colorMap)

	entries := strings.Split(env, ":")
	for _, entry := range entries {
		if entry == "" {
			continue
		}
		pair := strings.Split(entry, "=")
		if len(pair) != 2 {
			log.Printf("invalid $LS_COLORS entry: %s", entry)
			return colors
		}
		key, val := pair[0], pair[1]
		fg, bg := applyAnsiCodes(val, termbox.ColorDefault, termbox.ColorDefault)
		colors[key] = colorEntry{fg: fg, bg: bg}
	}

	return colors
}

// This function parses $LSCOLORS environment variable.
func parseColorsBSD(env string) colorMap {
	colors := make(colorMap)

	if len(env) != 22 {
		log.Printf("invalid $LSCOLORS variable: %s", env)
		return colors
	}

	colorNames := []string{"di", "ln", "so", "pi", "ex", "bd", "cd", "su", "sg", "tw", "ow"}
	colorCodes := map[byte]termbox.Attribute{
		'a': termbox.ColorBlack,
		'b': termbox.ColorRed,
		'c': termbox.ColorGreen,
		'd': termbox.ColorYellow, // brown
		'e': termbox.ColorBlue,
		'f': termbox.ColorMagenta,
		'g': termbox.ColorCyan,
		'h': termbox.ColorWhite, // light grey
		'A': termbox.AttrBold | termbox.ColorBlack,
		'B': termbox.AttrBold | termbox.ColorRed,
		'C': termbox.AttrBold | termbox.ColorGreen,
		'D': termbox.AttrBold | termbox.ColorYellow, // brown
		'E': termbox.AttrBold | termbox.ColorBlue,
		'F': termbox.AttrBold | termbox.ColorMagenta,
		'G': termbox.AttrBold | termbox.ColorCyan,
		'H': termbox.AttrBold | termbox.ColorWhite, // light grey
		'x': termbox.ColorDefault,
	}

	getColor := func(r byte) termbox.Attribute {
		if color, ok := colorCodes[r]; ok {
			return color
		}

		log.Printf("invalid $LSCOLORS entry: %c", r)
		return termbox.ColorDefault
	}

	for i, key := range colorNames {
		colors[key] = colorEntry{fg: getColor(env[i*2]), bg: getColor(env[i*2+1])}
	}

	return colors
}

func (cm colorMap) get(f *file) (termbox.Attribute, termbox.Attribute) {
	var key string

	switch {
	case f.linkState == working:
		key = "ln"
	case f.linkState == broken:
		key = "or"
	case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0:
		key = "tw"
	case f.IsDir() && f.Mode()&os.ModeSticky != 0:
		key = "st"
	case f.IsDir() && f.Mode()&0002 != 0:
		key = "ow"
	case f.IsDir():
		key = "di"
	case f.Mode()&os.ModeNamedPipe != 0:
		key = "pi"
	case f.Mode()&os.ModeSocket != 0:
		key = "so"
	case f.Mode()&os.ModeCharDevice != 0:
		key = "cd"
	case f.Mode()&os.ModeDevice != 0:
		key = "bd"
	case f.Mode()&os.ModeSetuid != 0:
		key = "su"
	case f.Mode()&os.ModeSetgid != 0:
		key = "sg"
	case f.Mode().IsRegular() && f.Mode()&0111 != 0:
		key = "ex"
	default:
		key = "*" + filepath.Ext(f.Name())
	}

	if val, ok := cm[key]; ok {
		return val.fg, val.bg
	}

	if val, ok := cm["fi"]; ok {
		return val.fg, val.bg
	}

	return termbox.ColorDefault, termbox.ColorDefault
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"reflect"
	"strings"
)

const (
	TWOSIDES = iota + 1
	ONLYTODB
	ONLYFROMDB
)

// database column
type Column struct {
	Name            string
	FieldName       string
	SQLType         SQLType
	Length          int
	Length2         int
	Nullable        bool
	Default         string
	Indexes         map[string]bool
	IsPrimaryKey    bool
	IsAutoIncrement bool
	MapType         int
	IsCreated       bool
	IsUpdated       bool
	IsDeleted       bool
	IsCascade       bool
	IsVersion       bool
	fieldPath       []string
	DefaultIsEmpty  bool
	EnumOptions     map[string]int
	SetOptions      map[string]int
}

func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
	return &Column{
		Name:            name,
		FieldName:       fieldName,
		SQLType:         sqlType,
		Length:          len1,
		Length2:         len2,
		Nullable:        nullable,
		Default:         "",
		Indexes:         make(map[string]bool),
		IsPrimaryKey:    false,
		IsAutoIncrement: false,
		MapType:         TWOSIDES,
		IsCreated:       false,
		IsUpdated:       false,
		IsDeleted:       false,
		IsCascade:       false,
		IsVersion:       false,
		fieldPath:       nil,
		DefaultIsEmpty:  false,
		EnumOptions:     make(map[string]int),
	}
}

// generate column description string according dialect
func (col *Column) String(d Dialect) string {
	sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "

	sql += d.SqlType(col) + " "

	if col.IsPrimaryKey {
		sql += "PRIMARY KEY "
		if col.IsAutoIncrement {
			sql += d.AutoIncrStr() + " "
		}
	}

	if d.ShowCreateNull() {
		if col.Nullable {
			sql += "NULL "
		} else {
			sql += "NOT NULL "
		}
	}

	if col.Default != "" {
		sql += "DEFAULT " + col.Default + " "
	}

	return sql
}

func (col *Column) StringNoPk(d Dialect) string {
	sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "

	sql += d.SqlType(col) + " "

	if d.ShowCreateNull() {
		if col.Nullable {
			sql += "NULL "
		} else {
			sql += "NOT NULL "
		}
	}

	if col.Default != "" {
		sql += "DEFAULT " + col.Default + " "
	}

	return sql
}

// return col's filed of struct's value
func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) {
	dataStruct := reflect.Indirect(reflect.ValueOf(bean))
	return col.ValueOfV(&dataStruct)
}

func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
	var fieldValue reflect.Value
	if col.fieldPath == nil {
		col.fieldPath = strings.Split(col.FieldName, ".")
	}

	if dataStruct.Type().Kind() == reflect.Map {
		keyValue := reflect.ValueOf(col.fieldPath[len(col.fieldPath)-1])
		fieldValue = dataStruct.MapIndex(keyValue)
		return &fieldValue, nil
	}

	level := len(col.fieldPath)
	fieldValue = dataStruct.FieldByName(col.fieldPath[0])
	for i := 0; i < level-1; i++ {
		if !fieldValue.IsValid() {
			break
		}
		if fieldValue.Kind() == reflect.Struct {
			fieldValue = fieldValue.FieldByName(col.fieldPath[i+1])
		} else if fieldValue.Kind() == reflect.Ptr {
			if fieldValue.IsNil() {
				fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
			}
			fieldValue = fieldValue.Elem().FieldByName(col.fieldPath[i+1])
		} else {
			return nil, fmt.Errorf("field  %v is not valid", col.FieldName)
		}
	}

	if !fieldValue.IsValid() {
		return nil, fmt.Errorf("field  %v is not valid", col.FieldName)
	}

	return &fieldValue, nil
}
<commit_msg>added column specify time zone support<commit_after>package core

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

const (
	TWOSIDES = iota + 1
	ONLYTODB
	ONLYFROMDB
)

// database column
type Column struct {
	Name            string
	FieldName       string
	SQLType         SQLType
	Length          int
	Length2         int
	Nullable        bool
	Default         string
	Indexes         map[string]bool
	IsPrimaryKey    bool
	IsAutoIncrement bool
	MapType         int
	IsCreated       bool
	IsUpdated       bool
	IsDeleted       bool
	IsCascade       bool
	IsVersion       bool
	fieldPath       []string
	DefaultIsEmpty  bool
	EnumOptions     map[string]int
	SetOptions      map[string]int
	DisableTimeZone bool
	TimeZone        *time.Location // column specified time zone
}

func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column {
	return &Column{
		Name:            name,
		FieldName:       fieldName,
		SQLType:         sqlType,
		Length:          len1,
		Length2:         len2,
		Nullable:        nullable,
		Default:         "",
		Indexes:         make(map[string]bool),
		IsPrimaryKey:    false,
		IsAutoIncrement: false,
		MapType:         TWOSIDES,
		IsCreated:       false,
		IsUpdated:       false,
		IsDeleted:       false,
		IsCascade:       false,
		IsVersion:       false,
		fieldPath:       nil,
		DefaultIsEmpty:  false,
		EnumOptions:     make(map[string]int),
	}
}

// generate column description string according dialect
func (col *Column) String(d Dialect) string {
	sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "

	sql += d.SqlType(col) + " "

	if col.IsPrimaryKey {
		sql += "PRIMARY KEY "
		if col.IsAutoIncrement {
			sql += d.AutoIncrStr() + " "
		}
	}

	if d.ShowCreateNull() {
		if col.Nullable {
			sql += "NULL "
		} else {
			sql += "NOT NULL "
		}
	}

	if col.Default != "" {
		sql += "DEFAULT " + col.Default + " "
	}

	return sql
}

func (col *Column) StringNoPk(d Dialect) string {
	sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "

	sql += d.SqlType(col) + " "

	if d.ShowCreateNull() {
		if col.Nullable {
			sql += "NULL "
		} else {
			sql += "NOT NULL "
		}
	}

	if col.Default != "" {
		sql += "DEFAULT " + col.Default + " "
	}

	return sql
}

// return col's filed of struct's value
func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) {
	dataStruct := reflect.Indirect(reflect.ValueOf(bean))
	return col.ValueOfV(&dataStruct)
}

func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) {
	var fieldValue reflect.Value
	if col.fieldPath == nil {
		col.fieldPath = strings.Split(col.FieldName, ".")
	}

	if dataStruct.Type().Kind() == reflect.Map {
		keyValue := reflect.ValueOf(col.fieldPath[len(col.fieldPath)-1])
		fieldValue = dataStruct.MapIndex(keyValue)
		return &fieldValue, nil
	}

	level := len(col.fieldPath)
	fieldValue = dataStruct.FieldByName(col.fieldPath[0])
	for i := 0; i < level-1; i++ {
		if !fieldValue.IsValid() {
			break
		}
		if fieldValue.Kind() == reflect.Struct {
			fieldValue = fieldValue.FieldByName(col.fieldPath[i+1])
		} else if fieldValue.Kind() == reflect.Ptr {
			if fieldValue.IsNil() {
				fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
			}
			fieldValue = fieldValue.Elem().FieldByName(col.fieldPath[i+1])
		} else {
			return nil, fmt.Errorf("field  %v is not valid", col.FieldName)
		}
	}

	if !fieldValue.IsValid() {
		return nil, fmt.Errorf("field  %v is not valid", col.FieldName)
	}

	return &fieldValue, nil
}
<|endoftext|>


<commit_before>package lua

import (
	"fmt"
	"github.com/yuin/gopher-lua/parse"
	"os"
	"testing"
)

const maxMemory = 40

var gluaTests []string = []string{
	"base.lua",
	"coroutine.lua",
	"db.lua",
	"issues.lua",
	"os.lua",
	"table.lua",
	"vm.lua",
	"math.lua",
	"strings.lua",
}

var luaTests []string = []string{
	"attrib.lua",
	"calls.lua",
	"closure.lua",
	"constructs.lua",
	"events.lua",
	"literals.lua",
	"locals.lua",
	"math.lua",
	"sort.lua",
	"strings.lua",
	"vararg.lua",
	"pm.lua",
	"files.lua",
}

func testScriptCompile(t *testing.T, script string) {
	file, err := os.Open(script)
	if err != nil {
		t.Fatal(err)
		return
	}
	chunk, err2 := parse.Parse(file, script)
	if err2 != nil {
		t.Fatal(err2)
		return
	}
	parse.Dump(chunk)
	proto, err3 := Compile(chunk, script)
	if err3 != nil {
		t.Fatal(err3)
		return
	}
	nop := func(s string) {}
	nop(proto.String())
}

func testScriptDir(t *testing.T, tests []string, directory string) {
	if err := os.Chdir(directory); err != nil {
		t.Error(err)
	}
	defer os.Chdir("..")
	for _, script := range tests {
		fmt.Printf("testing %s/%s\n", directory, script)
		testScriptCompile(t, script)
		L := NewState(Options{
			RegistrySize:        1024 * 20,
			CallStackSize:       1024,
			IncludeGoStackTrace: true,
		})
		L.SetMx(maxMemory)
		if err := L.DoFile(script); err != nil {
			t.Error(err)
		}
		L.Close()
	}
}

func TestGlua(t *testing.T) {
	testScriptDir(t, gluaTests, "_glua-tests")
}

func TestLua(t *testing.T) {
	testScriptDir(t, luaTests, "_lua5.1-tests")
}
<commit_msg>unit test for memory leak fix<commit_after>package lua

import (
	"fmt"
	"github.com/yuin/gopher-lua/parse"
	"os"
	"runtime"
	"sync/atomic"
	"testing"
	"time"
)

const maxMemory = 40

var gluaTests []string = []string{
	"base.lua",
	"coroutine.lua",
	"db.lua",
	"issues.lua",
	"os.lua",
	"table.lua",
	"vm.lua",
	"math.lua",
	"strings.lua",
}

var luaTests []string = []string{
	"attrib.lua",
	"calls.lua",
	"closure.lua",
	"constructs.lua",
	"events.lua",
	"literals.lua",
	"locals.lua",
	"math.lua",
	"sort.lua",
	"strings.lua",
	"vararg.lua",
	"pm.lua",
	"files.lua",
}

func testScriptCompile(t *testing.T, script string) {
	file, err := os.Open(script)
	if err != nil {
		t.Fatal(err)
		return
	}
	chunk, err2 := parse.Parse(file, script)
	if err2 != nil {
		t.Fatal(err2)
		return
	}
	parse.Dump(chunk)
	proto, err3 := Compile(chunk, script)
	if err3 != nil {
		t.Fatal(err3)
		return
	}
	nop := func(s string) {}
	nop(proto.String())
}

func testScriptDir(t *testing.T, tests []string, directory string) {
	if err := os.Chdir(directory); err != nil {
		t.Error(err)
	}
	defer os.Chdir("..")
	for _, script := range tests {
		fmt.Printf("testing %s/%s\n", directory, script)
		testScriptCompile(t, script)
		L := NewState(Options{
			RegistrySize:        1024 * 20,
			CallStackSize:       1024,
			IncludeGoStackTrace: true,
		})
		L.SetMx(maxMemory)
		if err := L.DoFile(script); err != nil {
			t.Error(err)
		}
		L.Close()
	}
}

var numActiveUserDatas int32 = 0

type finalizerStub struct{ x byte }

func allocFinalizerUserData(L *LState) int {
	ud := L.NewUserData()
	atomic.AddInt32(&numActiveUserDatas, 1)
	a := finalizerStub{}
	ud.Value = &a
	runtime.SetFinalizer(&a, func(aa *finalizerStub) {
		atomic.AddInt32(&numActiveUserDatas, -1)
	})
	L.Push(ud)
	return 1
}

func sleep(L *LState) int {
	time.Sleep(time.Duration(L.CheckInt(1)) * time.Millisecond)
	return 0
}

func countFinalizers(L *LState) int {
	L.Push(LNumber(numActiveUserDatas))
	return 1
}

// TestLocalVarFree verifies that tables and user user datas which are no longer referenced by the lua script are
// correctly gc-ed. There was a bug in gopher lua where local vars were not being gc-ed in all circumstances.
func TestLocalVarFree(t *testing.T) {
	s := `
		function Test(a, b, c)
			local a = { v = allocFinalizer() }
			local b = { v = allocFinalizer() }
			return a
		end
		Test(1,2,3)
		for i = 1, 100 do
			collectgarbage()
			if countFinalizers() == 0 then
				return
			end
			sleep(100)
		end
		error("user datas not finalized after 100 gcs")
`
	L := NewState()
	L.SetGlobal("allocFinalizer", L.NewFunction(allocFinalizerUserData))
	L.SetGlobal("sleep", L.NewFunction(sleep))
	L.SetGlobal("countFinalizers", L.NewFunction(countFinalizers))
	defer L.Close()
	if err := L.DoString(s); err != nil {
		t.Error(err)
	}
}

func TestGlua(t *testing.T) {
	testScriptDir(t, gluaTests, "_glua-tests")
}

func TestLua(t *testing.T) {
	testScriptDir(t, luaTests, "_lua5.1-tests")
}
<|endoftext|>


<commit_before>package xlsxtra

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	"github.com/tealeg/xlsx"
)

// Col retrieves values by header label from a row
type Col map[string]int

// NewCol creates a new Col from a col row
func NewCol(row *xlsx.Row) Col {
	col := make(Col)
	for i, cell := range row.Cells {
		title, _ := cell.String()
		if title != "" {
			col[title] = i + 1
			col[fmt.Sprintf("-%s", title)] = -col[title]
		}
	}
	return col
}

// Index of a given column header title
func (c Col) Index(title string) (int, error) {
	if i, ok := c[title]; ok {
		return i, nil
	}
	return 0, fmt.Errorf("Unknown column header: %s (%#v)",
		title, c)
}

// Indices of given column header titles
func (c Col) Indices(headers ...string) (
	[]int, error) {
	indices := make([]int, len(headers))
	for i, title := range headers {
		index, err := c.Index(title)
		if err != nil {
			return []int{}, err
		}
		indices[i] = index
	}
	return indices, nil
}

// IndexRow returns index of a given column header title
// inside a row.
func (c Col) IndexRow(
	row *xlsx.Row, title string) (int, error) {
	i, err := c.Index(title)
	if err != nil {
		return 0, err
	}
	if i <= len(row.Cells) {
		return i, nil
	}
	return i, fmt.Errorf(
		`IndexRow: Index %d out of range for column title: %s
Try to set a value in a column to the far right.
[%d][%s]`,
		i, title, len(row.Cells),
		strings.Join(ToString(row), "|"))
}

// Bool value of (row,col) in spreadsheet
func (c Col) Bool(row *xlsx.Row, header string) (bool,
	error) {
	val, err := c.String(row, header)
	if err != nil {
		return false, err
	}
	val = strings.ToLower(val)
	return val == "ja" || val == "yes" || val == "1", nil
}

// BoolMap value of (row,col) in spreadsheet
func (c Col) BoolMap(row *xlsx.Row, headers []string) (
	map[string]bool, error) {
	var err error
	bmap := make(map[string]bool)
	for _, header := range headers {
		bmap[header], err = c.Bool(row, header)
		if err != nil {
			return nil, err
		}
	}
	return bmap, nil
}

// Int value of (row,col) in spreadsheet
func (c Col) Int(row *xlsx.Row, header string) (int,
	error) {
	i, err := c.IndexRow(row, header)
	if err != nil {
		return 0, err
	}
	return row.Cells[i-1].Int()
}

// Float value of (row,col) in spreadsheet
func (c Col) Float(row *xlsx.Row, header string) (float64,
	error) {
	i, err := c.IndexRow(row, header)
	if err != nil {
		return 0, err
	}
	val := row.Cells[i-1].Value
	if strings.HasPrefix(val, "€") ||
		strings.HasPrefix(val, "$") {
		val = strings.TrimLeft(val, "€$ ")
	}
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return math.NaN(), err
	}
	return f, nil
}

// String value of (row,col) in spreadsheet
func (c Col) String(row *xlsx.Row, header string) (string,
	error) {
	i, err := c.IndexRow(row, header)
	if err != nil {
		return "", err
	}
	/** unreachable code fix this for all
		if i >= len(row.Cells) {
			return "", nil
		}
	**/
	return row.Cells[i-1].String()
}

// StringFloatMap converts column with days string into
// a map of floats.
func (c Col) StringFloatMap(row *xlsx.Row, header string,
	dmap map[string]float64, val float64, sep string,
	chars int) error {
	// days
	s, err := c.String(row, header)
	if err != nil {
		return err
	}
	ds := strings.Split(s, sep)
	for _, d := range ds {
		if chars > 0 && len(s) > chars {
			d = d[:chars]
		}
		dmap[d] = val
	}
	return nil
}
<commit_msg>rename argument for NewCol from row to header<commit_after>package xlsxtra

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	"github.com/tealeg/xlsx"
)

// Col retrieves values by header label from a row
type Col map[string]int

// NewCol creates a new Col from a header row
func NewCol(header *xlsx.Row) Col {
	col := make(Col)
	for i, cell := range header.Cells {
		title, _ := cell.String()
		if title != "" {
			col[title] = i + 1
			col[fmt.Sprintf("-%s", title)] = -col[title]
		}
	}
	return col
}

// Index of a given column header title
func (c Col) Index(title string) (int, error) {
	if i, ok := c[title]; ok {
		return i, nil
	}
	return 0, fmt.Errorf("Unknown column header: %s (%#v)",
		title, c)
}

// Indices of given column header titles
func (c Col) Indices(headers ...string) (
	[]int, error) {
	indices := make([]int, len(headers))
	for i, title := range headers {
		index, err := c.Index(title)
		if err != nil {
			return []int{}, err
		}
		indices[i] = index
	}
	return indices, nil
}

// IndexRow returns index of a given column header title
// inside a row.
func (c Col) IndexRow(
	row *xlsx.Row, title string) (int, error) {
	i, err := c.Index(title)
	if err != nil {
		return 0, err
	}
	if i <= len(row.Cells) {
		return i, nil
	}
	return i, fmt.Errorf(
		`IndexRow: Index %d out of range for column title: %s
Try to set a value in a column to the far right.
[%d][%s]`,
		i, title, len(row.Cells),
		strings.Join(ToString(row), "|"))
}

// Bool value of (row,col) in spreadsheet
func (c Col) Bool(row *xlsx.Row, header string) (bool,
	error) {
	val, err := c.String(row, header)
	if err != nil {
		return false, err
	}
	val = strings.ToLower(val)
	return val == "ja" || val == "yes" || val == "1", nil
}

// BoolMap value of (row,col) in spreadsheet
func (c Col) BoolMap(row *xlsx.Row, headers []string) (
	map[string]bool, error) {
	var err error
	bmap := make(map[string]bool)
	for _, header := range headers {
		bmap[header], err = c.Bool(row, header)
		if err != nil {
			return nil, err
		}
	}
	return bmap, nil
}

// Int value of (row,col) in spreadsheet
func (c Col) Int(row *xlsx.Row, header string) (int,
	error) {
	i, err := c.IndexRow(row, header)
	if err != nil {
		return 0, err
	}
	return row.Cells[i-1].Int()
}

// Float value of (row,col) in spreadsheet
func (c Col) Float(row *xlsx.Row, header string) (float64,
	error) {
	i, err := c.IndexRow(row, header)
	if err != nil {
		return 0, err
	}
	val := row.Cells[i-1].Value
	if strings.HasPrefix(val, "€") ||
		strings.HasPrefix(val, "$") {
		val = strings.TrimLeft(val, "€$ ")
	}
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		return math.NaN(), err
	}
	return f, nil
}

// String value of (row,col) in spreadsheet
func (c Col) String(row *xlsx.Row, header string) (string,
	error) {
	i, err := c.IndexRow(row, header)
	if err != nil {
		return "", err
	}
	/** unreachable code fix this for all
		if i >= len(row.Cells) {
			return "", nil
		}
	**/
	return row.Cells[i-1].String()
}

// StringFloatMap converts column with days string into
// a map of floats.
func (c Col) StringFloatMap(row *xlsx.Row, header string,
	dmap map[string]float64, val float64, sep string,
	chars int) error {
	// days
	s, err := c.String(row, header)
	if err != nil {
		return err
	}
	ds := strings.Split(s, sep)
	for _, d := range ds {
		if chars > 0 && len(s) > chars {
			d = d[:chars]
		}
		dmap[d] = val
	}
	return nil
}
<|endoftext|>


<commit_before>// combat utility functions

package main

func (g *game) HitDamage(dt dmgType, base int, armor int) int {
	min := base / 2
	attack := min + RandInt(base-min+1)
	if dt == DmgPhysical {
		attack -= RandInt(armor + 1)
	}
	if attack < 0 {
		attack = 0
	}
	return attack
}

func (m *monster) InflictDamage(g *game, damage, max int) {
	oldHP := g.Player.HP
	g.Player.HP -= damage
	if oldHP > max && g.Player.HP <= max {
		g.StoryPrintf("Critical HP: %d (hit by %s)", g.Player.HP, Indefinite(m.Kind.String(), false))
		g.ui.CriticalHPWarning(g)
	}
}

func (g *game) MakeMonstersAware() {
	for _, m := range g.Monsters {
		if m.HP <= 0 {
			continue
		}
		if g.Player.LOS[m.Pos] {
			m.MakeAware(g)
			if m.State != Resting {
				m.GatherBand(g)
			}
		}
	}
}

func (g *game) MakeNoise(noise int, at position) {
	dij := &normalPath{game: g}
	nm := Dijkstra(dij, []position{at}, noise)
	for _, m := range g.Monsters {
		if !m.Exists() {
			continue
		}
		if m.State == Hunting {
			continue
		}
		n, ok := nm[m.Pos]
		if !ok {
			continue
		}
		d := n.Cost
		v := noise - d
		if v <= 0 {
			continue
		}
		if v > 25 {
			v = 25
		}
		r := RandInt(30)
		if m.State == Resting {
			v /= 2
		}
		if v > r {
			if g.Player.LOS[m.Pos] {
				m.MakeHunt(g)
			} else {
				m.Target = at
				m.State = Wandering
			}
			m.GatherBand(g)
		}
	}
}

func (g *game) AttackMonster(mons *monster, ev event) {
	switch {
	case g.Player.Weapon == Frundis:
		if !g.HitMonster(DmgPhysical, mons, ev) {
			break
		}
		if RandInt(7) == 0 {
			mons.EnterConfusion(g, ev)
			g.PrintfStyled("Frundis glows… the %s appears confused.", logPlayerHit, mons.Kind)
		} else if RandInt(11) == 0 {
			g.Fog(mons.Pos, 3, ev)
			g.PrintfStyled("Frundis glows… the %s is surrounded by a dense fog.", logPlayerHit, mons.Kind)
		}
	case g.Player.Weapon.Cleave():
		var neighbors []position
		if g.Player.HasStatus(StatusConfusion) {
			neighbors = g.Dungeon.CardinalFreeNeighbors(g.Player.Pos)
		} else {
			neighbors = g.Dungeon.FreeNeighbors(g.Player.Pos)
		}
		for _, pos := range neighbors {
			mons, _ := g.MonsterAt(pos)
			if mons.Exists() {
				g.HitMonster(DmgPhysical, mons, ev)
			}
		}
	case g.Player.Weapon.Pierce():
		g.HitMonster(DmgPhysical, mons, ev)
		deltaX := mons.Pos.X - g.Player.Pos.X
		deltaY := mons.Pos.Y - g.Player.Pos.Y
		behind := position{g.Player.Pos.X + 2*deltaX, g.Player.Pos.Y + 2*deltaY}
		if behind.valid() {
			mons, _ := g.MonsterAt(behind)
			if mons.Exists() {
				g.HitMonster(DmgPhysical, mons, ev)
			}
		}
	case g.Player.Weapon == ElecWhip:
		g.HitConnected(mons.Pos, DmgMagical, ev)
	default:
		g.HitMonster(DmgPhysical, mons, ev)
		if (g.Player.Weapon == Sword || g.Player.Weapon == DoubleSword) && RandInt(4) == 0 {
			g.HitMonster(DmgPhysical, mons, ev)
		}
	}
}

func (g *game) HitConnected(pos position, dt dmgType, ev event) {
	// inspired from TOME4's Harbinger addon class
	d := g.Dungeon
	conn := map[position]bool{}
	stack := []position{pos}
	conn[pos] = true
	nb := make([]position, 0, 8)
	for len(stack) > 0 {
		pos = stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		mons, _ := g.MonsterAt(pos)
		if !mons.Exists() {
			continue
		}
		g.HitMonster(dt, mons, ev)
		nb = pos.Neighbors(nb, func(npos position) bool {
			return npos.valid() && d.Cell(npos).T != WallCell
		})
		for _, npos := range nb {
			if !conn[npos] {
				conn[npos] = true
				stack = append(stack, npos)
			}
		}
	}
}

func (g *game) HitNoise() int {
	noise := 12
	if g.Player.Weapon == Frundis || g.Player.Weapon == Dagger {
		noise -= 2
	}
	if g.Player.Armour == Robe {
		noise -= 1
	}
	noise += g.Player.Armor() / 2
	return noise
}

type dmgType int

const (
	DmgPhysical dmgType = iota
	DmgMagical
)

func (g *game) HitMonster(dt dmgType, mons *monster, ev event) (hit bool) {
	acc := RandInt(g.Player.Accuracy())
	evasion := RandInt(mons.Evasion)
	if mons.State == Resting {
		evasion /= 2 + 1
	}
	if acc > evasion {
		hit = true
		noise := 12
		if g.Player.Weapon == Frundis || g.Player.Weapon == Dagger {
			noise -= 2
		}
		noise += mons.Armor / 3
		g.MakeNoise(noise, mons.Pos)
		bonus := 0
		if g.Player.HasStatus(StatusBerserk) {
			bonus += 2 + RandInt(4)
		}
		attack := g.HitDamage(dt, g.Player.Attack()+bonus, mons.Armor)
		if mons.State == Resting {
			if g.Player.Weapon == Dagger {
				attack *= 4
			} else {
				attack *= 2
			}
		}
		oldHP := mons.HP
		mons.HP -= attack
		if mons.HP > 0 {
			g.PrintfStyled("You hit the %v (%d damage).", logPlayerHit, mons.Kind, attack)
		} else if oldHP > 0 {
			// test oldHP > 0 because of sword special attack
			g.PrintfStyled("You kill the %v (%d damage).", logPlayerHit, mons.Kind, attack)
			g.HandleKill(mons)
		}
		if mons.Kind == MonsBrizzia && RandInt(4) == 0 && !g.Player.HasStatus(StatusNausea) &&
			mons.Pos.Distance(g.Player.Pos) == 1 {
			g.Player.Statuses[StatusNausea]++
			g.PushEvent(&simpleEvent{ERank: ev.Rank() + 30 + RandInt(20), EAction: NauseaEnd})
			g.Print("The brizzia's corpse releases a nauseous gas. You feel sick.")
		}
	} else {
		g.Printf("You miss the %v.", mons.Kind)
	}
	mons.MakeHuntIfHurt(g)
	return hit
}

func (g *game) HandleKill(mons *monster) {
	g.Killed++
	if g.KilledMons == nil {
		g.KilledMons = map[monsterKind]int{}
	}
	g.KilledMons[mons.Kind]++
	if mons.Kind == MonsExplosiveNadre {
		mons.Explode(g)
	}
	if g.Doors[mons.Pos] {
		g.ComputeLOS()
	}
	if mons.Kind.Dangerousness() > 10 {
		g.StoryPrintf("You killed %s.", Indefinite(mons.Kind.String(), false))
	}
}
<commit_msg>some tweaks to staff Frundis<commit_after>// combat utility functions

package main

func (g *game) HitDamage(dt dmgType, base int, armor int) int {
	min := base / 2
	attack := min + RandInt(base-min+1)
	if dt == DmgPhysical {
		attack -= RandInt(armor + 1)
	}
	if attack < 0 {
		attack = 0
	}
	return attack
}

func (m *monster) InflictDamage(g *game, damage, max int) {
	oldHP := g.Player.HP
	g.Player.HP -= damage
	if oldHP > max && g.Player.HP <= max {
		g.StoryPrintf("Critical HP: %d (hit by %s)", g.Player.HP, Indefinite(m.Kind.String(), false))
		g.ui.CriticalHPWarning(g)
	}
}

func (g *game) MakeMonstersAware() {
	for _, m := range g.Monsters {
		if m.HP <= 0 {
			continue
		}
		if g.Player.LOS[m.Pos] {
			m.MakeAware(g)
			if m.State != Resting {
				m.GatherBand(g)
			}
		}
	}
}

func (g *game) MakeNoise(noise int, at position) {
	dij := &normalPath{game: g}
	nm := Dijkstra(dij, []position{at}, noise)
	for _, m := range g.Monsters {
		if !m.Exists() {
			continue
		}
		if m.State == Hunting {
			continue
		}
		n, ok := nm[m.Pos]
		if !ok {
			continue
		}
		d := n.Cost
		v := noise - d
		if v <= 0 {
			continue
		}
		if v > 25 {
			v = 25
		}
		r := RandInt(30)
		if m.State == Resting {
			v /= 2
		}
		if v > r {
			if g.Player.LOS[m.Pos] {
				m.MakeHunt(g)
			} else {
				m.Target = at
				m.State = Wandering
			}
			m.GatherBand(g)
		}
	}
}

func (g *game) AttackMonster(mons *monster, ev event) {
	switch {
	case g.Player.Weapon == Frundis:
		if !g.HitMonster(DmgPhysical, mons, ev) {
			break
		}
		if RandInt(5) == 0 {
			mons.EnterConfusion(g, ev)
			g.PrintfStyled("Frundis glows… the %s appears confused.", logPlayerHit, mons.Kind)
			if RandInt(3) == 0 {
				g.Fog(mons.Pos, 3, ev)
				g.PrintfStyled("Frundis glows… the %s is surrounded by a dense fog.", logPlayerHit, mons.Kind)
			}
		}
	case g.Player.Weapon.Cleave():
		var neighbors []position
		if g.Player.HasStatus(StatusConfusion) {
			neighbors = g.Dungeon.CardinalFreeNeighbors(g.Player.Pos)
		} else {
			neighbors = g.Dungeon.FreeNeighbors(g.Player.Pos)
		}
		for _, pos := range neighbors {
			mons, _ := g.MonsterAt(pos)
			if mons.Exists() {
				g.HitMonster(DmgPhysical, mons, ev)
			}
		}
	case g.Player.Weapon.Pierce():
		g.HitMonster(DmgPhysical, mons, ev)
		deltaX := mons.Pos.X - g.Player.Pos.X
		deltaY := mons.Pos.Y - g.Player.Pos.Y
		behind := position{g.Player.Pos.X + 2*deltaX, g.Player.Pos.Y + 2*deltaY}
		if behind.valid() {
			mons, _ := g.MonsterAt(behind)
			if mons.Exists() {
				g.HitMonster(DmgPhysical, mons, ev)
			}
		}
	case g.Player.Weapon == ElecWhip:
		g.HitConnected(mons.Pos, DmgMagical, ev)
	default:
		g.HitMonster(DmgPhysical, mons, ev)
		if (g.Player.Weapon == Sword || g.Player.Weapon == DoubleSword) && RandInt(4) == 0 {
			g.HitMonster(DmgPhysical, mons, ev)
		}
	}
}

func (g *game) HitConnected(pos position, dt dmgType, ev event) {
	// inspired from TOME4's Harbinger addon class
	d := g.Dungeon
	conn := map[position]bool{}
	stack := []position{pos}
	conn[pos] = true
	nb := make([]position, 0, 8)
	for len(stack) > 0 {
		pos = stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		mons, _ := g.MonsterAt(pos)
		if !mons.Exists() {
			continue
		}
		g.HitMonster(dt, mons, ev)
		nb = pos.Neighbors(nb, func(npos position) bool {
			return npos.valid() && d.Cell(npos).T != WallCell
		})
		for _, npos := range nb {
			if !conn[npos] {
				conn[npos] = true
				stack = append(stack, npos)
			}
		}
	}
}

func (g *game) HitNoise() int {
	noise := 12
	if g.Player.Weapon == Frundis || g.Player.Weapon == Dagger {
		noise -= 2
	}
	if g.Player.Armour == Robe {
		noise -= 1
	}
	noise += g.Player.Armor() / 2
	return noise
}

type dmgType int

const (
	DmgPhysical dmgType = iota
	DmgMagical
)

func (g *game) HitMonster(dt dmgType, mons *monster, ev event) (hit bool) {
	acc := RandInt(g.Player.Accuracy())
	evasion := RandInt(mons.Evasion)
	if mons.State == Resting {
		evasion /= 2 + 1
	}
	if acc > evasion {
		hit = true
		noise := 12
		if g.Player.Weapon == Frundis || g.Player.Weapon == Dagger {
			noise -= 2
		}
		noise += mons.Armor / 3
		g.MakeNoise(noise, mons.Pos)
		bonus := 0
		if g.Player.HasStatus(StatusBerserk) {
			bonus += 2 + RandInt(4)
		}
		attack := g.HitDamage(dt, g.Player.Attack()+bonus, mons.Armor)
		if mons.State == Resting {
			if g.Player.Weapon == Dagger {
				attack *= 4
			} else {
				attack *= 2
			}
		}
		oldHP := mons.HP
		mons.HP -= attack
		if mons.HP > 0 {
			g.PrintfStyled("You hit the %v (%d damage).", logPlayerHit, mons.Kind, attack)
		} else if oldHP > 0 {
			// test oldHP > 0 because of sword special attack
			g.PrintfStyled("You kill the %v (%d damage).", logPlayerHit, mons.Kind, attack)
			g.HandleKill(mons)
		}
		if mons.Kind == MonsBrizzia && RandInt(4) == 0 && !g.Player.HasStatus(StatusNausea) &&
			mons.Pos.Distance(g.Player.Pos) == 1 {
			g.Player.Statuses[StatusNausea]++
			g.PushEvent(&simpleEvent{ERank: ev.Rank() + 30 + RandInt(20), EAction: NauseaEnd})
			g.Print("The brizzia's corpse releases a nauseous gas. You feel sick.")
		}
	} else {
		g.Printf("You miss the %v.", mons.Kind)
	}
	mons.MakeHuntIfHurt(g)
	return hit
}

func (g *game) HandleKill(mons *monster) {
	g.Killed++
	if g.KilledMons == nil {
		g.KilledMons = map[monsterKind]int{}
	}
	g.KilledMons[mons.Kind]++
	if mons.Kind == MonsExplosiveNadre {
		mons.Explode(g)
	}
	if g.Doors[mons.Pos] {
		g.ComputeLOS()
	}
	if mons.Kind.Dangerousness() > 10 {
		g.StoryPrintf("You killed %s.", Indefinite(mons.Kind.String(), false))
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 Ka-Hing Cheung
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common

import (
	"context"
	"runtime/debug"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
)

type FusePanicLogger struct {
	Fs fuseutil.FileSystem
}

func LogPanic(err *error) {
	if e := recover(); e != nil {
		log.Errorf("stacktrace from panic: %v \n"+string(debug.Stack()), e)
		if *err == nil {
			*err = fuse.EIO
		}
	}
}

func (fs FusePanicLogger) StatFS(ctx context.Context, op *fuseops.StatFSOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.StatFS(ctx, op)
}
func (fs FusePanicLogger) LookUpInode(ctx context.Context, op *fuseops.LookUpInodeOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.LookUpInode(ctx, op)
}
func (fs FusePanicLogger) GetInodeAttributes(ctx context.Context, op *fuseops.GetInodeAttributesOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.GetInodeAttributes(ctx, op)
}
func (fs FusePanicLogger) SetInodeAttributes(ctx context.Context, op *fuseops.SetInodeAttributesOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.SetInodeAttributes(ctx, op)
}
func (fs FusePanicLogger) ForgetInode(ctx context.Context, op *fuseops.ForgetInodeOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ForgetInode(ctx, op)
}
func (fs FusePanicLogger) MkDir(ctx context.Context, op *fuseops.MkDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.MkDir(ctx, op)
}
func (fs FusePanicLogger) MkNode(ctx context.Context, op *fuseops.MkNodeOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.MkNode(ctx, op)
}
func (fs FusePanicLogger) CreateFile(ctx context.Context, op *fuseops.CreateFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.CreateFile(ctx, op)
}
func (fs FusePanicLogger) CreateLink(ctx context.Context, op *fuseops.CreateLinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.CreateLink(ctx, op)
}
func (fs FusePanicLogger) CreateSymlink(ctx context.Context, op *fuseops.CreateSymlinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.CreateSymlink(ctx, op)
}
func (fs FusePanicLogger) Rename(ctx context.Context, op *fuseops.RenameOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.Rename(ctx, op)
}
func (fs FusePanicLogger) RmDir(ctx context.Context, op *fuseops.RmDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.RmDir(ctx, op)
}
func (fs FusePanicLogger) Unlink(ctx context.Context, op *fuseops.UnlinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.Unlink(ctx, op)
}
func (fs FusePanicLogger) OpenDir(ctx context.Context, op *fuseops.OpenDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.OpenDir(ctx, op)
}
func (fs FusePanicLogger) ReadDir(ctx context.Context, op *fuseops.ReadDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReadDir(ctx, op)
}
func (fs FusePanicLogger) ReleaseDirHandle(ctx context.Context, op *fuseops.ReleaseDirHandleOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReleaseDirHandle(ctx, op)
}
func (fs FusePanicLogger) OpenFile(ctx context.Context, op *fuseops.OpenFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.OpenFile(ctx, op)
}
func (fs FusePanicLogger) ReadFile(ctx context.Context, op *fuseops.ReadFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReadFile(ctx, op)
}
func (fs FusePanicLogger) WriteFile(ctx context.Context, op *fuseops.WriteFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.WriteFile(ctx, op)
}
func (fs FusePanicLogger) SyncFile(ctx context.Context, op *fuseops.SyncFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.SyncFile(ctx, op)
}
func (fs FusePanicLogger) FlushFile(ctx context.Context, op *fuseops.FlushFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.FlushFile(ctx, op)
}
func (fs FusePanicLogger) ReleaseFileHandle(ctx context.Context, op *fuseops.ReleaseFileHandleOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReleaseFileHandle(ctx, op)
}
func (fs FusePanicLogger) ReadSymlink(ctx context.Context, op *fuseops.ReadSymlinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReadSymlink(ctx, op)
}
func (fs FusePanicLogger) RemoveXattr(ctx context.Context, op *fuseops.RemoveXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.RemoveXattr(ctx, op)
}
func (fs FusePanicLogger) GetXattr(ctx context.Context, op *fuseops.GetXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.GetXattr(ctx, op)
}
func (fs FusePanicLogger) ListXattr(ctx context.Context, op *fuseops.ListXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ListXattr(ctx, op)
}
func (fs FusePanicLogger) SetXattr(ctx context.Context, op *fuseops.SetXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.SetXattr(ctx, op)
}

func (fs FusePanicLogger) Destroy() {
	fs.Fs.Destroy()
}
<commit_msg>fix build with new jacobsa/fuse<commit_after>// Copyright 2019 Ka-Hing Cheung
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common

import (
	"context"
	"runtime/debug"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
)

type FusePanicLogger struct {
	Fs fuseutil.FileSystem
}

func LogPanic(err *error) {
	if e := recover(); e != nil {
		log.Errorf("stacktrace from panic: %v \n"+string(debug.Stack()), e)
		if *err == nil {
			*err = fuse.EIO
		}
	}
}

func (fs FusePanicLogger) StatFS(ctx context.Context, op *fuseops.StatFSOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.StatFS(ctx, op)
}
func (fs FusePanicLogger) LookUpInode(ctx context.Context, op *fuseops.LookUpInodeOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.LookUpInode(ctx, op)
}
func (fs FusePanicLogger) GetInodeAttributes(ctx context.Context, op *fuseops.GetInodeAttributesOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.GetInodeAttributes(ctx, op)
}
func (fs FusePanicLogger) SetInodeAttributes(ctx context.Context, op *fuseops.SetInodeAttributesOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.SetInodeAttributes(ctx, op)
}
func (fs FusePanicLogger) Fallocate(ctx context.Context, op *fuseops.FallocateOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.Fallocate(ctx, op)
}
func (fs FusePanicLogger) ForgetInode(ctx context.Context, op *fuseops.ForgetInodeOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ForgetInode(ctx, op)
}
func (fs FusePanicLogger) MkDir(ctx context.Context, op *fuseops.MkDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.MkDir(ctx, op)
}
func (fs FusePanicLogger) MkNode(ctx context.Context, op *fuseops.MkNodeOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.MkNode(ctx, op)
}
func (fs FusePanicLogger) CreateFile(ctx context.Context, op *fuseops.CreateFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.CreateFile(ctx, op)
}
func (fs FusePanicLogger) CreateLink(ctx context.Context, op *fuseops.CreateLinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.CreateLink(ctx, op)
}
func (fs FusePanicLogger) CreateSymlink(ctx context.Context, op *fuseops.CreateSymlinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.CreateSymlink(ctx, op)
}
func (fs FusePanicLogger) Rename(ctx context.Context, op *fuseops.RenameOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.Rename(ctx, op)
}
func (fs FusePanicLogger) RmDir(ctx context.Context, op *fuseops.RmDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.RmDir(ctx, op)
}
func (fs FusePanicLogger) Unlink(ctx context.Context, op *fuseops.UnlinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.Unlink(ctx, op)
}
func (fs FusePanicLogger) OpenDir(ctx context.Context, op *fuseops.OpenDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.OpenDir(ctx, op)
}
func (fs FusePanicLogger) ReadDir(ctx context.Context, op *fuseops.ReadDirOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReadDir(ctx, op)
}
func (fs FusePanicLogger) ReleaseDirHandle(ctx context.Context, op *fuseops.ReleaseDirHandleOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReleaseDirHandle(ctx, op)
}
func (fs FusePanicLogger) OpenFile(ctx context.Context, op *fuseops.OpenFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.OpenFile(ctx, op)
}
func (fs FusePanicLogger) ReadFile(ctx context.Context, op *fuseops.ReadFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReadFile(ctx, op)
}
func (fs FusePanicLogger) WriteFile(ctx context.Context, op *fuseops.WriteFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.WriteFile(ctx, op)
}
func (fs FusePanicLogger) SyncFile(ctx context.Context, op *fuseops.SyncFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.SyncFile(ctx, op)
}
func (fs FusePanicLogger) FlushFile(ctx context.Context, op *fuseops.FlushFileOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.FlushFile(ctx, op)
}
func (fs FusePanicLogger) ReleaseFileHandle(ctx context.Context, op *fuseops.ReleaseFileHandleOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReleaseFileHandle(ctx, op)
}
func (fs FusePanicLogger) ReadSymlink(ctx context.Context, op *fuseops.ReadSymlinkOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ReadSymlink(ctx, op)
}
func (fs FusePanicLogger) RemoveXattr(ctx context.Context, op *fuseops.RemoveXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.RemoveXattr(ctx, op)
}
func (fs FusePanicLogger) GetXattr(ctx context.Context, op *fuseops.GetXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.GetXattr(ctx, op)
}
func (fs FusePanicLogger) ListXattr(ctx context.Context, op *fuseops.ListXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.ListXattr(ctx, op)
}
func (fs FusePanicLogger) SetXattr(ctx context.Context, op *fuseops.SetXattrOp) (err error) {
	defer LogPanic(&err)
	return fs.Fs.SetXattr(ctx, op)
}

func (fs FusePanicLogger) Destroy() {
	fs.Fs.Destroy()
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package device

// A Weatherlink device is simulated by guessing what commands were
// requested based on the packet sizes.  It's not perfect but is a
// convenient way to allow low level protocol testing.

import (
	"io"
	"math/rand"
	"time"

	"github.com/ebarkie/weatherlink/data"
)

const (
	ack = 0x06 // Acknowledge
)

// Sim represents a simulted Weatherlink device.
type Sim struct {
	l            data.Loop // Current loop packet state
	nextLoopType int       // Loop type to send next (so they are interleaved)
}

// Dial initializes the state of a simulated Weatherlink device.
func (s *Sim) Dial(addr string) error {
	// Starting loop values which will pass typical QC processes.
	s.l.Bar.Altimeter = 29.0
	s.l.Bar.SeaLevel = 29.0
	s.l.Bar.Station = 29.0
	s.l.OutHumidity = 50
	s.l.OutTemp = 65.0
	s.l.Wind.Cur.Speed = 3

	return nil
}

// Close closes the simulated Weatherlink device.
func (s *Sim) Close() error {
	s.l = data.Loop{}
	s.nextLoopType = 0

	return nil
}

// Flush flushes the input buffers of the simulated Weatherlink device.
func (Sim) Flush() error {
	return nil
}

// Read reads up to the size of the provided byte buffer from the
// simulated Weatherlink device.
func (Sim) Read([]byte) (int, error) {
	return 0, io.ErrUnexpectedEOF
}

// ReadFull reads the full size of the provided byte buffer from the
// simulted Weatherlink device.
func (s *Sim) ReadFull(b []byte) (n int, err error) {
	switch len(b) {
	case 1:
		// Command ack
		b[0] = ack
		return 1, nil
	case 8:
		// GETTIME
		ct := data.ConsTime(time.Now())
		var p []byte
		p, err = ct.MarshalBinary()
		n = copy(b, p)
	case 99:
		// LPS 3 x

		// Interleave loop types.
		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		// Make observation values wander around like they would on a
		// real station.
		s.l.Bar.Altimeter = wander(s.l.Bar.Altimeter, 0.01)
		s.l.Bar.SeaLevel = wander(s.l.Bar.SeaLevel, 0.01)
		s.l.Bar.Station = wander(s.l.Bar.Station, 0.01)
		s.l.OutHumidity = int(wander(float64(s.l.OutHumidity), 1))
		s.l.OutTemp = wander(s.l.OutTemp, 0.5)
		s.l.Wind.Cur.Speed = int(wander(float64(s.l.Wind.Cur.Speed), 1))

		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		// Create 2s delay between packets.
		time.Sleep(2 * time.Second)
		var p []byte
		p, err = s.l.MarshalBinary()
		n = copy(b, p)
	default:
		err = io.ErrUnexpectedEOF
		return
	}

	return
}

// Write simulates a write of the byte buffer.
func (Sim) Write(b []byte) (int, error) {
	return len(b), nil
}

// wander takes a value and randomly adds +/- step or zero.
func wander(v, step float64) float64 {
	rand.Seed(int64(time.Now().Nanosecond()))
	return v + float64(rand.Intn(3)-1)*step
}
<commit_msg>More explicit placeholder<commit_after>// Copyright (c) 2016-2017 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package device

// A Weatherlink device is simulated by guessing what commands were
// requested based on the packet sizes.  It's not perfect but is a
// convenient way to allow low level protocol testing.

import (
	"io"
	"math/rand"
	"time"

	"github.com/ebarkie/weatherlink/data"
)

const (
	ack = 0x06 // Acknowledge
)

// Sim represents a simulted Weatherlink device.
type Sim struct {
	l            data.Loop // Current loop packet state
	nextLoopType int       // Loop type to send next (so they are interleaved)
}

// Dial initializes the state of a simulated Weatherlink device.
func (s *Sim) Dial(addr string) error {
	// Starting loop values which will pass typical QC processes.
	s.l.Bar.Altimeter = 29.0
	s.l.Bar.SeaLevel = 29.0
	s.l.Bar.Station = 29.0
	s.l.OutHumidity = 50
	s.l.OutTemp = 65.0
	s.l.Wind.Cur.Speed = 3

	return nil
}

// Close closes the simulated Weatherlink device.
func (s *Sim) Close() error {
	s.l = data.Loop{}
	s.nextLoopType = 0

	return nil
}

// Flush flushes the input buffers of the simulated Weatherlink device.
func (Sim) Flush() error {
	return nil
}

// Read reads up to the size of the provided byte buffer from the
// simulated Weatherlink device.
func (Sim) Read(_ []byte) (int, error) {
	return 0, io.ErrUnexpectedEOF
}

// ReadFull reads the full size of the provided byte buffer from the
// simulted Weatherlink device.
func (s *Sim) ReadFull(b []byte) (n int, err error) {
	switch len(b) {
	case 1:
		// Command ack
		b[0] = ack
		return 1, nil
	case 8:
		// GETTIME
		ct := data.ConsTime(time.Now())
		var p []byte
		p, err = ct.MarshalBinary()
		n = copy(b, p)
	case 99:
		// LPS 3 x

		// Interleave loop types.
		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		// Make observation values wander around like they would on a
		// real station.
		s.l.Bar.Altimeter = wander(s.l.Bar.Altimeter, 0.01)
		s.l.Bar.SeaLevel = wander(s.l.Bar.SeaLevel, 0.01)
		s.l.Bar.Station = wander(s.l.Bar.Station, 0.01)
		s.l.OutHumidity = int(wander(float64(s.l.OutHumidity), 1))
		s.l.OutTemp = wander(s.l.OutTemp, 0.5)
		s.l.Wind.Cur.Speed = int(wander(float64(s.l.Wind.Cur.Speed), 1))

		s.l.LoopType = s.nextLoopType + 1
		s.nextLoopType = (s.nextLoopType + 1) % 2

		// Create 2s delay between packets.
		time.Sleep(2 * time.Second)
		var p []byte
		p, err = s.l.MarshalBinary()
		n = copy(b, p)
	default:
		err = io.ErrUnexpectedEOF
		return
	}

	return
}

// Write simulates a write of the byte buffer.
func (Sim) Write(b []byte) (int, error) {
	return len(b), nil
}

// wander takes a value and randomly adds +/- step or zero.
func wander(v, step float64) float64 {
	rand.Seed(int64(time.Now().Nanosecond()))
	return v + float64(rand.Intn(3)-1)*step
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/tidwall/buntdb"
	"github.com/tidwall/redcon"
	"github.com/tidwall/resp"
	"github.com/tidwall/tile38/internal/log"
)

type errAOFHook struct {
	err error
}

func (err errAOFHook) Error() string {
	return fmt.Sprintf("hook: %v", err.err)
}

var errInvalidAOF = errors.New("invalid aof file")

func (server *Server) loadAOF() error {
	fi, err := server.aof.Stat()
	if err != nil {
		return err
	}
	start := time.Now()
	var count int
	defer func() {
		d := time.Now().Sub(start)
		ps := float64(count) / (float64(d) / float64(time.Second))
		suf := []string{"bytes/s", "KB/s", "MB/s", "GB/s", "TB/s"}
		bps := float64(fi.Size()) / (float64(d) / float64(time.Second))
		for i := 0; bps > 1024; i++ {
			if len(suf) == 1 {
				break
			}
			bps /= 1024
			suf = suf[1:]
		}
		byteSpeed := fmt.Sprintf("%.0f %s", bps, suf[0])
		log.Infof("AOF loaded %d commands: %.2fs, %.0f/s, %s",
			count, float64(d)/float64(time.Second), ps, byteSpeed)
	}()
	var buf []byte
	var args [][]byte
	var packet [0xFFFF]byte
	var msg Message
	for {
		n, err := server.aof.Read(packet[:])
		if err != nil {
			if err == io.EOF {
				if len(buf) > 0 {
					return io.ErrUnexpectedEOF
				}
				return nil
			}
			return err
		}
		server.aofsz += n
		data := packet[:n]
		if len(buf) > 0 {
			data = append(buf, data...)
		}
		var complete bool
		for {
			complete, args, _, data, err = redcon.ReadNextCommand(data, args[:0])
			if err != nil {
				return err
			}
			if !complete {
				break
			}
			if len(args) > 0 {
				msg.Args = msg.Args[:0]
				for _, arg := range args {
					msg.Args = append(msg.Args, string(arg))
				}
				if _, _, err := server.command(&msg, nil); err != nil {
					if commandErrIsFatal(err) {
						return err
					}
				}
				count++
			}
		}
		if len(data) > 0 {
			buf = append(buf[:0], data...)
		} else if len(buf) > 0 {
			buf = buf[:0]
		}
	}
}

// func qlower(s []byte) string {
// 	if len(s) == 3 {
// 		if s[0] == 'S' && s[1] == 'E' && s[2] == 'T' {
// 			return "set"
// 		}
// 		if s[0] == 'D' && s[1] == 'E' && s[2] == 'L' {
// 			return "del"
// 		}
// 	}
// 	for i := 0; i < len(s); i++ {
// 		if s[i] >= 'A' || s[i] <= 'Z' {
// 			return strings.ToLower(string(s))
// 		}
// 	}
// 	return string(s)
// }

func commandErrIsFatal(err error) bool {
	// FSET (and other writable commands) may return errors that we need
	// to ignore during the loading process. These errors may occur (though unlikely)
	// due to the aof rewrite operation.
	switch err {
	case errKeyNotFound, errIDNotFound:
		return false
	}
	return true
}

func (server *Server) flushAOF() {
	if len(server.aofbuf) > 0 {
		_, err := server.aof.Write(server.aofbuf)
		if err != nil {
			panic(err)
		}
		server.aofbuf = server.aofbuf[:0]
	}
}

func (server *Server) writeAOF(args []string, d *commandDetailsT) error {

	if d != nil && !d.updated {
		// just ignore writes if the command did not update
		return nil
	}

	if server.shrinking {
		nargs := make([]string, len(args))
		copy(nargs, args)
		server.shrinklog = append(server.shrinklog, nargs)
	}

	if server.aof != nil {
		atomic.StoreInt32(&server.aofdirty, 1) // prewrite optimization flag
		n := len(server.aofbuf)
		server.aofbuf = redcon.AppendArray(server.aofbuf, len(args))
		for _, arg := range args {
			server.aofbuf = redcon.AppendBulkString(server.aofbuf, arg)
		}
		server.aofsz += len(server.aofbuf) - n
	}

	// notify aof live connections that we have new data
	server.fcond.L.Lock()
	server.fcond.Broadcast()
	server.fcond.L.Unlock()

	// process geofences
	if d != nil {
		// webhook geofences
		if server.config.followHost() == "" {
			// for leader only
			if d.parent {
				// queue children
				for _, d := range d.children {
					if err := server.queueHooks(d); err != nil {
						return err
					}
				}
			} else {
				// queue parent
				if err := server.queueHooks(d); err != nil {
					return err
				}
			}
		}
		// live geofences
		server.lcond.L.Lock()
		if d.parent {
			// queue children
			for _, d := range d.children {
				server.lstack = append(server.lstack, d)
			}
		} else {
			// queue parent
			server.lstack = append(server.lstack, d)
		}
		server.lcond.Broadcast()
		server.lcond.L.Unlock()
	}
	return nil
}

func (server *Server) queueHooks(d *commandDetailsT) error {
	// big list of all of the messages
	var hmsgs []string
	var hooks []*Hook
	// find the hook by the key
	if hm, ok := server.hookcols[d.key]; ok {
		for _, hook := range hm {
			// match the fence
			msgs := FenceMatch(hook.Name, hook.ScanWriter, hook.Fence, hook.Metas, d)
			if len(msgs) > 0 {
				if hook.channel {
					server.Publish(hook.Name, msgs...)
				} else {
					// append each msg to the big list
					hmsgs = append(hmsgs, msgs...)
					hooks = append(hooks, hook)
				}
			}
		}
	}
	if len(hmsgs) == 0 {
		return nil
	}

	// queue the message in the buntdb database
	err := server.qdb.Update(func(tx *buntdb.Tx) error {
		for _, msg := range hmsgs {
			server.qidx++ // increment the log id
			key := hookLogPrefix + uint64ToString(server.qidx)
			_, _, err := tx.Set(key, string(msg), hookLogSetDefaults)
			if err != nil {
				return err
			}
			log.Debugf("queued hook: %d", server.qidx)
		}
		_, _, err := tx.Set("hook:idx", uint64ToString(server.qidx), nil)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return err
	}
	// all the messages have been queued.
	// notify the hooks
	for _, hook := range hooks {
		hook.Signal()
	}
	return nil
}

// Converts string to an integer
func stringToUint64(s string) uint64 {
	n, _ := strconv.ParseUint(s, 10, 64)
	return n
}

// Converts a uint to a string
func uint64ToString(u uint64) string {
	s := strings.Repeat("0", 20) + strconv.FormatUint(u, 10)
	return s[len(s)-20:]
}

type liveAOFSwitches struct {
	pos int64
}

func (s liveAOFSwitches) Error() string {
	return goingLive
}

func (server *Server) cmdAOFMD5(msg *Message) (res resp.Value, err error) {
	start := time.Now()
	vs := msg.Args[1:]
	var ok bool
	var spos, ssize string

	if vs, spos, ok = tokenval(vs); !ok || spos == "" {
		return NOMessage, errInvalidNumberOfArguments
	}
	if vs, ssize, ok = tokenval(vs); !ok || ssize == "" {
		return NOMessage, errInvalidNumberOfArguments
	}
	if len(vs) != 0 {
		return NOMessage, errInvalidNumberOfArguments
	}
	pos, err := strconv.ParseInt(spos, 10, 64)
	if err != nil || pos < 0 {
		return NOMessage, errInvalidArgument(spos)
	}
	size, err := strconv.ParseInt(ssize, 10, 64)
	if err != nil || size < 0 {
		return NOMessage, errInvalidArgument(ssize)
	}
	sum, err := server.checksum(pos, size)
	if err != nil {
		return NOMessage, err
	}
	switch msg.OutputType {
	case JSON:
		res = resp.StringValue(
			fmt.Sprintf(`{"ok":true,"md5":"%s","elapsed":"%s"}`, sum, time.Now().Sub(start)))
	case RESP:
		res = resp.SimpleStringValue(sum)
	}
	return res, nil
}

func (server *Server) cmdAOF(msg *Message) (res resp.Value, err error) {
	if server.aof == nil {
		return NOMessage, errors.New("aof disabled")
	}
	vs := msg.Args[1:]

	var ok bool
	var spos string
	if vs, spos, ok = tokenval(vs); !ok || spos == "" {
		return NOMessage, errInvalidNumberOfArguments
	}
	if len(vs) != 0 {
		return NOMessage, errInvalidNumberOfArguments
	}
	pos, err := strconv.ParseInt(spos, 10, 64)
	if err != nil || pos < 0 {
		return NOMessage, errInvalidArgument(spos)
	}
	f, err := os.Open(server.aof.Name())
	if err != nil {
		return NOMessage, err
	}
	defer f.Close()
	n, err := f.Seek(0, 2)
	if err != nil {
		return NOMessage, err
	}
	if n < pos {
		return NOMessage, errors.New("pos is too big, must be less that the aof_size of leader")
	}
	var s liveAOFSwitches
	s.pos = pos
	return NOMessage, s
}

func (server *Server) liveAOF(pos int64, conn net.Conn, rd *PipelineReader, msg *Message) error {
	server.mu.Lock()
	server.aofconnM[conn] = true
	server.mu.Unlock()
	defer func() {
		server.mu.Lock()
		delete(server.aofconnM, conn)
		server.mu.Unlock()
		conn.Close()
	}()

	if _, err := conn.Write([]byte("+OK\r\n")); err != nil {
		return err
	}

	server.mu.RLock()
	f, err := os.Open(server.aof.Name())
	server.mu.RUnlock()
	if err != nil {
		return err
	}
	defer f.Close()
	if _, err := f.Seek(pos, 0); err != nil {
		return err
	}
	cond := sync.NewCond(&sync.Mutex{})
	var mustQuit bool
	go func() {
		defer func() {
			cond.L.Lock()
			mustQuit = true
			cond.Broadcast()
			cond.L.Unlock()
		}()
		for {
			vs, err := rd.ReadMessages()
			if err != nil {
				if err != io.EOF {
					log.Error(err)
				}
				return
			}
			for _, v := range vs {
				switch v.Command() {
				default:
					log.Error("received a live command that was not QUIT")
					return
				case "quit", "":
					return
				}
			}
		}
	}()
	go func() {
		defer func() {
			cond.L.Lock()
			mustQuit = true
			cond.Broadcast()
			cond.L.Unlock()
		}()
		err := func() error {
			_, err := io.Copy(conn, f)
			if err != nil {
				return err
			}

			b := make([]byte, 4096)
			// The reader needs to be OK with the eof not
			for {
				n, err := f.Read(b)
				if err != io.EOF && n > 0 {
					if err != nil {
						return err
					}
					if _, err := conn.Write(b[:n]); err != nil {
						return err
					}
					continue
				}
				server.fcond.L.Lock()
				server.fcond.Wait()
				server.fcond.L.Unlock()
			}
		}()
		if err != nil {
			if !strings.Contains(err.Error(), "use of closed network connection") &&
				!strings.Contains(err.Error(), "bad file descriptor") {
				log.Error(err)
			}
			return
		}
	}()
	for {
		cond.L.Lock()
		if mustQuit {
			cond.L.Unlock()
			return nil
		}
		cond.Wait()
		cond.L.Unlock()
	}
}
<commit_msg>Fix aof loading problem<commit_after>package server

import (
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/tidwall/buntdb"
	"github.com/tidwall/redcon"
	"github.com/tidwall/resp"
	"github.com/tidwall/tile38/internal/log"
)

type errAOFHook struct {
	err error
}

func (err errAOFHook) Error() string {
	return fmt.Sprintf("hook: %v", err.err)
}

var errInvalidAOF = errors.New("invalid aof file")

func (server *Server) loadAOF() error {
	fi, err := server.aof.Stat()
	if err != nil {
		return err
	}
	start := time.Now()
	var count int
	defer func() {
		d := time.Now().Sub(start)
		ps := float64(count) / (float64(d) / float64(time.Second))
		suf := []string{"bytes/s", "KB/s", "MB/s", "GB/s", "TB/s"}
		bps := float64(fi.Size()) / (float64(d) / float64(time.Second))
		for i := 0; bps > 1024; i++ {
			if len(suf) == 1 {
				break
			}
			bps /= 1024
			suf = suf[1:]
		}
		byteSpeed := fmt.Sprintf("%.0f %s", bps, suf[0])
		log.Infof("AOF loaded %d commands: %.2fs, %.0f/s, %s",
			count, float64(d)/float64(time.Second), ps, byteSpeed)
	}()
	var buf []byte
	var args [][]byte
	var packet [0xFFFF]byte
	for {
		n, err := server.aof.Read(packet[:])
		if err != nil {
			if err == io.EOF {
				if len(buf) > 0 {
					return io.ErrUnexpectedEOF
				}
				return nil
			}
			return err
		}
		server.aofsz += n
		data := packet[:n]
		if len(buf) > 0 {
			data = append(buf, data...)
		}
		var complete bool
		for {
			complete, args, _, data, err = redcon.ReadNextCommand(data, args[:0])
			if err != nil {
				return err
			}
			if !complete {
				break
			}
			if len(args) > 0 {
				var msg Message
				msg.Args = msg.Args[:0]
				for _, arg := range args {
					msg.Args = append(msg.Args, string(arg))
				}
				if _, _, err := server.command(&msg, nil); err != nil {
					if commandErrIsFatal(err) {
						return err
					}
				}
				count++
			}
		}
		if len(data) > 0 {
			buf = append(buf[:0], data...)
		} else if len(buf) > 0 {
			buf = buf[:0]
		}
	}
}

// func qlower(s []byte) string {
// 	if len(s) == 3 {
// 		if s[0] == 'S' && s[1] == 'E' && s[2] == 'T' {
// 			return "set"
// 		}
// 		if s[0] == 'D' && s[1] == 'E' && s[2] == 'L' {
// 			return "del"
// 		}
// 	}
// 	for i := 0; i < len(s); i++ {
// 		if s[i] >= 'A' || s[i] <= 'Z' {
// 			return strings.ToLower(string(s))
// 		}
// 	}
// 	return string(s)
// }

func commandErrIsFatal(err error) bool {
	// FSET (and other writable commands) may return errors that we need
	// to ignore during the loading process. These errors may occur (though unlikely)
	// due to the aof rewrite operation.
	switch err {
	case errKeyNotFound, errIDNotFound:
		return false
	}
	return true
}

func (server *Server) flushAOF() {
	if len(server.aofbuf) > 0 {
		_, err := server.aof.Write(server.aofbuf)
		if err != nil {
			panic(err)
		}
		server.aofbuf = server.aofbuf[:0]
	}
}

func (server *Server) writeAOF(args []string, d *commandDetailsT) error {

	if d != nil && !d.updated {
		// just ignore writes if the command did not update
		return nil
	}

	if server.shrinking {
		nargs := make([]string, len(args))
		copy(nargs, args)
		server.shrinklog = append(server.shrinklog, nargs)
	}

	if server.aof != nil {
		atomic.StoreInt32(&server.aofdirty, 1) // prewrite optimization flag
		n := len(server.aofbuf)
		server.aofbuf = redcon.AppendArray(server.aofbuf, len(args))
		for _, arg := range args {
			server.aofbuf = redcon.AppendBulkString(server.aofbuf, arg)
		}
		server.aofsz += len(server.aofbuf) - n
	}

	// notify aof live connections that we have new data
	server.fcond.L.Lock()
	server.fcond.Broadcast()
	server.fcond.L.Unlock()

	// process geofences
	if d != nil {
		// webhook geofences
		if server.config.followHost() == "" {
			// for leader only
			if d.parent {
				// queue children
				for _, d := range d.children {
					if err := server.queueHooks(d); err != nil {
						return err
					}
				}
			} else {
				// queue parent
				if err := server.queueHooks(d); err != nil {
					return err
				}
			}
		}
		// live geofences
		server.lcond.L.Lock()
		if d.parent {
			// queue children
			for _, d := range d.children {
				server.lstack = append(server.lstack, d)
			}
		} else {
			// queue parent
			server.lstack = append(server.lstack, d)
		}
		server.lcond.Broadcast()
		server.lcond.L.Unlock()
	}
	return nil
}

func (server *Server) queueHooks(d *commandDetailsT) error {
	// big list of all of the messages
	var hmsgs []string
	var hooks []*Hook
	// find the hook by the key
	if hm, ok := server.hookcols[d.key]; ok {
		for _, hook := range hm {
			// match the fence
			msgs := FenceMatch(hook.Name, hook.ScanWriter, hook.Fence, hook.Metas, d)
			if len(msgs) > 0 {
				if hook.channel {
					server.Publish(hook.Name, msgs...)
				} else {
					// append each msg to the big list
					hmsgs = append(hmsgs, msgs...)
					hooks = append(hooks, hook)
				}
			}
		}
	}
	if len(hmsgs) == 0 {
		return nil
	}

	// queue the message in the buntdb database
	err := server.qdb.Update(func(tx *buntdb.Tx) error {
		for _, msg := range hmsgs {
			server.qidx++ // increment the log id
			key := hookLogPrefix + uint64ToString(server.qidx)
			_, _, err := tx.Set(key, string(msg), hookLogSetDefaults)
			if err != nil {
				return err
			}
			log.Debugf("queued hook: %d", server.qidx)
		}
		_, _, err := tx.Set("hook:idx", uint64ToString(server.qidx), nil)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return err
	}
	// all the messages have been queued.
	// notify the hooks
	for _, hook := range hooks {
		hook.Signal()
	}
	return nil
}

// Converts string to an integer
func stringToUint64(s string) uint64 {
	n, _ := strconv.ParseUint(s, 10, 64)
	return n
}

// Converts a uint to a string
func uint64ToString(u uint64) string {
	s := strings.Repeat("0", 20) + strconv.FormatUint(u, 10)
	return s[len(s)-20:]
}

type liveAOFSwitches struct {
	pos int64
}

func (s liveAOFSwitches) Error() string {
	return goingLive
}

func (server *Server) cmdAOFMD5(msg *Message) (res resp.Value, err error) {
	start := time.Now()
	vs := msg.Args[1:]
	var ok bool
	var spos, ssize string

	if vs, spos, ok = tokenval(vs); !ok || spos == "" {
		return NOMessage, errInvalidNumberOfArguments
	}
	if vs, ssize, ok = tokenval(vs); !ok || ssize == "" {
		return NOMessage, errInvalidNumberOfArguments
	}
	if len(vs) != 0 {
		return NOMessage, errInvalidNumberOfArguments
	}
	pos, err := strconv.ParseInt(spos, 10, 64)
	if err != nil || pos < 0 {
		return NOMessage, errInvalidArgument(spos)
	}
	size, err := strconv.ParseInt(ssize, 10, 64)
	if err != nil || size < 0 {
		return NOMessage, errInvalidArgument(ssize)
	}
	sum, err := server.checksum(pos, size)
	if err != nil {
		return NOMessage, err
	}
	switch msg.OutputType {
	case JSON:
		res = resp.StringValue(
			fmt.Sprintf(`{"ok":true,"md5":"%s","elapsed":"%s"}`, sum, time.Now().Sub(start)))
	case RESP:
		res = resp.SimpleStringValue(sum)
	}
	return res, nil
}

func (server *Server) cmdAOF(msg *Message) (res resp.Value, err error) {
	if server.aof == nil {
		return NOMessage, errors.New("aof disabled")
	}
	vs := msg.Args[1:]

	var ok bool
	var spos string
	if vs, spos, ok = tokenval(vs); !ok || spos == "" {
		return NOMessage, errInvalidNumberOfArguments
	}
	if len(vs) != 0 {
		return NOMessage, errInvalidNumberOfArguments
	}
	pos, err := strconv.ParseInt(spos, 10, 64)
	if err != nil || pos < 0 {
		return NOMessage, errInvalidArgument(spos)
	}
	f, err := os.Open(server.aof.Name())
	if err != nil {
		return NOMessage, err
	}
	defer f.Close()
	n, err := f.Seek(0, 2)
	if err != nil {
		return NOMessage, err
	}
	if n < pos {
		return NOMessage, errors.New("pos is too big, must be less that the aof_size of leader")
	}
	var s liveAOFSwitches
	s.pos = pos
	return NOMessage, s
}

func (server *Server) liveAOF(pos int64, conn net.Conn, rd *PipelineReader, msg *Message) error {
	server.mu.Lock()
	server.aofconnM[conn] = true
	server.mu.Unlock()
	defer func() {
		server.mu.Lock()
		delete(server.aofconnM, conn)
		server.mu.Unlock()
		conn.Close()
	}()

	if _, err := conn.Write([]byte("+OK\r\n")); err != nil {
		return err
	}

	server.mu.RLock()
	f, err := os.Open(server.aof.Name())
	server.mu.RUnlock()
	if err != nil {
		return err
	}
	defer f.Close()
	if _, err := f.Seek(pos, 0); err != nil {
		return err
	}
	cond := sync.NewCond(&sync.Mutex{})
	var mustQuit bool
	go func() {
		defer func() {
			cond.L.Lock()
			mustQuit = true
			cond.Broadcast()
			cond.L.Unlock()
		}()
		for {
			vs, err := rd.ReadMessages()
			if err != nil {
				if err != io.EOF {
					log.Error(err)
				}
				return
			}
			for _, v := range vs {
				switch v.Command() {
				default:
					log.Error("received a live command that was not QUIT")
					return
				case "quit", "":
					return
				}
			}
		}
	}()
	go func() {
		defer func() {
			cond.L.Lock()
			mustQuit = true
			cond.Broadcast()
			cond.L.Unlock()
		}()
		err := func() error {
			_, err := io.Copy(conn, f)
			if err != nil {
				return err
			}

			b := make([]byte, 4096)
			// The reader needs to be OK with the eof not
			for {
				n, err := f.Read(b)
				if err != io.EOF && n > 0 {
					if err != nil {
						return err
					}
					if _, err := conn.Write(b[:n]); err != nil {
						return err
					}
					continue
				}
				server.fcond.L.Lock()
				server.fcond.Wait()
				server.fcond.L.Unlock()
			}
		}()
		if err != nil {
			if !strings.Contains(err.Error(), "use of closed network connection") &&
				!strings.Contains(err.Error(), "bad file descriptor") {
				log.Error(err)
			}
			return
		}
	}()
	for {
		cond.L.Lock()
		if mustQuit {
			cond.L.Unlock()
			return nil
		}
		cond.Wait()
		cond.L.Unlock()
	}
}
<|endoftext|>