<commit_before>package core

import (
    "fmt"
    "io/ioutil"
    "os"
    "os/user"
    "time"

    "github.com/astaxie/beego/config"
)

const (
    CONF_NAME string = "app.conf"
    APP_NAME string = "watcher"
    permMode os.FileMode = 0666
)

// Singleton
var instantiated *Settings = nil

func SettingsPtr() *Settings {
    if instantiated == nil {
        instantiated = new(Settings);
    }
    return instantiated;
}

type Settings struct {
    HomeDir string
    Idle time.Duration
    Work time.Duration
    IdleConst time.Duration
    WorkConst time.Duration
    TotalIdle time.Duration
    TotalWork time.Duration
    Tick time.Duration
    Protect time.Duration
    StartTime time.Time
    UpTime time.Duration
    cfg config.ConfigContainer
    dir string
    Stage string
    Last_stage string
    Ready bool
    Paused bool
    Idle_work_title string
    Idle_work_body string
    Idle_work_image string
    Work_idle_title string
    Work_idle_body string
    Work_idle_image string
    Unfinished_idle_title string
    Unfinished_idle_body string
    Unfinished_idle_image string
    Alarm_file string
    Maximum_notify int
    Notify_count int
    Webserver_address string
}

func (s *Settings) GetHomeDir() (string, error) {

    if len(s.HomeDir) != 0 {
        return s.HomeDir, nil
    }

    user, err := user.Current()
    if err != nil {
        return s.HomeDir, err
    }

    s.HomeDir = user.HomeDir

    return s.HomeDir, nil
}

func (s *Settings) Init() *Settings {

    fmt.Printf("Settings init ...\n")

    if len(s.HomeDir) == 0 {
        s.GetHomeDir()
    }

    s.StartTime = time.Now()
    s.dir = fmt.Sprintf("%s/.%s/", s.HomeDir, APP_NAME)

    s.Paused = false
    s.WorkConst = 2700 * time.Second // 45min
    s.IdleConst = 900 * time.Second // 15min
    s.Tick = 1 * time.Second
    s.Protect = 30 * time.Second
    s.Stage = "work" // work|idle|signal
    s.Idle_work_title = "Внимание"
    s.Idle_work_body = "Ты отдыхаешь уже {idle_time} пора приниматся за работу!"
    s.Idle_work_image = ""
    s.Work_idle_title = "Внимание"
    s.Work_idle_body = "Ты работешь уже {work_time}, иди отдохни, выпей чаю!"
    s.Work_idle_image = ""
    s.Unfinished_idle_title = "Внимание"
    s.Unfinished_idle_body = "{idle} ещё не прошло, иди отдохни, выпей чаю!"
    s.Unfinished_idle_image = ""
    s.Alarm_file = "aperture_logo_bells_01_01.wav"
    s.Webserver_address = "0.0.0.0:8080"
    s.Maximum_notify = 3

//    create app conf dir
    fileList, _ := ioutil.ReadDir(s.HomeDir)

    var exist bool
    for _, file := range fileList {
        if file.Name() == "."+APP_NAME {
            exist = true
            break
        }
    }

    if !exist {
        dir := fmt.Sprintf(`%s/.%s`, s.HomeDir, APP_NAME)
        fmt.Printf("create dir: %s\n", dir)
        os.MkdirAll(dir, os.ModePerm)
    }

    return s
}

func (s *Settings) Save() (*Settings, error) {

    if len(s.HomeDir) == 0 {
        s.GetHomeDir()
    }

    if _, err := os.Stat(s.dir + CONF_NAME); os.IsNotExist(err) {
        ioutil.WriteFile(s.dir + CONF_NAME, []byte{}, permMode)
    }

    cfg, err := config.NewConfig("ini", s.dir + CONF_NAME)
    if err != nil {
        return s, err
    }

    cfg.Set("paused", fmt.Sprintf("%t", s.Paused))
    cfg.Set("idle", fmt.Sprintf("%v", s.IdleConst.Seconds()))
    cfg.Set("work", fmt.Sprintf("%v", s.WorkConst.Seconds()))
    cfg.Set("protect", fmt.Sprintf("%v", s.Protect.Seconds()))
    cfg.Set("idle_work_title", s.Idle_work_title)
    cfg.Set("idle_work_body", s.Idle_work_body)
    cfg.Set("idle_work_image", s.Idle_work_image)
    cfg.Set("work_idle_title", s.Work_idle_title)
    cfg.Set("work_idle_body", s.Work_idle_body)
    cfg.Set("work_idle_image", s.Work_idle_image)
    cfg.Set("unfinished_idle_title", s.Unfinished_idle_title)
    cfg.Set("unfinished_idle_body", s.Unfinished_idle_body)
    cfg.Set("unfinished_idle_image", s.Unfinished_idle_image)
    cfg.Set("alarm_file", s.Alarm_file)
    cfg.Set("webserver_address", s.Webserver_address)
    cfg.Set("maximum_notify", string(s.Maximum_notify))

    if err := cfg.SaveConfigFile(s.dir + CONF_NAME); err != nil {
        fmt.Printf("err with create conf file: %s\n", s.dir + CONF_NAME)
        return s, err
    }

    return s, nil
}

func (s *Settings) Load() (*Settings, error) {

    fmt.Printf("read config: %s\n", s.dir + CONF_NAME)

    if _, err := os.Stat(s.dir + CONF_NAME); os.IsNotExist(err) {
        return s.Save()
    }

    // read config file
    cfg, err := config.NewConfig("ini", s.dir + CONF_NAME)
    if err != nil {
        return s, err
    }

    second := func(key string) time.Duration {
        val, _ := cfg.Int(key)
        return time.Duration(val) * time.Second
    }

    s.Ready = true
    s.Paused, _ = cfg.Bool("paused")
    s.IdleConst = second("idle")
    s.WorkConst = second("work")
    s.Protect = second("protect")
    s.Idle_work_title = cfg.String("idle_work_title")
    s.Idle_work_body = cfg.String("idle_work_body")
    s.Idle_work_image = cfg.String("idle_work_image")
    s.Work_idle_title = cfg.String("work_idle_title")
    s.Work_idle_body = cfg.String("work_idle_body")
    s.Work_idle_image = cfg.String("work_idle_image")
    s.Unfinished_idle_title = cfg.String("unfinished_idle_title")
    s.Unfinished_idle_body = cfg.String("unfinished_idle_body")
    s.Unfinished_idle_image = cfg.String("unfinished_idle_image")
    s.Alarm_file = cfg.String("alarm_file")
    s.Webserver_address = cfg.String("webserver_address")
    s.Maximum_notify, _ = cfg.Int("maximum_notify")

    return s, nil
}
<commit_msg>fix maximum_notify variable<commit_after>package core

import (
    "fmt"
    "io/ioutil"
    "os"
    "os/user"
    "time"

    "github.com/astaxie/beego/config"
)

const (
    CONF_NAME string = "app.conf"
    APP_NAME string = "watcher"
    permMode os.FileMode = 0666
)

// Singleton
var instantiated *Settings = nil

func SettingsPtr() *Settings {
    if instantiated == nil {
        instantiated = new(Settings);
    }
    return instantiated;
}

type Settings struct {
    HomeDir string
    Idle time.Duration
    Work time.Duration
    IdleConst time.Duration
    WorkConst time.Duration
    TotalIdle time.Duration
    TotalWork time.Duration
    Tick time.Duration
    Protect time.Duration
    StartTime time.Time
    UpTime time.Duration
    cfg config.ConfigContainer
    dir string
    Stage string
    Last_stage string
    Ready bool
    Paused bool
    Idle_work_title string
    Idle_work_body string
    Idle_work_image string
    Work_idle_title string
    Work_idle_body string
    Work_idle_image string
    Unfinished_idle_title string
    Unfinished_idle_body string
    Unfinished_idle_image string
    Alarm_file string
    Maximum_notify int
    Notify_count int
    Webserver_address string
}

func (s *Settings) GetHomeDir() (string, error) {

    if len(s.HomeDir) != 0 {
        return s.HomeDir, nil
    }

    user, err := user.Current()
    if err != nil {
        return s.HomeDir, err
    }

    s.HomeDir = user.HomeDir

    return s.HomeDir, nil
}

func (s *Settings) Init() *Settings {

    fmt.Printf("Settings init ...\n")

    if len(s.HomeDir) == 0 {
        s.GetHomeDir()
    }

    s.StartTime = time.Now()
    s.dir = fmt.Sprintf("%s/.%s/", s.HomeDir, APP_NAME)

    s.Paused = false
    s.WorkConst = 2700 * time.Second // 45min
    s.IdleConst = 900 * time.Second // 15min
    s.Tick = 1 * time.Second
    s.Protect = 30 * time.Second
    s.Stage = "work" // work|idle|signal
    s.Idle_work_title = "Внимание"
    s.Idle_work_body = "Ты отдыхаешь уже {idle_time} пора приниматся за работу!"
    s.Idle_work_image = ""
    s.Work_idle_title = "Внимание"
    s.Work_idle_body = "Ты работешь уже {work_time}, иди отдохни, выпей чаю!"
    s.Work_idle_image = ""
    s.Unfinished_idle_title = "Внимание"
    s.Unfinished_idle_body = "{idle} ещё не прошло, иди отдохни, выпей чаю!"
    s.Unfinished_idle_image = ""
    s.Alarm_file = "aperture_logo_bells_01_01.wav"
    s.Webserver_address = "0.0.0.0:8080"
    s.Maximum_notify = 3

//    create app conf dir
    fileList, _ := ioutil.ReadDir(s.HomeDir)

    var exist bool
    for _, file := range fileList {
        if file.Name() == "."+APP_NAME {
            exist = true
            break
        }
    }

    if !exist {
        dir := fmt.Sprintf(`%s/.%s`, s.HomeDir, APP_NAME)
        fmt.Printf("create dir: %s\n", dir)
        os.MkdirAll(dir, os.ModePerm)
    }

    return s
}

func (s *Settings) Save() (*Settings, error) {

    if len(s.HomeDir) == 0 {
        s.GetHomeDir()
    }

    if _, err := os.Stat(s.dir + CONF_NAME); os.IsNotExist(err) {
        ioutil.WriteFile(s.dir + CONF_NAME, []byte{}, permMode)
    }

    cfg, err := config.NewConfig("ini", s.dir + CONF_NAME)
    if err != nil {
        return s, err
    }

    cfg.Set("paused", fmt.Sprintf("%t", s.Paused))
    cfg.Set("idle", fmt.Sprintf("%v", s.IdleConst.Seconds()))
    cfg.Set("work", fmt.Sprintf("%v", s.WorkConst.Seconds()))
    cfg.Set("protect", fmt.Sprintf("%v", s.Protect.Seconds()))
    cfg.Set("idle_work_title", s.Idle_work_title)
    cfg.Set("idle_work_body", s.Idle_work_body)
    cfg.Set("idle_work_image", s.Idle_work_image)
    cfg.Set("work_idle_title", s.Work_idle_title)
    cfg.Set("work_idle_body", s.Work_idle_body)
    cfg.Set("work_idle_image", s.Work_idle_image)
    cfg.Set("unfinished_idle_title", s.Unfinished_idle_title)
    cfg.Set("unfinished_idle_body", s.Unfinished_idle_body)
    cfg.Set("unfinished_idle_image", s.Unfinished_idle_image)
    cfg.Set("alarm_file", s.Alarm_file)
    cfg.Set("webserver_address", s.Webserver_address)
    cfg.Set("maximum_notify", fmt.Sprintf("%d", s.Maximum_notify))

    if err := cfg.SaveConfigFile(s.dir + CONF_NAME); err != nil {
        fmt.Printf("err with create conf file: %s\n", s.dir + CONF_NAME)
        return s, err
    }

    return s, nil
}

func (s *Settings) Load() (*Settings, error) {

    fmt.Printf("read config: %s\n", s.dir + CONF_NAME)

    if _, err := os.Stat(s.dir + CONF_NAME); os.IsNotExist(err) {
        return s.Save()
    }

    // read config file
    cfg, err := config.NewConfig("ini", s.dir + CONF_NAME)
    if err != nil {
        return s, err
    }

    second := func(key string) time.Duration {
        val, _ := cfg.Int(key)
        return time.Duration(val) * time.Second
    }

    s.Ready = true
    s.Paused, _ = cfg.Bool("paused")
    s.IdleConst = second("idle")
    s.WorkConst = second("work")
    s.Protect = second("protect")
    s.Idle_work_title = cfg.String("idle_work_title")
    s.Idle_work_body = cfg.String("idle_work_body")
    s.Idle_work_image = cfg.String("idle_work_image")
    s.Work_idle_title = cfg.String("work_idle_title")
    s.Work_idle_body = cfg.String("work_idle_body")
    s.Work_idle_image = cfg.String("work_idle_image")
    s.Unfinished_idle_title = cfg.String("unfinished_idle_title")
    s.Unfinished_idle_body = cfg.String("unfinished_idle_body")
    s.Unfinished_idle_image = cfg.String("unfinished_idle_image")
    s.Alarm_file = cfg.String("alarm_file")
    s.Webserver_address = cfg.String("webserver_address")
    s.Maximum_notify, _ = cfg.Int("maximum_notify")

    return s, nil
}
<|endoftext|>


