<commit_before>package git

/*
#include <git2.h>

extern int _go_git_visit_submodule(git_repository *repo, void *fct);
*/
import "C"
import (
	"runtime"
	"unsafe"
)

// SubmoduleUpdateOptions
type SubmoduleUpdateOptions struct {
	*CheckoutOpts
	*RemoteCallbacks
	CloneCheckoutStrategy CheckoutStrategy
	Signature             *Signature
}

// Submodule
type Submodule struct {
	ptr *C.git_submodule
}

type SubmoduleUpdate int

const (
	SubmoduleUpdateReset    SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_RESET
	SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT
	SubmoduleUpdateRebase   SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE
	SubmoduleUpdateMerge    SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE
	SubmoduleUpdateNone     SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE
)

type SubmoduleIgnore int

const (
	SubmoduleIgnoreReset     SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_RESET
	SubmoduleIgnoreNone      SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_NONE
	SubmoduleIgnoreUntracked SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_UNTRACKED
	SubmoduleIgnoreDirty     SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_DIRTY
	SubmoduleIgnoreAll       SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_ALL
)

type SubmoduleStatus int

const (
	SubmoduleStatusInHead          SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_HEAD
	SubmoduleStatusInIndex         SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_INDEX
	SubmoduleStatusInConfig        SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_CONFIG
	SubmoduleStatusInWd            SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_WD
	SubmoduleStatusIndexAdded      SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_ADDED
	SubmoduleStatusIndexDeleted    SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_DELETED
	SubmoduleStatusIndexModified   SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_MODIFIED
	SubmoduleStatusWdUninitialized SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_UNINITIALIZED
	SubmoduleStatusWdAdded         SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_ADDED
	SubmoduleStatusWdDeleted       SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_DELETED
	SubmoduleStatusWdModified      SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_MODIFIED
	SubmoduleStatusWdIndexModified SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED
	SubmoduleStatusWdWdModified    SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_WD_MODIFIED
	SubmoduleStatusWdUntracked     SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_UNTRACKED
)

type SubmoduleRecurse int

const (
	SubmoduleRecurseNo       SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_NO
	SubmoduleRecurseYes      SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_YES
	SubmoduleRecurseOndemand SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_ONDEMAND
)

func SubmoduleStatusIsUnmodified(status int) bool {
	o := SubmoduleStatus(status) & ^(SubmoduleStatusInHead | SubmoduleStatusInIndex |
		SubmoduleStatusInConfig | SubmoduleStatusInWd)
	return o == 0
}

func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	sub := new(Submodule)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_lookup(&sub.ptr, repo.ptr, cname)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return sub, nil
}

type SubmoduleCbk func(sub *Submodule, name string) int

//export SubmoduleVisitor
func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, cfct unsafe.Pointer) C.int {
	sub := &Submodule{(*C.git_submodule)(csub)}
	fct := *(*SubmoduleCbk)(cfct)
	return (C.int)(fct(sub, C.GoString(name)))
}

func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C._go_git_visit_submodule(repo.ptr, unsafe.Pointer(&cbk))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (repo *Repository) AddSubmodule(url, path string, use_git_link bool) (*Submodule, error) {
	curl := C.CString(url)
	defer C.free(unsafe.Pointer(curl))
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	sub := new(Submodule)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_add_setup(&sub.ptr, repo.ptr, curl, cpath, cbool(use_git_link))
	if ret < 0 {
		return nil, MakeGitError(ret)
	}
	return sub, nil
}

func (sub *Submodule) FinalizeAdd() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_add_finalize(sub.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) AddToIndex(write_index bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_add_to_index(sub.ptr, cbool(write_index))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Save() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_save(sub.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Owner() *Repository {
	repo := C.git_submodule_owner(sub.ptr)
	//FIXME: how to handle dangling references ?
	return &Repository{repo}
}

func (sub *Submodule) Name() string {
	n := C.git_submodule_name(sub.ptr)
	return C.GoString(n)
}

func (sub *Submodule) Path() string {
	n := C.git_submodule_path(sub.ptr)
	return C.GoString(n)
}

func (sub *Submodule) Url() string {
	n := C.git_submodule_url(sub.ptr)
	return C.GoString(n)
}

func (sub *Submodule) SetUrl(url string) error {
	curl := C.CString(url)
	defer C.free(unsafe.Pointer(curl))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_set_url(sub.ptr, curl)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) IndexId() *Oid {
	idx := C.git_submodule_index_id(sub.ptr)
	if idx == nil {
		return nil
	}
	return newOidFromC(idx)
}

func (sub *Submodule) HeadId() *Oid {
	idx := C.git_submodule_head_id(sub.ptr)
	if idx == nil {
		return nil
	}
	return newOidFromC(idx)
}

func (sub *Submodule) WdId() *Oid {
	idx := C.git_submodule_wd_id(sub.ptr)
	if idx == nil {
		return nil
	}
	return newOidFromC(idx)
}

func (sub *Submodule) Ignore() SubmoduleIgnore {
	o := C.git_submodule_ignore(sub.ptr)
	return SubmoduleIgnore(o)
}

func (sub *Submodule) SetIgnore(ignore SubmoduleIgnore) SubmoduleIgnore {
	o := C.git_submodule_set_ignore(sub.ptr, C.git_submodule_ignore_t(ignore))
	return SubmoduleIgnore(o)
}

func (sub *Submodule) UpdateStrategy() SubmoduleUpdate {
	o := C.git_submodule_update_strategy(sub.ptr)
	return SubmoduleUpdate(o)
}

func (sub *Submodule) SetUpdate(update SubmoduleUpdate) SubmoduleUpdate {
	o := C.git_submodule_set_update(sub.ptr, C.git_submodule_update_t(update))
	return SubmoduleUpdate(o)
}

func (sub *Submodule) FetchRecurseSubmodules() SubmoduleRecurse {
	return SubmoduleRecurse(C.git_submodule_fetch_recurse_submodules(sub.ptr))
}

func (sub *Submodule) SetFetchRecurseSubmodules(recurse SubmoduleRecurse) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_set_fetch_recurse_submodules(sub.ptr, C.git_submodule_recurse_t(recurse))
	if ret < 0 {
		return MakeGitError(C.int(ret))
	}
	return nil
}

func (sub *Submodule) Init(overwrite bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_init(sub.ptr, cbool(overwrite))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Sync() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_sync(sub.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Open() (*Repository, error) {
	repo := new(Repository)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_open(&repo.ptr, sub.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}
	return repo, nil
}

func (sub *Submodule) Reload(force bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_reload(sub.ptr, cbool(force))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (repo *Repository) ReloadAllSubmodules(force bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_reload_all(repo.ptr, cbool(force))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
	var copts C.git_submodule_update_options
	err := populateSubmoduleUpdateOptions(&copts, opts)
	if err != nil {
		return err
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_update(sub.ptr, cbool(init), &copts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) error {
	C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)

	if opts == nil {
		return nil
	}

	populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts)
	populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks)
	ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy)

	sig, err := opts.Signature.toC()
	if err != nil {
		return err
	}
	ptr.signature = sig

	return nil
}
<commit_msg>submodule: use HandleList for C function callbacks<commit_after>package git

/*
#include <git2.h>

extern int _go_git_visit_submodule(git_repository *repo, void *fct);
*/
import "C"
import (
	"runtime"
	"unsafe"
)

// SubmoduleUpdateOptions
type SubmoduleUpdateOptions struct {
	*CheckoutOpts
	*RemoteCallbacks
	CloneCheckoutStrategy CheckoutStrategy
	Signature             *Signature
}

// Submodule
type Submodule struct {
	ptr *C.git_submodule
}

type SubmoduleUpdate int

const (
	SubmoduleUpdateReset    SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_RESET
	SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT
	SubmoduleUpdateRebase   SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE
	SubmoduleUpdateMerge    SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE
	SubmoduleUpdateNone     SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE
)

type SubmoduleIgnore int

const (
	SubmoduleIgnoreReset     SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_RESET
	SubmoduleIgnoreNone      SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_NONE
	SubmoduleIgnoreUntracked SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_UNTRACKED
	SubmoduleIgnoreDirty     SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_DIRTY
	SubmoduleIgnoreAll       SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_ALL
)

type SubmoduleStatus int

const (
	SubmoduleStatusInHead          SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_HEAD
	SubmoduleStatusInIndex         SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_INDEX
	SubmoduleStatusInConfig        SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_CONFIG
	SubmoduleStatusInWd            SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_WD
	SubmoduleStatusIndexAdded      SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_ADDED
	SubmoduleStatusIndexDeleted    SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_DELETED
	SubmoduleStatusIndexModified   SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_MODIFIED
	SubmoduleStatusWdUninitialized SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_UNINITIALIZED
	SubmoduleStatusWdAdded         SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_ADDED
	SubmoduleStatusWdDeleted       SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_DELETED
	SubmoduleStatusWdModified      SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_MODIFIED
	SubmoduleStatusWdIndexModified SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED
	SubmoduleStatusWdWdModified    SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_WD_MODIFIED
	SubmoduleStatusWdUntracked     SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_UNTRACKED
)

type SubmoduleRecurse int

const (
	SubmoduleRecurseNo       SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_NO
	SubmoduleRecurseYes      SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_YES
	SubmoduleRecurseOndemand SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_ONDEMAND
)

func SubmoduleStatusIsUnmodified(status int) bool {
	o := SubmoduleStatus(status) & ^(SubmoduleStatusInHead | SubmoduleStatusInIndex |
		SubmoduleStatusInConfig | SubmoduleStatusInWd)
	return o == 0
}

func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	sub := new(Submodule)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_lookup(&sub.ptr, repo.ptr, cname)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}

	return sub, nil
}

type SubmoduleCbk func(sub *Submodule, name string) int

//export SubmoduleVisitor
func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) C.int {
	sub := &Submodule{(*C.git_submodule)(csub)}

	if callback, ok := pointerHandles.Get(handle).(SubmoduleCbk); ok {
		return (C.int)(callback(sub, C.GoString(name)))
	} else {
		return -1
	}
}

func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	handle := pointerHandles.Track(cbk)
	defer pointerHandles.Untrack(handle)

	ret := C._go_git_visit_submodule(repo.ptr, handle)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (repo *Repository) AddSubmodule(url, path string, use_git_link bool) (*Submodule, error) {
	curl := C.CString(url)
	defer C.free(unsafe.Pointer(curl))
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	sub := new(Submodule)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_add_setup(&sub.ptr, repo.ptr, curl, cpath, cbool(use_git_link))
	if ret < 0 {
		return nil, MakeGitError(ret)
	}
	return sub, nil
}