<commit_before>package core

/*
#cgo CFLAGS: -I./src/include
#include "lwip/tcp.h"
*/
import "C"
import (
	"context"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net"
	"sync"
	"unsafe"
)

type tcpConn struct {
	sync.Mutex

	pcb         *C.struct_tcp_pcb
	handler     ConnectionHandler
	network     string
	remoteAddr  net.Addr
	localAddr   net.Addr
	connKeyArg  unsafe.Pointer
	connKey     uint32
	closing     bool
	localClosed bool
	aborting    bool
	ctx         context.Context
	cancel      context.CancelFunc

	// Data from remote not yet write to local will buffer into this channel.
	localWriteCh    chan []byte
	localWriteSubCh chan []byte
}

func checkTCPConns() {
	tcpConns.Range(func(_, c interface{}) bool {
		state := c.(*tcpConn).pcb.state
		if c.(*tcpConn).pcb == nil ||
			state == C.CLOSED ||
			state == C.CLOSE_WAIT {
			c.(*tcpConn).Release()
		}
		return true
	})
}

func NewTCPConnection(pcb *C.struct_tcp_pcb, handler ConnectionHandler) (Connection, error) {
	// prepare key
	connKeyArg := NewConnKeyArg()
	connKey := rand.Uint32()
	SetConnKeyVal(unsafe.Pointer(connKeyArg), connKey)

	if tcpConnectionHandler == nil {
		return nil, errors.New("no registered TCP connection handlers found")
	}

	ctx, cancel := context.WithCancel(context.Background())

	conn := &tcpConn{
		pcb:             pcb,
		handler:         handler,
		network:         "tcp",
		localAddr:       ParseTCPAddr(IPAddrNTOA(pcb.remote_ip), uint16(pcb.remote_port)),
		remoteAddr:      ParseTCPAddr(IPAddrNTOA(pcb.local_ip), uint16(pcb.local_port)),
		connKeyArg:      connKeyArg,
		connKey:         connKey,
		closing:         false,
		localClosed:     false,
		aborting:        false,
		ctx:             ctx,
		cancel:          cancel,
		localWriteCh:    make(chan []byte, 32),
		localWriteSubCh: make(chan []byte, 1),
	}

	// Associate conn with key and save to the global map.
	tcpConns.Store(connKey, conn)

	go checkTCPConns()

	// Pass the key as arg for subsequent tcp callbacks.
	C.tcp_arg(pcb, unsafe.Pointer(connKeyArg))

	SetTCPRecvCallback(pcb)
	SetTCPSentCallback(pcb)
	SetTCPErrCallback(pcb)
	SetTCPPollCallback(pcb, C.u8_t(1)) // interval 1 means Poll will be called twice a second

	err := handler.Connect(conn, conn.RemoteAddr())
	if err != nil {
		return nil, err
	}
	return conn, nil
}

func (conn *tcpConn) RemoteAddr() net.Addr {
	return conn.remoteAddr
}

func (conn *tcpConn) LocalAddr() net.Addr {
	return conn.localAddr
}

func (conn *tcpConn) Receive(data []byte) error {
	if conn.isClosing() {
		return errors.New(fmt.Sprintf("conn %v <-> %v is closing", conn.LocalAddr(), conn.RemoteAddr()))
	}

	err := conn.handler.DidReceive(conn, data)
	if err != nil {
		return errors.New(fmt.Sprintf("write proxy failed: %v", err))
	}

	C.tcp_recved(conn.pcb, C.u16_t(len(data)))

	return nil
}

func (conn *tcpConn) tryWriteLocal() {
	lwipMutex.Lock()
	defer lwipMutex.Unlock()

Loop:
	for {
		// Using 2 select to ensure data in localWriteSubCh will be drained first.
		select {
		case data := <-conn.localWriteSubCh:
			written, err := conn.tcpWrite(data)
			if !written || err != nil {
				// TODO the check is for debug purpose, and should be removed later
				if len(conn.localWriteSubCh) > 0 {
					log.Fatal("send to non-empty sub chan")
				}
				// Data not written, buffer again.
				conn.localWriteSubCh <- data
				break Loop
			}
		default:
		}

		select {
		case data := <-conn.localWriteSubCh:
			written, err := conn.tcpWrite(data)
			if !written || err != nil {
				// TODO the check is for debug purpose, and should be removed later
				if len(conn.localWriteSubCh) > 0 {
					log.Fatal("send to non-empty sub chan")
				}
				// Data not written, buffer again.
				conn.localWriteSubCh <- data
				break Loop
			}
		case data := <-conn.localWriteCh:
			written, err := conn.tcpWrite(data)
			if !written || err != nil {
				// TODO the check is for debug purpose, and should be removed later
				if len(conn.localWriteSubCh) > 0 {
					log.Fatal("send to non-empty sub chan")
				}
				// If writing is not success, buffer to the sub channel, and next time
				// we try to read from the sub channel first. Using a sub channel here
				// because the data must be sent in correct order and we have no way
				// to prepend data to the head of a channel.
				conn.localWriteSubCh <- data
				break Loop
			}
		default:
			break Loop
		}
	}

	// TODO the check is for debug purpose, and should be removed later
	if conn.pcb == nil {
		log.Fatal("tcp_output nil pcb")
	}
	// Actually send data.
	err := C.tcp_output(conn.pcb)
	if err != C.ERR_OK {
		log.Printf("tcp_output error with lwip error code: %v", int(err))
	}
}

// tcpWrite enqueues data to snd_buf, and treats ERR_MEM returned by tcp_write not an error,
// but instead tells the caller that data is not successfully enqueued, and should try
// again another time. By calling this function, the lwIP thread is assumed to be already
// locked by the caller.
func (conn *tcpConn) tcpWrite(data []byte) (bool, error) {
	if len(data) <= int(conn.pcb.snd_buf) {
		// Enqueue data, data copy here! Copying is required because lwIP must keep the data until they
		// are acknowledged (receiving ACK segments) by other hosts for retransmission purposes, it's
		// not obvious how to implement zero-copy here.
		err := C.tcp_write(conn.pcb, unsafe.Pointer(&data[0]), C.u16_t(len(data)), C.TCP_WRITE_FLAG_COPY)
		if err == C.ERR_OK {
			return true, nil
		} else if err != C.ERR_MEM {
			return false, errors.New(fmt.Sprintf("lwip tcp_write failed with error code: %v", int(err)))
		}
	}
	return false, nil
}

func (conn *tcpConn) Write(data []byte) (int, error) {
	if conn.isLocalClosed() {
		return 0, errors.New(fmt.Sprintf("conn %v <-> %v is closing", conn.LocalAddr(), conn.RemoteAddr()))
	}

	var written = false
	var err error

	// If there isn't any pending data left, we can try to write the data first to avoid one copy,
	// if there is pending data not yet sent, we must copy and buffer the data in order to maintain
	// the transmission order.
	if !conn.hasPendingLocalData() {
		lwipMutex.Lock()
		written, err = conn.tcpWrite(data)
		lwipMutex.Unlock()
		if err != nil {
			return 0, err
		}
	}

	if !written {
		select {
		// Buffer the data here and try sending it later, one could set a smaller localWriteCh size
		// to limit data copying times and memory usage, by sacrificing performance. But writing data
		// to local is quite fast, thus it should be safe even has a size of 1 localWriteCh.
		case conn.localWriteCh <- append([]byte(nil), data...): // data copy here!
		case <-conn.ctx.Done():
			return 0, conn.ctx.Err()
		}
	}

	// Try to send pending data if any, and call tcp_output().
	go conn.tryWriteLocal()

	return len(data), nil
}

func (conn *tcpConn) Sent(len uint16) error {
	conn.handler.DidSend(conn, len)
	// Some packets are acknowledged by local client, check if any pending data to send.
	return conn.CheckState()
}

func (conn *tcpConn) isClosing() bool {
	conn.Lock()
	defer conn.Unlock()
	return conn.closing
}

func (conn *tcpConn) isAborting() bool {
	conn.Lock()
	defer conn.Unlock()
	return conn.aborting
}

func (conn *tcpConn) isLocalClosed() bool {
	conn.Lock()
	defer conn.Unlock()
	return conn.localClosed
}

func (conn *tcpConn) hasPendingLocalData() bool {
	if len(conn.localWriteCh) > 0 || len(conn.localWriteSubCh) > 0 {
		return true
	}
	return false
}

func (conn *tcpConn) CheckState() error {
	// Still have data to send
	if conn.hasPendingLocalData() && !conn.isLocalClosed() {
		go conn.tryWriteLocal()
		// Return and wait for the Sent() callback to be called, and then check again.
		return NewLWIPError(LWIP_ERR_OK)
	}

	if conn.isClosing() || conn.isLocalClosed() {
		conn.closeInternal()
	}

	if conn.isAborting() {
		conn.abortInternal()
		return NewLWIPError(LWIP_ERR_ABRT)
	}

	return NewLWIPError(LWIP_ERR_OK)
}

func (conn *tcpConn) Close() error {
	conn.Lock()
	defer conn.Unlock()

	// Close maybe called outside of lwIP thread, we should not call tcp_close() in this
	// function, instead just make a flag to indicate we are closing the connection.
	conn.closing = true
	return nil
}

func (conn *tcpConn) setLocalClosed() error {
	conn.Lock()
	defer conn.Unlock()

	conn.localClosed = true
	return nil
}

func (conn *tcpConn) closeInternal() error {
	if conn.pcb == nil {
		log.Fatal("nil pcb when close, maybe aborted already")
	}

	C.tcp_arg(conn.pcb, nil)
	C.tcp_recv(conn.pcb, nil)
	C.tcp_sent(conn.pcb, nil)
	C.tcp_err(conn.pcb, nil)
	C.tcp_poll(conn.pcb, nil, 0)

	conn.Release()

	conn.cancel()

	err := C.tcp_close(conn.pcb)
	if err == C.ERR_OK {
		return nil
	} else {
		return errors.New(fmt.Sprint("close TCP connection failed, lwip error code %d", int(err)))
	}
}

func (conn *tcpConn) abortInternal() {
	log.Printf("abort TCP connection %v->%v", conn.LocalAddr(), conn.RemoteAddr())
	conn.Release()
	C.tcp_abort(conn.pcb)
}

func (conn *tcpConn) Abort() {
	conn.Lock()
	defer conn.Unlock()

	conn.aborting = true
}

// The corresponding pcb is already freed when this callback is called
func (conn *tcpConn) Err(err error) {
	log.Printf("error on TCP connection %v->%v: %v", conn.LocalAddr(), conn.RemoteAddr(), err)
	conn.Release()
	conn.cancel()
	conn.handler.DidClose(conn)
}

func (conn *tcpConn) LocalDidClose() error {
	log.Printf("local close TCP connection %v->%v", conn.LocalAddr(), conn.RemoteAddr())
	conn.handler.LocalDidClose(conn)
	conn.setLocalClosed()    // flag closing
	return conn.CheckState() // check pending data
}

func (conn *tcpConn) Release() {
	if _, found := tcpConns.Load(conn.connKey); found {
		FreeConnKeyArg(conn.connKeyArg)
		tcpConns.Delete(conn.connKey)
	}
	log.Printf("ended TCP connection %v->%v", conn.LocalAddr(), conn.RemoteAddr())
}

func (conn *tcpConn) Poll() error {
	return conn.CheckState()
}
<commit_msg>checks are causing error and seems not necessary<commit_after>package core

/*
#cgo CFLAGS: -I./src/include
#include "lwip/tcp.h"
*/
import "C"
import (
	"context"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net"
	"sync"
	"unsafe"
)

type tcpConn struct {
	sync.Mutex

	pcb         *C.struct_tcp_pcb
	handler     ConnectionHandler
	network     string
	remoteAddr  net.Addr
	localAddr   net.Addr
	connKeyArg  unsafe.Pointer
	connKey     uint32
	closing     bool
	localClosed bool
	aborting    bool
	ctx         context.Context
	cancel      context.CancelFunc

	// Data from remote not yet write to local will buffer into this channel.
	localWriteCh    chan []byte
	localWriteSubCh chan []byte
}

// func checkTCPConns() {
// 	tcpConns.Range(func(_, c interface{}) bool {
// 		state := c.(*tcpConn).pcb.state
// 		if c.(*tcpConn).pcb == nil ||
// 			state == C.CLOSED ||
// 			state == C.CLOSE_WAIT {
// 			c.(*tcpConn).Release()
// 		}
// 		return true
// 	})
// }

func NewTCPConnection(pcb *C.struct_tcp_pcb, handler ConnectionHandler) (Connection, error) {
	// prepare key
	connKeyArg := NewConnKeyArg()
	connKey := rand.Uint32()
	SetConnKeyVal(unsafe.Pointer(connKeyArg), connKey)

	if tcpConnectionHandler == nil {
		return nil, errors.New("no registered TCP connection handlers found")
	}

	ctx, cancel := context.WithCancel(context.Background())

	conn := &tcpConn{
		pcb:             pcb,
		handler:         handler,
		network:         "tcp",
		localAddr:       ParseTCPAddr(IPAddrNTOA(pcb.remote_ip), uint16(pcb.remote_port)),
		remoteAddr:      ParseTCPAddr(IPAddrNTOA(pcb.local_ip), uint16(pcb.local_port)),
		connKeyArg:      connKeyArg,
		connKey:         connKey,
		closing:         false,
		localClosed:     false,
		aborting:        false,
		ctx:             ctx,
		cancel:          cancel,
		localWriteCh:    make(chan []byte, 32),
		localWriteSubCh: make(chan []byte, 1),
	}

	// Associate conn with key and save to the global map.
	tcpConns.Store(connKey, conn)

	// go checkTCPConns()

	// Pass the key as arg for subsequent tcp callbacks.
	C.tcp_arg(pcb, unsafe.Pointer(connKeyArg))

	SetTCPRecvCallback(pcb)
	SetTCPSentCallback(pcb)
	SetTCPErrCallback(pcb)
	SetTCPPollCallback(pcb, C.u8_t(1)) // interval 1 means Poll will be called twice a second

	err := handler.Connect(conn, conn.RemoteAddr())
	if err != nil {
		return nil, err
	}
	return conn, nil
}

func (conn *tcpConn) RemoteAddr() net.Addr {
	return conn.remoteAddr
}

func (conn *tcpConn) LocalAddr() net.Addr {
	return conn.localAddr
}

func (conn *tcpConn) Receive(data []byte) error {
	if conn.isClosing() {
		return errors.New(fmt.Sprintf("conn %v <-> %v is closing", conn.LocalAddr(), conn.RemoteAddr()))
	}

	err := conn.handler.DidReceive(conn, data)
	if err != nil {
		return errors.New(fmt.Sprintf("write proxy failed: %v", err))
	}

	C.tcp_recved(conn.pcb, C.u16_t(len(data)))

	return nil
}

func (conn *tcpConn) tryWriteLocal() {
	lwipMutex.Lock()
	defer lwipMutex.Unlock()

Loop:
	for {
		// Using 2 select to ensure data in localWriteSubCh will be drained first.
		select {
		case data := <-conn.localWriteSubCh:
			written, err := conn.tcpWrite(data)
			if !written || err != nil {
				// TODO the check is for debug purpose, and should be removed later
				if len(conn.localWriteSubCh) > 0 {
					log.Fatal("send to non-empty sub chan")
				}
				// Data not written, buffer again.
				conn.localWriteSubCh <- data
				break Loop
			}
		default:
		}

		select {
		case data := <-conn.localWriteSubCh:
			written, err := conn.tcpWrite(data)
			if !written || err != nil {
				// TODO the check is for debug purpose, and should be removed later
				if len(conn.localWriteSubCh) > 0 {
					log.Fatal("send to non-empty sub chan")
				}
				// Data not written, buffer again.
				conn.localWriteSubCh <- data
				break Loop
			}
		case data := <-conn.localWriteCh:
			written, err := conn.tcpWrite(data)
			if !written || err != nil {
				// TODO the check is for debug purpose, and should be removed later
				if len(conn.localWriteSubCh) > 0 {
					log.Fatal("send to non-empty sub chan")
				}
				// If writing is not success, buffer to the sub channel, and next time
				// we try to read from the sub channel first. Using a sub channel here
				// because the data must be sent in correct order and we have no way
				// to prepend data to the head of a channel.
				conn.localWriteSubCh <- data
				break Loop
			}
		default:
			break Loop
		}
	}

	// TODO the check is for debug purpose, and should be removed later
	if conn.pcb == nil {
		log.Fatal("tcp_output nil pcb")
	}
	// Actually send data.
	err := C.tcp_output(conn.pcb)
	if err != C.ERR_OK {
		log.Printf("tcp_output error with lwip error code: %v", int(err))
	}
}

// tcpWrite enqueues data to snd_buf, and treats ERR_MEM returned by tcp_write not an error,
// but instead tells the caller that data is not successfully enqueued, and should try
// again another time. By calling this function, the lwIP thread is assumed to be already
// locked by the caller.
func (conn *tcpConn) tcpWrite(data []byte) (bool, error) {
	if len(data) <= int(conn.pcb.snd_buf) {
		// Enqueue data, data copy here! Copying is required because lwIP must keep the data until they
		// are acknowledged (receiving ACK segments) by other hosts for retransmission purposes, it's
		// not obvious how to implement zero-copy here.
		err := C.tcp_write(conn.pcb, unsafe.Pointer(&data[0]), C.u16_t(len(data)), C.TCP_WRITE_FLAG_COPY)
		if err == C.ERR_OK {
			return true, nil
		} else if err != C.ERR_MEM {
			return false, errors.New(fmt.Sprintf("lwip tcp_write failed with error code: %v", int(err)))
		}
	}
	return false, nil
}

func (conn *tcpConn) Write(data []byte) (int, error) {
	if conn.isLocalClosed() {
		return 0, errors.New(fmt.Sprintf("conn %v <-> %v is closing", conn.LocalAddr(), conn.RemoteAddr()))
	}

	var written = false
	var err error

	// If there isn't any pending data left, we can try to write the data first to avoid one copy,
	// if there is pending data not yet sent, we must copy and buffer the data in order to maintain
	// the transmission order.
	if !conn.hasPendingLocalData() {
		lwipMutex.Lock()
		written, err = conn.tcpWrite(data)
		lwipMutex.Unlock()
		if err != nil {
			return 0, err
		}
	}

	if !written {
		select {
		// Buffer the data here and try sending it later, one could set a smaller localWriteCh size
		// to limit data copying times and memory usage, by sacrificing performance. But writing data
		// to local is quite fast, thus it should be safe even has a size of 1 localWriteCh.
		case conn.localWriteCh <- append([]byte(nil), data...): // data copy here!
		case <-conn.ctx.Done():
			return 0, conn.ctx.Err()
		}
	}

	// Try to send pending data if any, and call tcp_output().
	go conn.tryWriteLocal()

	return len(data), nil
}

func (conn *tcpConn) Sent(len uint16) error {
	conn.handler.DidSend(conn, len)
	// Some packets are acknowledged by local client, check if any pending data to send.
	return conn.CheckState()
}

func (conn *tcpConn) isClosing() bool {
	conn.Lock()
	defer conn.Unlock()
	return conn.closing
}

func (conn *tcpConn) isAborting() bool {
	conn.Lock()
	defer conn.Unlock()
	return conn.aborting
}

func (conn *tcpConn) isLocalClosed() bool {
	conn.Lock()
	defer conn.Unlock()
	return conn.localClosed
}

func (conn *tcpConn) hasPendingLocalData() bool {
	if len(conn.localWriteCh) > 0 || len(conn.localWriteSubCh) > 0 {
		return true
	}
	return false
}

func (conn *tcpConn) CheckState() error {
	// Still have data to send
	if conn.hasPendingLocalData() && !conn.isLocalClosed() {
		go conn.tryWriteLocal()
		// Return and wait for the Sent() callback to be called, and then check again.
		return NewLWIPError(LWIP_ERR_OK)
	}

	if conn.isClosing() || conn.isLocalClosed() {
		conn.closeInternal()
	}

	if conn.isAborting() {
		conn.abortInternal()
		return NewLWIPError(LWIP_ERR_ABRT)
	}

	return NewLWIPError(LWIP_ERR_OK)
}

func (conn *tcpConn) Close() error {
	conn.Lock()
	defer conn.Unlock()

	// Close maybe called outside of lwIP thread, we should not call tcp_close() in this
	// function, instead just make a flag to indicate we are closing the connection.
	conn.closing = true
	return nil
}

func (conn *tcpConn) setLocalClosed() error {
	conn.Lock()
	defer conn.Unlock()

	conn.localClosed = true
	return nil
}

func (conn *tcpConn) closeInternal() error {
	if conn.pcb == nil {
		log.Fatal("nil pcb when close, maybe aborted already")
	}

	C.tcp_arg(conn.pcb, nil)
	C.tcp_recv(conn.pcb, nil)
	C.tcp_sent(conn.pcb, nil)
	C.tcp_err(conn.pcb, nil)
	C.tcp_poll(conn.pcb, nil, 0)

	conn.Release()

	conn.cancel()

	err := C.tcp_close(conn.pcb)
	if err == C.ERR_OK {
		return nil
	} else {
		return errors.New(fmt.Sprint("close TCP connection failed, lwip error code %d", int(err)))
	}
}

func (conn *tcpConn) abortInternal() {
	log.Printf("abort TCP connection %v->%v", conn.LocalAddr(), conn.RemoteAddr())
	conn.Release()
	C.tcp_abort(conn.pcb)
}

func (conn *tcpConn) Abort() {
	conn.Lock()
	defer conn.Unlock()

	conn.aborting = true
}

// The corresponding pcb is already freed when this callback is called
func (conn *tcpConn) Err(err error) {
	log.Printf("error on TCP connection %v->%v: %v", conn.LocalAddr(), conn.RemoteAddr(), err)
	conn.Release()
	conn.cancel()
	conn.handler.DidClose(conn)
}

func (conn *tcpConn) LocalDidClose() error {
	log.Printf("local close TCP connection %v->%v", conn.LocalAddr(), conn.RemoteAddr())
	conn.handler.LocalDidClose(conn)
	conn.setLocalClosed()    // flag closing
	return conn.CheckState() // check pending data
}

func (conn *tcpConn) Release() {
	if _, found := tcpConns.Load(conn.connKey); found {
		FreeConnKeyArg(conn.connKeyArg)
		tcpConns.Delete(conn.connKey)
	}
	log.Printf("ended TCP connection %v->%v", conn.LocalAddr(), conn.RemoteAddr())
}

func (conn *tcpConn) Poll() error {
	return conn.CheckState()
}
<|endoftext|>


<commit_before>package cc

func maxPower(s string) int {
	return 0
}
<commit_msg>solve 1446 use onepass<commit_after>package cc

import "github.com/catorpilor/leetcode/utils"

func maxPower(s string) int {
	return useOnepass(s)
}

// useOnepass time complexity O(N), space complexity O(1)
func useOnepass(s string) int {
	n := len(s)
	if n <= 1 {
		return n
	}
	ans, cur := 1, 1
	for i := 1; i < n; i++ {
		if s[i] != s[i-1] {
			cur = 1
			continue
		}
		cur++
		ans = utils.Max(ans, cur)
	}
	return ans
}
<|endoftext|>


<commit_before>package app_files

import (
	"archive/zip"
	"io"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/cf/errors"
	"github.com/cloudfoundry/gofileutils/fileutils"
)

type Zipper interface {
	Zip(dirToZip string, targetFile *os.File) (err error)
	IsZipFile(path string) bool
	Unzip(appDir string, destDir string) (err error)
	GetZipSize(zipFile *os.File) (int64, error)
}

type ApplicationZipper struct{}

func (zipper ApplicationZipper) Zip(dirOrZipFile string, targetFile *os.File) (err error) {
	if zipper.IsZipFile(dirOrZipFile) {
		err = fileutils.CopyPathToWriter(dirOrZipFile, targetFile)
	} else {
		err = writeZipFile(dirOrZipFile, targetFile)
	}
	targetFile.Seek(0, os.SEEK_SET)
	return
}

func (zipper ApplicationZipper) IsZipFile(file string) (result bool) {
	_, err := zip.OpenReader(file)
	return err == nil
}

func writeZipFile(dir string, targetFile *os.File) error {
	isEmpty, err := fileutils.IsDirEmpty(dir)
	if err != nil {
		return err
	}

	if isEmpty {
		return errors.NewEmptyDirError(dir)
	}

	writer := zip.NewWriter(targetFile)
	defer writer.Close()

	appfiles := ApplicationFiles{}
	return appfiles.WalkAppFiles(dir, func(fileName string, fullPath string) error {
		fileInfo, err := os.Stat(fullPath)
		if err != nil {
			return err
		}

		header, err := zip.FileInfoHeader(fileInfo)
		if err != nil {
			return err
		}

		header.Name = filepath.ToSlash(fileName)

		if fileInfo.IsDir() {
			header.Name += "/"
		}

		zipFilePart, err := writer.CreateHeader(header)
		if err != nil {
			return err
		}

		if fileInfo.IsDir() {
			return nil
		} else {
			return fileutils.CopyPathToWriter(fullPath, zipFilePart)
		}
	})
}

func (zipper ApplicationZipper) Unzip(appDir string, destDir string) (err error) {
	r, err := zip.OpenReader(appDir)
	if err != nil {
		return
	}
	defer r.Close()

	for _, f := range r.File {
		func() {
			if f.FileInfo().IsDir() {
				os.MkdirAll(filepath.Join(destDir, f.Name), os.ModeDir|os.ModePerm)
				return
			}

			var rc io.ReadCloser
			rc, err = f.Open()
			if err != nil {
				return
			}

			// functional scope from above is important
			// otherwise this only closes the last file handle
			defer rc.Close()

			destFilePath := filepath.Join(destDir, f.Name)

			err = fileutils.CopyReaderToPath(rc, destFilePath)
			if err != nil {
				return
			}

			err = os.Chmod(destFilePath, f.FileInfo().Mode())
			if err != nil {
				return
			}
		}()
	}

	return
}