func (sub *Submodule) FinalizeAdd() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_add_finalize(sub.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) AddToIndex(write_index bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_add_to_index(sub.ptr, cbool(write_index))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Save() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_save(sub.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Owner() *Repository {
	repo := C.git_submodule_owner(sub.ptr)
	//FIXME: how to handle dangling references ?
	return &Repository{repo}
}

func (sub *Submodule) Name() string {
	n := C.git_submodule_name(sub.ptr)
	return C.GoString(n)
}

func (sub *Submodule) Path() string {
	n := C.git_submodule_path(sub.ptr)
	return C.GoString(n)
}

func (sub *Submodule) Url() string {
	n := C.git_submodule_url(sub.ptr)
	return C.GoString(n)
}

func (sub *Submodule) SetUrl(url string) error {
	curl := C.CString(url)
	defer C.free(unsafe.Pointer(curl))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_set_url(sub.ptr, curl)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) IndexId() *Oid {
	idx := C.git_submodule_index_id(sub.ptr)
	if idx == nil {
		return nil
	}
	return newOidFromC(idx)
}

func (sub *Submodule) HeadId() *Oid {
	idx := C.git_submodule_head_id(sub.ptr)
	if idx == nil {
		return nil
	}
	return newOidFromC(idx)
}

func (sub *Submodule) WdId() *Oid {
	idx := C.git_submodule_wd_id(sub.ptr)
	if idx == nil {
		return nil
	}
	return newOidFromC(idx)
}

func (sub *Submodule) Ignore() SubmoduleIgnore {
	o := C.git_submodule_ignore(sub.ptr)
	return SubmoduleIgnore(o)
}

func (sub *Submodule) SetIgnore(ignore SubmoduleIgnore) SubmoduleIgnore {
	o := C.git_submodule_set_ignore(sub.ptr, C.git_submodule_ignore_t(ignore))
	return SubmoduleIgnore(o)
}

func (sub *Submodule) UpdateStrategy() SubmoduleUpdate {
	o := C.git_submodule_update_strategy(sub.ptr)
	return SubmoduleUpdate(o)
}

func (sub *Submodule) SetUpdate(update SubmoduleUpdate) SubmoduleUpdate {
	o := C.git_submodule_set_update(sub.ptr, C.git_submodule_update_t(update))
	return SubmoduleUpdate(o)
}

func (sub *Submodule) FetchRecurseSubmodules() SubmoduleRecurse {
	return SubmoduleRecurse(C.git_submodule_fetch_recurse_submodules(sub.ptr))
}

func (sub *Submodule) SetFetchRecurseSubmodules(recurse SubmoduleRecurse) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_set_fetch_recurse_submodules(sub.ptr, C.git_submodule_recurse_t(recurse))
	if ret < 0 {
		return MakeGitError(C.int(ret))
	}
	return nil
}

func (sub *Submodule) Init(overwrite bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_init(sub.ptr, cbool(overwrite))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Sync() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_sync(sub.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Open() (*Repository, error) {
	repo := new(Repository)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_open(&repo.ptr, sub.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}
	return repo, nil
}

func (sub *Submodule) Reload(force bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_reload(sub.ptr, cbool(force))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (repo *Repository) ReloadAllSubmodules(force bool) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_reload_all(repo.ptr, cbool(force))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
	var copts C.git_submodule_update_options
	err := populateSubmoduleUpdateOptions(&copts, opts)
	if err != nil {
		return err
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_submodule_update(sub.ptr, cbool(init), &copts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) error {
	C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)

	if opts == nil {
		return nil
	}

	populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts)
	populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks)
	ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy)

	sig, err := opts.Signature.toC()
	if err != nil {
		return err
	}
	ptr.signature = sig

	return nil
}
<|endoftext|>


<commit_before>package sync

import (
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/deckarep/golang-set"
	"github.com/dreampuf/evernote-sdk-golang/client"
	"github.com/dreampuf/evernote-sdk-golang/notestore"
	"github.com/dreampuf/evernote-sdk-golang/types"
	"github.com/dreampuf/evernote-sdk-golang/userstore"
	"github.com/pkg/errors"
)

const minimumFetchIntervalSeconds = 15 * 60
const cacheExtension = ".yml"

// Sync local cache from evernote server
func Sync(cacheRoot string, noteCacheDirName string, resourceCacheDirName string, clientKey string, clientSecret string, developerToken string, isSandbox bool, notebookName string) error {
	cacheRoot = path.Clean(cacheRoot)
	noteCacheDir := path.Join(cacheRoot, noteCacheDirName)
	resourceCacheDir := path.Join(cacheRoot, resourceCacheDirName)

	if err := os.MkdirAll(noteCacheDir, os.ModePerm); err != nil {
		return errors.Wrapf(err, "couldn't create note cache path %v", noteCacheDir)
	}

	if err := os.MkdirAll(resourceCacheDir, os.ModePerm); err != nil {
		return errors.Wrapf(err, "couldn't create resource cache path %v", resourceCacheDir)
	}

	cli := client.NewClient(clientKey, clientSecret, getEnvironment(isSandbox))

	us, err := getUserStore(cli, isSandbox)
	if err != nil {
		return err
	}

	ns, err := getNoteStore(cli, us, &developerToken)
	if err != nil {
		return err
	}

	notUpdated, err := checkUpdate(ns, &cacheRoot, &developerToken)
	if err != nil {
		return err
	}
	if notUpdated {
		return errors.Errorf("note is not updated")
	}

	bookGUID, err := findNotebookGUID(&notebookName, cli, ns, &developerToken)
	if err != nil {
		return err
	}

	metadatas, err := findMetadatas(ns, bookGUID, &developerToken)
	if err != nil {
		return err
	}

	cachedIds, err := createCachedNoteIDMap(&noteCacheDir)
	if err != nil {
		return err
	}

	for _, note := range metadatas.GetNotes() {
		fmt.Printf("processing %v\n", note.GUID)

		noteCachePath := path.Join(noteCacheDir, string(note.GUID)+cacheExtension)
		cachedNote, err := readCachedNote(&noteCachePath, &note.GUID)
		if err != nil {
			return errors.Wrapf(err, "can't read cached note")
		}

		if cachedNote == nil || *cachedNote.UpdateSequenceNum != *note.UpdateSequenceNum {
			note, err := ns.GetNote(developerToken, note.GUID, true, false, false, false)
			if err != nil {
				return errors.Wrapf(err, "can't get note %v", *note.GUID)
			}

			tags, err := ns.GetNoteTagNames(developerToken, *note.GUID)
			if err != nil {
				return errors.Wrapf(err, "can't get note tags %v", *note.GUID)
			}
			note.TagNames = tags

			fmt.Printf("downloaded %v[%v]\n", *note.Title, *note.GUID)

			writeCachedNoteToFile(&noteCachePath, note)
			saveResources(&resourceCacheDir, cachedNote, note, ns, &developerToken)
		}

		cachedIds.Remove(note.GUID)
	}

	for _, id := range cachedIds.ToSlice() {
		guid := id.(string)
		os.Remove(noteCacheDir + guid + cacheExtension)
	}

	return nil
}

func getEnvironment(isSandbox bool) client.EnvironmentType {
	if isSandbox {
		return client.SANDBOX
	}
	return client.PRODUCTION
}

func getUserStore(cli *client.EvernoteClient, isSandbox bool) (*userstore.UserStoreClient, error) {
	us, err := cli.GetUserStore()
	if err != nil {
		return nil, errors.Wrapf(err, "can't get user store (environment: %v)", getEnvironment(isSandbox))
	}

	versionOk, err := us.CheckVersion("chienote", userstore.EDAM_VERSION_MAJOR, userstore.EDAM_VERSION_MINOR)
	if !versionOk {
		return nil, errors.New("user store isn't correct version")
	}
	if err != nil {
		return nil, errors.Wrap(err, "error occured on checking user store version")
	}

	return us, nil
}

func getNoteStore(cli *client.EvernoteClient, us *userstore.UserStoreClient, developerToken *string) (*notestore.NoteStoreClient, error) {
	url, err := us.GetNoteStoreUrl(*developerToken)
	if err != nil {
		return nil, errors.Wrap(err, "can't get note store url")
	}
	if url == "" {
		return nil, errors.New("empty notestore url received")
	}

	ns, err := cli.GetNoteStoreWithURL(url)
	if err != nil {
		return nil, errors.Wrap(err, "can't get note store")
	}

	return ns, nil
}

func findNotebookGUID(notebookName *string, cli *client.EvernoteClient, ns *notestore.NoteStoreClient, developerToken *string) (notebookGUID *types.GUID, err error) {
	notebooks, err := ns.ListNotebooks(*developerToken)
	if err != nil {
		return nil, errors.Wrap(err, "can't get notebook list")
	}

	for _, book := range notebooks {
		if *book.Name == *notebookName {
			notebookGUID = book.GUID
			break
		}
	}

	if notebookGUID == nil || *notebookGUID == "" {
		return nil, errors.Errorf("can't get notebook %v", *notebookName)
	}

	return notebookGUID, nil
}

func findMetadatas(ns *notestore.NoteStoreClient, bookGUID *types.GUID, developerToken *string) (metadatas *notestore.NotesMetadataList, err error) {
	ascending := false
	filter := &notestore.NoteFilter{NotebookGuid: bookGUID, Ascending: &ascending}

	var resultSpec notestore.NotesMetadataResultSpec
	metadatas, err = ns.FindNotesMetadata(*developerToken, filter, 0, 1000, &resultSpec)
	if err != nil {
		return nil, errors.Wrap(err, "couldn't find notes")
	}
	return metadatas, err
}

func createCachedNoteIDMap(noteCachePath *string) (mapset.Set, error) {
	cachedIDs := mapset.NewSet()
	cacheFileInfos, err := ioutil.ReadDir(*noteCachePath)
	if err != nil {
		return nil, errors.Wrapf(err, "can't read cache directory %v", *noteCachePath)
	}

	for _, cacheFileInfo := range cacheFileInfos {
		if strings.HasSuffix(cacheFileInfo.Name(), cacheExtension) {
			cachedIDs.Add(strings.Replace(cacheFileInfo.Name(), cacheExtension, "", 1))
		}
	}

	return cachedIDs, nil
}

func readCachedNoteFile(noteCachePath *string) (*types.Note, error) {
	cacheReader, err := os.OpenFile(*noteCachePath, os.O_RDONLY, os.ModePerm)
	if err != nil {
		return nil, errors.Wrapf(err, "can't open cached note file %v", *noteCachePath)
	}
	defer cacheReader.Close()

	cachedYAML, err := ioutil.ReadAll(cacheReader)
	if err != nil {
		return nil, errors.Wrapf(err, "can't read cached note file %v", *noteCachePath)
	}

	cachedNote := &types.Note{}
	if err := yaml.Unmarshal(cachedYAML, cachedNote); err != nil {
		return nil, errors.Wrapf(err, "can't parse cached yaml file %v", *noteCachePath)
	}

	return cachedNote, nil
}

func readCachedNote(noteCachePath *string, noteGUID *types.GUID) (cachedNote *types.Note, err error) {
	if _, err := os.Stat(*noteCachePath); err == nil {
		cachedNote, err = readCachedNoteFile(noteCachePath)
		if err != nil {
			return nil, errors.Wrapf(err, "error occurs when reading cached note file %v", *noteCachePath)
		}
	}

	return cachedNote, nil
}

func writeCachedNoteToFile(cachePath *string, note *types.Note) error {
	yamlBytes, err := yaml.Marshal(note)
	if err != nil {
		return errors.Wrapf(err, "can't marshal note as YAML %v", *cachePath)
	}

	if err := ioutil.WriteFile(*cachePath, yamlBytes, os.ModePerm); err != nil {
		return errors.Wrapf(err, "can't write marshaled note to %v", *cachePath)
	}

	return nil
}

func saveResources(resourceCacheDir *string, cachedNote *types.Note, receivedNote *types.Note, ns *notestore.NoteStoreClient, developerToken *string) error {
	localResourceMap := map[types.GUID]int32{}
	if cachedNote != nil {
		for _, cachedResource := range cachedNote.Resources {
			localResourceMap[*cachedResource.GUID] = *cachedResource.UpdateSequenceNum
		}
	}

	for _, resource := range receivedNote.Resources {
		fetchingNeeded := false
		cachedUpdateNum, exists := localResourceMap[*resource.GUID]
		if exists {

			if cachedUpdateNum != *resource.UpdateSequenceNum {
				fetchingNeeded = true
			}
			delete(localResourceMap, *resource.GUID)
		} else {
			fetchingNeeded = true
		}

		if fetchingNeeded {
			resourceWithBytes, err := ns.GetResource(*developerToken, *resource.GUID, true, false, true, false)
			if err != nil {
				return errors.Wrapf(err, "can't get resource %v", *resource.Attributes.FileName)
			}

			p := path.Join(*resourceCacheDir, hex.EncodeToString(resourceWithBytes.Data.BodyHash)+"-"+*resourceWithBytes.Attributes.FileName)
			ioutil.WriteFile(p, resourceWithBytes.Data.Body, os.ModePerm)
			fmt.Println("write resource to " + p)
		} else {
			fmt.Printf("not updated %v\n", *resource.Attributes.FileName)
		}
	}

	return nil
}

func checkUpdate(ns *notestore.NoteStoreClient, cacheDir *string, developerToken *string) (notUpdated bool, err error) {
	syncState, err := ns.GetSyncState(*developerToken)
	if err != nil {
		return false, errors.Wrap(err, "can't get sync state")
	}

	syncStatePath := path.Join(*cacheDir, "sync_state.yml")

	prevStateBytes, err := ioutil.ReadFile(syncStatePath)
	if err == nil {
		prevState := &notestore.SyncState{Uploaded: new(int64)}
		if err := yaml.Unmarshal(prevStateBytes, prevState); err == nil {
			if prevState.UpdateCount == syncState.UpdateCount {
				return true, nil
			}

			if syncState.CurrentTime-prevState.CurrentTime < minimumFetchIntervalSeconds*1000 {
				return false, errors.Errorf("you should wait at least %v seconds from last update", minimumFetchIntervalSeconds)
			}
		}
	}

	stateBytes, err := yaml.Marshal(syncState)
	if err != nil {
		return false, errors.Wrapf(err, "can't marshal sync state")
	}

	if err := ioutil.WriteFile(syncStatePath, stateBytes, os.ModePerm); err != nil {
		return false, errors.Wrapf(err, "can't write sync state %v", syncStatePath)
	}

	return false, nil
}
<commit_msg>Support copy-and-pasted attachments<commit_after>package sync

import (
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/deckarep/golang-set"
	"github.com/dreampuf/evernote-sdk-golang/client"
	"github.com/dreampuf/evernote-sdk-golang/notestore"
	"github.com/dreampuf/evernote-sdk-golang/types"
	"github.com/dreampuf/evernote-sdk-golang/userstore"
	"github.com/pkg/errors"
)

const minimumFetchIntervalSeconds = 15 * 60
const cacheExtension = ".yml"

// Sync local cache from evernote server
func Sync(cacheRoot string, noteCacheDirName string, resourceCacheDirName string, clientKey string, clientSecret string, developerToken string, isSandbox bool, notebookName string) error {
	cacheRoot = path.Clean(cacheRoot)
	noteCacheDir := path.Join(cacheRoot, noteCacheDirName)
	resourceCacheDir := path.Join(cacheRoot, resourceCacheDirName)

	if err := os.MkdirAll(noteCacheDir, os.ModePerm); err != nil {
		return errors.Wrapf(err, "couldn't create note cache path %v", noteCacheDir)
	}

	if err := os.MkdirAll(resourceCacheDir, os.ModePerm); err != nil {
		return errors.Wrapf(err, "couldn't create resource cache path %v", resourceCacheDir)
	}

	cli := client.NewClient(clientKey, clientSecret, getEnvironment(isSandbox))

	us, err := getUserStore(cli, isSandbox)
	if err != nil {
		return err
	}

	ns, err := getNoteStore(cli, us, &developerToken)
	if err != nil {
		return err
	}

	notUpdated, err := checkUpdate(ns, &cacheRoot, &developerToken)
	if err != nil {
		return err
	}
	if notUpdated {
		return errors.Errorf("note is not updated")
	}

	bookGUID, err := findNotebookGUID(&notebookName, cli, ns, &developerToken)
	if err != nil {
		return err
	}

	metadatas, err := findMetadatas(ns, bookGUID, &developerToken)
	if err != nil {
		return err
	}

	cachedIds, err := createCachedNoteIDMap(&noteCacheDir)
	if err != nil {
		return err
	}

	for _, note := range metadatas.GetNotes() {
		fmt.Printf("processing %v\n", note.GUID)

		noteCachePath := path.Join(noteCacheDir, string(note.GUID)+cacheExtension)
		cachedNote, err := readCachedNote(&noteCachePath, &note.GUID)
		if err != nil {
			return errors.Wrapf(err, "can't read cached note")
		}

		if cachedNote == nil || *cachedNote.UpdateSequenceNum != *note.UpdateSequenceNum {
			note, err := ns.GetNote(developerToken, note.GUID, true, false, false, false)
			if err != nil {
				return errors.Wrapf(err, "can't get note %v", *note.GUID)
			}

			tags, err := ns.GetNoteTagNames(developerToken, *note.GUID)
			if err != nil {
				return errors.Wrapf(err, "can't get note tags %v", *note.GUID)
			}
			note.TagNames = tags

			fmt.Printf("downloaded %v[%v]\n", *note.Title, *note.GUID)

			writeCachedNoteToFile(&noteCachePath, note)
			saveResources(&resourceCacheDir, cachedNote, note, ns, &developerToken)
		}

		cachedIds.Remove(note.GUID)
	}

	for _, id := range cachedIds.ToSlice() {
		guid := id.(string)
		os.Remove(noteCacheDir + guid + cacheExtension)
	}

	return nil
}

func getEnvironment(isSandbox bool) client.EnvironmentType {
	if isSandbox {
		return client.SANDBOX
	}
	return client.PRODUCTION
}

func getUserStore(cli *client.EvernoteClient, isSandbox bool) (*userstore.UserStoreClient, error) {
	us, err := cli.GetUserStore()
	if err != nil {
		return nil, errors.Wrapf(err, "can't get user store (environment: %v)", getEnvironment(isSandbox))
	}

	versionOk, err := us.CheckVersion("chienote", userstore.EDAM_VERSION_MAJOR, userstore.EDAM_VERSION_MINOR)
	if !versionOk {
		return nil, errors.New("user store isn't correct version")
	}
	if err != nil {
		return nil, errors.Wrap(err, "error occured on checking user store version")
	}

	return us, nil
}

func getNoteStore(cli *client.EvernoteClient, us *userstore.UserStoreClient, developerToken *string) (*notestore.NoteStoreClient, error) {
	url, err := us.GetNoteStoreUrl(*developerToken)
	if err != nil {
		return nil, errors.Wrap(err, "can't get note store url")
	}
	if url == "" {
		return nil, errors.New("empty notestore url received")
	}

	ns, err := cli.GetNoteStoreWithURL(url)
	if err != nil {
		return nil, errors.Wrap(err, "can't get note store")
	}

	return ns, nil
}

func findNotebookGUID(notebookName *string, cli *client.EvernoteClient, ns *notestore.NoteStoreClient, developerToken *string) (notebookGUID *types.GUID, err error) {
	notebooks, err := ns.ListNotebooks(*developerToken)
	if err != nil {
		return nil, errors.Wrap(err, "can't get notebook list")
	}

	for _, book := range notebooks {
		if *book.Name == *notebookName {
			notebookGUID = book.GUID
			break
		}
	}

	if notebookGUID == nil || *notebookGUID == "" {
		return nil, errors.Errorf("can't get notebook %v", *notebookName)
	}

	return notebookGUID, nil
}

func findMetadatas(ns *notestore.NoteStoreClient, bookGUID *types.GUID, developerToken *string) (metadatas *notestore.NotesMetadataList, err error) {
	ascending := false
	filter := &notestore.NoteFilter{NotebookGuid: bookGUID, Ascending: &ascending}

	var resultSpec notestore.NotesMetadataResultSpec
	metadatas, err = ns.FindNotesMetadata(*developerToken, filter, 0, 1000, &resultSpec)
	if err != nil {
		return nil, errors.Wrap(err, "couldn't find notes")
	}
	return metadatas, err
}

func createCachedNoteIDMap(noteCachePath *string) (mapset.Set, error) {
	cachedIDs := mapset.NewSet()
	cacheFileInfos, err := ioutil.ReadDir(*noteCachePath)
	if err != nil {
		return nil, errors.Wrapf(err, "can't read cache directory %v", *noteCachePath)
	}

	for _, cacheFileInfo := range cacheFileInfos {
		if strings.HasSuffix(cacheFileInfo.Name(), cacheExtension) {
			cachedIDs.Add(strings.Replace(cacheFileInfo.Name(), cacheExtension, "", 1))
		}
	}

	return cachedIDs, nil
}

func readCachedNoteFile(noteCachePath *string) (*types.Note, error) {
	cacheReader, err := os.OpenFile(*noteCachePath, os.O_RDONLY, os.ModePerm)
	if err != nil {
		return nil, errors.Wrapf(err, "can't open cached note file %v", *noteCachePath)
	}
	defer cacheReader.Close()

	cachedYAML, err := ioutil.ReadAll(cacheReader)
	if err != nil {
		return nil, errors.Wrapf(err, "can't read cached note file %v", *noteCachePath)
	}

	cachedNote := &types.Note{}
	if err := yaml.Unmarshal(cachedYAML, cachedNote); err != nil {
		return nil, errors.Wrapf(err, "can't parse cached yaml file %v", *noteCachePath)
	}

	return cachedNote, nil
}

func readCachedNote(noteCachePath *string, noteGUID *types.GUID) (cachedNote *types.Note, err error) {
	if _, err := os.Stat(*noteCachePath); err == nil {
		cachedNote, err = readCachedNoteFile(noteCachePath)
		if err != nil {
			return nil, errors.Wrapf(err, "error occurs when reading cached note file %v", *noteCachePath)
		}
	}

	return cachedNote, nil
}

func writeCachedNoteToFile(cachePath *string, note *types.Note) error {
	yamlBytes, err := yaml.Marshal(note)
	if err != nil {
		return errors.Wrapf(err, "can't marshal note as YAML %v", *cachePath)
	}

	if err := ioutil.WriteFile(*cachePath, yamlBytes, os.ModePerm); err != nil {
		return errors.Wrapf(err, "can't write marshaled note to %v", *cachePath)
	}

	return nil
}

func saveResources(resourceCacheDir *string, cachedNote *types.Note, receivedNote *types.Note, ns *notestore.NoteStoreClient, developerToken *string) error {
	localResourceMap := map[types.GUID]int32{}
	if cachedNote != nil {
		for _, cachedResource := range cachedNote.Resources {
			localResourceMap[*cachedResource.GUID] = *cachedResource.UpdateSequenceNum
		}
	}

	for _, resource := range receivedNote.Resources {
		fetchingNeeded := false
		cachedUpdateNum, exists := localResourceMap[*resource.GUID]
		if exists {

			if cachedUpdateNum != *resource.UpdateSequenceNum {
				fetchingNeeded = true
			}
			delete(localResourceMap, *resource.GUID)
		} else {
			fetchingNeeded = true
		}

		if fetchingNeeded {
			resourceWithBytes, err := ns.GetResource(*developerToken, *resource.GUID, true, false, true, false)
			if err != nil {
				return errors.Wrapf(err, "can't get resource %v", *resource.Attributes.FileName)
			}

			var resourceFileName string
			if resourceWithBytes.Attributes.FileName == nil {
				// https://dev.evernote.com/doc/articles/resources.php#downloading
				var extension string
				switch *resourceWithBytes.Mime {
				case "image/gif":
					extension = ".gif"
				case "image/jpeg":
					extension = ".jpg"
				case "image/png":
					extension = ".png"
				case "audio/wav":
					extension = ".wav"
				case "audio/mpeg":
					extension = ".mp3"
				case "audio/amr":
					extension = ".amr"
				case "audio/pdf":
					extension = ".pdf"
				}
				resourceFileName = hex.EncodeToString(resourceWithBytes.Data.BodyHash) + extension
			} else {
				resourceFileName = hex.EncodeToString(resourceWithBytes.Data.BodyHash) + "-" + *resourceWithBytes.Attributes.FileName
			}

			p := path.Join(*resourceCacheDir, resourceFileName)
			ioutil.WriteFile(p, resourceWithBytes.Data.Body, os.ModePerm)
			fmt.Println("write resource to " + p)
		} else {
			fmt.Printf("not updated %v\n", *resource.Attributes.FileName)
		}
	}

	return nil
}

func checkUpdate(ns *notestore.NoteStoreClient, cacheDir *string, developerToken *string) (notUpdated bool, err error) {
	syncState, err := ns.GetSyncState(*developerToken)
	if err != nil {
		return false, errors.Wrap(err, "can't get sync state")
	}

	syncStatePath := path.Join(*cacheDir, "sync_state.yml")

	prevStateBytes, err := ioutil.ReadFile(syncStatePath)
	if err == nil {
		prevState := &notestore.SyncState{Uploaded: new(int64)}
		if err := yaml.Unmarshal(prevStateBytes, prevState); err == nil {
			if prevState.UpdateCount == syncState.UpdateCount {
				return true, nil
			}

			if syncState.CurrentTime-prevState.CurrentTime < minimumFetchIntervalSeconds*1000 {
				return false, errors.Errorf("you should wait at least %v seconds from last update", minimumFetchIntervalSeconds)
			}
		}
	}

	stateBytes, err := yaml.Marshal(syncState)
	if err != nil {
		return false, errors.Wrapf(err, "can't marshal sync state")
	}

	if err := ioutil.WriteFile(syncStatePath, stateBytes, os.ModePerm); err != nil {
		return false, errors.Wrapf(err, "can't write sync state %v", syncStatePath)
	}

	return false, nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/url"
	"os"
	"strconv"
	"testing"
	"time"
)

func TestSync(t *testing.T) {
	exp := 1
	executed := false
	// Sync is depth first
	fs, es := injectableSync("", "", fakeLookup([]int{1, 0, 6}, []int{0, 4, 5}, []int{2, 3}),
		func(f File) error {
			executed = true
			n, _ := strconv.Atoi(f.Url.Path)
			if n != exp {
				t.Errorf("%d should have been %d", n, exp)
			}
			exp++
			return nil
		})
Loop:
	for {
		select {
		case _, ok := <-fs:
			if !ok {
				break Loop
			}
		case <-es:
		}
	}
	if !executed {
		t.Error("No Files were generated")
	}
}

func fakeLookup(nums ...[]int) LookupFn {
	i := 0
	return func(f File) (IndexFn, error) {
		return func(f File, files chan File, errs chan error) {
			for _, n := range nums[i] {
				ff := stringReadFn("")
				if n == 0 {
					i++
					ff = nil
				}
				files <- File{
					Url:      url.URL{Path: strconv.Itoa(n)},
					FileFunc: ff,
				}
			}
		}, nil
	}
}

func TestLocalNew(t *testing.T) {
	testLocal(t, func(f File) File {
		return f
	}, true)
}

func TestLocalOverwriteOlder(t *testing.T) {
	testLocal(t, func(f File) File {
		err := Local(f)
		if err != nil {
			t.Error(err)
		}
		f.Mtime = f.Mtime.Add(time.Second)
		return f
	}, true)
}

func TestLocalNotOverwriteNewer(t *testing.T) {
	testLocal(t, func(f File) File {
		err := Local(f)
		if err != nil {
			t.Error(err)
		}
		f.Mtime = f.Mtime.Add(-time.Second)
		return f
	}, false)
}

func TestCreateDirs(t *testing.T) {
	testLocal(t, func(f File) File {
		f.Url.Path += "/a/dir/oh/uh/hi/ho"
		return f
	}, true)
}

// Trying to overwrite a directory fails
func TestLocalOverwriteDir(t *testing.T) {
	tmp, rm := TempDir()
	defer rm()
	f := someTestFile(tmp)
	os.Mkdir(f.Url.Path, 777)
	os.Chtimes(f.Url.Path, time.Now(), time.Now().Add(-time.Hour))
	if Local(f) == nil {
		t.Error("should have failed")
	}
}

func testLocal(t *testing.T, init func(File) File, overwrite bool) {
	tmp, rm := TempDir()
	of := someTestFile(tmp)
	f := init(of)
	err := Local(f)
	if err != nil {
		t.Error(err)
	}
	if overwrite {
		of = f
	}
	checkFile(t, of)
	rm()
}

func someTestFile(tmp string) File {
	return File{
		Url:      url.URL{Path: tmp + "/a"},
		Mtime:    time.Now(),
		FileFunc: stringReadFn("test"),
	}
}

func createSomeFile(tmp string) {
	return
}

func checkFile(t *testing.T, f File) {
	st, err := os.Stat(f.Url.Path)

	f.Mtime = removeSubSecond(f.Mtime)

	if err != nil && os.IsNotExist(err) {
		t.Error("File does not exist:", f.Url)
	} else {
		if !(st.ModTime().Equal(f.Mtime)) {
			t.Errorf("Not overwritten")
		}
	}
}

// OSX does not store time resolutions below seconds
func removeSubSecond(in time.Time) time.Time {
	return time.Date(
		in.Year(),
		in.Month(),
		in.Day(),
		in.Hour(),
		in.Minute(),
		in.Second(),
		0, in.Location())
}
<commit_msg>fix sync test<commit_after>package main

import (
	"net/url"
	"os"
	"strconv"
	"testing"
	"time"
)

func TestSync(t *testing.T) {
	// Sync is depth first
	input := [][]int{{1, 0, 6}, {0, 4, 5}, {2, 3}}
	expN := 1
	fs, es := injectableSync("", "", fakeLookup(input),
		func(f File) error {
			n, _ := strconv.Atoi(f.Url.Path)
			if n != expN {
				t.Errorf("%d should have been %d", n, expN)
			}
			expN++
			return nil
		})
Loop:
	for {
		select {
		case _, ok := <-fs:
			if !ok {
				break Loop
			}
		case <-es:
		}
	}
	expected := flattenInts(input)
	if expN < len(expected) {
		t.Error("Missing Files:", expected[expN:])
	}
}

func fakeLookup(nums [][]int) LookupFn {
	i := 0
	return func(f File) (IndexFn, error) {
		return func(f File, files chan File, errs chan error) {
			for _, n := range nums[i] {
				ff := stringReadFn("")
				if n == 0 {
					i++
					ff = nil
				}
				files <- File{
					Url:      url.URL{Path: strconv.Itoa(n)},
					FileFunc: ff,
				}
			}
		}, nil
	}
}

func flattenInts(ints [][]int) []int {
	res := []int{}
	for _, g := range ints {
		for _, n := range g {
			if n == 0 {
				continue
			}
			res = append(res, n)
		}
	}
	return res
}

func TestLocalNew(t *testing.T) {
	testLocal(t, func(f File) File {
		return f
	}, true)
}

func TestLocalOverwriteOlder(t *testing.T) {
	testLocal(t, func(f File) File {
		err := Local(f)
		if err != nil {
			t.Error(err)
		}
		f.Mtime = f.Mtime.Add(time.Second)
		return f
	}, true)
}

func TestLocalNotOverwriteNewer(t *testing.T) {
	testLocal(t, func(f File) File {
		err := Local(f)
		if err != nil {
			t.Error(err)
		}
		f.Mtime = f.Mtime.Add(-time.Second)
		return f
	}, false)
}

func TestCreateDirs(t *testing.T) {
	testLocal(t, func(f File) File {
		f.Url.Path += "/a/dir/oh/uh/hi/ho"
		return f
	}, true)
}

// Trying to overwrite a directory fails
func TestLocalOverwriteDir(t *testing.T) {
	tmp, rm := TempDir()
	defer rm()
	f := someTestFile(tmp)
	os.Mkdir(f.Url.Path, 777)
	os.Chtimes(f.Url.Path, time.Now(), time.Now().Add(-time.Hour))
	if Local(f) == nil {
		t.Error("should have failed")
	}
}

func testLocal(t *testing.T, init func(File) File, overwrite bool) {
	tmp, rm := TempDir()
	of := someTestFile(tmp)
	f := init(of)
	err := Local(f)
	if err != nil {
		t.Error(err)
	}
	if overwrite {
		of = f
	}
	checkFile(t, of)
	rm()
}

func someTestFile(tmp string) File {
	return File{
		Url:      url.URL{Path: tmp + "/a"},
		Mtime:    time.Now(),
		FileFunc: stringReadFn("test"),
	}
}

func createSomeFile(tmp string) {
	return
}

func checkFile(t *testing.T, f File) {
	st, err := os.Stat(f.Url.Path)

	f.Mtime = removeSubSecond(f.Mtime)

	if err != nil && os.IsNotExist(err) {
		t.Error("File does not exist:", f.Url)
	} else {
		if !(st.ModTime().Equal(f.Mtime)) {
			t.Errorf("Not overwritten")
		}
	}
}

// OSX does not store time resolutions below seconds
func removeSubSecond(in time.Time) time.Time {
	return time.Date(
		in.Year(),
		in.Month(),
		in.Day(),
		in.Hour(),
		in.Minute(),
		in.Second(),
		0, in.Location())
}
<|endoftext|>


<commit_before>package tak

import (
	"fmt"
)

type position3 struct {
	Position
	alloc struct {
		Height [3 * 3]uint8
		Stacks [3 * 3]uint64
		Groups [6]uint64
	}
}

type position4 struct {
	Position
	alloc struct {
		Height [4 * 4]uint8
		Stacks [4 * 4]uint64
		Groups [8]uint64
	}
}

type position5 struct {
	Position
	alloc struct {
		Height [5 * 5]uint8
		Stacks [5 * 5]uint64
		Groups [10]uint64
	}
}

type position6 struct {
	Position
	alloc struct {
		Height [6 * 6]uint8
		Stacks [6 * 6]uint64
		Groups [12]uint64
	}
}

type position7 struct {
	Position
	alloc struct {
		Height [7 * 7]uint8
		Stacks [7 * 7]uint64
		Groups [14]uint64
	}
}

type position8 struct {
	Position
	alloc struct {
		Height [8 * 8]uint8
		Stacks [8 * 8]uint64
		Groups [16]uint64
	}
}

func alloc(tpl *Position) *Position {
	switch tpl.Size() {
	case 3:
		a := &position3{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 4:
		a := &position4{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 5:
		a := &position5{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 6:
		a := &position6{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 7:
		a := &position7{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 8:
		a := &position8{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	default:
		panic(fmt.Sprintf("illegal size: %d", tpl.Size()))
	}
}

func copyPosition(p *Position, out *Position) {
	h := out.Height
	s := out.Stacks
	g := out.analysis.WhiteGroups

	*out = *p
	out.Height = h
	out.Stacks = s
	out.analysis.WhiteGroups = g[:0]

	copy(out.Height, p.Height)
	copy(out.Stacks, p.Stacks)
}

func Alloc(size int) *Position {
	p := Position{cfg: &Config{Size: 5}}
	return alloc(&p)
}
<commit_msg>Don't hardcode size 5<commit_after>package tak

import "fmt"

type position3 struct {
	Position
	alloc struct {
		Height [3 * 3]uint8
		Stacks [3 * 3]uint64
		Groups [6]uint64
	}
}

type position4 struct {
	Position
	alloc struct {
		Height [4 * 4]uint8
		Stacks [4 * 4]uint64
		Groups [8]uint64
	}
}

type position5 struct {
	Position
	alloc struct {
		Height [5 * 5]uint8
		Stacks [5 * 5]uint64
		Groups [10]uint64
	}
}

type position6 struct {
	Position
	alloc struct {
		Height [6 * 6]uint8
		Stacks [6 * 6]uint64
		Groups [12]uint64
	}
}

type position7 struct {
	Position
	alloc struct {
		Height [7 * 7]uint8
		Stacks [7 * 7]uint64
		Groups [14]uint64
	}
}

type position8 struct {
	Position
	alloc struct {
		Height [8 * 8]uint8
		Stacks [8 * 8]uint64
		Groups [16]uint64
	}
}

func alloc(tpl *Position) *Position {
	switch tpl.Size() {
	case 3:
		a := &position3{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 4:
		a := &position4{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 5:
		a := &position5{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 6:
		a := &position6{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 7:
		a := &position7{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	case 8:
		a := &position8{Position: *tpl}
		a.Height = a.alloc.Height[:]
		a.Stacks = a.alloc.Stacks[:]
		a.analysis.WhiteGroups = a.alloc.Groups[:0]
		copy(a.Height, tpl.Height)
		copy(a.Stacks, tpl.Stacks)

		return &a.Position
	default:
		panic(fmt.Sprintf("illegal size: %d", tpl.Size()))
	}
}

func copyPosition(p *Position, out *Position) {
	h := out.Height
	s := out.Stacks
	g := out.analysis.WhiteGroups

	*out = *p
	out.Height = h
	out.Stacks = s
	out.analysis.WhiteGroups = g[:0]

	copy(out.Height, p.Height)
	copy(out.Stacks, p.Stacks)
}

func Alloc(size int) *Position {
	p := Position{cfg: &Config{Size: size}}
	return alloc(&p)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Alexander Neumann <alexander@bumpern.de>
// Copyright (c) 2017, Christophe-Marie Duquesne <chmd@chmd.fr>
//
// This file was adapted from restic https://github.com/restic/chunker
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package rabinkarp64

import (
	"sync"

	"github.com/chmduquesne/rollinghash"
)

const Size = 8

type tables struct {
	out [256]Pol
	mod [256]Pol
}

// tables are cacheable for a given pol and windowsize
type index struct {
	pol        Pol
	windowsize int
}

type RabinKarp64 struct {
	pol      Pol
	tables   tables
	polShift uint
	value    Pol

	// window is treated like a circular buffer, where the oldest element
	// is indicated by d.oldest
	window []byte
	oldest int
}

// cache precomputed tables, these are read-only anyway
var cache struct {
	// For a given polynom and a given window size, we get a table
	entries map[index]tables
	sync.Mutex
}

func init() {
	cache.entries = make(map[index]tables)
}

func (d *RabinKarp64) initTables() {
	windowsize := len(d.window)
	idx := index{d.pol, windowsize}

	cache.Lock()
	t, ok := cache.entries[idx]
	cache.Unlock()
	if ok {
		d.tables = t
		return
	}

	// calculate table for sliding out bytes. The byte to slide out is used as
	// the index for the table, the value contains the following:
	// out_table[b] = Hash(b || 0 ||        ...        || 0)
	//                          \ windowsize-1 zero bytes /
	// To slide out byte b_0 for window size w with known hash
	// H := H(b_0 || ... || b_w), it is sufficient to add out_table[b_0]:
	//    H(b_0 || ... || b_w) + H(b_0 || 0 || ... || 0)
	//  = H(b_0 + b_0 || b_1 + 0 || ... || b_w + 0)
	//  = H(    0     || b_1 || ...     || b_w)
	//
	// Afterwards a new byte can be shifted in.
	for b := 0; b < 256; b++ {
		var h Pol

		h <<= 8
		h |= Pol(b)
		h = h.Mod(d.pol)
		for i := 0; i < windowsize-1; i++ {
			h <<= 8
			h |= Pol(0)
			h = h.Mod(d.pol)
		}
		d.tables.out[b] = h
	}

	// calculate table for reduction mod Polynomial
	k := d.pol.Deg()
	for b := 0; b < 256; b++ {
		// mod_table[b] = A | B, where A = (b(x) * x^k mod pol) and  B = b(x) * x^k
		//
		// The 8 bits above deg(Polynomial) determine what happens next and so
		// these bits are used as a lookup to this table. The value is split in
		// two parts: Part A contains the result of the modulus operation, part
		// B is used to cancel out the 8 top bits so that one XOR operation is
		// enough to reduce modulo Polynomial
		d.tables.mod[b] = Pol(uint64(b)<<uint(k)).Mod(d.pol) | (Pol(b) << uint(k))
	}

	cache.Lock()
	cache.entries[idx] = d.tables
	cache.Unlock()
}

func NewFromPol(p Pol) *RabinKarp64 {
	res := &RabinKarp64{
		pol:      p,
		tables:   tables{},
		polShift: uint(p.Deg() - 8),
		window:   make([]byte, 0, rollinghash.DefaultWindowCap),
		value:    0,
	}
	res.Reset()
	return res
}

func New() *RabinKarp64 {
	p, err := RandomPolynomial(1)
	if err != nil {
		panic(err)
	}
	return NewFromPol(p)
}

func (d *RabinKarp64) Reset() {
	d.window = d.window[:1]
	d.window[0] = 0
	d.oldest = 0
	d.value = 0
}

// Size is 8 bytes
func (d *RabinKarp64) Size() int { return Size }

// BlockSize is 1 byte
func (d *RabinKarp64) BlockSize() int { return 1 }

// Write (re)initializes the rolling window with the input byte slice and
// adds its data to the digest. It never returns an error.
func (d *RabinKarp64) Write(data []byte) (int, error) {
	// Copy the window
	l := len(data)
	if l == 0 {
		l = 1
	}
	if len(d.window) >= l {
		d.window = d.window[:l]
	} else {
		d.window = make([]byte, l)
	}
	copy(d.window, data)

	for _, b := range d.window {
		d.value <<= 8
		d.value |= Pol(b)
		d.value = d.value.Mod(d.pol)
	}

	d.initTables()

	// TODO: compute the checksum
	return len(d.window), nil
}

// Sum64 returns the hash as a uint64
func (d *RabinKarp64) Sum64() uint64 {
	return uint64(d.value)
}

// Sum returns the hash as byte slice
func (d *RabinKarp64) Sum(b []byte) []byte {
	v := d.Sum64()
	return append(b, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

// Roll updates the checksum of the window from the entering byte. You
// MUST initialize a window with Write() before calling this method.
func (d *RabinKarp64) Roll(c byte) {
	// extract the entering/leaving bytes and update the circular buffer.
	enter := c
	leave := uint64(d.window[d.oldest])
	d.window[d.oldest] = c
	d.oldest += 1
	if d.oldest >= len(d.window) {
		d.oldest = 0
	}

	d.value ^= d.tables.out[leave]
	index := byte(d.value >> d.polShift)
	d.value <<= 8
	d.value |= Pol(enter)
	d.value ^= d.tables.mod[index]
}
<commit_msg>Passing a pointer rather than a copy for the tables in rabinkarp64<commit_after>// Copyright (c) 2014, Alexander Neumann <alexander@bumpern.de>
// Copyright (c) 2017, Christophe-Marie Duquesne <chmd@chmd.fr>
//
// This file was adapted from restic https://github.com/restic/chunker
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package rabinkarp64

import (
	"sync"

	"github.com/chmduquesne/rollinghash"
)

const Size = 8

type tables struct {
	out [256]Pol
	mod [256]Pol
}

// tables are cacheable for a given pol and windowsize
type index struct {
	pol        Pol
	windowsize int
}

type RabinKarp64 struct {
	pol      Pol
	tables   *tables
	polShift uint
	value    Pol

	// window is treated like a circular buffer, where the oldest element
	// is indicated by d.oldest
	window []byte
	oldest int
}

// cache precomputed tables, these are read-only anyway
var cache struct {
	// For a given polynom and a given window size, we get a table
	entries map[index]*tables
	sync.Mutex
}

func init() {
	cache.entries = make(map[index]*tables)
}

func (d *RabinKarp64) initTables() {
	windowsize := len(d.window)
	idx := index{d.pol, windowsize}

	cache.Lock()
	t, ok := cache.entries[idx]
	cache.Unlock()
	if ok {
		d.tables = t
		return
	}

	t = &tables{}

	// calculate table for sliding out bytes. The byte to slide out is used as
	// the index for the table, the value contains the following:
	// out_table[b] = Hash(b || 0 ||        ...        || 0)
	//                          \ windowsize-1 zero bytes /
	// To slide out byte b_0 for window size w with known hash
	// H := H(b_0 || ... || b_w), it is sufficient to add out_table[b_0]:
	//    H(b_0 || ... || b_w) + H(b_0 || 0 || ... || 0)
	//  = H(b_0 + b_0 || b_1 + 0 || ... || b_w + 0)
	//  = H(    0     || b_1 || ...     || b_w)
	//
	// Afterwards a new byte can be shifted in.
	for b := 0; b < 256; b++ {
		var h Pol

		h <<= 8
		h |= Pol(b)
		h = h.Mod(d.pol)
		for i := 0; i < windowsize-1; i++ {
			h <<= 8
			h |= Pol(0)
			h = h.Mod(d.pol)
		}
		t.out[b] = h
	}

	// calculate table for reduction mod Polynomial
	k := d.pol.Deg()
	for b := 0; b < 256; b++ {
		// mod_table[b] = A | B, where A = (b(x) * x^k mod pol) and  B = b(x) * x^k
		//
		// The 8 bits above deg(Polynomial) determine what happens next and so
		// these bits are used as a lookup to this table. The value is split in
		// two parts: Part A contains the result of the modulus operation, part
		// B is used to cancel out the 8 top bits so that one XOR operation is
		// enough to reduce modulo Polynomial
		t.mod[b] = Pol(uint64(b)<<uint(k)).Mod(d.pol) | (Pol(b) << uint(k))
	}

	d.tables = t
	cache.Lock()
	cache.entries[idx] = d.tables
	cache.Unlock()
}

func NewFromPol(p Pol) *RabinKarp64 {
	res := &RabinKarp64{
		pol:      p,
		tables:   nil,
		polShift: uint(p.Deg() - 8),
		window:   make([]byte, 0, rollinghash.DefaultWindowCap),
		value:    0,
	}
	res.Reset()
	return res
}

func New() *RabinKarp64 {
	p, err := RandomPolynomial(1)
	if err != nil {
		panic(err)
	}
	return NewFromPol(p)
}

func (d *RabinKarp64) Reset() {
	d.window = d.window[:1]
	d.window[0] = 0
	d.oldest = 0
	d.tables = nil
	d.value = 0
}

// Size is 8 bytes
func (d *RabinKarp64) Size() int { return Size }

// BlockSize is 1 byte
func (d *RabinKarp64) BlockSize() int { return 1 }

// Write (re)initializes the rolling window with the input byte slice and
// adds its data to the digest. It never returns an error.
func (d *RabinKarp64) Write(data []byte) (int, error) {
	// Copy the window
	l := len(data)
	if l == 0 {
		l = 1
	}
	if len(d.window) >= l {
		d.window = d.window[:l]
	} else {
		d.window = make([]byte, l)
	}
	copy(d.window, data)

	for _, b := range d.window {
		d.value <<= 8
		d.value |= Pol(b)
		d.value = d.value.Mod(d.pol)
	}

	d.initTables()

	// TODO: compute the checksum
	return len(d.window), nil
}

// Sum64 returns the hash as a uint64
func (d *RabinKarp64) Sum64() uint64 {
	return uint64(d.value)
}

// Sum returns the hash as byte slice
func (d *RabinKarp64) Sum(b []byte) []byte {
	v := d.Sum64()
	return append(b, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}

// Roll updates the checksum of the window from the entering byte. You
// MUST initialize a window with Write() before calling this method.
func (d *RabinKarp64) Roll(c byte) {
	// extract the entering/leaving bytes and update the circular buffer.
	enter := c
	leave := uint64(d.window[d.oldest])
	d.window[d.oldest] = c
	d.oldest += 1
	if d.oldest >= len(d.window) {
		d.oldest = 0
	}

	d.value ^= d.tables.out[leave]
	index := byte(d.value >> d.polShift)
	d.value <<= 8
	d.value |= Pol(enter)
	d.value ^= d.tables.mod[index]
}
<|endoftext|>


<commit_before>// skip

// Copyright 2014 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.

// Usage:
// fibo <n>	compute fibonacci(n), n must be >= 0
// fibo -bench	benchmark fibonacci computation (takes about 1 min)
//
// Additional flags:
// -half        add values using two half-digit additions
// -opt		optimize memory allocation through reuse
// -short	only print the first 10 digits of very large fibonacci numbers

// Command fibo is a stand-alone test and benchmark to
// evaluate the performance of bignum arithmetic written
// entirely in Go.
package main

import (
	"flag"
	"fmt"
	"math/big" // only used for printing
	"os"
	"strconv"
	"testing"
	"text/tabwriter"
	"time"
)

var (
	bench = flag.Bool("bench", false, "run benchmarks")
	half  = flag.Bool("half", false, "use half-digit addition")
	opt   = flag.Bool("opt", false, "optimize memory usage")
	short = flag.Bool("short", false, "only print first 10 digits of result")
)

// A large natural number is represented by a nat, each "digit" is
// a big.Word; the value zero corresponds to the empty nat slice.
type nat []big.Word

const W = 1 << (5 + ^big.Word(0)>>63) // big.Word size in bits

// The following methods are extracted from math/big to make this a
// stand-alone program that can easily be run without dependencies
// and compiled with different compilers.

func (z nat) make(n int) nat {
	if n <= cap(z) {
		return z[:n] // reuse z
	}
	// Choosing a good value for e has significant performance impact
	// because it increases the chance that a value can be reused.
	const e = 4 // extra capacity
	return make(nat, n, n+e)
}

// z = x
func (z nat) set(x nat) nat {
	z = z.make(len(x))
	copy(z, x)
	return z
}

// z = x + y
// (like add, but operating on half-digits at a time)
func (z nat) halfAdd(x, y nat) nat {
	m := len(x)
	n := len(y)

	switch {
	case m < n:
		return z.add(y, x)
	case m == 0:
		// n == 0 because m >= n; result is 0
		return z.make(0)
	case n == 0:
		// result is x
		return z.set(x)
	}
	// m >= n > 0

	const W2 = W / 2         // half-digit size in bits
	const M2 = (1 << W2) - 1 // lower half-digit mask

	z = z.make(m + 1)
	var c big.Word
	for i := 0; i < n; i++ {
		// lower half-digit
		c += x[i]&M2 + y[i]&M2
		d := c & M2
		c >>= W2
		// upper half-digit
		c += x[i]>>W2 + y[i]>>W2
		z[i] = c<<W2 | d
		c >>= W2
	}
	for i := n; i < m; i++ {
		// lower half-digit
		c += x[i] & M2
		d := c & M2
		c >>= W2
		// upper half-digit
		c += x[i] >> W2
		z[i] = c<<W2 | d
		c >>= W2
	}
	if c != 0 {
		z[m] = c
		m++
	}
	return z[:m]
}

// z = x + y
func (z nat) add(x, y nat) nat {
	m := len(x)
	n := len(y)

	switch {
	case m < n:
		return z.add(y, x)
	case m == 0:
		// n == 0 because m >= n; result is 0
		return z.make(0)
	case n == 0:
		// result is x
		return z.set(x)
	}
	// m >= n > 0

	z = z.make(m + 1)
	var c big.Word

	for i, xi := range x[:n] {
		yi := y[i]
		zi := xi + yi + c
		z[i] = zi
		// see "Hacker's Delight", section 2-12 (overflow detection)
		c = ((xi & yi) | ((xi | yi) &^ zi)) >> (W - 1)
	}
	for i, xi := range x[n:] {
		zi := xi + c
		z[n+i] = zi
		c = (xi &^ zi) >> (W - 1)
		if c == 0 {
			copy(z[n+i+1:], x[i+1:])
			break
		}
	}
	if c != 0 {
		z[m] = c
		m++
	}
	return z[:m]
}

func bitlen(x big.Word) int {
	n := 0
	for x > 0 {
		x >>= 1
		n++
	}
	return n
}

func (x nat) bitlen() int {
	if i := len(x); i > 0 {
		return (i-1)*W + bitlen(x[i-1])
	}
	return 0
}

func (x nat) String() string {
	const shortLen = 10
	s := new(big.Int).SetBits(x).String()
	if *short && len(s) > shortLen {
		s = s[:shortLen] + "..."
	}
	return s
}

func fibo(n int, half, opt bool) nat {
	switch n {
	case 0:
		return nil
	case 1:
		return nat{1}
	}
	f0 := nat(nil)
	f1 := nat{1}
	if half {
		if opt {
			var f2 nat // reuse f2
			for i := 1; i < n; i++ {
				f2 = f2.halfAdd(f1, f0)
				f0, f1, f2 = f1, f2, f0
			}
		} else {
			for i := 1; i < n; i++ {
				f2 := nat(nil).halfAdd(f1, f0) // allocate a new f2 each time
				f0, f1 = f1, f2
			}
		}
	} else {
		if opt {
			var f2 nat // reuse f2
			for i := 1; i < n; i++ {
				f2 = f2.add(f1, f0)
				f0, f1, f2 = f1, f2, f0
			}
		} else {
			for i := 1; i < n; i++ {
				f2 := nat(nil).add(f1, f0) // allocate a new f2 each time
				f0, f1 = f1, f2
			}
		}
	}
	return f1 // was f2 before shuffle
}

var tests = []struct {
	n    int
	want string
}{
	{0, "0"},
	{1, "1"},
	{2, "1"},
	{3, "2"},
	{4, "3"},
	{5, "5"},
	{6, "8"},
	{7, "13"},
	{8, "21"},
	{9, "34"},
	{10, "55"},
	{100, "354224848179261915075"},
	{1000, "43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875"},
}

func test(half, opt bool) {
	for _, test := range tests {
		got := fibo(test.n, half, opt).String()
		if got != test.want {
			fmt.Printf("error: got std fibo(%d) = %s; want %s\n", test.n, got, test.want)
			os.Exit(1)
		}
	}
}

func selfTest() {
	if W != 32 && W != 64 {
		fmt.Printf("error: unexpected wordsize %d", W)
		os.Exit(1)
	}
	for i := 0; i < 4; i++ {
		test(i&2 == 0, i&1 != 0)
	}
}

func doFibo(n int) {
	start := time.Now()
	f := fibo(n, *half, *opt)
	t := time.Since(start)
	fmt.Printf("fibo(%d) = %s (%d bits, %s)\n", n, f, f.bitlen(), t)
}

func benchFibo(b *testing.B, n int, half, opt bool) {
	for i := 0; i < b.N; i++ {
		fibo(n, half, opt)
	}
}

func doBench(half, opt bool) {
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', tabwriter.AlignRight)
	fmt.Fprintf(w, "wordsize = %d, half = %v, opt = %v\n", W, half, opt)
	fmt.Fprintf(w, "n\talloc count\talloc bytes\tns/op\ttime/op\t\n")
	for n := 1; n <= 1e6; n *= 10 {
		res := testing.Benchmark(func(b *testing.B) { benchFibo(b, n, half, opt) })
		fmt.Fprintf(w, "%d\t%d\t%d\t%d\t%s\t\n", n, res.AllocsPerOp(), res.AllocedBytesPerOp(), res.NsPerOp(), time.Duration(res.NsPerOp()))
	}
	fmt.Fprintln(w)
	w.Flush()
}

func main() {
	selfTest()
	flag.Parse()

	if args := flag.Args(); len(args) > 0 {
		// command-line use
		fmt.Printf("half = %v, opt = %v, wordsize = %d bits\n", *half, *opt, W)
		for _, arg := range args {
			n, err := strconv.Atoi(arg)
			if err != nil || n < 0 {
				fmt.Println("invalid argument", arg)
				continue
			}
			doFibo(n)
		}
		return
	}

	if *bench {
		for i := 0; i < 4; i++ {
			doBench(i&2 == 0, i&1 != 0)
		}
	}
}
<commit_msg>Fix whitespace (use blanks consistently).<commit_after>// skip

// Copyright 2014 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.

// Usage:
// fibo <n>     compute fibonacci(n), n must be >= 0
// fibo -bench  benchmark fibonacci computation (takes about 1 min)
//
// Additional flags:
// -half        add values using two half-digit additions
// -opt         optimize memory allocation through reuse
// -short       only print the first 10 digits of very large fibonacci numbers

// Command fibo is a stand-alone test and benchmark to
// evaluate the performance of bignum arithmetic written
// entirely in Go.
package main

import (
	"flag"
	"fmt"
	"math/big" // only used for printing
	"os"
	"strconv"
	"testing"
	"text/tabwriter"
	"time"
)

var (
	bench = flag.Bool("bench", false, "run benchmarks")
	half  = flag.Bool("half", false, "use half-digit addition")
	opt   = flag.Bool("opt", false, "optimize memory usage")
	short = flag.Bool("short", false, "only print first 10 digits of result")
)

// A large natural number is represented by a nat, each "digit" is
// a big.Word; the value zero corresponds to the empty nat slice.
type nat []big.Word

const W = 1 << (5 + ^big.Word(0)>>63) // big.Word size in bits

// The following methods are extracted from math/big to make this a
// stand-alone program that can easily be run without dependencies
// and compiled with different compilers.

func (z nat) make(n int) nat {
	if n <= cap(z) {
		return z[:n] // reuse z
	}
	// Choosing a good value for e has significant performance impact
	// because it increases the chance that a value can be reused.
	const e = 4 // extra capacity
	return make(nat, n, n+e)
}

// z = x
func (z nat) set(x nat) nat {
	z = z.make(len(x))
	copy(z, x)
	return z
}

// z = x + y
// (like add, but operating on half-digits at a time)
func (z nat) halfAdd(x, y nat) nat {
	m := len(x)
	n := len(y)

	switch {
	case m < n:
		return z.add(y, x)
	case m == 0:
		// n == 0 because m >= n; result is 0
		return z.make(0)
	case n == 0:
		// result is x
		return z.set(x)
	}
	// m >= n > 0

	const W2 = W / 2         // half-digit size in bits
	const M2 = (1 << W2) - 1 // lower half-digit mask

	z = z.make(m + 1)
	var c big.Word
	for i := 0; i < n; i++ {
		// lower half-digit
		c += x[i]&M2 + y[i]&M2
		d := c & M2
		c >>= W2
		// upper half-digit
		c += x[i]>>W2 + y[i]>>W2
		z[i] = c<<W2 | d
		c >>= W2
	}
	for i := n; i < m; i++ {
		// lower half-digit
		c += x[i] & M2
		d := c & M2
		c >>= W2
		// upper half-digit
		c += x[i] >> W2
		z[i] = c<<W2 | d
		c >>= W2
	}
	if c != 0 {
		z[m] = c
		m++
	}
	return z[:m]
}

// z = x + y
func (z nat) add(x, y nat) nat {
	m := len(x)
	n := len(y)

	switch {
	case m < n:
		return z.add(y, x)
	case m == 0:
		// n == 0 because m >= n; result is 0
		return z.make(0)
	case n == 0:
		// result is x
		return z.set(x)
	}
	// m >= n > 0

	z = z.make(m + 1)
	var c big.Word

	for i, xi := range x[:n] {
		yi := y[i]
		zi := xi + yi + c
		z[i] = zi
		// see "Hacker's Delight", section 2-12 (overflow detection)
		c = ((xi & yi) | ((xi | yi) &^ zi)) >> (W - 1)
	}
	for i, xi := range x[n:] {
		zi := xi + c
		z[n+i] = zi
		c = (xi &^ zi) >> (W - 1)
		if c == 0 {
			copy(z[n+i+1:], x[i+1:])
			break
		}
	}
	if c != 0 {
		z[m] = c
		m++
	}
	return z[:m]
}

func bitlen(x big.Word) int {
	n := 0
	for x > 0 {
		x >>= 1
		n++
	}
	return n
}

func (x nat) bitlen() int {
	if i := len(x); i > 0 {
		return (i-1)*W + bitlen(x[i-1])
	}
	return 0
}

func (x nat) String() string {
	const shortLen = 10
	s := new(big.Int).SetBits(x).String()
	if *short && len(s) > shortLen {
		s = s[:shortLen] + "..."
	}
	return s
}

func fibo(n int, half, opt bool) nat {
	switch n {
	case 0:
		return nil
	case 1:
		return nat{1}
	}
	f0 := nat(nil)
	f1 := nat{1}
	if half {
		if opt {
			var f2 nat // reuse f2
			for i := 1; i < n; i++ {
				f2 = f2.halfAdd(f1, f0)
				f0, f1, f2 = f1, f2, f0
			}
		} else {
			for i := 1; i < n; i++ {
				f2 := nat(nil).halfAdd(f1, f0) // allocate a new f2 each time
				f0, f1 = f1, f2
			}
		}
	} else {
		if opt {
			var f2 nat // reuse f2
			for i := 1; i < n; i++ {
				f2 = f2.add(f1, f0)
				f0, f1, f2 = f1, f2, f0
			}
		} else {
			for i := 1; i < n; i++ {
				f2 := nat(nil).add(f1, f0) // allocate a new f2 each time
				f0, f1 = f1, f2
			}
		}
	}
	return f1 // was f2 before shuffle
}

var tests = []struct {
	n    int
	want string
}{
	{0, "0"},
	{1, "1"},
	{2, "1"},
	{3, "2"},
	{4, "3"},
	{5, "5"},
	{6, "8"},
	{7, "13"},
	{8, "21"},
	{9, "34"},
	{10, "55"},
	{100, "354224848179261915075"},
	{1000, "43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875"},
}

func test(half, opt bool) {
	for _, test := range tests {
		got := fibo(test.n, half, opt).String()
		if got != test.want {
			fmt.Printf("error: got std fibo(%d) = %s; want %s\n", test.n, got, test.want)
			os.Exit(1)
		}
	}
}

func selfTest() {
	if W != 32 && W != 64 {
		fmt.Printf("error: unexpected wordsize %d", W)
		os.Exit(1)
	}
	for i := 0; i < 4; i++ {
		test(i&2 == 0, i&1 != 0)
	}
}

func doFibo(n int) {
	start := time.Now()
	f := fibo(n, *half, *opt)
	t := time.Since(start)
	fmt.Printf("fibo(%d) = %s (%d bits, %s)\n", n, f, f.bitlen(), t)
}

func benchFibo(b *testing.B, n int, half, opt bool) {
	for i := 0; i < b.N; i++ {
		fibo(n, half, opt)
	}
}

func doBench(half, opt bool) {
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', tabwriter.AlignRight)
	fmt.Fprintf(w, "wordsize = %d, half = %v, opt = %v\n", W, half, opt)
	fmt.Fprintf(w, "n\talloc count\talloc bytes\tns/op\ttime/op\t\n")
	for n := 1; n <= 1e6; n *= 10 {
		res := testing.Benchmark(func(b *testing.B) { benchFibo(b, n, half, opt) })
		fmt.Fprintf(w, "%d\t%d\t%d\t%d\t%s\t\n", n, res.AllocsPerOp(), res.AllocedBytesPerOp(), res.NsPerOp(), time.Duration(res.NsPerOp()))
	}
	fmt.Fprintln(w)
	w.Flush()
}

func main() {
	selfTest()
	flag.Parse()

	if args := flag.Args(); len(args) > 0 {
		// command-line use
		fmt.Printf("half = %v, opt = %v, wordsize = %d bits\n", *half, *opt, W)
		for _, arg := range args {
			n, err := strconv.Atoi(arg)
			if err != nil || n < 0 {
				fmt.Println("invalid argument", arg)
				continue
			}
			doFibo(n)
		}
		return
	}

	if *bench {
		for i := 0; i < 4; i++ {
			doBench(i&2 == 0, i&1 != 0)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

var (
	id         string
	resolution string
	page       string
)

type VCode struct {
	Playurl string `xml:"playurl"`
}

func init() {
	flag.StringVar(&id, "id", "", "video id")
	flag.StringVar(&resolution, "res", "720p", "video resolution")
	flag.Parse()
	if id == "" {
		panic(errors.New("video should not be empty!"))
	}
	page = fmt.Sprintf(`http://www.letv.com/ptv/vplay/%s.html`, id)
}

func main() {
	var err error
	if resp, err := http.Get(page); err == nil {
		defer resp.Body.Close()
		if data, err := ioutil.ReadAll(resp.Body); err == nil {
			html := string(data)
			idx := strings.Index(html, "v_code=")
			vCode := html[idx+7:]
			vCode = vCode[:strings.Index(vCode, "'")]
			//url decode
			if vCode, err = url.QueryUnescape(vCode); err == nil {
				//decode base64
				if data, err = base64.StdEncoding.DecodeString(vCode); err == nil {
					var vCodeObj VCode
					if err = xml.Unmarshal(data, &vCodeObj); err == nil {
						var playUrlMap map[string]interface{}
						if err = json.Unmarshal([]byte(vCodeObj.Playurl), &playUrlMap); err == nil {
							val := getVal(playUrlMap, "dispatch", resolution)
							fmt.Printf("%s\n", realUrl(val[0].(string)))
						}
					}
				}
			}

		}
	}
	if err != nil {
		panic(err)
	}
}

func getVal(playUrlMap map[string]interface{}, dispatch, resolution string) []interface{} {
	val := playUrlMap[dispatch].(map[string]interface{})[resolution]
	return val.([]interface{})
}

func realUrl(fakeUrl string) string {
	idx := strings.Index(fakeUrl, "?")
	idx1 := strings.LastIndex(fakeUrl[:idx], "/")
	idx2 := strings.Index(fakeUrl[7:], "/")
	fakeCode := fakeUrl[idx1+1 : idx]
	realCode, _ := base64.StdEncoding.DecodeString(fakeCode)
	return fakeUrl[:idx2+8] + string(realCode) + fakeUrl[idx:]
}
<commit_msg>add usage<commit_after>package main

import (
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
)

var (
	id         string
	resolution string
	page       string
)

type VCode struct {
	Playurl string `xml:"playurl"`
}

func init() {
	flag.StringVar(&id, "id", "", "video id, required")
	flag.StringVar(&resolution, "res", "720p", "video resolution, optional")
	flag.Parse()
	if id == "" {
		usage()
	}
	page = fmt.Sprintf(`http://www.letv.com/ptv/vplay/%s.html`, id)
}

func usage() {
	fmt.Println("usage:")
	flag.PrintDefaults()
	os.Exit(1)
}

func main() {
	var err error
	if resp, err := http.Get(page); err == nil {
		defer resp.Body.Close()
		if data, err := ioutil.ReadAll(resp.Body); err == nil {
			html := string(data)
			idx := strings.Index(html, "v_code=")
			vCode := html[idx+7:]
			vCode = vCode[:strings.Index(vCode, "'")]
			//url decode
			if vCode, err = url.QueryUnescape(vCode); err == nil {
				//decode base64
				if data, err = base64.StdEncoding.DecodeString(vCode); err == nil {
					var vCodeObj VCode
					if err = xml.Unmarshal(data, &vCodeObj); err == nil {
						var playUrlMap map[string]interface{}
						if err = json.Unmarshal([]byte(vCodeObj.Playurl), &playUrlMap); err == nil {
							val := getVal(playUrlMap, "dispatch", resolution)
							fmt.Printf("%s\n", realUrl(val[0].(string)))
						}
					}
				}
			}

		}
	}
	if err != nil {
		panic(err)
	}
}

func getVal(playUrlMap map[string]interface{}, dispatch, resolution string) []interface{} {
	val := playUrlMap[dispatch].(map[string]interface{})[resolution]
	return val.([]interface{})
}

func realUrl(fakeUrl string) string {
	idx := strings.Index(fakeUrl, "?")
	idx1 := strings.LastIndex(fakeUrl[:idx], "/")
	idx2 := strings.Index(fakeUrl[7:], "/")
	fakeCode := fakeUrl[idx1+1 : idx]
	realCode, _ := base64.StdEncoding.DecodeString(fakeCode)
	return fakeUrl[:idx2+8] + string(realCode) + fakeUrl[idx:]
}
<|endoftext|>


<commit_before>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
)

var (
	fileRegister       map[string]bool
	fileRegisterLock   sync.RWMutex
	readerRegister     map[string]func() io.Reader
	readerRegisterLock sync.RWMutex
)

// RegisterLocalFile adds the given file to the file whitelist,
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
// Alternatively you can allow the use of all local files with
// the DSN parameter 'allowAllFiles=true'
//
//  filePath := "/home/gopher/data.csv"
//  mysql.RegisterLocalFile(filePath)
//  err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
//  if err != nil {
//  ...
//
func RegisterLocalFile(filePath string) {
	fileRegisterLock.Lock()
	// lazy map init
	if fileRegister == nil {
		fileRegister = make(map[string]bool)
	}

	fileRegister[strings.Trim(filePath, `"`)] = true
	fileRegisterLock.Unlock()
}

// DeregisterLocalFile removes the given filepath from the whitelist.
func DeregisterLocalFile(filePath string) {
	fileRegisterLock.Lock()
	delete(fileRegister, strings.Trim(filePath, `"`))
	fileRegisterLock.Unlock()
}

// RegisterReaderHandler registers a handler function which is used
// to receive a io.Reader.
// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>".
// If the handler returns a io.ReadCloser Close() is called when the
// request is finished.
//
//  mysql.RegisterReaderHandler("data", func() io.Reader {
//  	var csvReader io.Reader // Some Reader that returns CSV data
//  	... // Open Reader here
//  	return csvReader
//  })
//  err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
//  if err != nil {
//  ...
//
func RegisterReaderHandler(name string, handler func() io.Reader) {
	readerRegisterLock.Lock()
	// lazy map init
	if readerRegister == nil {
		readerRegister = make(map[string]func() io.Reader)
	}

	readerRegister[name] = handler
	readerRegisterLock.Unlock()
}

// DeregisterReaderHandler removes the ReaderHandler function with
// the given name from the registry.
func DeregisterReaderHandler(name string) {
	readerRegisterLock.Lock()
	delete(readerRegister, name)
	readerRegisterLock.Unlock()
}

func deferredClose(err *error, closer io.Closer) {
	closeErr := closer.Close()
	if *err == nil {
		*err = closeErr
	}
}

func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
	var rdr io.Reader
	var data []byte

	if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
		// The server might return an an absolute path. See issue #355.
		name = name[idx+8:]

		readerRegisterLock.RLock()
		handler, inMap := readerRegister[name]
		readerRegisterLock.RUnlock()

		if inMap {
			rdr = handler()
			if rdr != nil {
				data = make([]byte, 4+mc.maxWriteSize)

				if cl, ok := rdr.(io.Closer); ok {
					defer deferredClose(&err, cl)
				}
			} else {
				err = fmt.Errorf("Reader '%s' is <nil>", name)
			}
		} else {
			err = fmt.Errorf("Reader '%s' is not registered", name)
		}
	} else { // File
		name = strings.Trim(name, `"`)
		fileRegisterLock.RLock()
		fr := fileRegister[name]
		fileRegisterLock.RUnlock()
		if mc.cfg.AllowAllFiles || fr {
			var file *os.File
			var fi os.FileInfo

			if file, err = os.Open(name); err == nil {
				defer deferredClose(&err, file)

				// get file size
				if fi, err = file.Stat(); err == nil {
					rdr = file
					if fileSize := int(fi.Size()); fileSize <= mc.maxWriteSize {
						data = make([]byte, 4+fileSize)
					} else if fileSize <= mc.maxPacketAllowed {
						data = make([]byte, 4+mc.maxWriteSize)
					} else {
						err = fmt.Errorf("local file '%s' too large: size: %d, max: %d", name, fileSize, mc.maxPacketAllowed)
					}
				}
			}
		} else {
			err = fmt.Errorf("local file '%s' is not registered", name)
		}
	}

	// send content packets
	if err == nil {
		var n int
		for err == nil {
			n, err = rdr.Read(data[4:])
			if n > 0 {
				if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
					return ioErr
				}
			}
		}
		if err == io.EOF {
			err = nil
		}
	}

	// send empty packet (termination)
	if data == nil {
		data = make([]byte, 4)
	}
	if ioErr := mc.writePacket(data[:4]); ioErr != nil {
		return ioErr
	}

	// read OK packet
	if err == nil {
		return mc.readResultOK()
	}

	mc.readPacket()
	return err
}
<commit_msg>LOAD LOCAL can send file larger than max_allowed_packet<commit_after>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
)

var (
	fileRegister       map[string]bool
	fileRegisterLock   sync.RWMutex
	readerRegister     map[string]func() io.Reader
	readerRegisterLock sync.RWMutex
)

// RegisterLocalFile adds the given file to the file whitelist,
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
// Alternatively you can allow the use of all local files with
// the DSN parameter 'allowAllFiles=true'
//
//  filePath := "/home/gopher/data.csv"
//  mysql.RegisterLocalFile(filePath)
//  err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
//  if err != nil {
//  ...
//
func RegisterLocalFile(filePath string) {
	fileRegisterLock.Lock()
	// lazy map init
	if fileRegister == nil {
		fileRegister = make(map[string]bool)
	}

	fileRegister[strings.Trim(filePath, `"`)] = true
	fileRegisterLock.Unlock()
}

// DeregisterLocalFile removes the given filepath from the whitelist.
func DeregisterLocalFile(filePath string) {
	fileRegisterLock.Lock()
	delete(fileRegister, strings.Trim(filePath, `"`))
	fileRegisterLock.Unlock()
}

// RegisterReaderHandler registers a handler function which is used
// to receive a io.Reader.
// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>".
// If the handler returns a io.ReadCloser Close() is called when the
// request is finished.
//
//  mysql.RegisterReaderHandler("data", func() io.Reader {
//  	var csvReader io.Reader // Some Reader that returns CSV data
//  	... // Open Reader here
//  	return csvReader
//  })
//  err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
//  if err != nil {
//  ...
//
func RegisterReaderHandler(name string, handler func() io.Reader) {
	readerRegisterLock.Lock()
	// lazy map init
	if readerRegister == nil {
		readerRegister = make(map[string]func() io.Reader)
	}

	readerRegister[name] = handler
	readerRegisterLock.Unlock()
}

// DeregisterReaderHandler removes the ReaderHandler function with
// the given name from the registry.
func DeregisterReaderHandler(name string) {
	readerRegisterLock.Lock()
	delete(readerRegister, name)
	readerRegisterLock.Unlock()
}

func deferredClose(err *error, closer io.Closer) {
	closeErr := closer.Close()
	if *err == nil {
		*err = closeErr
	}
}

func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
	var rdr io.Reader
	var data []byte
	packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
	if mc.maxWriteSize < packetSize {
		packetSize = mc.maxWriteSize
	}

	if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
		// The server might return an an absolute path. See issue #355.
		name = name[idx+8:]

		readerRegisterLock.RLock()
		handler, inMap := readerRegister[name]
		readerRegisterLock.RUnlock()

		if inMap {
			rdr = handler()
			if rdr != nil {
				if cl, ok := rdr.(io.Closer); ok {
					defer deferredClose(&err, cl)
				}
			} else {
				err = fmt.Errorf("Reader '%s' is <nil>", name)
			}
		} else {
			err = fmt.Errorf("Reader '%s' is not registered", name)
		}
	} else { // File
		name = strings.Trim(name, `"`)
		fileRegisterLock.RLock()
		fr := fileRegister[name]
		fileRegisterLock.RUnlock()
		if mc.cfg.AllowAllFiles || fr {
			var file *os.File
			var fi os.FileInfo

			if file, err = os.Open(name); err == nil {
				defer deferredClose(&err, file)

				// get file size
				if fi, err = file.Stat(); err == nil {
					rdr = file
					if fileSize := int(fi.Size()); fileSize < packetSize {
						packetSize = fileSize
					}
				}
			}
		} else {
			err = fmt.Errorf("local file '%s' is not registered", name)
		}
	}

	// send content packets
	if err == nil {
		data := make([]byte, 4+packetSize)
		var n int
		for err == nil {
			n, err = rdr.Read(data[4:])
			if n > 0 {
				if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
					return ioErr
				}
			}
		}
		if err == io.EOF {
			err = nil
		}
	}

	// send empty packet (termination)
	if data == nil {
		data = make([]byte, 4)
	}
	if ioErr := mc.writePacket(data[:4]); ioErr != nil {
		return ioErr
	}

	// read OK packet
	if err == nil {
		return mc.readResultOK()
	}

	mc.readPacket()
	return err
}
<|endoftext|>


<commit_before>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package rec

import (
	"fmt"
	"io"

	"my/ev/packet"
	"my/ev/sim"
)

const (
	EFHM_DEFINITION = 0
	EFHM_TRADE      = 1
	EFHM_QUOTE      = 2
	EFHM_ORDER      = 3
	EFHM_REFRESHED  = 100
	EFHM_STOPPED    = 101
)
const (
	EFH_ORDER_BID = 1
	EFH_ORDER_ASK = -1
)

type efhm_header struct {
	Type           uint8
	TickCondition  uint8
	QueuePosition  uint16
	UnderlyingId   uint32
	SecurityId     uint64
	SequenceNumber uint64
	TimeStamp      uint64
}

type efhm_order struct {
	efhm_header
	TradeStatus     uint8
	OrderType       uint8
	OrderSide       int8
	_pad            byte
	Price           uint32
	Size            uint32
	AoNSize         uint32
	CustomerSize    uint32
	CustomerAoNSize uint32
	BDSize          uint32
	BDAoNSize       uint32
}

type efhm_quote struct {
	efhm_header
	TradeStatus        uint8
	_pad               [3]byte
	BidPrice           uint32
	BidSize            uint32
	BidOrderSize       uint32
	BidAoNSize         uint32
	BidCustomerSize    uint32
	BidCustomerAoNSize uint32
	BidBDSize          uint32
	BidBDAoNSize       uint32
	AskPrice           uint32
	AskSize            uint32
	AskOrderSize       uint32
	AskAoNSize         uint32
	AskCustomerSize    uint32
	AskCustomerAoNSize uint32
	AskBDSize          uint32
	AskBDAoNSize       uint32
}

type efhm_trade struct {
	efhm_header
	Price          uint32
	Size           uint32
	TradeCondition uint8
}

func (m efhm_header) String() string {
	switch m.Type {
	case EFHM_QUOTE, EFHM_ORDER, EFHM_TRADE:
		return fmt.Sprintf("HDR{T:%d, TC:%d, QP:%d, UId:%08x, SId:%08x, SN:%d, TS:%08x}",
			m.Type,
			m.TickCondition,
			m.QueuePosition,
			m.UnderlyingId,
			m.SecurityId,
			m.SequenceNumber,
			m.TimeStamp,
		)
	default:
		return fmt.Sprintf("HDR{T:%d}", m.Type)
	}
}
func (m efhm_order) String() string {
	return fmt.Sprintf("%s ORD{TS:%d, OT:%d, OS:%+d, P:%10d, S:%d, AS:%d, CS:%d, CAS:%d, BS:%d, BAS:%d}",
		m.efhm_header,
		m.TradeStatus,
		m.OrderType,
		m.OrderSide,
		m.Price,
		m.Size,
		m.AoNSize,
		m.CustomerSize,
		m.CustomerAoNSize,
		m.BDSize,
		m.BDAoNSize,
	)
}
func (m efhm_quote) String() string {
	return fmt.Sprintf("%s QUO{TS:%d, "+
		"Bid{P:%10d, S:%d, OS:%d, AS:%d, CS:%d, CAS:%d, BS:%d, BAS:%d}, "+
		"Ask{P:%10d, S:%d, OS:%d, AS:%d, CS:%d, CAS:%d, BS:%d, BAS:%d}"+
		"}",
		m.efhm_header,
		m.TradeStatus,
		m.BidPrice,
		m.BidSize,
		m.BidOrderSize,
		m.BidAoNSize,
		m.BidCustomerSize,
		m.BidCustomerAoNSize,
		m.BidBDSize,
		m.BidBDAoNSize,
		m.AskPrice,
		m.AskSize,
		m.AskOrderSize,
		m.AskAoNSize,
		m.AskCustomerSize,
		m.AskCustomerAoNSize,
		m.AskBDSize,
		m.AskBDAoNSize,
	)
}
func (m efhm_trade) String() string {
	return fmt.Sprintf("%s TRD{P:%10d, S:%d, TC:%d}",
		m.efhm_header,
		m.Price,
		m.Size,
		m.TradeCondition,
	)
}

type EfhLoggerPrinter interface {
	PrintOrder(efhm_order)
	PrintQuote(efhm_quote)
	PrintTrade(efhm_trade)
}

type testefhPrinter struct {
	w io.Writer
}

var _ EfhLoggerPrinter = &testefhPrinter{}

func NewTestefhPrinter(w io.Writer) EfhLoggerPrinter {
	return &testefhPrinter{w: w}
}
func (p *testefhPrinter) PrintOrder(o efhm_order) {
	fmt.Fprintln(p.w, o)
}
func (p *testefhPrinter) PrintQuote(o efhm_quote) {
	fmt.Fprintln(p.w, o)
}
func (p *testefhPrinter) PrintTrade(m efhm_trade) {
	fmt.Fprintln(p.w, m)
}

type EfhLogger struct {
	TobLogger
	printer EfhLoggerPrinter
	mode    EfhLoggerOutputMode
	stream  Stream
}

var _ sim.Observer = &EfhLogger{}

func NewEfhLogger(p EfhLoggerPrinter) *EfhLogger {
	l := &EfhLogger{
		printer:   p,
		TobLogger: *NewTobLogger(),
		stream:    *NewStream(),
	}
	return l
}

type EfhLoggerOutputMode byte

const (
	EfhLoggerOutputOrders EfhLoggerOutputMode = iota
	EfhLoggerOutputQuotes
)

func (l *EfhLogger) SetOutputMode(mode EfhLoggerOutputMode) {
	l.mode = mode
}

func (l *EfhLogger) MessageArrived(idm *sim.SimMessage) {
	l.stream.MessageArrived(idm)
	l.TobLogger.MessageArrived(idm)
	if m, ok := l.stream.getExchangeMessage().(packet.TradeMessage); ok {
		oid, price, size := m.TradeInfo()
		l.lastOptionId = oid
		l.genUpdateTrades(price, size)
	}
}

func (l *EfhLogger) AfterBookUpdate(book sim.Book, operation sim.SimOperation) {
	if l.mode == EfhLoggerOutputOrders {
		if l.TobLogger.AfterBookUpdate(book, operation, TobUpdateNew) {
			l.genUpdateOrders(l.bid)
			l.genUpdateOrders(l.ask)
		}
	} else {
		if l.TobLogger.AfterBookUpdate(book, operation, TobUpdateNewForce) {
			l.genUpdateQuotes()
		}
	}
}

func (l *EfhLogger) genUpdateHeader(messageType uint8) efhm_header {
	return efhm_header{
		Type:           messageType,
		SecurityId:     l.lastOptionId.ToUint64(),
		SequenceNumber: l.stream.getSeqNum(),
		TimeStamp:      l.stream.getTimestamp(),
	}
}
func (l *EfhLogger) genUpdateOrders(tob tob) {
	if !tob.updated() {
		return
	}
	m := efhm_order{
		efhm_header: l.genUpdateHeader(EFHM_ORDER),
		Price:       uint32(tob.New.Price),
		Size:        uint32(tob.New.Size),
		OrderType:   1,
	}
	switch tob.Side {
	case packet.MarketSideBid:
		m.OrderSide = EFH_ORDER_BID
	case packet.MarketSideAsk:
		m.OrderSide = EFH_ORDER_ASK
	}
	l.printer.PrintOrder(m)
}
func (l *EfhLogger) genUpdateQuotes() {
	m := efhm_quote{
		efhm_header: l.genUpdateHeader(EFHM_QUOTE),
		BidPrice:    uint32(l.bid.New.Price),
		BidSize:     uint32(l.bid.New.Size),
		AskPrice:    uint32(l.ask.New.Price),
		AskSize:     uint32(l.ask.New.Size),
	}
	l.printer.PrintQuote(m)
}
func (l *EfhLogger) genUpdateTrades(price packet.Price, size int) {
	m := efhm_trade{
		efhm_header: l.genUpdateHeader(EFHM_TRADE),
		Price:       uint32(packet.PriceTo4Dec(price)),
		Size:        uint32(size),
	}
	l.printer.PrintTrade(m)
}
<commit_msg>rec:EfhLogger: refactor app message generation<commit_after>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package rec

import (
	"fmt"
	"io"

	"my/ev/packet"
	"my/ev/sim"
)

const (
	EFHM_DEFINITION = 0
	EFHM_TRADE      = 1
	EFHM_QUOTE      = 2
	EFHM_ORDER      = 3
	EFHM_REFRESHED  = 100
	EFHM_STOPPED    = 101
)
const (
	EFH_ORDER_BID = 1
	EFH_ORDER_ASK = -1
)

type efhm_header struct {
	Type           uint8
	TickCondition  uint8
	QueuePosition  uint16
	UnderlyingId   uint32
	SecurityId     uint64
	SequenceNumber uint64
	TimeStamp      uint64
}

type efhm_order struct {
	efhm_header
	TradeStatus     uint8
	OrderType       uint8
	OrderSide       int8
	_pad            byte
	Price           uint32
	Size            uint32
	AoNSize         uint32
	CustomerSize    uint32
	CustomerAoNSize uint32
	BDSize          uint32
	BDAoNSize       uint32
}

type efhm_quote struct {
	efhm_header
	TradeStatus        uint8
	_pad               [3]byte
	BidPrice           uint32
	BidSize            uint32
	BidOrderSize       uint32
	BidAoNSize         uint32
	BidCustomerSize    uint32
	BidCustomerAoNSize uint32
	BidBDSize          uint32
	BidBDAoNSize       uint32
	AskPrice           uint32
	AskSize            uint32
	AskOrderSize       uint32
	AskAoNSize         uint32
	AskCustomerSize    uint32
	AskCustomerAoNSize uint32
	AskBDSize          uint32
	AskBDAoNSize       uint32
}

type efhm_trade struct {
	efhm_header
	Price          uint32
	Size           uint32
	TradeCondition uint8
}

func (m efhm_header) String() string {
	switch m.Type {
	case EFHM_QUOTE, EFHM_ORDER, EFHM_TRADE:
		return fmt.Sprintf("HDR{T:%d, TC:%d, QP:%d, UId:%08x, SId:%08x, SN:%d, TS:%08x}",
			m.Type,
			m.TickCondition,
			m.QueuePosition,
			m.UnderlyingId,
			m.SecurityId,
			m.SequenceNumber,
			m.TimeStamp,
		)
	default:
		return fmt.Sprintf("HDR{T:%d}", m.Type)
	}
}
func (m efhm_order) String() string {
	return fmt.Sprintf("%s ORD{TS:%d, OT:%d, OS:%+d, P:%10d, S:%d, AS:%d, CS:%d, CAS:%d, BS:%d, BAS:%d}",
		m.efhm_header,
		m.TradeStatus,
		m.OrderType,
		m.OrderSide,
		m.Price,
		m.Size,
		m.AoNSize,
		m.CustomerSize,
		m.CustomerAoNSize,
		m.BDSize,
		m.BDAoNSize,
	)
}
func (m efhm_quote) String() string {
	return fmt.Sprintf("%s QUO{TS:%d, "+
		"Bid{P:%10d, S:%d, OS:%d, AS:%d, CS:%d, CAS:%d, BS:%d, BAS:%d}, "+
		"Ask{P:%10d, S:%d, OS:%d, AS:%d, CS:%d, CAS:%d, BS:%d, BAS:%d}"+
		"}",
		m.efhm_header,
		m.TradeStatus,
		m.BidPrice,
		m.BidSize,
		m.BidOrderSize,
		m.BidAoNSize,
		m.BidCustomerSize,
		m.BidCustomerAoNSize,
		m.BidBDSize,
		m.BidBDAoNSize,
		m.AskPrice,
		m.AskSize,
		m.AskOrderSize,
		m.AskAoNSize,
		m.AskCustomerSize,
		m.AskCustomerAoNSize,
		m.AskBDSize,
		m.AskBDAoNSize,
	)
}
func (m efhm_trade) String() string {
	return fmt.Sprintf("%s TRD{P:%10d, S:%d, TC:%d}",
		m.efhm_header,
		m.Price,
		m.Size,
		m.TradeCondition,
	)
}

type EfhLoggerPrinter interface {
	PrintOrder(efhm_order)
	PrintQuote(efhm_quote)
	PrintTrade(efhm_trade)
}

type testefhPrinter struct {
	w io.Writer
}

var _ EfhLoggerPrinter = &testefhPrinter{}

func NewTestefhPrinter(w io.Writer) EfhLoggerPrinter {
	return &testefhPrinter{w: w}
}
func (p *testefhPrinter) PrintOrder(o efhm_order) {
	fmt.Fprintln(p.w, o)
}
func (p *testefhPrinter) PrintQuote(o efhm_quote) {
	fmt.Fprintln(p.w, o)
}
func (p *testefhPrinter) PrintTrade(m efhm_trade) {
	fmt.Fprintln(p.w, m)
}

type EfhLogger struct {
	TobLogger
	printer EfhLoggerPrinter
	mode    EfhLoggerOutputMode
	stream  Stream
}

var _ sim.Observer = &EfhLogger{}

func NewEfhLogger(p EfhLoggerPrinter) *EfhLogger {
	l := &EfhLogger{
		printer:   p,
		TobLogger: *NewTobLogger(),
		stream:    *NewStream(),
	}
	return l
}

type EfhLoggerOutputMode byte

const (
	EfhLoggerOutputOrders EfhLoggerOutputMode = iota
	EfhLoggerOutputQuotes
)

func (l *EfhLogger) SetOutputMode(mode EfhLoggerOutputMode) {
	l.mode = mode
}

func (l *EfhLogger) MessageArrived(idm *sim.SimMessage) {
	l.stream.MessageArrived(idm)
	l.TobLogger.MessageArrived(idm)
	if m, ok := l.stream.getExchangeMessage().(packet.TradeMessage); ok {
		l.genUpdateTrades(m)
	}
}

func (l *EfhLogger) AfterBookUpdate(book sim.Book, operation sim.SimOperation) {
	if l.mode == EfhLoggerOutputOrders {
		if l.TobLogger.AfterBookUpdate(book, operation, TobUpdateNew) {
			l.genUpdateOrders(l.bid)
			l.genUpdateOrders(l.ask)
		}
	} else {
		if l.TobLogger.AfterBookUpdate(book, operation, TobUpdateNewForce) {
			l.genUpdateQuotes()
		}
	}
}

func (l *EfhLogger) genUpdateHeaderForOption(messageType uint8, oid packet.OptionId) efhm_header {
	return efhm_header{
		Type:           messageType,
		SecurityId:     oid.ToUint64(),
		SequenceNumber: l.stream.getSeqNum(),
		TimeStamp:      l.stream.getTimestamp(),
	}
}
func (l *EfhLogger) genUpdateHeader(messageType uint8) efhm_header {
	return l.genUpdateHeaderForOption(messageType, l.TobLogger.lastOptionId)
}
func (l *EfhLogger) genUpdateOrders(tob tob) {
	if !tob.updated() {
		return
	}
	m := efhm_order{
		efhm_header: l.genUpdateHeader(EFHM_ORDER),
		Price:       uint32(tob.New.Price),
		Size:        uint32(tob.New.Size),
		OrderType:   1,
	}
	switch tob.Side {
	case packet.MarketSideBid:
		m.OrderSide = EFH_ORDER_BID
	case packet.MarketSideAsk:
		m.OrderSide = EFH_ORDER_ASK
	}
	l.printer.PrintOrder(m)
}
func (l *EfhLogger) genUpdateQuotes() {
	m := efhm_quote{
		efhm_header: l.genUpdateHeader(EFHM_QUOTE),
		BidPrice:    uint32(l.bid.New.Price),
		BidSize:     uint32(l.bid.New.Size),
		AskPrice:    uint32(l.ask.New.Price),
		AskSize:     uint32(l.ask.New.Size),
	}
	l.printer.PrintQuote(m)
}
func (l *EfhLogger) genUpdateTrades(msg packet.TradeMessage) {
	oid, price, size := msg.TradeInfo()
	m := efhm_trade{
		efhm_header: l.genUpdateHeaderForOption(EFHM_TRADE, oid),
		Price:       uint32(packet.PriceTo4Dec(price)),
		Size:        uint32(size),
	}
	l.printer.PrintTrade(m)
}
<|endoftext|>