func (zipper ApplicationZipper) GetZipSize(zipFile *os.File) (int64, error) {
	zipFileSize := int64(0)

	stat, err := zipFile.Stat()
	if err != nil {
		return 0, err
	}

	zipFileSize = int64(stat.Size())

	return zipFileSize, nil
}
<commit_msg>Update ApplicationZipper Unzip()<commit_after>package app_files

import (
	"archive/zip"
	"io"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/cf/errors"
	"github.com/cloudfoundry/gofileutils/fileutils"
)

type Zipper interface {
	Zip(dirToZip string, targetFile *os.File) (err error)
	IsZipFile(path string) bool
	Unzip(appDir string, destDir string) (err error)
	GetZipSize(zipFile *os.File) (int64, error)
}

type ApplicationZipper struct{}

func (zipper ApplicationZipper) Zip(dirOrZipFile string, targetFile *os.File) (err error) {
	if zipper.IsZipFile(dirOrZipFile) {
		err = fileutils.CopyPathToWriter(dirOrZipFile, targetFile)
	} else {
		err = writeZipFile(dirOrZipFile, targetFile)
	}
	targetFile.Seek(0, os.SEEK_SET)
	return
}

func (zipper ApplicationZipper) IsZipFile(file string) (result bool) {
	_, err := zip.OpenReader(file)
	return err == nil
}

func writeZipFile(dir string, targetFile *os.File) error {
	isEmpty, err := fileutils.IsDirEmpty(dir)
	if err != nil {
		return err
	}

	if isEmpty {
		return errors.NewEmptyDirError(dir)
	}

	writer := zip.NewWriter(targetFile)
	defer writer.Close()

	appfiles := ApplicationFiles{}
	return appfiles.WalkAppFiles(dir, func(fileName string, fullPath string) error {
		fileInfo, err := os.Stat(fullPath)
		if err != nil {
			return err
		}

		header, err := zip.FileInfoHeader(fileInfo)
		if err != nil {
			return err
		}

		header.Name = filepath.ToSlash(fileName)

		if fileInfo.IsDir() {
			header.Name += "/"
		}

		zipFilePart, err := writer.CreateHeader(header)
		if err != nil {
			return err
		}

		if fileInfo.IsDir() {
			return nil
		} else {
			return fileutils.CopyPathToWriter(fullPath, zipFilePart)
		}
	})
}

func (zipper ApplicationZipper) Unzip(appDir string, destDir string) error {
	r, err := zip.OpenReader(appDir)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {
		// anonymous func allows the defer of rc.Close()
		err = func() error {
			if f.FileInfo().IsDir() {
				os.MkdirAll(filepath.Join(destDir, f.Name), os.ModeDir|os.ModePerm)
				return nil
			}

			var rc io.ReadCloser
			rc, err = f.Open()
			if err != nil {
				return err
			}

			defer rc.Close()

			destFilePath := filepath.Join(destDir, f.Name)

			err = fileutils.CopyReaderToPath(rc, destFilePath)
			if err != nil {
				return err
			}

			err = os.Chmod(destFilePath, f.FileInfo().Mode())
			if err != nil {
				return err
			}

			return nil
		}()

		if err != nil {
			return err
		}
	}

	return nil
}

func (zipper ApplicationZipper) GetZipSize(zipFile *os.File) (int64, error) {
	zipFileSize := int64(0)

	stat, err := zipFile.Stat()
	if err != nil {
		return 0, err
	}

	zipFileSize = int64(stat.Size())

	return zipFileSize, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

type CarChaincode struct {
}

const carIndexStr string = "_cars"
const insurerIndexStr string = "_insurers"
const registrationProposalIndexStr string = "_registrationProposals"

func (t *CarChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Car demo Init")

	var aval int
	var err error

	_, args := stub.GetFunctionAndParameters()
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1 integer to test chain.")
	}

	// initialize the chaincode
	aval, err = strconv.Atoi(args[0])
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}

	// write the state to the ledger
	// make a test var "abc" in order to able to query it and see if it worked
	err = stub.PutState("abc", []byte(strconv.Itoa(aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// clear the car index
	err = clearCarIndex(carIndexStr, stub)
	if err != nil {
		return shim.Error(err.Error())
	}

	// clear the insurer index
	err = clearInsurerIndex(insurerIndexStr, stub)
	if err != nil {
		return shim.Error(err.Error())
	}

	// clear the registration proposal index
	err = clearRegistrationProposalIndex(registrationProposalIndexStr, stub)
	if err != nil {
		return shim.Error(err.Error())
	}

	fmt.Println("Init terminated")
	return shim.Success(nil)
}

/*
 * Invokes an action on the ledger.
 *
 * Expects 'username' and 'role' as first two parameters.
 * Unrestricted queries can only be done from test files.
 */
func (t *CarChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()

	if len(args) < 2 {
		return shim.Error("Invoke expects 'username' and 'role' as first two args.")
	}

	username := args[0]
	role := args[1]
	args = args[2:]

	fmt.Printf("Invoke is running as user '%s' with role '%s'\n", username, role)
	fmt.Printf("Invoke is running function '%s' with args: %s\n", function, strings.Join(args, ", "))

	if function == "create" {
		if role != "garage" {
			return shim.Error("'create' expects you to be a garage user")
		}
		return t.create(stub, username, args)
	} else if function == "read" {
		if len(args) != 1 {
			return shim.Error("'read' expects a key to do the look up")
		} else if reflect.TypeOf(stub).String() != "*shim.MockStub" {
			// only allow unrestricted queries from the test files
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to do unrestricted queries on the ledger.", role))
		} else {
			return t.read(stub, args[0])
		}
	} else if function == "readCar" {
		if len(args) != 1 {
			return shim.Error("'readCar' expects a car vin to do the look up")
		} else {
			return t.readCar(stub, username, args[0])
		}
	} else if function == "readRegistrationProposals" {
		if role != "dot" {
			// only the DOT is allowed to read registration proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to read reigistration proposals.", role))
		} else {
			return t.readRegistrationProposals(stub)
		}
	} else if function == "register" {
		if len(args) != 1 {
			return shim.Error("'register' expects a car vin to register")
		} else if role != "dot" {
			// only the DOT is allowed to register new cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to register cars.", role))
		} else {
			return t.register(stub, username, args[0])
		}
	} else if function == "confirm" {
		if len(args) != 2 {
			return shim.Error("'confirm' expects a car vin and numberplate to confirm a car")
		} else if role != "dot" {
			// only the DOT is allowed to confirm cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to confirm cars.", role))
		} else {
			return t.confirm(stub, username, args)
		}
	} else if function == "transfer" {
		if len(args) != 2 {
			return shim.Error("'transfer' expects a car vin and name of the new owner to confirm a car")
		} else if role != "user" {
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to confirm cars.", role))
		} else {
			return t.transfer(stub, username, args)
		}
	} else if function == "revoke" {
		if len(args) != 1 {
			return shim.Error("'revoke' expects a car vin to revoke a car")
		} else if role != "dot" {
			// only the DOT is allowed to revoke cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to revoke cars.", role))
		} else {
			return t.revoke(stub, username, args)
		}
	} else if function == "delete" {
		if len(args) != 1 {
			return shim.Error("'delete' expects a car vin to delete a car")
		} else if role != "dot" {
			// only the DOT is allowed to delete cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to revoke cars.", role))
		} else {
			return t.delete(stub, args)
		}
	} else if function == "insureProposal" {
		if len(args) != 2 {
			return shim.Error("'insureProposal' expects a car vin and an insurance company")
		} else if role != "user" {
			// only normal users are allowed to do insurance proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to create an insurance proposal.", role))
		} else {
			return t.insureProposal(stub, username, args[0], args[1])
		}
	} else if function == "insuranceAccept" {
		if len(args) != 2 {
			return shim.Error("'insuranceAccept' expects a car vin and an insurance company")
		} else if role != "insurer" {
			// only normal users are allowed to do insurance proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to create an insurance proposal.", role))
		} else {
			return t.insuranceAccept(stub, username, args[0], args[1])
		}
	} else if function == "getInsurer" {
		if len(args) != 1 {
			return shim.Error("'getInsurer' expects an insurance company name")
		} else if role != "insurer" {
			// only normal users are allowed to do insurance proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to create an insurance proposal.", role))
		} else {
			return t.getInsurer(stub, args[0])
		}
	}

	return shim.Error("Invoke did not find function: " + function)
}

/*
 * Reads ledger state from position 'key'.
 *
 * Can be any of:
 *  - Car   (expects car timestamp as key)
 *  - User  (expects user name as key)
 *  - or an index like '_cars'
 *
 * On success,
 * returns ledger state in bytes at position 'key'.
 */
func (t *CarChaincode) read(stub shim.ChaincodeStubInterface, key string) pb.Response {
	if key == "" {
		return shim.Error("'read' expects a non-empty key to do the look up")
	}

	valAsBytes, err := stub.GetState(key)
	if err != nil {
		return shim.Error("Failed to fetch value at key '" + key + "' from ledger")
	}

	return shim.Success(valAsBytes)
}

func main() {
	err := shim.Start(new(CarChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<commit_msg>allows garage users to transfer() cars<commit_after>package main

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

type CarChaincode struct {
}

const carIndexStr string = "_cars"
const insurerIndexStr string = "_insurers"
const registrationProposalIndexStr string = "_registrationProposals"

func (t *CarChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("Car demo Init")

	var aval int
	var err error

	_, args := stub.GetFunctionAndParameters()
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1 integer to test chain.")
	}

	// initialize the chaincode
	aval, err = strconv.Atoi(args[0])
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}

	// write the state to the ledger
	// make a test var "abc" in order to able to query it and see if it worked
	err = stub.PutState("abc", []byte(strconv.Itoa(aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// clear the car index
	err = clearCarIndex(carIndexStr, stub)
	if err != nil {
		return shim.Error(err.Error())
	}

	// clear the insurer index
	err = clearInsurerIndex(insurerIndexStr, stub)
	if err != nil {
		return shim.Error(err.Error())
	}

	// clear the registration proposal index
	err = clearRegistrationProposalIndex(registrationProposalIndexStr, stub)
	if err != nil {
		return shim.Error(err.Error())
	}

	fmt.Println("Init terminated")
	return shim.Success(nil)
}

/*
 * Invokes an action on the ledger.
 *
 * Expects 'username' and 'role' as first two parameters.
 * Unrestricted queries can only be done from test files.
 */
func (t *CarChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()

	if len(args) < 2 {
		return shim.Error("Invoke expects 'username' and 'role' as first two args.")
	}

	username := args[0]
	role := args[1]
	args = args[2:]

	fmt.Printf("Invoke is running as user '%s' with role '%s'\n", username, role)
	fmt.Printf("Invoke is running function '%s' with args: %s\n", function, strings.Join(args, ", "))

	if function == "create" {
		if role != "garage" {
			return shim.Error("'create' expects you to be a garage user")
		}
		return t.create(stub, username, args)
	} else if function == "read" {
		if len(args) != 1 {
			return shim.Error("'read' expects a key to do the look up")
		} else if reflect.TypeOf(stub).String() != "*shim.MockStub" {
			// only allow unrestricted queries from the test files
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to do unrestricted queries on the ledger.", role))
		} else {
			return t.read(stub, args[0])
		}
	} else if function == "readCar" {
		if len(args) != 1 {
			return shim.Error("'readCar' expects a car vin to do the look up")
		} else {
			return t.readCar(stub, username, args[0])
		}
	} else if function == "readRegistrationProposals" {
		if role != "dot" {
			// only the DOT is allowed to read registration proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to read reigistration proposals.", role))
		} else {
			return t.readRegistrationProposals(stub)
		}
	} else if function == "register" {
		if len(args) != 1 {
			return shim.Error("'register' expects a car vin to register")
		} else if role != "dot" {
			// only the DOT is allowed to register new cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to register cars.", role))
		} else {
			return t.register(stub, username, args[0])
		}
	} else if function == "confirm" {
		if len(args) != 2 {
			return shim.Error("'confirm' expects a car vin and numberplate to confirm a car")
		} else if role != "dot" {
			// only the DOT is allowed to confirm cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to confirm cars.", role))
		} else {
			return t.confirm(stub, username, args)
		}
	} else if function == "transfer" {
		if len(args) != 2 {
			return shim.Error("'transfer' expects a car vin and name of the new owner to transfer a car")
		} else if role == "user" || role == "garage" {
            // only allow users and garage users to transer cars
            return t.transfer(stub, username, args)
		} else {
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to transfer cars.", role))
		}
	} else if function == "revoke" {
		if len(args) != 1 {
			return shim.Error("'revoke' expects a car vin to revoke a car")
		} else if role != "dot" {
			// only the DOT is allowed to revoke cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to revoke cars.", role))
		} else {
			return t.revoke(stub, username, args)
		}
	} else if function == "delete" {
		if len(args) != 1 {
			return shim.Error("'delete' expects a car vin to delete a car")
		} else if role != "dot" {
			// only the DOT is allowed to delete cars
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to revoke cars.", role))
		} else {
			return t.delete(stub, args)
		}
	} else if function == "insureProposal" {
		if len(args) != 2 {
			return shim.Error("'insureProposal' expects a car vin and an insurance company")
		} else if role != "user" {
			// only normal users are allowed to do insurance proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to create an insurance proposal.", role))
		} else {
			return t.insureProposal(stub, username, args[0], args[1])
		}
	} else if function == "insuranceAccept" {
		if len(args) != 2 {
			return shim.Error("'insuranceAccept' expects a car vin and an insurance company")
		} else if role != "insurer" {
			// only normal users are allowed to do insurance proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to create an insurance proposal.", role))
		} else {
			return t.insuranceAccept(stub, username, args[0], args[1])
		}
	} else if function == "getInsurer" {
		if len(args) != 1 {
			return shim.Error("'getInsurer' expects an insurance company name")
		} else if role != "insurer" {
			// only normal users are allowed to do insurance proposals
			return shim.Error(fmt.Sprintf("Sorry, role '%s' is not allowed to create an insurance proposal.", role))
		} else {
			return t.getInsurer(stub, args[0])
		}
	}

	return shim.Error("Invoke did not find function: " + function)
}

/*
 * Reads ledger state from position 'key'.
 *
 * Can be any of:
 *  - Car   (expects car timestamp as key)
 *  - User  (expects user name as key)
 *  - or an index like '_cars'
 *
 * On success,
 * returns ledger state in bytes at position 'key'.
 */
func (t *CarChaincode) read(stub shim.ChaincodeStubInterface, key string) pb.Response {
	if key == "" {
		return shim.Error("'read' expects a non-empty key to do the look up")
	}

	valAsBytes, err := stub.GetState(key)
	if err != nil {
		return shim.Error("Failed to fetch value at key '" + key + "' from ledger")
	}

	return shim.Success(valAsBytes)
}

func main() {
	err := shim.Start(new(CarChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cpu

import (
	"runtime"
)

// byteOrder is a subset of encoding/binary.ByteOrder.
type byteOrder interface {
	Uint32([]byte) uint32
	Uint64([]byte) uint64
}

type littleEndian struct{}
type bigEndian struct{}

func (littleEndian) Uint32(b []byte) uint32 {
	_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}

func (littleEndian) Uint64(b []byte) uint64 {
	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
		uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}

func (bigEndian) Uint32(b []byte) uint32 {
	_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
	return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}

func (bigEndian) Uint64(b []byte) uint64 {
	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
	return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
		uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}

// hostByteOrder returns binary.LittleEndian on little-endian machines and
// binary.BigEndian on big-endian machines.
func hostByteOrder() byteOrder {
	switch runtime.GOARCH {
	case "386", "amd64", "amd64p32",
		"arm", "arm64",
		"mipsle", "mips64le", "mips64p32le",
		"ppc64le",
		"riscv", "riscv64":
		return littleEndian{}
	case "armbe", "arm64be",
		"mips", "mips64", "mips64p32",
		"ppc", "ppc64",
		"s390", "s390x",
		"sparc", "sparc64":
		return bigEndian{}
	}
	panic("unknown architecture")
}
<commit_msg>cpu: add all GOARCHes supported by gccgo<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cpu

import (
	"runtime"
)

// byteOrder is a subset of encoding/binary.ByteOrder.
type byteOrder interface {
	Uint32([]byte) uint32
	Uint64([]byte) uint64
}

type littleEndian struct{}
type bigEndian struct{}

func (littleEndian) Uint32(b []byte) uint32 {
	_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}

func (littleEndian) Uint64(b []byte) uint64 {
	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
		uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}

func (bigEndian) Uint32(b []byte) uint32 {
	_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
	return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}

func (bigEndian) Uint64(b []byte) uint64 {
	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
	return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
		uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}

// hostByteOrder returns binary.LittleEndian on little-endian machines and
// binary.BigEndian on big-endian machines.
func hostByteOrder() byteOrder {
	switch runtime.GOARCH {
	case "386", "amd64", "amd64p32",
		"alpha",
		"arm", "arm64",
		"mipsle", "mips64le", "mips64p32le",
		"nios2",
		"ppc64le",
		"riscv", "riscv64",
		"sh":
		return littleEndian{}
	case "armbe", "arm64be",
		"m68k",
		"mips", "mips64", "mips64p32",
		"ppc", "ppc64",
		"s390", "s390x",
		"shbe",
		"sparc", "sparc64":
		return bigEndian{}
	}
	panic("unknown architecture")
}
<|endoftext|>


<commit_before>// Copyright 2019 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dt

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"unicode"
)

// Empty represents an empty Device Tree value.
type Empty struct{}

// PHandle represents a pointer to another Node.
type PHandle uint32

// PropertyType is an enum of possible property types.
type PropertyType int

// These are the possible values for PropertyType.
const (
	EmptyType PropertyType = iota
	U32Type
	U64Type
	StringType
	PropEncodedArrayType
	PHandleType
	StringListType
)

// StandardPropertyTypes maps properties to values as defined by the spec.
var StandardPropertyTypes = map[string]PropertyType{
	"compatible":     StringListType,
	"model":          StringType,
	"phandle":        PHandleType,
	"status":         StringType,
	"#address-cells": U32Type,
	"#size-cells":    U32Type,
	"reg":            PropEncodedArrayType, // TODO: support cells
	"virtual-reg":    U32Type,
	"ranges":         PropEncodedArrayType, // TODO: or EmptyType
	"dma-ranges":     PropEncodedArrayType, // TODO: or EmptyType
	"name":           StringType,           // deprecated
	"device_tree":    StringType,           // deprecated
}

// Node is one Node in the Device Tree.
type Node struct {
	Name       string
	Properties []Property `json:",omitempty"`
	Children   []*Node    `json:",omitempty"`
}

// Walk calls f on a Node and alls its descendents.
func (n *Node) Walk(f func(*Node) error) error {
	if err := f(n); err != nil {
		return err
	}
	for _, child := range n.Children {
		if err := child.Walk(f); err != nil {
			return err
		}
	}
	return nil
}

// Find finds a Node starting at a node, given a matching function.
func (n *Node) Find(f func(*Node) bool) (*Node, bool) {
	if ok := f(n); ok {
		return n, ok
	}
	for _, child := range n.Children {
		if nn, ok := child.Find(f); ok {
			return nn, ok
		}
	}
	return nil, false
}

// FindAll returns all Node starting at a node, given a matching function.
func (n *Node) FindAll(f func(*Node) bool) ([]*Node, bool) {
	var nodes []*Node
	if ok := f(n); ok {
		nodes = append(nodes, n)
	}

	for _, child := range n.Children {
		if matching, ok := child.FindAll(f); ok {
			nodes = append(nodes, matching...)
		}
	}
	if len(nodes) == 0 {
		return nil, false
	}
	return nodes, true
}

// NodeByName uses Find to find a node by name.
func (n *Node) NodeByName(name string) (*Node, bool) {
	return n.Find(func(n *Node) bool {
		return n.Name == name
	})
}

// LookProperty finds a property by name.
func (n *Node) LookProperty(name string) (*Property, bool) {
	for _, p := range n.Properties {
		if p.Name == name {
			return &p, true
		}
	}
	return nil, false
}

// Property is a name-value pair. Note the PropertyType of Value is not
// encoded.
type Property struct {
	Name  string
	Value []byte
}

// PredictType makes a prediction on what value the property contains based on
// its name and data. The data types are not encoded in the data structure, so
// some heuristics are used.
func (p *Property) PredictType() PropertyType {
	// Standard properties
	if value, ok := StandardPropertyTypes[p.Name]; ok {
		if _, err := p.AsType(value); err == nil {
			return value
		}
	}

	// Heuristic match
	if _, err := p.AsEmpty(); err == nil {
		return EmptyType
	}
	if _, err := p.AsString(); err == nil {
		return StringType
	}
	if _, err := p.AsStringList(); err == nil {
		return StringListType
	}
	if _, err := p.AsU32(); err == nil {
		return U32Type
	}
	if _, err := p.AsU64(); err == nil {
		return U64Type
	}
	return PropEncodedArrayType
}

// AsType converts a Property to a Go type using one of the AsXYX() functions.
// The resulting Go type is as follows:
//
//     AsType(fdt.EmptyType)            -> fdt.Empty
//     AsType(fdt.U32Type)              -> uint32
//     AsType(fdt.U64Type)              -> uint64
//     AsType(fdt.StringType)           -> string
//     AsType(fdt.PropEncodedArrayType) -> []byte
//     AsType(fdt.PHandleType)          -> fdt.PHandle
//     AsType(fdt.StringListType)       -> []string
func (p *Property) AsType(val PropertyType) (interface{}, error) {
	switch val {
	case EmptyType:
		return p.AsEmpty()
	case U32Type:
		return p.AsU32()
	case U64Type:
		return p.AsU64()
	case StringType:
		return p.AsString()
	case PropEncodedArrayType:
		return p.AsPropEncodedArray()
	case PHandleType:
		return p.AsPHandle()
	case StringListType:
		return p.AsStringList()
	}
	return nil, fmt.Errorf("%d not in the PropertyType enum", val)
}

// AsEmpty converts the property to the Go fdt.Empty type.
func (p *Property) AsEmpty() (Empty, error) {
	if len(p.Value) != 0 {
		return Empty{}, fmt.Errorf("property %q is not <empty>", p.Name)
	}
	return Empty{}, nil
}

// AsU32 converts the property to the Go uint32 type.
func (p *Property) AsU32() (uint32, error) {
	if len(p.Value) != 4 {
		return 0, fmt.Errorf("property %q is not <u32>", p.Name)
	}
	var val uint32
	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
	return val, err
}

// AsU64 converts the property to the Go uint64 type.
func (p *Property) AsU64() (uint64, error) {
	if len(p.Value) != 8 {
		return 0, fmt.Errorf("property %q is not <u64>", p.Name)
	}
	var val uint64
	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
	return val, err
}

// AsString converts the property to the Go string type. The trailing null
// character is stripped.
func (p *Property) AsString() (string, error) {
	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
		return "", fmt.Errorf("property %q is not <string>", p.Name)
	}
	str := p.Value[:len(p.Value)-1]
	if !isPrintableASCII(str) {
		return "", fmt.Errorf("property %q is not <string>", p.Name)
	}
	return string(str), nil
}

// AsPropEncodedArray converts the property to the Go []byte type.
func (p *Property) AsPropEncodedArray() ([]byte, error) {
	return p.Value, nil
}

// AsPHandle converts the property to the Go fdt.PHandle type.
func (p *Property) AsPHandle() (PHandle, error) {
	val, err := p.AsU32()
	return PHandle(val), err
}

// AsStringList converts the property to the Go []string type. The trailing
// null character of each string is stripped.
func (p *Property) AsStringList() ([]string, error) {
	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
		return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
	}
	value := p.Value
	strs := []string{}
	for len(p.Value) > 0 {
		nextNull := bytes.IndexByte(value, 0) // cannot be -1
		var str []byte
		str, value = value[:nextNull], value[nextNull+1:]
		if !isPrintableASCII(str) {
			return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
		}
		strs = append(strs, string(str))
	}
	return strs, nil
}

func isPrintableASCII(s []byte) bool {
	for _, v := range s {
		if v > unicode.MaxASCII || !unicode.IsPrint(rune(v)) {
			return false
		}
	}
	return true
}
<commit_msg>change the lookup functions to return references<commit_after>// Copyright 2019 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dt

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"unicode"
)

// Empty represents an empty Device Tree value.
type Empty struct{}

// PHandle represents a pointer to another Node.
type PHandle uint32

// PropertyType is an enum of possible property types.
type PropertyType int

// These are the possible values for PropertyType.
const (
	EmptyType PropertyType = iota
	U32Type
	U64Type
	StringType
	PropEncodedArrayType
	PHandleType
	StringListType
)

// StandardPropertyTypes maps properties to values as defined by the spec.
var StandardPropertyTypes = map[string]PropertyType{
	"compatible":     StringListType,
	"model":          StringType,
	"phandle":        PHandleType,
	"status":         StringType,
	"#address-cells": U32Type,
	"#size-cells":    U32Type,
	"reg":            PropEncodedArrayType, // TODO: support cells
	"virtual-reg":    U32Type,
	"ranges":         PropEncodedArrayType, // TODO: or EmptyType
	"dma-ranges":     PropEncodedArrayType, // TODO: or EmptyType
	"name":           StringType,           // deprecated
	"device_tree":    StringType,           // deprecated
}

// Node is one Node in the Device Tree.
type Node struct {
	Name       string
	Properties []Property `json:",omitempty"`
	Children   []*Node    `json:",omitempty"`
}

// Walk calls f on a Node and alls its descendents.
func (n *Node) Walk(f func(*Node) error) error {
	if err := f(n); err != nil {
		return err
	}
	for idx := range n.Children {
		if err := n.Children[idx].Walk(f); err != nil {
			return err
		}
	}
	return nil
}

// Find finds a Node starting at a node, given a matching function.
func (n *Node) Find(f func(*Node) bool) (*Node, bool) {
	if ok := f(n); ok {
		return n, ok
	}
	for idx := range n.Children {
		if nn, ok := n.Children[idx].Find(f); ok {
			return nn, ok
		}
	}
	return nil, false
}

// FindAll returns all Node starting at a node, given a matching function.
func (n *Node) FindAll(f func(*Node) bool) ([]*Node, bool) {
	var nodes []*Node
	if ok := f(n); ok {
		nodes = append(nodes, n)
	}

	for idx := range n.Children {
		if matching, ok := n.Children[idx].FindAll(f); ok {
			nodes = append(nodes, matching...)
		}
	}
	if len(nodes) == 0 {
		return nil, false
	}
	return nodes, true
}

// NodeByName uses Find to find a node by name.
func (n *Node) NodeByName(name string) (*Node, bool) {
	return n.Find(func(n *Node) bool {
		return n.Name == name
	})
}

// LookProperty finds a property by name.
func (n *Node) LookProperty(name string) (*Property, bool) {
	for idx := range n.Properties {
		if n.Properties[idx].Name == name {
			return &n.Properties[idx], true
		}
	}
	return nil, false
}

// Property is a name-value pair. Note the PropertyType of Value is not
// encoded.
type Property struct {
	Name  string
	Value []byte
}

// PredictType makes a prediction on what value the property contains based on
// its name and data. The data types are not encoded in the data structure, so
// some heuristics are used.
func (p *Property) PredictType() PropertyType {
	// Standard properties
	if value, ok := StandardPropertyTypes[p.Name]; ok {
		if _, err := p.AsType(value); err == nil {
			return value
		}
	}

	// Heuristic match
	if _, err := p.AsEmpty(); err == nil {
		return EmptyType
	}
	if _, err := p.AsString(); err == nil {
		return StringType
	}
	if _, err := p.AsStringList(); err == nil {
		return StringListType
	}
	if _, err := p.AsU32(); err == nil {
		return U32Type
	}
	if _, err := p.AsU64(); err == nil {
		return U64Type
	}
	return PropEncodedArrayType
}

// AsType converts a Property to a Go type using one of the AsXYX() functions.
// The resulting Go type is as follows:
//
//     AsType(fdt.EmptyType)            -> fdt.Empty
//     AsType(fdt.U32Type)              -> uint32
//     AsType(fdt.U64Type)              -> uint64
//     AsType(fdt.StringType)           -> string
//     AsType(fdt.PropEncodedArrayType) -> []byte
//     AsType(fdt.PHandleType)          -> fdt.PHandle
//     AsType(fdt.StringListType)       -> []string
func (p *Property) AsType(val PropertyType) (interface{}, error) {
	switch val {
	case EmptyType:
		return p.AsEmpty()
	case U32Type:
		return p.AsU32()
	case U64Type:
		return p.AsU64()
	case StringType:
		return p.AsString()
	case PropEncodedArrayType:
		return p.AsPropEncodedArray()
	case PHandleType:
		return p.AsPHandle()
	case StringListType:
		return p.AsStringList()
	}
	return nil, fmt.Errorf("%d not in the PropertyType enum", val)
}

// AsEmpty converts the property to the Go fdt.Empty type.
func (p *Property) AsEmpty() (Empty, error) {
	if len(p.Value) != 0 {
		return Empty{}, fmt.Errorf("property %q is not <empty>", p.Name)
	}
	return Empty{}, nil
}

// AsU32 converts the property to the Go uint32 type.
func (p *Property) AsU32() (uint32, error) {
	if len(p.Value) != 4 {
		return 0, fmt.Errorf("property %q is not <u32>", p.Name)
	}
	var val uint32
	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
	return val, err
}

// AsU64 converts the property to the Go uint64 type.
func (p *Property) AsU64() (uint64, error) {
	if len(p.Value) != 8 {
		return 0, fmt.Errorf("property %q is not <u64>", p.Name)
	}
	var val uint64
	err := binary.Read(bytes.NewBuffer(p.Value), binary.BigEndian, &val)
	return val, err
}

// AsString converts the property to the Go string type. The trailing null
// character is stripped.
func (p *Property) AsString() (string, error) {
	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
		return "", fmt.Errorf("property %q is not <string>", p.Name)
	}
	str := p.Value[:len(p.Value)-1]
	if !isPrintableASCII(str) {
		return "", fmt.Errorf("property %q is not <string>", p.Name)
	}
	return string(str), nil
}

// AsPropEncodedArray converts the property to the Go []byte type.
func (p *Property) AsPropEncodedArray() ([]byte, error) {
	return p.Value, nil
}

// AsPHandle converts the property to the Go fdt.PHandle type.
func (p *Property) AsPHandle() (PHandle, error) {
	val, err := p.AsU32()
	return PHandle(val), err
}

// AsStringList converts the property to the Go []string type. The trailing
// null character of each string is stripped.
func (p *Property) AsStringList() ([]string, error) {
	if len(p.Value) == 0 || p.Value[len(p.Value)-1] != 0 {
		return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
	}
	value := p.Value
	strs := []string{}
	for len(p.Value) > 0 {
		nextNull := bytes.IndexByte(value, 0) // cannot be -1
		var str []byte
		str, value = value[:nextNull], value[nextNull+1:]
		if !isPrintableASCII(str) {
			return nil, fmt.Errorf("property %q is not <stringlist>", p.Name)
		}
		strs = append(strs, string(str))
	}
	return strs, nil
}

func isPrintableASCII(s []byte) bool {
	for _, v := range s {
		if v > unicode.MaxASCII || !unicode.IsPrint(rune(v)) {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package mux

import (
	"errors"
	"fmt"
	"log"
	"sync"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/client"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
)

// Mux will manage all connections and subscriptions. Will check if subscriptions
// limit is reached and spawn new connection when that happens. It will also listen
// to all incomming client messages and reconnect client with all its subscriptions
// in case of a failure
type Mux struct {
	cid           int
	publicChan    chan msg.Msg
	publicClients map[int]*client.Client
	privateChan   chan msg.Msg
	privateClient *client.Client
	mtx           *sync.RWMutex
	Err           error
	transform     bool
	apikey        string
	apisec        string
	subInfo       map[int64]event.Info
	authenticated bool
}

// New returns pointer to instance of mux
func New() *Mux {
	return &Mux{
		publicChan:    make(chan msg.Msg),
		privateChan:   make(chan msg.Msg),
		publicClients: make(map[int]*client.Client),
		mtx:           &sync.RWMutex{},
		subInfo:       map[int64]event.Info{},
	}
}

// TransformRaw enables data transformation and mapping to appropriate
// models before sending it to consumer
func (m *Mux) TransformRaw() *Mux {
	m.transform = true
	return m
}

// WithAPIKEY accepts and persists api key
func (m *Mux) WithAPIKEY(key string) *Mux {
	m.apikey = key
	return m
}

// WithAPISEC accepts and persists api sec
func (m *Mux) WithAPISEC(sec string) *Mux {
	m.apisec = sec
	return m
}

// Subscribe - given the details in form of event.Subscribe,
// subscribes client to public channels
func (m *Mux) Subscribe(sub event.Subscribe) *Mux {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	if m.Err != nil {
		return m
	}

	if alreadySubscribed := m.publicClients[m.cid].Subs.Added(sub); alreadySubscribed {
		return m
	}

	m.publicClients[m.cid].Subscribe(sub)

	if limitReached := m.publicClients[m.cid].Subs.LimitReached(); limitReached {
		log.Printf("subs limit is reached on cid: %d, spawning new conn\n", m.cid)
		m.addPublicClient()
	}
	return m
}

// Start creates initial clients for accepting connections
func (m *Mux) Start() *Mux {
	if m.Err != nil {
		return m
	}

	if m.hasAPIKeys() && m.privateClient == nil {
		m.addPrivateClient()
	}

	return m.addPublicClient()
}

// Listen accepts a callback func that will get called each time mux
// receives a message from any of its clients/subscriptions. It
// should be called last, after all setup calls are made
func (m *Mux) Listen(cb func(interface{}, error)) error {
	if m.Err != nil {
		return m.Err
	}

	for {
		select {
		case ms, ok := <-m.publicChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("conn:%d has failed | err:%s | reconnecting", ms.CID, ms.Err))
				m.resetPublicClient(ms.CID)
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessRaw(m.subInfo))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case ms, ok := <-m.privateChan:
			if !ok {
				return errors.New("private channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("err: %s | reconnecting", ms.Err))
				m.resetPrivateClient()
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessPrivateRaw())
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		}
	}
}

// Send meant for authenticated input, takes payload in form of interface
// and calls client with it
func (m *Mux) Send(pld interface{}) error {
	if !m.authenticated || m.privateClient == nil {
		return errors.New("not authorized")
	}
	return m.privateClient.Send(pld)
}

func (m *Mux) hasAPIKeys() bool {
	return len(m.apikey) != 0 && len(m.apisec) != 0
}

func (m *Mux) handleEvent(i event.Info, err error) (event.Info, error) {
	switch i.Event {
	case "subscribed":
		m.subInfo[i.ChanID] = i
	case "auth":
		if i.Status == "OK" {
			m.subInfo[i.ChanID] = i
			m.authenticated = true
		}
	}
	// add more cases if/when needed
	return i, err
}

func (m *Mux) resetPublicClient(cid int) {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	// pull old client subscriptions
	subs := m.publicClients[cid].Subs.GetAll()
	// add fresh client
	m.addPublicClient()
	// resubscribe old events
	for _, sub := range subs {
		log.Printf("resubscribing: %+v\n", sub)
		m.Subscribe(sub)
	}
	// remove old, closed channel from the list
	delete(m.publicClients, cid)
}

func (m *Mux) resetPrivateClient() {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	m.privateClient = nil
	m.addPrivateClient()
}

func (m *Mux) addPublicClient() *Mux {
	// adding new client so making sure we increment cid
	m.cid++
	// create new public client and pass error to mux if any
	c := client.New(m.cid).Public()
	if c.Err != nil {
		m.Err = c.Err
		return m
	}
	// add new client to list for later reference
	m.publicClients[m.cid] = c
	// start listening for incoming client messages
	go c.Read(m.publicChan)
	return m
}

func (m *Mux) addPrivateClient() *Mux {
	// create new private client and pass error to mux if any
	c := client.New(0).Private(m.apikey, m.apisec)
	if c.Err != nil {
		m.Err = c.Err
		return m
	}

	m.privateClient = c
	go c.Read(m.privateChan)
	return m
}
<commit_msg>removing redundant locks<commit_after>package mux

import (
	"errors"
	"fmt"
	"log"
	"sync"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/client"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
)

// Mux will manage all connections and subscriptions. Will check if subscriptions
// limit is reached and spawn new connection when that happens. It will also listen
// to all incomming client messages and reconnect client with all its subscriptions
// in case of a failure
type Mux struct {
	cid           int
	publicChan    chan msg.Msg
	publicClients map[int]*client.Client
	privateChan   chan msg.Msg
	privateClient *client.Client
	mtx           *sync.RWMutex
	Err           error
	transform     bool
	apikey        string
	apisec        string
	subInfo       map[int64]event.Info
	authenticated bool
}

// New returns pointer to instance of mux
func New() *Mux {
	return &Mux{
		publicChan:    make(chan msg.Msg),
		privateChan:   make(chan msg.Msg),
		publicClients: make(map[int]*client.Client),
		mtx:           &sync.RWMutex{},
		subInfo:       map[int64]event.Info{},
	}
}

// TransformRaw enables data transformation and mapping to appropriate
// models before sending it to consumer
func (m *Mux) TransformRaw() *Mux {
	m.transform = true
	return m
}

// WithAPIKEY accepts and persists api key
func (m *Mux) WithAPIKEY(key string) *Mux {
	m.apikey = key
	return m
}

// WithAPISEC accepts and persists api sec
func (m *Mux) WithAPISEC(sec string) *Mux {
	m.apisec = sec
	return m
}

// Subscribe - given the details in form of event.Subscribe,
// subscribes client to public channels
func (m *Mux) Subscribe(sub event.Subscribe) *Mux {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	if m.Err != nil {
		return m
	}

	if alreadySubscribed := m.publicClients[m.cid].Subs.Added(sub); alreadySubscribed {
		return m
	}

	m.publicClients[m.cid].Subscribe(sub)

	if limitReached := m.publicClients[m.cid].Subs.LimitReached(); limitReached {
		log.Printf("subs limit is reached on cid: %d, spawning new conn\n", m.cid)
		m.addPublicClient()
	}
	return m
}

// Start creates initial clients for accepting connections
func (m *Mux) Start() *Mux {
	if m.Err != nil {
		return m
	}

	if m.hasAPIKeys() && m.privateClient == nil {
		m.addPrivateClient()
	}

	return m.addPublicClient()
}

// Listen accepts a callback func that will get called each time mux
// receives a message from any of its clients/subscriptions. It
// should be called last, after all setup calls are made
func (m *Mux) Listen(cb func(interface{}, error)) error {
	if m.Err != nil {
		return m.Err
	}

	for {
		select {
		case ms, ok := <-m.publicChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("conn:%d has failed | err:%s | reconnecting", ms.CID, ms.Err))
				m.resetPublicClient(ms.CID)
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessRaw(m.subInfo))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case ms, ok := <-m.privateChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("err: %s | reconnecting", ms.Err))
				m.resetPrivateClient()
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessPrivateRaw())
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		}
	}
}

// Send meant for authenticated input, takes payload in form of interface
// and calls client with it
func (m *Mux) Send(pld interface{}) error {
	if !m.authenticated || m.privateClient == nil {
		return errors.New("not authorized")
	}
	return m.privateClient.Send(pld)
}

func (m *Mux) hasAPIKeys() bool {
	return len(m.apikey) != 0 && len(m.apisec) != 0
}

func (m *Mux) handleEvent(i event.Info, err error) (event.Info, error) {
	switch i.Event {
	case "subscribed":
		m.subInfo[i.ChanID] = i
	case "auth":
		if i.Status == "OK" {
			m.subInfo[i.ChanID] = i
			m.authenticated = true
		}
	}
	// add more cases if/when needed
	return i, err
}

func (m *Mux) resetPublicClient(cid int) {
	// pull old client subscriptions
	subs := m.publicClients[cid].Subs.GetAll()
	// add fresh client
	m.addPublicClient()
	// resubscribe old events
	for _, sub := range subs {
		log.Printf("resubscribing: %+v\n", sub)
		m.Subscribe(sub)
	}
	// remove old, closed channel from the list
	delete(m.publicClients, cid)
}

func (m *Mux) resetPrivateClient() {
	m.privateClient = nil
	m.addPrivateClient()
}

func (m *Mux) addPublicClient() *Mux {
	// adding new client so making sure we increment cid
	m.cid++
	// create new public client and pass error to mux if any
	c := client.New(m.cid).Public()
	if c.Err != nil {
		m.Err = c.Err
		return m
	}
	// add new client to list for later reference
	m.publicClients[m.cid] = c
	// start listening for incoming client messages
	go c.Read(m.publicChan)
	return m
}

func (m *Mux) addPrivateClient() *Mux {
	// create new private client and pass error to mux if any
	c := client.New(0).Private(m.apikey, m.apisec)
	if c.Err != nil {
		m.Err = c.Err
		return m
	}

	m.privateClient = c
	go c.Read(m.privateChan)
	return m
}
<|endoftext|>


<commit_before>package place

import (
	"bytes"
	"crypto/sha256"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/util"
	"gopkg.in/h2non/filetype.v1"
	"gopkg.in/h2non/filetype.v1/types"
)

type Place struct {
	ConfigPath string
	Config     Config
	Db         *leveldb.DB
	Preview    bool
	Number     int
	History    History
	TmpFile    map[string][]byte
}

func (p *Place) Run(files []string) {
	p.loadConfig()
	p.getNumber()
	p.History.Files = make(map[string]string)
	log.Debug("Run:", files)
	for _, v := range files {
		p.run(v)
	}
	if !p.Preview {
		p.saveHistory()
	}
}

func (p *Place) getNumber() {
	name := []byte("count")
	bs, _ := p.Db.Get(name, nil)
	p.Number = Bytes2Int(bs)
	p.Number += 1
	log.Debugf("操作次数: %d", p.Number)
	p.Db.Put(name, Int2Bytes(p.Number), nil)
}

func (p *Place) saveHistory() {
	log.Debugf("保存历史: %v", p.History.Files)
	p.History.Timestamp = time.Now()
	bf := bytes.NewBuffer(nil)
	enc := gob.NewEncoder(bf)
	err := enc.Encode(p.History)
	if err != nil {
		panic("操作记录编码失败")
	}
	p.Db.Put([]byte(fmt.Sprintf("no-%04d", p.Number)), bf.Bytes(), nil)
}

func (p *Place) loadConfig() {
	p.Config = Config{}
	p.Config.Load(p.ConfigPath)
}

func (p *Place) run(file string) {
	info, err := os.Stat(file)
	if os.IsNotExist(err) {
		log.Error("文件未找到: ", file)
		return
	}
	log.Debugf("%s 目录: %t", file, info.IsDir())
	if info.IsDir() {
		log.Info("忽略目录: ", file)
	} else {
		log.Debug("处理文件: ", file)
		bs, _ := ioutil.ReadFile(file)
		head := bs[:261]
		if filetype.IsImage(head) {
			log.Debugf("文件 %s 是图片", file)
		} else {
			log.Debugf("文件 %s 不是图片", file)
		}
		hash := sha256.New()
		hash.Write(bs)
		log.Debugf("%s sha256: %x", file, hash.Sum(nil))
		kind := Mime(head, file)
		old := p.find(hash.Sum(nil))
		if old == "" {
			newFile, err := p.moveName(kind, file, info)
			if err == nil {
				if p.Preview {
					log.Infof("预览: %s >>> %s", file, newFile)
				} else {
					log.Infof("移动: %s >>> %s", file, newFile)
					p.History.Files[file] = newFile
					os.Rename(file, newFile)
					p.Db.Put(BytesPrefix("f-", hash.Sum(nil)), []byte(newFile), nil)
				}
			} else {
				log.Errorf("%s %s Mime: %s, Subtype: %s", err, file, kind.MIME.Type, kind.MIME.Subtype)
			}
		} else {
			log.Warnf("文件 %s 重复, 原文件 %s", file, old)
		}
	}
}

func (p *Place) find(hash []byte) string {
	file, err := p.Db.Get(BytesPrefix("f-", hash), nil)
	if err != nil {
		return ""
	}
	return string(file)
}

func (p *Place) moveName(t types.Type, file string, info os.FileInfo) (string, error) {
	log.Debugf("mime: %s, subtype: %s, 搬移文件: %s", t.MIME.Type, t.MIME.Subtype, file)
	ext := path.Ext(file)
	for _, ap := range p.Config.Paths {
		subtypes := []string{""}
		if ap.Subtype != "" {
			subtypes = Split(ap.Subtype)
		}
		exts := []string{""}
		if ap.Ext != "" {
			exts = Split(ap.Ext)
		}
		for _, st := range subtypes {
			for _, apExt := range exts {
				if matching(ap, strings.ToLower(st), t, ext, apExt) {
					newDir := ap.Dir
					if ap.Subdir != "" {
						newDir = path.Join(newDir, TimeFormat(info.ModTime(), ap.Subdir))
					}
					dir := ToPath(newDir)
					newFile := path.Join(dir, path.Base(file))
					info, err := os.Stat(newFile)
					if os.IsNotExist(err) {
						return newFile, nil
					}
					if info.IsDir() {
						return file, errors.New("同名目录已经存在")
					} else {
						return file, errors.New("同名文件已经存在")
					}
				}
			}
		}
	}
	return file, errors.New("无匹配设置")
}

func matching(ap *Path, subType string, t types.Type, ext string, apExt string) bool {
	if (strings.EqualFold(ap.Mime, t.MIME.Type) && strings.HasPrefix(t.MIME.Subtype, subType)) ||
		(ap.Mime == "" && ap.Subtype == "" && t == filetype.Unknown) ||
		(strings.EqualFold(ap.Mime, t.MIME.Type) && ap.Subtype == "") {
		if apExt == "" || strings.EqualFold(apExt, ext) {
			return true
		}
	}
	return false
}

func (p *Place) Scan() {
	log.Info("开始目录扫描")
	p.loadConfig()

	p.TmpFile = make(map[string][]byte)
	iter := p.Db.NewIterator(util.BytesPrefix([]byte("f-")), nil)
	for iter.Next() {
		file := string(iter.Value())
		p.TmpFile[file] = iter.Key()
		log.Debug("缓存文件: ", file)
	}
	iter.Release()

	for _, ap := range p.Config.Paths {
		log.Debug("扫描目录: ", ap.Dir)
		p.scanning(ToPath(ap.Dir))
	}

	for file, key := range p.TmpFile {
		log.Debug("删除缓存: ", file)
		p.Db.Delete(key, nil)
	}
}

func (p *Place) scanning(dir string) {
	err := filepath.Walk(dir, func(filename string, fi os.FileInfo, err error) error {
		if filename == dir {
			return nil
		}
		if fi.IsDir() {
			p.scanning(filename)
		} else {
			log.Debug("扫描文件: ", filename)
			_, ok := p.TmpFile[filename]
			if ok {
				delete(p.TmpFile, filename)
			} else {
				bs, _ := ioutil.ReadFile(filename)
				hash := sha256.New()
				hash.Write(bs)
				key := BytesPrefix("f-", hash.Sum(nil))
				old, err := p.Db.Get(key, nil)
				if err == nil {
					if filename != string(old) {
						log.Warnf("文件重复: %s = %s", filename, old)
					}
				} else {
					log.Info("新增文件: ", filename)
					p.Db.Put(key, []byte(filename), nil)
				}
			}
		}
		return nil
	})
	if err != nil {
		log.Error("目录扫描错误: ", err)
	}
}

func (p *Place) Reset() {
	log.Info("开始根据配置重置目录")

	p.TmpFile = make(map[string][]byte)
	iter := p.Db.NewIterator(util.BytesPrefix([]byte("f-")), nil)
	for iter.Next() {
		file := string(iter.Value())
		p.TmpFile[file] = iter.Key()
		log.Debug("缓存文件: ", file)
	}
	iter.Release()

	for _, ap := range p.Config.Paths {
		log.Debug("扫描目录: ", ap.Dir)
		p.reset(ToPath(ap.Dir))
	}
}

func (p *Place) reset(dir string) {
	count := 0
	err := filepath.Walk(dir, func(filename string, fi os.FileInfo, err error) error {
		if filename == dir {
			return nil
		}
		count += 1
		if fi.IsDir() {
			p.reset(filename)
		} else {
			log.Debug("重置文件: ", filename)
			bs, _ := ioutil.ReadFile(filename)
			head := bs[:261]
			kind, _ := filetype.Match(head)
			newFile, err := p.moveName(kind, filename, fi)
			if err != nil || newFile == filename {
				log.Debug("无需移动: ", filename)
			} else {
				log.Infof("移动: %s >>> %s", filename, newFile)
				os.Rename(filename, newFile)
				key, _ := p.TmpFile[filename]
				p.Db.Put(key, []byte(newFile), nil)
			}
		}
		return nil
	})
	if err == nil {
		log.Debugf("%s 子文件数量: %d", dir, count)
		if count == 0 {
			log.Info("删除空目录: ", dir)
			os.Remove(dir)
		}
	} else {
		log.Error("目录扫描错误: ", err)
	}
}
<commit_msg>fix #16<commit_after>package place

import (
	"bufio"
	"bytes"
	"crypto/sha256"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/util"
	"gopkg.in/h2non/filetype.v1"
	"gopkg.in/h2non/filetype.v1/types"
)

type Place struct {
	ConfigPath string
	Config     Config
	Db         *leveldb.DB
	Preview    bool
	Number     int
	History    History
	TmpFile    map[string][]byte
}

func (p *Place) Run(files []string) {
	p.loadConfig()
	p.getNumber()
	p.History.Files = make(map[string]string)
	log.Debug("Run:", files)
	for _, v := range files {
		p.run(v)
	}
	if !p.Preview {
		p.saveHistory()
	}
}

func (p *Place) getNumber() {
	name := []byte("count")
	bs, _ := p.Db.Get(name, nil)
	p.Number = Bytes2Int(bs)
	p.Number += 1
	log.Debugf("操作次数: %d", p.Number)
	p.Db.Put(name, Int2Bytes(p.Number), nil)
}

func (p *Place) saveHistory() {
	log.Debugf("保存历史: %v", p.History.Files)
	p.History.Timestamp = time.Now()
	bf := bytes.NewBuffer(nil)
	enc := gob.NewEncoder(bf)
	err := enc.Encode(p.History)
	if err != nil {
		panic("操作记录编码失败")
	}
	p.Db.Put([]byte(fmt.Sprintf("no-%04d", p.Number)), bf.Bytes(), nil)
}

func (p *Place) loadConfig() {
	p.Config = Config{}
	p.Config.Load(p.ConfigPath)
}

func (p *Place) run(file string) {
	info, err := os.Stat(file)
	if os.IsNotExist(err) {
		log.Error("文件未找到: ", file)
		return
	}
	log.Debugf("%s 目录: %t", file, info.IsDir())
	if info.IsDir() {
		log.Info("忽略目录: ", file)
	} else {
		log.Debug("处理文件: ", file)
		var (
			f     *os.File
			count int
			kind  types.Type
		)
		if f, err = os.Open(file); err != nil {
			log.Errorf("文件 %s 无法读取", file)
			return
		}
		reader := bufio.NewReader(f)
		buffer := make([]byte, 16384)
		hash := sha256.New()
		isHead := true
		for {
			if count, err = reader.Read(buffer); err != nil {
				break
			}
			if isHead {
				isHead = false
				head := buffer[:261]
				if filetype.IsImage(head) {
					log.Debugf("文件 %s 是图片", file)
				} else {
					log.Debugf("文件 %s 不是图片", file)
				}
				kind = Mime(head, file)
			}
			hash.Write(buffer[:count])
		}
		log.Debugf("%s sha256: %x", file, hash.Sum(nil))
		// bs, _ := ioutil.ReadFile(file)
		old := p.find(hash.Sum(nil))
		if old == "" {
			newFile, err := p.moveName(kind, file, info)
			if err == nil {
				if p.Preview {
					log.Infof("预览: %s >>> %s", file, newFile)
				} else {
					log.Infof("移动: %s >>> %s", file, newFile)
					p.History.Files[file] = newFile
					os.Rename(file, newFile)
					p.Db.Put(BytesPrefix("f-", hash.Sum(nil)), []byte(newFile), nil)
				}
			} else {
				log.Errorf("%s %s Mime: %s, Subtype: %s", err, file, kind.MIME.Type, kind.MIME.Subtype)
			}
		} else {
			log.Warnf("文件 %s 重复, 原文件 %s", file, old)
		}
	}
}

func (p *Place) find(hash []byte) string {
	file, err := p.Db.Get(BytesPrefix("f-", hash), nil)
	if err != nil {
		return ""
	}
	return string(file)
}

func (p *Place) moveName(t types.Type, file string, info os.FileInfo) (string, error) {
	log.Debugf("mime: %s, subtype: %s, 搬移文件: %s", t.MIME.Type, t.MIME.Subtype, file)
	ext := path.Ext(file)
	for _, ap := range p.Config.Paths {
		subtypes := []string{""}
		if ap.Subtype != "" {
			subtypes = Split(ap.Subtype)
		}
		exts := []string{""}
		if ap.Ext != "" {
			exts = Split(ap.Ext)
		}
		for _, st := range subtypes {
			for _, apExt := range exts {
				if matching(ap, strings.ToLower(st), t, ext, apExt) {
					newDir := ap.Dir
					if ap.Subdir != "" {
						newDir = path.Join(newDir, TimeFormat(info.ModTime(), ap.Subdir))
					}
					dir := ToPath(newDir)
					newFile := path.Join(dir, path.Base(file))
					info, err := os.Stat(newFile)
					if os.IsNotExist(err) {
						return newFile, nil
					}
					if info.IsDir() {
						return file, errors.New("同名目录已经存在")
					} else {
						return file, errors.New("同名文件已经存在")
					}
				}
			}
		}
	}
	return file, errors.New("无匹配设置")
}

func matching(ap *Path, subType string, t types.Type, ext string, apExt string) bool {
	if (strings.EqualFold(ap.Mime, t.MIME.Type) && strings.HasPrefix(t.MIME.Subtype, subType)) ||
		(ap.Mime == "" && ap.Subtype == "" && t == filetype.Unknown) ||
		(strings.EqualFold(ap.Mime, t.MIME.Type) && ap.Subtype == "") {
		if apExt == "" || strings.EqualFold(apExt, ext) {
			return true
		}
	}
	return false
}

func (p *Place) Scan() {
	log.Info("开始目录扫描")
	p.loadConfig()

	p.TmpFile = make(map[string][]byte)
	iter := p.Db.NewIterator(util.BytesPrefix([]byte("f-")), nil)
	for iter.Next() {
		file := string(iter.Value())
		p.TmpFile[file] = iter.Key()
		log.Debug("缓存文件: ", file)
	}
	iter.Release()

	for _, ap := range p.Config.Paths {
		log.Debug("扫描目录: ", ap.Dir)
		p.scanning(ToPath(ap.Dir))
	}

	for file, key := range p.TmpFile {
		log.Debug("删除缓存: ", file)
		p.Db.Delete(key, nil)
	}
}

func (p *Place) scanning(dir string) {
	err := filepath.Walk(dir, func(filename string, fi os.FileInfo, err error) error {
		if filename == dir {
			return nil
		}
		if fi.IsDir() {
			p.scanning(filename)
		} else {
			log.Debug("扫描文件: ", filename)
			_, ok := p.TmpFile[filename]
			if ok {
				delete(p.TmpFile, filename)
			} else {
				bs, _ := ioutil.ReadFile(filename)
				hash := sha256.New()
				hash.Write(bs)
				key := BytesPrefix("f-", hash.Sum(nil))
				old, err := p.Db.Get(key, nil)
				if err == nil {
					if filename != string(old) {
						log.Warnf("文件重复: %s = %s", filename, old)
					}
				} else {
					log.Info("新增文件: ", filename)
					p.Db.Put(key, []byte(filename), nil)
				}
			}
		}
		return nil
	})
	if err != nil {
		log.Error("目录扫描错误: ", err)
	}
}

func (p *Place) Reset() {
	log.Info("开始根据配置重置目录")

	p.TmpFile = make(map[string][]byte)
	iter := p.Db.NewIterator(util.BytesPrefix([]byte("f-")), nil)
	for iter.Next() {
		file := string(iter.Value())
		p.TmpFile[file] = iter.Key()
		log.Debug("缓存文件: ", file)
	}
	iter.Release()

	for _, ap := range p.Config.Paths {
		log.Debug("扫描目录: ", ap.Dir)
		p.reset(ToPath(ap.Dir))
	}
}

func (p *Place) reset(dir string) {
	count := 0
	err := filepath.Walk(dir, func(filename string, fi os.FileInfo, err error) error {
		if filename == dir {
			return nil
		}
		count += 1
		if fi.IsDir() {
			p.reset(filename)
		} else {
			log.Debug("重置文件: ", filename)
			bs, _ := ioutil.ReadFile(filename)
			head := bs[:261]
			kind, _ := filetype.Match(head)
			newFile, err := p.moveName(kind, filename, fi)
			if err != nil || newFile == filename {
				log.Debug("无需移动: ", filename)
			} else {
				log.Infof("移动: %s >>> %s", filename, newFile)
				os.Rename(filename, newFile)
				key, _ := p.TmpFile[filename]
				p.Db.Put(key, []byte(newFile), nil)
			}
		}
		return nil
	})
	if err == nil {
		log.Debugf("%s 子文件数量: %d", dir, count)
		if count == 0 {
			log.Info("删除空目录: ", dir)
			os.Remove(dir)
		}
	} else {
		log.Error("目录扫描错误: ", err)
	}
}
<|endoftext|>


<commit_before>package ploop

// #include <ploop/libploop.h>
import "C"
import "fmt"

type PloopErr struct {
	C int
	s string
}

// SYSEXIT_* errors
const (
	_ = iota
	E_CREAT
	E_DEVICE
	E_DEVIOC
	E_OPEN
	E_MALLOC
	E_READ
	E_WRITE
	E_RESERVED_8
	E_SYSFS
	E_RESERVED_10
	E_PLOOPFMT
	E_SYS
	E_PROTOCOL
	E_LOOP
	E_FSTAT
	E_FSYNC
	E_EBUSY
	E_FLOCK
	E_FTRUNCATE
	E_FALLOCATE
	E_MOUNT
	E_UMOUNT
	E_LOCK
	E_MKFS
	E_RESERVED_25
	E_RESIZE_FS
	E_MKDIR
	E_RENAME
	E_ABORT
	E_RELOC
	E_RESERVED_31
	E_RESERVED_32
	E_CHANGE_GPT
	E_RESERVED_34
	E_UNLINK
	E_MKNOD
	E_PLOOPINUSE
	E_PARAM
	E_DISKDESCR
	E_DEV_NOT_MOUNTED
	E_FSCK
	E_RESERVED_42
	E_NOSNAP
)

var ErrCodes = []string{
	E_CREAT:           "E_CREAT",
	E_DEVICE:          "E_DEVICE",
	E_DEVIOC:          "E_DEVIOC",
	E_OPEN:            "E_OPEN",
	E_MALLOC:          "E_MALLOC",
	E_READ:            "E_READ",
	E_WRITE:           "E_WRITE",
	E_RESERVED_8:      "E_RESERVED",
	E_SYSFS:           "E_SYSFS",
	E_RESERVED_10:     "E_RESERVED",
	E_PLOOPFMT:        "E_PLOOPFMT",
	E_SYS:             "E_SYS",
	E_PROTOCOL:        "E_PROTOCOL",
	E_LOOP:            "E_LOOP",
	E_FSTAT:           "E_FSTAT",
	E_FSYNC:           "E_FSYNC",
	E_EBUSY:           "E_EBUSY",
	E_FLOCK:           "E_FLOCK",
	E_FTRUNCATE:       "E_FTRUNCATE",
	E_FALLOCATE:       "E_FALLOCATE",
	E_MOUNT:           "E_MOUNT",
	E_UMOUNT:          "E_UMOUNT",
	E_LOCK:            "E_LOCK",
	E_MKFS:            "E_MKFS",
	E_RESERVED_25:     "E_RESERVED",
	E_RESIZE_FS:       "E_RESIZE_FS",
	E_MKDIR:           "E_MKDIR",
	E_RENAME:          "E_RENAME",
	E_ABORT:           "E_ABORT",
	E_RELOC:           "E_RELOC",
	E_RESERVED_31:     "E_RESERVED",
	E_RESERVED_32:     "E_RESERVED",
	E_CHANGE_GPT:      "E_CHANGE_GPT",
	E_RESERVED_34:     "E_RESERVED",
	E_UNLINK:          "E_UNLINK",
	E_MKNOD:           "E_MKNOD",
	E_PLOOPINUSE:      "E_PLOOPINUSE",
	E_PARAM:           "E_PARAM",
	E_DISKDESCR:       "E_DISKDESCR",
	E_DEV_NOT_MOUNTED: "E_DEV_NOT_MOUNTED",
	E_FSCK:            "E_FSCK",
	E_RESERVED_42:     "E_RESERVED",
	E_NOSNAP:          "E_NOSNAP",
}

func (e *PloopErr) Error() string {
	return fmt.Sprintf("ploop error %d (%s): %s", e.C, ErrCodes[e.C], e.s)
}

func mkerr(ret C.int) error {
	if ret == 0 {
		return nil
	}

	return &PloopErr{C: int(ret), s: C.GoString(C.ploop_get_last_error())}
}
<commit_msg>Error(): report unknown errors<commit_after>package ploop

// #include <ploop/libploop.h>
import "C"
import "fmt"

type PloopErr struct {
	C int
	s string
}

// SYSEXIT_* errors
const (
	_ = iota
	E_CREAT
	E_DEVICE
	E_DEVIOC
	E_OPEN
	E_MALLOC
	E_READ
	E_WRITE
	E_RESERVED_8
	E_SYSFS
	E_RESERVED_10
	E_PLOOPFMT
	E_SYS
	E_PROTOCOL
	E_LOOP
	E_FSTAT
	E_FSYNC
	E_EBUSY
	E_FLOCK
	E_FTRUNCATE
	E_FALLOCATE
	E_MOUNT
	E_UMOUNT
	E_LOCK
	E_MKFS
	E_RESERVED_25
	E_RESIZE_FS
	E_MKDIR
	E_RENAME
	E_ABORT
	E_RELOC
	E_RESERVED_31
	E_RESERVED_32
	E_CHANGE_GPT
	E_RESERVED_34
	E_UNLINK
	E_MKNOD
	E_PLOOPINUSE
	E_PARAM
	E_DISKDESCR
	E_DEV_NOT_MOUNTED
	E_FSCK
	E_RESERVED_42
	E_NOSNAP
)

var ErrCodes = []string{
	E_CREAT:           "E_CREAT",
	E_DEVICE:          "E_DEVICE",
	E_DEVIOC:          "E_DEVIOC",
	E_OPEN:            "E_OPEN",
	E_MALLOC:          "E_MALLOC",
	E_READ:            "E_READ",
	E_WRITE:           "E_WRITE",
	E_RESERVED_8:      "E_RESERVED",
	E_SYSFS:           "E_SYSFS",
	E_RESERVED_10:     "E_RESERVED",
	E_PLOOPFMT:        "E_PLOOPFMT",
	E_SYS:             "E_SYS",
	E_PROTOCOL:        "E_PROTOCOL",
	E_LOOP:            "E_LOOP",
	E_FSTAT:           "E_FSTAT",
	E_FSYNC:           "E_FSYNC",
	E_EBUSY:           "E_EBUSY",
	E_FLOCK:           "E_FLOCK",
	E_FTRUNCATE:       "E_FTRUNCATE",
	E_FALLOCATE:       "E_FALLOCATE",
	E_MOUNT:           "E_MOUNT",
	E_UMOUNT:          "E_UMOUNT",
	E_LOCK:            "E_LOCK",
	E_MKFS:            "E_MKFS",
	E_RESERVED_25:     "E_RESERVED",
	E_RESIZE_FS:       "E_RESIZE_FS",
	E_MKDIR:           "E_MKDIR",
	E_RENAME:          "E_RENAME",
	E_ABORT:           "E_ABORT",
	E_RELOC:           "E_RELOC",
	E_RESERVED_31:     "E_RESERVED",
	E_RESERVED_32:     "E_RESERVED",
	E_CHANGE_GPT:      "E_CHANGE_GPT",
	E_RESERVED_34:     "E_RESERVED",
	E_UNLINK:          "E_UNLINK",
	E_MKNOD:           "E_MKNOD",
	E_PLOOPINUSE:      "E_PLOOPINUSE",
	E_PARAM:           "E_PARAM",
	E_DISKDESCR:       "E_DISKDESCR",
	E_DEV_NOT_MOUNTED: "E_DEV_NOT_MOUNTED",
	E_FSCK:            "E_FSCK",
	E_RESERVED_42:     "E_RESERVED",
	E_NOSNAP:          "E_NOSNAP",
}

func (e *PloopErr) Error() string {
	s := "E_UNKNOWN"
	if e.C > 0 && e.C < len(ErrCodes) {
		s = ErrCodes[e.C]
	}

	return fmt.Sprintf("ploop error %d (%s): %s", e.C, s, e.s)
}

func mkerr(ret C.int) error {
	if ret == 0 {
		return nil
	}

	return &PloopErr{C: int(ret), s: C.GoString(C.ploop_get_last_error())}
}
<|endoftext|>