<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package pkg

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	log "github.com/Sirupsen/logrus"

	"mynewt.apache.org/newt/newt/interfaces"
	"mynewt.apache.org/newt/newt/newtutil"
	"mynewt.apache.org/newt/newt/repo"
	"mynewt.apache.org/newt/util"
	"mynewt.apache.org/newt/viper"
	"mynewt.apache.org/newt/yaml"
)

var PackageHashIgnoreDirs = map[string]bool{
	"obj": true,
	"bin": true,
	".":   true,
}

var LocalPackageSpecialNames = map[string]bool{
	"src":     true,
	"include": true,
	"bin":     true,
}

type LocalPackage struct {
	repo        *repo.Repo
	name        string
	basePath    string
	packageType interfaces.PackageType

	// General information about the package
	desc *PackageDesc

	// Package init function name and stage.  These are used to generate the
	// sysinit C file.
	init map[string]int

	// Extra package-specific settings that don't come from syscfg.  For
	// example, SELFTEST gets set when the newt test command is used.
	injectedSettings map[string]string

	// Settings read from pkg.yml.
	PkgV *viper.Viper

	// Settings read from syscfg.yml.
	SyscfgV *viper.Viper

	// Names of all source yml files; used to determine if rebuild required.
	cfgFilenames []string
}

func NewLocalPackage(r *repo.Repo, pkgDir string) *LocalPackage {
	pkg := &LocalPackage{
		desc:             &PackageDesc{},
		PkgV:             viper.New(),
		SyscfgV:          viper.New(),
		repo:             r,
		basePath:         filepath.ToSlash(filepath.Clean(pkgDir)),
		init:             map[string]int{},
		injectedSettings: map[string]string{},
	}
	return pkg
}

func (pkg *LocalPackage) Name() string {
	return pkg.name
}

func (pkg *LocalPackage) FullName() string {
	r := pkg.Repo()
	if r.IsLocal() {
		return pkg.Name()
	} else {
		return newtutil.BuildPackageString(r.Name(), pkg.Name())
	}
}

func (pkg *LocalPackage) BasePath() string {
	return pkg.basePath
}

func (pkg *LocalPackage) RelativePath() string {
	proj := interfaces.GetProject()
	return strings.TrimPrefix(pkg.BasePath(), proj.Path())
}

func (pkg *LocalPackage) Type() interfaces.PackageType {
	return pkg.packageType
}

func (pkg *LocalPackage) Repo() interfaces.RepoInterface {
	return pkg.repo
}

func (pkg *LocalPackage) Desc() *PackageDesc {
	return pkg.desc
}

func (pkg *LocalPackage) SetName(name string) {
	pkg.name = name
	// XXX: Also set "pkg.name" in viper object (possibly just remove cached
	// variable from code entirely).
}

func (pkg *LocalPackage) SetBasePath(basePath string) {
	pkg.basePath = filepath.ToSlash(filepath.Clean(basePath))
}

func (pkg *LocalPackage) SetType(packageType interfaces.PackageType) {
	pkg.packageType = packageType
	// XXX: Also set "pkg.type" in viper object (possibly just remove cached
	// variable from code entirely).
}

func (pkg *LocalPackage) SetDesc(desc *PackageDesc) {
	pkg.desc = desc
	// XXX: Also set desc fields in viper object (possibly just remove cached
	// variable from code entirely).
}

func (pkg *LocalPackage) SetRepo(r *repo.Repo) {
	pkg.repo = r
}

func (pkg *LocalPackage) Hash() (string, error) {
	hash := sha1.New()

	err := filepath.Walk(pkg.basePath,
		func(path string, info os.FileInfo, err error) error {
			name := info.Name()
			if PackageHashIgnoreDirs[name] {
				return filepath.SkipDir
			}

			if info.IsDir() {
				// SHA the directory name into the hash
				hash.Write([]byte(name))
			} else {
				// SHA the file name & contents into the hash
				contents, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}
				hash.Write(contents)
			}
			return nil
		})
	if err != nil && err != filepath.SkipDir {
		return "", util.NewNewtError(err.Error())
	}

	hashStr := fmt.Sprintf("%x", hash.Sum(nil))

	return hashStr, nil
}

func (pkg *LocalPackage) CfgFilenames() []string {
	return pkg.cfgFilenames
}

func (pkg *LocalPackage) AddCfgFilename(cfgFilename string) {
	pkg.cfgFilenames = append(pkg.cfgFilenames, cfgFilename)
}

func (pkg *LocalPackage) readDesc(v *viper.Viper) (*PackageDesc, error) {
	pdesc := &PackageDesc{}

	pdesc.Author = v.GetString("pkg.author")
	pdesc.Homepage = v.GetString("pkg.homepage")
	pdesc.Description = v.GetString("pkg.description")
	pdesc.Keywords = v.GetStringSlice("pkg.keywords")

	return pdesc, nil
}

func (pkg *LocalPackage) sequenceString(key string) string {
	var buffer bytes.Buffer

	if pkg.PkgV != nil {
		for _, f := range pkg.PkgV.GetStringSlice(key) {
			buffer.WriteString("    - " + yaml.EscapeString(f) + "\n")
		}
	}

	if buffer.Len() == 0 {
		return ""
	} else {
		return key + ":\n" + buffer.String()
	}
}

func (lpkg *LocalPackage) SaveSyscfgVals() error {
	dirpath := lpkg.BasePath()
	if err := os.MkdirAll(dirpath, 0755); err != nil {
		return util.NewNewtError(err.Error())
	}

	filepath := dirpath + "/" + SYSCFG_YAML_FILENAME

	syscfgVals := lpkg.SyscfgV.GetStringMapString("syscfg.vals")
	if syscfgVals == nil || len(syscfgVals) == 0 {
		os.Remove(filepath)
		return nil
	}

	file, err := os.Create(filepath)
	if err != nil {
		return util.NewNewtError(err.Error())
	}
	defer file.Close()

	names := make([]string, 0, len(syscfgVals))
	for k, _ := range syscfgVals {
		names = append(names, k)
	}
	sort.Strings(names)

	fmt.Fprintf(file, "### Package: %s\n", lpkg.Name())
	fmt.Fprintf(file, "\n")
	fmt.Fprintf(file, "syscfg.vals:\n")
	for _, name := range names {
		fmt.Fprintf(file, "    %s: %s\n", name, syscfgVals[name])
	}

	return nil
}

// Saves the package's pkg.yml file.
// NOTE: This does not save every field in the package.  Only the fields
// necessary for creating a new target get saved.
func (pkg *LocalPackage) Save() error {
	dirpath := pkg.BasePath()
	if err := os.MkdirAll(dirpath, 0755); err != nil {
		return util.NewNewtError(err.Error())
	}

	filepath := dirpath + "/" + PACKAGE_FILE_NAME
	file, err := os.Create(filepath)
	if err != nil {
		return util.NewNewtError(err.Error())
	}
	defer file.Close()

	file.WriteString("### Package: " + pkg.Name() + "\n")

	// XXX: Just iterate viper object's settings rather than calling out
	// cached settings individually.
	file.WriteString("pkg.name: " + yaml.EscapeString(pkg.Name()) + "\n")
	file.WriteString("pkg.type: " +
		yaml.EscapeString(PackageTypeNames[pkg.Type()]) + "\n")
	file.WriteString("pkg.description: " +
		yaml.EscapeString(pkg.Desc().Description) + "\n")
	file.WriteString("pkg.author: " +
		yaml.EscapeString(pkg.Desc().Author) + "\n")
	file.WriteString("pkg.homepage: " +
		yaml.EscapeString(pkg.Desc().Homepage) + "\n")

	file.WriteString("\n")

	file.WriteString(pkg.sequenceString("pkg.aflags"))
	file.WriteString(pkg.sequenceString("pkg.cflags"))
	file.WriteString(pkg.sequenceString("pkg.features"))
	file.WriteString(pkg.sequenceString("pkg.lflags"))

	return nil
}

// Load reads everything that isn't identity specific into the
// package
func (pkg *LocalPackage) Load() error {
	// Load configuration
	log.Debugf("Loading configuration for package %s", pkg.basePath)

	var err error

	pkg.PkgV, err = util.ReadConfig(pkg.basePath,
		strings.TrimSuffix(PACKAGE_FILE_NAME, ".yml"))
	if err != nil {
		return err
	}
	pkg.AddCfgFilename(pkg.basePath + "/" + PACKAGE_FILE_NAME)

	// Set package name from the package
	pkg.name = pkg.PkgV.GetString("pkg.name")

	typeString := pkg.PkgV.GetString("pkg.type")
	pkg.packageType = PACKAGE_TYPE_LIB
	for t, n := range PackageTypeNames {
		if typeString == n {
			pkg.packageType = t
			break
		}
	}

	init := pkg.PkgV.GetStringMapString("pkg.init")
	for name, stageStr := range init {
		stage, err := strconv.ParseInt(stageStr, 10, 64)
		if err != nil {
			return util.NewNewtError(fmt.Sprintf("Parsing pkg %s config: %s",
				pkg.FullName(), err.Error()))
		}
		pkg.init[name] = int(stage)
	}
	initFnName := pkg.PkgV.GetString("pkg.init_function")
	initStage := pkg.PkgV.GetInt("pkg.init_stage")

	if initFnName != "" {
		pkg.init[initFnName] = initStage
	}

	// Read the package description from the file
	pkg.desc, err = pkg.readDesc(pkg.PkgV)
	if err != nil {
		return err
	}

	// Load syscfg settings.
	if util.NodeExist(pkg.basePath + "/" + SYSCFG_YAML_FILENAME) {
		pkg.SyscfgV, err = util.ReadConfig(pkg.basePath,
			strings.TrimSuffix(SYSCFG_YAML_FILENAME, ".yml"))
		if err != nil {
			return err
		}
		pkg.AddCfgFilename(pkg.basePath + "/" + SYSCFG_YAML_FILENAME)
	}

	return nil
}

func (pkg *LocalPackage) Init() map[string]int {
	return pkg.init
}

func (pkg *LocalPackage) InjectedSettings() map[string]string {
	return pkg.injectedSettings
}

func (pkg *LocalPackage) Clone(newRepo *repo.Repo,
	newName string) *LocalPackage {

	// XXX: Validate name.

	// Copy the package.
	newPkg := *pkg
	newPkg.repo = newRepo
	newPkg.name = newName
	newPkg.basePath = newRepo.Path() + "/" + newPkg.name

	// Insert the clone into the global package map.
	proj := interfaces.GetProject()
	pMap := proj.PackageList()

	(*pMap[newRepo.Name()])[newPkg.name] = &newPkg

	return &newPkg
}

func LoadLocalPackage(repo *repo.Repo, pkgDir string) (*LocalPackage, error) {
	pkg := NewLocalPackage(repo, pkgDir)
	err := pkg.Load()
	return pkg, err
}

func LocalPackageSpecialName(dirName string) bool {
	_, ok := LocalPackageSpecialNames[dirName]
	return ok
}

func ReadLocalPackageRecursive(repo *repo.Repo,
	pkgList map[string]interfaces.PackageInterface, basePath string,
	pkgName string, searchedMap map[string]struct{}) ([]string, error) {

	var warnings []string

	dirList, err := repo.FilteredSearchList(pkgName, searchedMap)
	if err != nil {
		return warnings, util.NewNewtError(err.Error())
	}

	for _, name := range dirList {
		if LocalPackageSpecialName(name) || strings.HasPrefix(name, ".") {
			continue
		}

		subWarnings, err := ReadLocalPackageRecursive(repo, pkgList,
			basePath, filepath.Join(pkgName, name), searchedMap)
		warnings = append(warnings, subWarnings...)
		if err != nil {
			return warnings, err
		}
	}

	if util.NodeNotExist(filepath.Join(basePath, pkgName,
		PACKAGE_FILE_NAME)) {

		return warnings, nil
	}

	pkg, err := LoadLocalPackage(repo, filepath.Join(basePath, pkgName))
	if err != nil {
		warnings = append(warnings, err.Error())
		return warnings, nil
	}

	if oldPkg, ok := pkgList[pkg.Name()]; ok {
		oldlPkg := oldPkg.(*LocalPackage)
		warnings = append(warnings,
			fmt.Sprintf("Multiple packages with same pkg.name=%s "+
				"in repo %s; path1=%s path2=%s", oldlPkg.Name(), repo.Name(),
				oldlPkg.BasePath(), pkg.BasePath()))

		return warnings, nil
	}

	pkgList[pkg.Name()] = pkg

	return warnings, nil
}

func ReadLocalPackages(repo *repo.Repo, basePath string) (
	*map[string]interfaces.PackageInterface, []string, error) {

	pkgMap := &map[string]interfaces.PackageInterface{}

	// Keep track of which directories we have traversed.  Prevent infinite
	// loops caused by symlink cycles by not inspecting the same directory
	// twice.
	searchedMap := map[string]struct{}{}

	warnings, err := ReadLocalPackageRecursive(repo, *pkgMap,
		basePath, "", searchedMap)

	return pkgMap, warnings, err
}
<commit_msg>newt - Remove obsolete pkg.features setting.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package pkg

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	log "github.com/Sirupsen/logrus"

	"mynewt.apache.org/newt/newt/interfaces"
	"mynewt.apache.org/newt/newt/newtutil"
	"mynewt.apache.org/newt/newt/repo"
	"mynewt.apache.org/newt/util"
	"mynewt.apache.org/newt/viper"
	"mynewt.apache.org/newt/yaml"
)

var PackageHashIgnoreDirs = map[string]bool{
	"obj": true,
	"bin": true,
	".":   true,
}

var LocalPackageSpecialNames = map[string]bool{
	"src":     true,
	"include": true,
	"bin":     true,
}

type LocalPackage struct {
	repo        *repo.Repo
	name        string
	basePath    string
	packageType interfaces.PackageType

	// General information about the package
	desc *PackageDesc

	// Package init function name and stage.  These are used to generate the
	// sysinit C file.
	init map[string]int

	// Extra package-specific settings that don't come from syscfg.  For
	// example, SELFTEST gets set when the newt test command is used.
	injectedSettings map[string]string

	// Settings read from pkg.yml.
	PkgV *viper.Viper

	// Settings read from syscfg.yml.
	SyscfgV *viper.Viper

	// Names of all source yml files; used to determine if rebuild required.
	cfgFilenames []string
}

func NewLocalPackage(r *repo.Repo, pkgDir string) *LocalPackage {
	pkg := &LocalPackage{
		desc:             &PackageDesc{},
		PkgV:             viper.New(),
		SyscfgV:          viper.New(),
		repo:             r,
		basePath:         filepath.ToSlash(filepath.Clean(pkgDir)),
		init:             map[string]int{},
		injectedSettings: map[string]string{},
	}
	return pkg
}

func (pkg *LocalPackage) Name() string {
	return pkg.name
}

func (pkg *LocalPackage) FullName() string {
	r := pkg.Repo()
	if r.IsLocal() {
		return pkg.Name()
	} else {
		return newtutil.BuildPackageString(r.Name(), pkg.Name())
	}
}

func (pkg *LocalPackage) BasePath() string {
	return pkg.basePath
}

func (pkg *LocalPackage) RelativePath() string {
	proj := interfaces.GetProject()
	return strings.TrimPrefix(pkg.BasePath(), proj.Path())
}

func (pkg *LocalPackage) Type() interfaces.PackageType {
	return pkg.packageType
}

func (pkg *LocalPackage) Repo() interfaces.RepoInterface {
	return pkg.repo
}

func (pkg *LocalPackage) Desc() *PackageDesc {
	return pkg.desc
}

func (pkg *LocalPackage) SetName(name string) {
	pkg.name = name
	// XXX: Also set "pkg.name" in viper object (possibly just remove cached
	// variable from code entirely).
}

func (pkg *LocalPackage) SetBasePath(basePath string) {
	pkg.basePath = filepath.ToSlash(filepath.Clean(basePath))
}

func (pkg *LocalPackage) SetType(packageType interfaces.PackageType) {
	pkg.packageType = packageType
	// XXX: Also set "pkg.type" in viper object (possibly just remove cached
	// variable from code entirely).
}

func (pkg *LocalPackage) SetDesc(desc *PackageDesc) {
	pkg.desc = desc
	// XXX: Also set desc fields in viper object (possibly just remove cached
	// variable from code entirely).
}

func (pkg *LocalPackage) SetRepo(r *repo.Repo) {
	pkg.repo = r
}

func (pkg *LocalPackage) Hash() (string, error) {
	hash := sha1.New()

	err := filepath.Walk(pkg.basePath,
		func(path string, info os.FileInfo, err error) error {
			name := info.Name()
			if PackageHashIgnoreDirs[name] {
				return filepath.SkipDir
			}

			if info.IsDir() {
				// SHA the directory name into the hash
				hash.Write([]byte(name))
			} else {
				// SHA the file name & contents into the hash
				contents, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}
				hash.Write(contents)
			}
			return nil
		})
	if err != nil && err != filepath.SkipDir {
		return "", util.NewNewtError(err.Error())
	}

	hashStr := fmt.Sprintf("%x", hash.Sum(nil))

	return hashStr, nil
}

func (pkg *LocalPackage) CfgFilenames() []string {
	return pkg.cfgFilenames
}

func (pkg *LocalPackage) AddCfgFilename(cfgFilename string) {
	pkg.cfgFilenames = append(pkg.cfgFilenames, cfgFilename)
}

func (pkg *LocalPackage) readDesc(v *viper.Viper) (*PackageDesc, error) {
	pdesc := &PackageDesc{}

	pdesc.Author = v.GetString("pkg.author")
	pdesc.Homepage = v.GetString("pkg.homepage")
	pdesc.Description = v.GetString("pkg.description")
	pdesc.Keywords = v.GetStringSlice("pkg.keywords")

	return pdesc, nil
}

func (pkg *LocalPackage) sequenceString(key string) string {
	var buffer bytes.Buffer

	if pkg.PkgV != nil {
		for _, f := range pkg.PkgV.GetStringSlice(key) {
			buffer.WriteString("    - " + yaml.EscapeString(f) + "\n")
		}
	}

	if buffer.Len() == 0 {
		return ""
	} else {
		return key + ":\n" + buffer.String()
	}
}

func (lpkg *LocalPackage) SaveSyscfgVals() error {
	dirpath := lpkg.BasePath()
	if err := os.MkdirAll(dirpath, 0755); err != nil {
		return util.NewNewtError(err.Error())
	}

	filepath := dirpath + "/" + SYSCFG_YAML_FILENAME

	syscfgVals := lpkg.SyscfgV.GetStringMapString("syscfg.vals")
	if syscfgVals == nil || len(syscfgVals) == 0 {
		os.Remove(filepath)
		return nil
	}

	file, err := os.Create(filepath)
	if err != nil {
		return util.NewNewtError(err.Error())
	}
	defer file.Close()

	names := make([]string, 0, len(syscfgVals))
	for k, _ := range syscfgVals {
		names = append(names, k)
	}
	sort.Strings(names)

	fmt.Fprintf(file, "### Package: %s\n", lpkg.Name())
	fmt.Fprintf(file, "\n")
	fmt.Fprintf(file, "syscfg.vals:\n")
	for _, name := range names {
		fmt.Fprintf(file, "    %s: %s\n", name, syscfgVals[name])
	}

	return nil
}

// Saves the package's pkg.yml file.
// NOTE: This does not save every field in the package.  Only the fields
// necessary for creating a new target get saved.
func (pkg *LocalPackage) Save() error {
	dirpath := pkg.BasePath()
	if err := os.MkdirAll(dirpath, 0755); err != nil {
		return util.NewNewtError(err.Error())
	}

	filepath := dirpath + "/" + PACKAGE_FILE_NAME
	file, err := os.Create(filepath)
	if err != nil {
		return util.NewNewtError(err.Error())
	}
	defer file.Close()

	file.WriteString("### Package: " + pkg.Name() + "\n")

	// XXX: Just iterate viper object's settings rather than calling out
	// cached settings individually.
	file.WriteString("pkg.name: " + yaml.EscapeString(pkg.Name()) + "\n")
	file.WriteString("pkg.type: " +
		yaml.EscapeString(PackageTypeNames[pkg.Type()]) + "\n")
	file.WriteString("pkg.description: " +
		yaml.EscapeString(pkg.Desc().Description) + "\n")
	file.WriteString("pkg.author: " +
		yaml.EscapeString(pkg.Desc().Author) + "\n")
	file.WriteString("pkg.homepage: " +
		yaml.EscapeString(pkg.Desc().Homepage) + "\n")

	file.WriteString("\n")

	file.WriteString(pkg.sequenceString("pkg.aflags"))
	file.WriteString(pkg.sequenceString("pkg.cflags"))
	file.WriteString(pkg.sequenceString("pkg.lflags"))

	return nil
}

// Load reads everything that isn't identity specific into the
// package
func (pkg *LocalPackage) Load() error {
	// Load configuration
	log.Debugf("Loading configuration for package %s", pkg.basePath)

	var err error

	pkg.PkgV, err = util.ReadConfig(pkg.basePath,
		strings.TrimSuffix(PACKAGE_FILE_NAME, ".yml"))
	if err != nil {
		return err
	}
	pkg.AddCfgFilename(pkg.basePath + "/" + PACKAGE_FILE_NAME)

	// Set package name from the package
	pkg.name = pkg.PkgV.GetString("pkg.name")

	typeString := pkg.PkgV.GetString("pkg.type")
	pkg.packageType = PACKAGE_TYPE_LIB
	for t, n := range PackageTypeNames {
		if typeString == n {
			pkg.packageType = t
			break
		}
	}

	init := pkg.PkgV.GetStringMapString("pkg.init")
	for name, stageStr := range init {
		stage, err := strconv.ParseInt(stageStr, 10, 64)
		if err != nil {
			return util.NewNewtError(fmt.Sprintf("Parsing pkg %s config: %s",
				pkg.FullName(), err.Error()))
		}
		pkg.init[name] = int(stage)
	}
	initFnName := pkg.PkgV.GetString("pkg.init_function")
	initStage := pkg.PkgV.GetInt("pkg.init_stage")

	if initFnName != "" {
		pkg.init[initFnName] = initStage
	}

	// Read the package description from the file
	pkg.desc, err = pkg.readDesc(pkg.PkgV)
	if err != nil {
		return err
	}

	// Load syscfg settings.
	if util.NodeExist(pkg.basePath + "/" + SYSCFG_YAML_FILENAME) {
		pkg.SyscfgV, err = util.ReadConfig(pkg.basePath,
			strings.TrimSuffix(SYSCFG_YAML_FILENAME, ".yml"))
		if err != nil {
			return err
		}
		pkg.AddCfgFilename(pkg.basePath + "/" + SYSCFG_YAML_FILENAME)
	}

	return nil
}

func (pkg *LocalPackage) Init() map[string]int {
	return pkg.init
}

func (pkg *LocalPackage) InjectedSettings() map[string]string {
	return pkg.injectedSettings
}

func (pkg *LocalPackage) Clone(newRepo *repo.Repo,
	newName string) *LocalPackage {

	// XXX: Validate name.

	// Copy the package.
	newPkg := *pkg
	newPkg.repo = newRepo
	newPkg.name = newName
	newPkg.basePath = newRepo.Path() + "/" + newPkg.name

	// Insert the clone into the global package map.
	proj := interfaces.GetProject()
	pMap := proj.PackageList()

	(*pMap[newRepo.Name()])[newPkg.name] = &newPkg

	return &newPkg
}

func LoadLocalPackage(repo *repo.Repo, pkgDir string) (*LocalPackage, error) {
	pkg := NewLocalPackage(repo, pkgDir)
	err := pkg.Load()
	return pkg, err
}

func LocalPackageSpecialName(dirName string) bool {
	_, ok := LocalPackageSpecialNames[dirName]
	return ok
}

func ReadLocalPackageRecursive(repo *repo.Repo,
	pkgList map[string]interfaces.PackageInterface, basePath string,
	pkgName string, searchedMap map[string]struct{}) ([]string, error) {

	var warnings []string

	dirList, err := repo.FilteredSearchList(pkgName, searchedMap)
	if err != nil {
		return warnings, util.NewNewtError(err.Error())
	}

	for _, name := range dirList {
		if LocalPackageSpecialName(name) || strings.HasPrefix(name, ".") {
			continue
		}

		subWarnings, err := ReadLocalPackageRecursive(repo, pkgList,
			basePath, filepath.Join(pkgName, name), searchedMap)
		warnings = append(warnings, subWarnings...)
		if err != nil {
			return warnings, err
		}
	}

	if util.NodeNotExist(filepath.Join(basePath, pkgName,
		PACKAGE_FILE_NAME)) {

		return warnings, nil
	}

	pkg, err := LoadLocalPackage(repo, filepath.Join(basePath, pkgName))
	if err != nil {
		warnings = append(warnings, err.Error())
		return warnings, nil
	}

	if oldPkg, ok := pkgList[pkg.Name()]; ok {
		oldlPkg := oldPkg.(*LocalPackage)
		warnings = append(warnings,
			fmt.Sprintf("Multiple packages with same pkg.name=%s "+
				"in repo %s; path1=%s path2=%s", oldlPkg.Name(), repo.Name(),
				oldlPkg.BasePath(), pkg.BasePath()))

		return warnings, nil
	}

	pkgList[pkg.Name()] = pkg

	return warnings, nil
}

func ReadLocalPackages(repo *repo.Repo, basePath string) (
	*map[string]interfaces.PackageInterface, []string, error) {

	pkgMap := &map[string]interfaces.PackageInterface{}

	// Keep track of which directories we have traversed.  Prevent infinite
	// loops caused by symlink cycles by not inspecting the same directory
	// twice.
	searchedMap := map[string]struct{}{}

	warnings, err := ReadLocalPackageRecursive(repo, *pkgMap,
		basePath, "", searchedMap)

	return pkgMap, warnings, err
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package protocol

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/dustin/go-coap"
	"github.com/ugorji/go/codec"

	"mynewt.apache.org/newt/util"
)

type OicRsp struct {
	Read  map[string]interface{} `codec:"r"`
	Write map[string]interface{} `codec:"w"`
}

/*
 * Not able to install custom decoder for indefite length objects with the codec.
 * So we need to decode the whole response, and then re-encode the newtmgr response
 * part.
 */
func DeserializeOmgrReq(data []byte) (*NmgrReq, error) {
	req := coap.Message{}
	err := req.UnmarshalBinary(data)
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf(
			"Oicmgr request invalid %s", err.Error()))
	}
	log.Debugf("Deserialized COAP %+v", req)

	var rsp OicRsp
	err = codec.NewDecoderBytes(req.Payload, new(codec.CborHandle)).Decode(&rsp)
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf("Invalid incoming cbor: %s",
			err.Error()))
	}

	nmr := &NmgrReq{}

	var ndata []byte = make([]byte, 0)
	if len(rsp.Read) != 0 {
		err = codec.NewEncoderBytes(&ndata,
			new(codec.CborHandle)).Encode(rsp.Read)
		nmr.Op = NMGR_OP_READ_RSP
	} else {
		err = codec.NewEncoderBytes(&ndata,
			new(codec.CborHandle)).Encode(rsp.Read)
		nmr.Op = NMGR_OP_WRITE_RSP
	}
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf("Internal error: %s",
			err.Error()))
	}

	nmr.Len = uint16(len(ndata))
	nmr.Flags = NMGR_F_JSON_RSP_COMPLETE
	nmr.Group = 0
	nmr.Seq = 0
	nmr.Id = 0

	nmr.Data = ndata

	log.Debugf("Deserialized response %+v", nmr)

	return nmr, nil
}

func (nmr *NmgrReq) SerializeOmgrRequest(data []byte) ([]byte, error) {
	req := coap.Message{
		Type:      coap.Confirmable,
		MessageID: uint16(nmr.Seq),
	}
	if nmr.Op == NMGR_OP_READ {
		req.Code = coap.GET
	} else {
		req.Code = coap.PUT
	}
	req.SetPathString("/omgr")
	req.AddOption(coap.URIQuery, fmt.Sprintf("gr=%d", nmr.Group))
	req.AddOption(coap.URIQuery, fmt.Sprintf("id=%d", nmr.Id))

	req.Payload = nmr.Data

	log.Debugf("Serializing request %+v into buffer %+v", nmr, data)

	data, err := req.MarshalBinary()
	if err != nil {
		return nil, util.NewNewtError(
			fmt.Sprintf("Failed to encode: %s\n", err.Error()))
	}
	return data, nil
}
<commit_msg>newtmgr oic; decode to even more generic entity. Return error if COAP response code says so.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package protocol

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/dustin/go-coap"
	"github.com/ugorji/go/codec"

	"mynewt.apache.org/newt/util"
)

type OicRsp struct {
	Read  interface{} `codec:"r"`
	Write interface{} `codec:"w"`
}

/*
 * Not able to install custom decoder for indefite length objects with the codec.
 * So we need to decode the whole response, and then re-encode the newtmgr response
 * part.
 */
func DeserializeOmgrReq(data []byte) (*NmgrReq, error) {
	req := coap.Message{}
	err := req.UnmarshalBinary(data)
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf(
			"Oicmgr request invalid %s", err.Error()))
	}
	if req.Code != coap.Created && req.Code != coap.Deleted &&
		req.Code != coap.Valid && req.Code != coap.Changed &&
		req.Code != coap.Content {
		return nil, util.NewNewtError(fmt.Sprintf(
			"OIC error rsp: %s", req.Code.String()))
	}

	var rsp OicRsp
	err = codec.NewDecoderBytes(req.Payload, new(codec.CborHandle)).Decode(&rsp)
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf("Invalid incoming cbor: %s",
			err.Error()))
	}
	log.Debugf("Deserialized response %+v", rsp)

	nmr := &NmgrReq{}

	var ndata []byte = make([]byte, 0)

	if rsp.Read != nil {
		err = codec.NewEncoderBytes(&ndata,
			new(codec.CborHandle)).Encode(rsp.Read)
		nmr.Op = NMGR_OP_READ_RSP
	} else {
		err = codec.NewEncoderBytes(&ndata,
			new(codec.CborHandle)).Encode(rsp.Write)
		nmr.Op = NMGR_OP_WRITE_RSP
	}
	if err != nil {
		return nil, util.NewNewtError(fmt.Sprintf("Internal error: %s",
			err.Error()))
	}

	nmr.Len = uint16(len(ndata))
	nmr.Flags = NMGR_F_JSON_RSP_COMPLETE
	nmr.Group = 0
	nmr.Seq = 0
	nmr.Id = 0

	nmr.Data = ndata

	log.Debugf("Deserialized response %+v", nmr)

	return nmr, nil
}

func (nmr *NmgrReq) SerializeOmgrRequest(data []byte) ([]byte, error) {
	req := coap.Message{
		Type:      coap.Confirmable,
		MessageID: uint16(nmr.Seq),
	}
	if nmr.Op == NMGR_OP_READ {
		req.Code = coap.GET
	} else {
		req.Code = coap.PUT
	}
	req.SetPathString("/omgr")
	req.AddOption(coap.URIQuery, fmt.Sprintf("gr=%d", nmr.Group))
	req.AddOption(coap.URIQuery, fmt.Sprintf("id=%d", nmr.Id))

	req.Payload = nmr.Data

	log.Debugf("Serializing request %+v into buffer %+v", nmr, data)

	data, err := req.MarshalBinary()
	if err != nil {
		return nil, util.NewNewtError(
			fmt.Sprintf("Failed to encode: %s\n", err.Error()))
	}
	return data, nil
}
<|endoftext|>


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

// Package morass implements file system-backed sorting.
//
// Use morass when you don't want your data to be a quagmire.
//
// Sort data larger than can fit in memory.
//
//  morass məˈras/
//  1. An area of muddy or boggy ground.
//  2. A complicated or confused situation.
package morass

import (
	"container/heap"
	"encoding/gob"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"runtime"
	"sort"
	"sync"
)

var (
	registerLock = &sync.Mutex{}
	registered   = make(map[reflect.Type]struct{})
	nextID       = 0
)

func register(e interface{}, t reflect.Type) {
	registerLock.Lock()
	defer registerLock.Unlock()
	defer func() {
		recover()                  // The only panic that we can get is from trying to register a base type.
		registered[t] = struct{}{} // Remember for next time.
	}()

	if _, exists := registered[t]; !exists {
		registered[t] = struct{}{}
		gob.RegisterName(fmt.Sprintf("ℳ%d", nextID), e)
		nextID++
	}
}

// LessInterface wraps the Less method.
type LessInterface interface {
	// Is the receiver less than the parameterised interface
	Less(i interface{}) bool
}

type sorter []LessInterface

func (s sorter) Len() int { return len(s) }

func (s sorter) Less(i, j int) bool { return s[i].Less(s[j]) }

func (s sorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type file struct {
	head    LessInterface
	file    *os.File
	encoder *gob.Encoder
	decoder *gob.Decoder
}

type files []*file

func (f files) Len() int { return len(f) }

func (f files) Less(i, j int) bool { return f[i].head.Less(f[j].head) }

func (f files) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

func (f *files) Pop() (i interface{}) {
	i = (*f)[len(*f)-1]
	*f = (*f)[:len(*f)-1]
	return
}

func (f *files) Push(x interface{}) { *f = append(*f, x.(*file)) }

// Morass implements sorting of very large data sets.
type Morass struct {
	typ reflect.Type

	pos, length int64

	// dir and prefix specify the location
	// of temporary file.
	dir    string
	prefix string

	// AutoClear specifies that the Morass
	// should call Clear when emptied by
	// a call to Pull.
	AutoClear bool

	// AutoClean specifies that the Morass
	// should call delete temporary sort
	// files when it has been emptied by
	// a call to Pull.
	AutoClean bool

	// fast indicates sorting was performed
	// entirely in memory.
	fast bool

	chunk     sorter
	chunkSize int
	pool      chan sorter
	writable  chan sorter

	filesLock sync.Mutex
	files     files

	errLock sync.Mutex
	_err    error
}

// New creates a new Morass. prefix and dir are passed to ioutil.TempDir. chunkSize specifies
// the amount of sorting to be done in memory, concurrent specifies that temporary file
// writing occurs concurrently with sorting.
// An error is returned if no temporary directory can be created.
// Note that the type is registered with the underlying gob encoder using the name ℳn, where
// n is a sequentially assigned integer string, when the type registered. This is done to avoid using
// too much space and will cause problems when using gob itself on this type. If you intend
// use gob itself with this the type, preregister with gob and morass will use the existing
// registration.
func New(e interface{}, prefix, dir string, chunkSize int, concurrent bool) (*Morass, error) {
	d, err := ioutil.TempDir(dir, prefix)
	if err != nil {
		return nil, err
	}

	m := &Morass{
		chunkSize: chunkSize,
		prefix:    prefix,
		dir:       d,
		pool:      make(chan sorter, 2),
		writable:  make(chan sorter, 1),
		files:     files{},
	}

	m.typ = reflect.TypeOf(e)
	register(e, m.typ)

	m.chunk = make(sorter, 0, chunkSize)
	if concurrent {
		m.pool <- nil
	}

	runtime.SetFinalizer(m, func(x *Morass) {
		if x.AutoClean {
			x.CleanUp()
		}
	})

	return m, nil
}

// Push a value on to the Morass. Returns any error that occurs.
func (m *Morass) Push(e LessInterface) error {
	if typ := reflect.TypeOf(e); typ != m.typ {
		return fmt.Errorf("morass: type mismatch: %s != %s", typ, m.typ)
	}

	if err := m.err(); err != nil {
		return err
	}

	if m.chunk == nil {
		return errors.New("morass: push on finalised morass")
	}

	if len(m.chunk) == m.chunkSize {
		m.writable <- m.chunk
		go m.write()
		m.chunk = <-m.pool
		if err := m.err(); err != nil {
			return err
		}
		if cap(m.chunk) == 0 {
			m.chunk = make(sorter, 0, m.chunkSize)
		}
	}

	m.chunk = append(m.chunk, e)
	m.pos++
	m.length++

	return nil
}

func (m *Morass) write() {
	writing := <-m.writable
	defer func() {
		m.pool <- writing[:0]
	}()

	sort.Sort(writing)

	tf, err := ioutil.TempFile(m.dir, m.prefix)
	if err != nil {
		m.setErr(err)
		return
	}

	enc := gob.NewEncoder(tf)
	dec := gob.NewDecoder(tf)
	f := &file{head: nil, file: tf, encoder: enc, decoder: dec}

	m.filesLock.Lock()
	m.files = append(m.files, f)
	m.filesLock.Unlock()

	for _, e := range writing {
		if err := enc.Encode(&e); err != nil {
			m.setErr(err)
			return
		}
	}

	m.setErr(tf.Sync())
}

func (m *Morass) setErr(err error) {
	m.errLock.Lock()
	m._err = err
	m.errLock.Unlock()
}

func (m *Morass) err() error {
	m.errLock.Lock()
	defer m.errLock.Unlock()
	return m._err
}

// Pos returns the current position of the cursor in the Morass.
func (m *Morass) Pos() int64 { return m.pos }

// Len returns the current length of the Morass.
func (m *Morass) Len() int64 { return m.length }

// Finalise is called to indicate that the last element has been pushed on to the Morass
// and write out final data.
func (m *Morass) Finalise() error {
	if err := m.err(); err != nil {
		return err
	}

	if m.chunk != nil {
		if m.pos < int64(cap(m.chunk)) {
			m.fast = true
			sort.Sort(m.chunk)
		} else {
			if len(m.chunk) > 0 {
				m.writable <- m.chunk
				m.chunk = nil
				m.write()
				if err := m.err(); err != nil {
					return err
				}
			}
		}
		m.pos = 0
	} else {
		return nil
	}

	if !m.fast {
		for _, f := range m.files {
			_, err := f.file.Seek(0, 0)
			if err != nil {
				return err
			}
			err = f.decoder.Decode(&f.head)
			if err != nil && err != io.EOF {
				return err
			}
		}

		heap.Init(&m.files)
	}

	return nil
}

// Clear resets the Morass to an empty state.
func (m *Morass) Clear() error {
	var err error
	for _, f := range m.files {
		err = f.file.Close()
		if err != nil {
			return err
		}
		err = os.Remove(f.file.Name())
		if err != nil {
			return err
		}
	}
	m._err = nil
	m.files = m.files[:0]
	m.pos = 0
	m.length = 0
	select {
	case m.chunk = <-m.pool:
		if m.chunk == nil {
			m.chunk = make(sorter, 0, m.chunkSize)
		}
	default:
	}

	return nil
}

// CleanUp deletes the file system components of the Morass. After this call
// the Morass is not usable.
func (m *Morass) CleanUp() error {
	return os.RemoveAll(m.dir)
}

// Pull sets the settable value e to the lowest value in the Morass.
// If io.EOF is returned the Morass is empty. Any other error results
// in no value being set on e.
func (m *Morass) Pull(e LessInterface) error {
	var err error
	v := reflect.ValueOf(e)
	if !reflect.Indirect(v).CanSet() {
		return errors.New("morass: Cannot set e")
	}

	if m.fast {
		switch {
		case m.chunk != nil && m.pos < int64(len(m.chunk)):
			e = m.chunk[m.pos].(LessInterface)
			m.pos++
		case m.chunk != nil:
			m.pool <- m.chunk[:0]
			m.chunk = nil
			fallthrough
		default:
			if m.AutoClear {
				m.Clear()
			}
			err = io.EOF
		}
	} else {
		if m.files.Len() > 0 {
			low := heap.Pop(&m.files).(*file)
			e = low.head
			m.pos++
			switch err = low.decoder.Decode(&low.head); err {
			case nil:
				heap.Push(&m.files, low)
			case io.EOF:
				err = nil
				fallthrough
			default:
				low.file.Close()
				if m.AutoClear {
					os.Remove(low.file.Name())
				}
			}
		} else {
			if m.AutoClear {
				m.Clear()
			}
			if m.AutoClean {
				os.RemoveAll(m.dir)
			}
			err = io.EOF
		}
	}

	if err != io.EOF {
		reflect.Indirect(v).Set(reflect.ValueOf(e))
	}

	return err
}
<commit_msg>s/length/len/g<commit_after>// Copyright ©2011-2012 The bíogo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package morass implements file system-backed sorting.
//
// Use morass when you don't want your data to be a quagmire.
//
// Sort data larger than can fit in memory.
//
//  morass məˈras/
//  1. An area of muddy or boggy ground.
//  2. A complicated or confused situation.
package morass

import (
	"container/heap"
	"encoding/gob"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"runtime"
	"sort"
	"sync"
)

var (
	registerLock = &sync.Mutex{}
	registered   = make(map[reflect.Type]struct{})
	nextID       = 0
)

func register(e interface{}, t reflect.Type) {
	registerLock.Lock()
	defer registerLock.Unlock()
	defer func() {
		recover()                  // The only panic that we can get is from trying to register a base type.
		registered[t] = struct{}{} // Remember for next time.
	}()

	if _, exists := registered[t]; !exists {
		registered[t] = struct{}{}
		gob.RegisterName(fmt.Sprintf("ℳ%d", nextID), e)
		nextID++
	}
}

// LessInterface wraps the Less method.
type LessInterface interface {
	// Is the receiver less than the parameterised interface
	Less(i interface{}) bool
}

type sorter []LessInterface

func (s sorter) Len() int { return len(s) }

func (s sorter) Less(i, j int) bool { return s[i].Less(s[j]) }

func (s sorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

type file struct {
	head    LessInterface
	file    *os.File
	encoder *gob.Encoder
	decoder *gob.Decoder
}

type files []*file

func (f files) Len() int { return len(f) }

func (f files) Less(i, j int) bool { return f[i].head.Less(f[j].head) }

func (f files) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

func (f *files) Pop() (i interface{}) {
	i = (*f)[len(*f)-1]
	*f = (*f)[:len(*f)-1]
	return
}

func (f *files) Push(x interface{}) { *f = append(*f, x.(*file)) }

// Morass implements sorting of very large data sets.
type Morass struct {
	typ reflect.Type

	pos, len int64

	// dir and prefix specify the location
	// of temporary files.
	dir    string
	prefix string

	// AutoClear specifies that the Morass
	// should call Clear when emptied by
	// a call to Pull.
	AutoClear bool

	// AutoClean specifies that the Morass
	// should call delete temporary sort
	// files when it has been emptied by
	// a call to Pull.
	AutoClean bool

	// fast indicates sorting was performed
	// entirely in memory.
	fast bool

	chunk     sorter
	chunkSize int
	pool      chan sorter
	writable  chan sorter

	filesLock sync.Mutex
	files     files

	errLock sync.Mutex
	_err    error
}

// New creates a new Morass. prefix and dir are passed to ioutil.TempDir. chunkSize specifies
// the amount of sorting to be done in memory, concurrent specifies that temporary file
// writing occurs concurrently with sorting.
// An error is returned if no temporary directory can be created.
// Note that the type is registered with the underlying gob encoder using the name ℳn, where
// n is a sequentially assigned integer string, when the type registered. This is done to avoid using
// too much space and will cause problems when using gob itself on this type. If you intend
// use gob itself with this the type, preregister with gob and morass will use the existing
// registration.
func New(e interface{}, prefix, dir string, chunkSize int, concurrent bool) (*Morass, error) {
	d, err := ioutil.TempDir(dir, prefix)
	if err != nil {
		return nil, err
	}

	m := &Morass{
		chunkSize: chunkSize,
		prefix:    prefix,
		dir:       d,
		pool:      make(chan sorter, 2),
		writable:  make(chan sorter, 1),
		files:     files{},
	}

	m.typ = reflect.TypeOf(e)
	register(e, m.typ)

	m.chunk = make(sorter, 0, chunkSize)
	if concurrent {
		m.pool <- nil
	}

	runtime.SetFinalizer(m, func(x *Morass) {
		if x.AutoClean {
			x.CleanUp()
		}
	})

	return m, nil
}

// Push a value on to the Morass. Returns any error that occurs.
func (m *Morass) Push(e LessInterface) error {
	if typ := reflect.TypeOf(e); typ != m.typ {
		return fmt.Errorf("morass: type mismatch: %s != %s", typ, m.typ)
	}

	if err := m.err(); err != nil {
		return err
	}

	if m.chunk == nil {
		return errors.New("morass: push on finalised morass")
	}

	if len(m.chunk) == m.chunkSize {
		m.writable <- m.chunk
		go m.write()
		m.chunk = <-m.pool
		if err := m.err(); err != nil {
			return err
		}
		if cap(m.chunk) == 0 {
			m.chunk = make(sorter, 0, m.chunkSize)
		}
	}

	m.chunk = append(m.chunk, e)
	m.pos++
	m.len++

	return nil
}

func (m *Morass) write() {
	writing := <-m.writable
	defer func() {
		m.pool <- writing[:0]
	}()

	sort.Sort(writing)

	tf, err := ioutil.TempFile(m.dir, m.prefix)
	if err != nil {
		m.setErr(err)
		return
	}

	enc := gob.NewEncoder(tf)
	dec := gob.NewDecoder(tf)
	f := &file{head: nil, file: tf, encoder: enc, decoder: dec}

	m.filesLock.Lock()
	m.files = append(m.files, f)
	m.filesLock.Unlock()

	for _, e := range writing {
		if err := enc.Encode(&e); err != nil {
			m.setErr(err)
			return
		}
	}

	m.setErr(tf.Sync())
}

func (m *Morass) setErr(err error) {
	m.errLock.Lock()
	m._err = err
	m.errLock.Unlock()
}

func (m *Morass) err() error {
	m.errLock.Lock()
	defer m.errLock.Unlock()
	return m._err
}

// Pos returns the current position of the cursor in the Morass.
func (m *Morass) Pos() int64 { return m.pos }

// Len returns the current length of the Morass.
func (m *Morass) Len() int64 { return m.len }

// Finalise is called to indicate that the last element has been pushed on to the Morass
// and write out final data.
func (m *Morass) Finalise() error {
	if err := m.err(); err != nil {
		return err
	}

	if m.chunk != nil {
		if m.pos < int64(cap(m.chunk)) {
			m.fast = true
			sort.Sort(m.chunk)
		} else {
			if len(m.chunk) > 0 {
				m.writable <- m.chunk
				m.chunk = nil
				m.write()
				if err := m.err(); err != nil {
					return err
				}
			}
		}
		m.pos = 0
	} else {
		return nil
	}

	if !m.fast {
		for _, f := range m.files {
			_, err := f.file.Seek(0, 0)
			if err != nil {
				return err
			}
			err = f.decoder.Decode(&f.head)
			if err != nil && err != io.EOF {
				return err
			}
		}

		heap.Init(&m.files)
	}

	return nil
}

// Clear resets the Morass to an empty state.
func (m *Morass) Clear() error {
	var err error
	for _, f := range m.files {
		err = f.file.Close()
		if err != nil {
			return err
		}
		err = os.Remove(f.file.Name())
		if err != nil {
			return err
		}
	}
	m._err = nil
	m.files = m.files[:0]
	m.pos = 0
	m.len = 0
	select {
	case m.chunk = <-m.pool:
		if m.chunk == nil {
			m.chunk = make(sorter, 0, m.chunkSize)
		}
	default:
	}

	return nil
}

// CleanUp deletes the file system components of the Morass. After this call
// the Morass is not usable.
func (m *Morass) CleanUp() error {
	return os.RemoveAll(m.dir)
}

// Pull sets the settable value e to the lowest value in the Morass.
// If io.EOF is returned the Morass is empty. Any other error results
// in no value being set on e.
func (m *Morass) Pull(e LessInterface) error {
	var err error
	v := reflect.ValueOf(e)
	if !reflect.Indirect(v).CanSet() {
		return errors.New("morass: Cannot set e")
	}

	if m.fast {
		switch {
		case m.chunk != nil && m.pos < int64(len(m.chunk)):
			e = m.chunk[m.pos].(LessInterface)
			m.pos++
		case m.chunk != nil:
			m.pool <- m.chunk[:0]
			m.chunk = nil
			fallthrough
		default:
			if m.AutoClear {
				m.Clear()
			}
			err = io.EOF
		}
	} else {
		if m.files.Len() > 0 {
			low := heap.Pop(&m.files).(*file)
			e = low.head
			m.pos++
			switch err = low.decoder.Decode(&low.head); err {
			case nil:
				heap.Push(&m.files, low)
			case io.EOF:
				err = nil
				fallthrough
			default:
				low.file.Close()
				if m.AutoClear {
					os.Remove(low.file.Name())
				}
			}
		} else {
			if m.AutoClear {
				m.Clear()
			}
			if m.AutoClean {
				os.RemoveAll(m.dir)
			}
			err = io.EOF
		}
	}

	if err != io.EOF {
		reflect.Indirect(v).Set(reflect.ValueOf(e))
	}

	return err
}
<|endoftext|>


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

package trace

import (
	"context"
	"net/http"
)

type key int

const (
	tracerLogHandlerID key = 32702 // random key
)

// Handler wrap a trace handler outer the original http.Handler
func Handler(name string, handler http.Handler) http.Handler {
	return http.HandlerFunc(HandleFunc(name, handler.ServeHTTP))
}

// HandleFunc wrap a trace handle func outer the original http handle func
func HandleFunc(name string, handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var tracer Trace

		if id := r.Header.Get("x-request-id"); len(id) > 0 {
			tracer = WithID(name, id)
		} else {
			tracer = New(name)
		}

		realIP := func(r *http.Request) string {
			if ip, exists := r.Header["X-Real-IP"]; exists && len(ip) > 0 {
				return ip[0]
			}
			if ips, exists := r.Header["X-Forwarded-For"]; exists && len(ips) > 0 {
				return ips[0]
			}
			return r.RemoteAddr
		}

		tracer.Infof("event=[request-in] remote=[%s] method=[%s] url=[%s]", realIP(r), r.Method, r.URL.String())
		defer tracer.Info("event=[request-out]")

		w.Header().Set("x-request-id", tracer.ID())
		handler(w, r.WithContext(context.WithValue(r.Context(), tracerLogHandlerID, tracer)))
	}
}

// GetTraceFromRequest get the Trace var from the req context, if there is no such a trace utility, return nil
func GetTraceFromRequest(r *http.Request) Trace {
	return GetTraceFromContext(r.Context())
}

// GetTraceFromContext get the Trace var from the context, if there is no such a trace utility, return nil
func GetTraceFromContext(ctx context.Context) Trace {
	if tracer, ok := ctx.Value(tracerLogHandlerID).(Trace); ok {
		return tracer
	}
	return New("default-trace")
}

// WithTraceForContext will return a new context wrapped a trace handler around the original ctx
func WithTraceForContext(ctx context.Context, traceName string, traceID ...string) context.Context {
	return context.WithValue(ctx, tracerLogHandlerID, New(traceName, traceID...))
}

// WithTraceForContext2 will return a new context wrapped a trace handler around the original ctx
func WithTraceForContext2(ctx context.Context, tracer Trace) context.Context {
	if tracer == nil {
		return ctx
	}
	return context.WithValue(ctx, tracerLogHandlerID, tracer)
}
<commit_msg>update trace handler<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package trace

import (
	"context"
	"net/http"
	"strings"
)

type key int

const (
	tracerLogHandlerID key = 32702 // random key
	realIPValueID      key = 16221
)

// Handler wrap a trace handler outer the original http.Handler
func Handler(name string, handler http.Handler) http.Handler {
	return http.HandlerFunc(HandleFunc(name, handler.ServeHTTP))
}

// HandleFunc wrap a trace handle func outer the original http handle func
func HandleFunc(name string, handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var tracer Trace
		if id := r.Header.Get("x-request-id"); len(id) > 0 {
			tracer = WithID(name, id)
		} else {
			tracer = New(name)
		}
		w.Header().Set("x-request-id", tracer.ID())

		lastRoute, ip := func(r *http.Request) (string, string) {
			lastRoute := strings.Split(r.RemoteAddr, ":")[0]
			if ip, exists := r.Header["X-Real-IP"]; exists && len(ip) > 0 {
				return lastRoute, ip[0]
			}
			if ips, exists := r.Header["X-Forwarded-For"]; exists && len(ips) > 0 {
				return lastRoute, ips[0]
			}
			return lastRoute, lastRoute
		}(r)

		tracer.Infof("event=[request-in] remote=[%s] route=[%s] method=[%s] url=[%s]", ip, lastRoute, r.Method, r.URL.String())
		defer tracer.Info("event=[request-out]")

		ctx := context.WithValue(r.Context(), tracerLogHandlerID, tracer)
		ctx = context.WithValue(ctx, realIPValueID, ip)

		handler(w, r.WithContext(ctx))
	}
}

// GetTraceFromRequest get the Trace var from the req context, if there is no such a trace utility, return nil
func GetTraceFromRequest(r *http.Request) Trace {
	return GetTraceFromContext(r.Context())
}

// GetTraceFromContext get the Trace var from the context, if there is no such a trace utility, return nil
func GetTraceFromContext(ctx context.Context) Trace {
	if tracer, ok := ctx.Value(tracerLogHandlerID).(Trace); ok {
		return tracer
	}
	return New("default-trace")
}

// GetRealIPFromContext get the remote endpoint from request, if not found, return an empty string
func GetRealIPFromContext(ctx context.Context) string {
	if ip, ok := ctx.Value(realIPValueID).(string); ok {
		return ip
	}
	return ""
}

// WithTraceForContext will return a new context wrapped a trace handler around the original ctx
func WithTraceForContext(ctx context.Context, traceName string, traceID ...string) context.Context {
	return context.WithValue(ctx, tracerLogHandlerID, New(traceName, traceID...))
}

// WithTraceForContext2 will return a new context wrapped a trace handler around the original ctx
func WithTraceForContext2(ctx context.Context, tracer Trace) context.Context {
	if tracer == nil {
		return ctx
	}
	return context.WithValue(ctx, tracerLogHandlerID, tracer)
}
<|endoftext|>


<commit_before>package cache

import (
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
	"sync"
	"time"
)

const Version = "1.0"

type Item struct {
	Object     interface{}
	Expiration *time.Time
}

// Returns true if the item has expired.
func (i *Item) Expired() bool {
	if i.Expiration == nil {
		return false
	}
	return i.Expiration.Before(time.Now())
}

type Cache struct {
	*cache
	// If this is confusing, see the comment at the bottom of the New() function
}

type cache struct {
	DefaultExpiration time.Duration
	Items             map[string]*Item
	mu                *sync.Mutex
	janitor           *janitor
}

// Adds an item to the cache, replacing any existing item. If the duration is 0, the
// cache's default expiration time is used. If it is -1, the item never expires.
func (c *cache) Set(k string, x interface{}, d time.Duration) {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.set(k, x, d)
}

func (c *cache) set(k string, x interface{}, d time.Duration) {
	var e *time.Time
	if d == 0 {
		d = c.DefaultExpiration
	}
	if d > 0 {
		t := time.Now().Add(d)
		e = &t
	}
	c.Items[k] = &Item{
		Object:     x,
		Expiration: e,
	}
}

// Adds an item to the cache only if an item doesn't already exist for the given key,
// or if the existing item has expired. Returns an error if not.
func (c *cache) Add(k string, x interface{}, d time.Duration) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	_, found := c.get(k)
	if found {
		return fmt.Errorf("Item %s already exists", k)
	}
	c.set(k, x, d)
	return nil
}

// Sets a new value for the cache item only if it already exists. Returns an error if
// it does not.
func (c *cache) Replace(k string, x interface{}, d time.Duration) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	_, found := c.get(k)
	if !found {
		return fmt.Errorf("Item %s doesn't exist", k)
	}
	c.set(k, x, d)
	return nil
}

// Gets an item from the cache. Returns the item or nil, and a bool indicating whether
// the given key was found in the cache.
func (c *cache) Get(k string) (interface{}, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()

	return c.get(k)
}

func (c *cache) get(k string) (interface{}, bool) {
	item, found := c.Items[k]
	if !found {
		return nil, false
	}
	if item.Expired() {
		c.delete(k)
		return nil, false
	}
	return item.Object, true
}

// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, uint8,
// uint32, uint64, float32 or float64 by n. Returns an error if the item's value is
// not an integer, if it was not found, or if it is not possible to increment it by
// n. Passing a negative number will cause the item to be decremented.
func (c *cache) IncrementFloat(k string, n float64) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	v, found := c.Items[k]
	if !found || v.Expired() {
		return fmt.Errorf("Item not found")
	}

	t := reflect.TypeOf(v.Object)
	switch t.Kind() {
	default:
		return fmt.Errorf("The value of %s is not an integer", k)
	case reflect.Uint:
		v.Object = v.Object.(uint) + uint(n)
	case reflect.Uintptr:
		v.Object = v.Object.(uintptr) + uintptr(n)
	case reflect.Uint8:
		v.Object = v.Object.(uint8) + uint8(n)
	case reflect.Uint16:
		v.Object = v.Object.(uint16) + uint16(n)
	case reflect.Uint32:
		v.Object = v.Object.(uint32) + uint32(n)
	case reflect.Uint64:
		v.Object = v.Object.(uint64) + uint64(n)
	case reflect.Int:
		v.Object = v.Object.(int) + int(n)
	case reflect.Int8:
		v.Object = v.Object.(int8) + int8(n)
	case reflect.Int16:
		v.Object = v.Object.(int16) + int16(n)
	case reflect.Int32:
		v.Object = v.Object.(int32) + int32(n)
	case reflect.Int64:
		v.Object = v.Object.(int64) + int64(n)
	case reflect.Float32:
		v.Object = v.Object.(float32) + float32(n)
	case reflect.Float64:
		v.Object = v.Object.(float64) + n
	}
	return nil
}

// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, uint8,
// uint32, or uint64, float32 or float64 by n. Returns an error if the item's value
// is not an integer, if it was not found, or if it is not possible to increment it
// by n. Passing a negative number will cause the item to be decremented.
func (c *cache) Increment(k string, n int64) error {
	return c.IncrementFloat(k, float64(n))
}

// Decrement an item of type int, int8, int16, int32, int64, uintptr, uint, uint8,
// uint32, or uint64, float32 or float64 by n. Returns an error if the item's value
// is not an integer, if it was not found, or if it is not possible to decrement it
// by n.
func (c *cache) Decrement(k string, n int64) error {
	return c.Increment(k, n*-1)
}

// Deletes an item from the cache. Does nothing if the item does not exist in the cache.
func (c *cache) Delete(k string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.delete(k)
}

func (c *cache) delete(k string) {
	delete(c.Items, k)
}

// Deletes all expired items from the cache.
func (c *cache) DeleteExpired() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for k, v := range c.Items {
		if v.Expired() {
			c.delete(k)
		}
	}
}

// Writes the cache's items (using Gob) to an io.Writer.
func (c *cache) Save(w io.Writer) error {
	enc := gob.NewEncoder(w)

	defer func() {
		if x := recover(); x != nil {
			fmt.Printf(`The Gob library paniced while registering the cache's item types!
Information: %v

The cache will not be saved.
Please report under what conditions this happened, and particularly what special type of objects
were stored in cache, at https://github.com/pmylund/go-cache/issues/new`, x)
		}
	}()
	for _, v := range c.Items {
		gob.Register(v.Object)
	}
	err := enc.Encode(&c.Items)
	return err
}

// Saves the cache's items to the given filename, creating the file if it
// doesn't exist, and overwriting it if it does.
func (c *cache) SaveFile(fname string) error {
	fp, err := os.Create(fname)
	if err != nil {
		return err
	}
	return c.Save(fp)
}

// Adds (Gob-serialized) cache items from an io.Reader, excluding any items that
// already exist in the current cache.
func (c *cache) Load(r io.Reader) error {
	dec := gob.NewDecoder(r)
	items := map[string]*Item{}
	err := dec.Decode(&items)
	if err == nil {
		for k, v := range items {
			_, found := c.Items[k]
			if !found {
				c.Items[k] = v
			}
		}
	}
	return err
}

// Loads and adds cache items from the given filename, excluding any items that
// already exist in the current cache.
func (c *cache) LoadFile(fname string) error {
	fp, err := os.Open(fname)
	if err != nil {
		return err
	}
	return c.Load(fp)
}

// Deletes all items from the cache.
func (c *cache) Flush() {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.Items = map[string]*Item{}
}

type janitor struct {
	Interval time.Duration
	stop     chan bool
}

func (j *janitor) Run(c *cache) {
	j.stop = make(chan bool)
	tick := time.Tick(j.Interval)
	for {
		select {
		case <-tick:
			c.DeleteExpired()
		case <-j.stop:
			return
		}
	}
}

func (j *janitor) Stop() {
	j.stop <- true
}

func stopJanitor(c *Cache) {
	c.janitor.Stop()
}

// Returns a new cache with a given default expiration duration and default cleanup
// interval. If the expiration duration is less than 1, the items in the cache never
// expire and must be deleted manually. If the cleanup interval is less than one,
// expired items are not deleted from the cache before their next lookup or before
// calling DeleteExpired.
func New(de, ci time.Duration) *Cache {
	if de == 0 {
		de = -1
	}
	c := &cache{
		DefaultExpiration: de,
		Items:             map[string]*Item{},
		mu:                &sync.Mutex{},
	}
	if ci > 0 {
		j := &janitor{
			Interval: ci,
		}
		c.janitor = j
		go j.Run(c)
	}
	// This trick ensures that the janitor goroutine (which--granted it was enabled--is
	// running DeleteExpired on c forever) does not keep the returned C object from being
	// garbage collected. When it is garbage collected, the finalizer stops the janitor
	// goroutine, after which c is collected.
	C := &Cache{c}
	if ci > 0 {
		runtime.SetFinalizer(C, stopJanitor)
	}
	return C
}
<commit_msg>Newline after the error message, or url gets concatenated with the stack trace<commit_after>package cache

import (
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
	"sync"
	"time"
)

const Version = "1.0"

type Item struct {
	Object     interface{}
	Expiration *time.Time
}

// Returns true if the item has expired.
func (i *Item) Expired() bool {
	if i.Expiration == nil {
		return false
	}
	return i.Expiration.Before(time.Now())
}

type Cache struct {
	*cache
	// If this is confusing, see the comment at the bottom of the New() function
}

type cache struct {
	DefaultExpiration time.Duration
	Items             map[string]*Item
	mu                *sync.Mutex
	janitor           *janitor
}

// Adds an item to the cache, replacing any existing item. If the duration is 0, the
// cache's default expiration time is used. If it is -1, the item never expires.
func (c *cache) Set(k string, x interface{}, d time.Duration) {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.set(k, x, d)
}

func (c *cache) set(k string, x interface{}, d time.Duration) {
	var e *time.Time
	if d == 0 {
		d = c.DefaultExpiration
	}
	if d > 0 {
		t := time.Now().Add(d)
		e = &t
	}
	c.Items[k] = &Item{
		Object:     x,
		Expiration: e,
	}
}

// Adds an item to the cache only if an item doesn't already exist for the given key,
// or if the existing item has expired. Returns an error if not.
func (c *cache) Add(k string, x interface{}, d time.Duration) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	_, found := c.get(k)
	if found {
		return fmt.Errorf("Item %s already exists", k)
	}
	c.set(k, x, d)
	return nil
}

// Sets a new value for the cache item only if it already exists. Returns an error if
// it does not.
func (c *cache) Replace(k string, x interface{}, d time.Duration) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	_, found := c.get(k)
	if !found {
		return fmt.Errorf("Item %s doesn't exist", k)
	}
	c.set(k, x, d)
	return nil
}

// Gets an item from the cache. Returns the item or nil, and a bool indicating whether
// the given key was found in the cache.
func (c *cache) Get(k string) (interface{}, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()

	return c.get(k)
}

func (c *cache) get(k string) (interface{}, bool) {
	item, found := c.Items[k]
	if !found {
		return nil, false
	}
	if item.Expired() {
		c.delete(k)
		return nil, false
	}
	return item.Object, true
}

// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, uint8,
// uint32, uint64, float32 or float64 by n. Returns an error if the item's value is
// not an integer, if it was not found, or if it is not possible to increment it by
// n. Passing a negative number will cause the item to be decremented.
func (c *cache) IncrementFloat(k string, n float64) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	v, found := c.Items[k]
	if !found || v.Expired() {
		return fmt.Errorf("Item not found")
	}

	t := reflect.TypeOf(v.Object)
	switch t.Kind() {
	default:
		return fmt.Errorf("The value of %s is not an integer", k)
	case reflect.Uint:
		v.Object = v.Object.(uint) + uint(n)
	case reflect.Uintptr:
		v.Object = v.Object.(uintptr) + uintptr(n)
	case reflect.Uint8:
		v.Object = v.Object.(uint8) + uint8(n)
	case reflect.Uint16:
		v.Object = v.Object.(uint16) + uint16(n)
	case reflect.Uint32:
		v.Object = v.Object.(uint32) + uint32(n)
	case reflect.Uint64:
		v.Object = v.Object.(uint64) + uint64(n)
	case reflect.Int:
		v.Object = v.Object.(int) + int(n)
	case reflect.Int8:
		v.Object = v.Object.(int8) + int8(n)
	case reflect.Int16:
		v.Object = v.Object.(int16) + int16(n)
	case reflect.Int32:
		v.Object = v.Object.(int32) + int32(n)
	case reflect.Int64:
		v.Object = v.Object.(int64) + int64(n)
	case reflect.Float32:
		v.Object = v.Object.(float32) + float32(n)
	case reflect.Float64:
		v.Object = v.Object.(float64) + n
	}
	return nil
}

// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, uint8,
// uint32, or uint64, float32 or float64 by n. Returns an error if the item's value
// is not an integer, if it was not found, or if it is not possible to increment it
// by n. Passing a negative number will cause the item to be decremented.
func (c *cache) Increment(k string, n int64) error {
	return c.IncrementFloat(k, float64(n))
}

// Decrement an item of type int, int8, int16, int32, int64, uintptr, uint, uint8,
// uint32, or uint64, float32 or float64 by n. Returns an error if the item's value
// is not an integer, if it was not found, or if it is not possible to decrement it
// by n.
func (c *cache) Decrement(k string, n int64) error {
	return c.Increment(k, n*-1)
}

// Deletes an item from the cache. Does nothing if the item does not exist in the cache.
func (c *cache) Delete(k string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.delete(k)
}

func (c *cache) delete(k string) {
	delete(c.Items, k)
}

// Deletes all expired items from the cache.
func (c *cache) DeleteExpired() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for k, v := range c.Items {
		if v.Expired() {
			c.delete(k)
		}
	}
}

// Writes the cache's items (using Gob) to an io.Writer.
func (c *cache) Save(w io.Writer) error {
	enc := gob.NewEncoder(w)

	defer func() {
		if x := recover(); x != nil {
			fmt.Printf(`The Gob library paniced while registering the cache's item types!
Information: %v

The cache will not be saved.
Please report under what conditions this happened, and particularly what special type of objects
were stored in cache, at https://github.com/pmylund/go-cache/issues/new
`, x)
		}
	}()
	for _, v := range c.Items {
		gob.Register(v.Object)
	}
	err := enc.Encode(&c.Items)
	return err
}

// Saves the cache's items to the given filename, creating the file if it
// doesn't exist, and overwriting it if it does.
func (c *cache) SaveFile(fname string) error {
	fp, err := os.Create(fname)
	if err != nil {
		return err
	}
	return c.Save(fp)
}

// Adds (Gob-serialized) cache items from an io.Reader, excluding any items that
// already exist in the current cache.
func (c *cache) Load(r io.Reader) error {
	dec := gob.NewDecoder(r)
	items := map[string]*Item{}
	err := dec.Decode(&items)
	if err == nil {
		for k, v := range items {
			_, found := c.Items[k]
			if !found {
				c.Items[k] = v
			}
		}
	}
	return err
}

// Loads and adds cache items from the given filename, excluding any items that
// already exist in the current cache.
func (c *cache) LoadFile(fname string) error {
	fp, err := os.Open(fname)
	if err != nil {
		return err
	}
	return c.Load(fp)
}

// Deletes all items from the cache.
func (c *cache) Flush() {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.Items = map[string]*Item{}
}

type janitor struct {
	Interval time.Duration
	stop     chan bool
}

func (j *janitor) Run(c *cache) {
	j.stop = make(chan bool)
	tick := time.Tick(j.Interval)
	for {
		select {
		case <-tick:
			c.DeleteExpired()
		case <-j.stop:
			return
		}
	}
}

func (j *janitor) Stop() {
	j.stop <- true
}

func stopJanitor(c *Cache) {
	c.janitor.Stop()
}

// Returns a new cache with a given default expiration duration and default cleanup
// interval. If the expiration duration is less than 1, the items in the cache never
// expire and must be deleted manually. If the cleanup interval is less than one,
// expired items are not deleted from the cache before their next lookup or before
// calling DeleteExpired.
func New(de, ci time.Duration) *Cache {
	if de == 0 {
		de = -1
	}
	c := &cache{
		DefaultExpiration: de,
		Items:             map[string]*Item{},
		mu:                &sync.Mutex{},
	}
	if ci > 0 {
		j := &janitor{
			Interval: ci,
		}
		c.janitor = j
		go j.Run(c)
	}
	// This trick ensures that the janitor goroutine (which--granted it was enabled--is
	// running DeleteExpired on c forever) does not keep the returned C object from being
	// garbage collected. When it is garbage collected, the finalizer stops the janitor
	// goroutine, after which c is collected.
	C := &Cache{c}
	if ci > 0 {
		runtime.SetFinalizer(C, stopJanitor)
	}
	return C
}
<|endoftext|>


<commit_before>package raymond

import (
	"io/ioutil"
	"path"
	"testing"

	"gopkg.in/yaml.v2"
)

// @todo Replace that by adding yaml tags in raymondTest struct
type mustacheTest struct {
	Name     string
	Desc     string
	Data     interface{}
	Template string
	Expected string
	Partials map[string]string
}

type mustacheTestFile struct {
	Overview string
	Tests    []mustacheTest
}

func testsFromMustacheFile(fileName string) []raymondTest {
	result := []raymondTest{}

	fileData, err := ioutil.ReadFile(path.Join("mustache", "specs", fileName))
	if err != nil {
		panic(err)
	}

	var testFile mustacheTestFile
	if err := yaml.Unmarshal(fileData, &testFile); err != nil {
		panic(err)
	}

	for _, mustacheTest := range testFile.Tests {
		test := raymondTest{
			name:   mustacheTest.Name,
			input:  mustacheTest.Template,
			data:   mustacheTest.Data,
			output: mustacheTest.Expected,
		}

		result = append(result, test)
	}

	return result
}

// func TestMustacheComments(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("comments.yml"))
// }

// func TestMustacheDelimiters(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("delimiters.yml"))
// }

func TestMustacheInterpolation(t *testing.T) {
	launchRaymondTests(t, testsFromMustacheFile("interpolation.yml"))
}

// func TestMustacheInverted(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("inverted.yml"))
// }

// func TestMustachePartials(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("partials.yml"))
// }

// func TestMustacheSections(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("sections.yml"))
// }

// func TestMustacheLambdas(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("~lambdas.yml"))
// }
<commit_msg>Skips mustache tests with alternative delimiter<commit_after>package raymond

import (
	"io/ioutil"
	"path"
	"regexp"
	"testing"

	"gopkg.in/yaml.v2"
)

//
// Note that, as the JS implementation, we do not support:
//   - support alternative delimeters
//   - the mustache lambda spec
//

type mustacheTest struct {
	Name     string
	Desc     string
	Data     interface{}
	Template string
	Expected string
	Partials map[string]string
}

type mustacheTestFile struct {
	Overview string
	Tests    []mustacheTest
}

var (
	rAltDelim = regexp.MustCompile(regexp.QuoteMeta("{{="))
)

func testsFromMustacheFile(fileName string) []raymondTest {
	result := []raymondTest{}

	fileData, err := ioutil.ReadFile(path.Join("mustache", "specs", fileName))
	if err != nil {
		panic(err)
	}

	var testFile mustacheTestFile
	if err := yaml.Unmarshal(fileData, &testFile); err != nil {
		panic(err)
	}

	for _, mustacheTest := range testFile.Tests {
		if mustBeSkipped(mustacheTest) {
			continue
		}

		test := raymondTest{
			name:   mustacheTest.Name,
			input:  mustacheTest.Template,
			data:   mustacheTest.Data,
			output: mustacheTest.Expected,
		}

		result = append(result, test)
	}

	return result
}

// returns true if test must be skipped
func mustBeSkipped(test mustacheTest) bool {
	// @todo Skip partials tests "Failed Lookup" and "Standalone Indentation"
	return haveAltDelimiter(test)
}

// returns true if test have alternative delimeter in template or in partials
func haveAltDelimiter(test mustacheTest) bool {
	// check template
	if rAltDelim.MatchString(test.Template) {
		return true
	}

	// check partials
	for _, partial := range test.Partials {
		if rAltDelim.MatchString(partial) {
			return true
		}
	}

	return false
}

// func TestMustacheComments(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("comments.yml"))
// }

func TestMustacheDelimiters(t *testing.T) {
	launchRaymondTests(t, testsFromMustacheFile("delimiters.yml"))
}

func TestMustacheInterpolation(t *testing.T) {
	launchRaymondTests(t, testsFromMustacheFile("interpolation.yml"))
}

// func TestMustacheInverted(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("inverted.yml"))
// }

// func TestMustachePartials(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("partials.yml"))
// }

// func TestMustacheSections(t *testing.T) {
// 	launchRaymondTests(t, testsFromMustacheFile("sections.yml"))
// }
<|endoftext|>


<commit_before>// Copyright (c) 2013 - Michael Woolnough <michael.woolnough@gmail.com>
//
// 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 OWNER 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 minecraft

import (
	"fmt"
	"github.com/MJKWoolnough/minecraft/nbt"
)

var (
	chunkRequired = []struct {
		name string
		nbt.TagId
	}{
		{"HeightMap", nbt.Tag_IntArray},
		{"InhabitedTime", nbt.Tag_Long},
		{"LastUpdate", nbt.Tag_Long},
		{"Sections", nbt.Tag_List},
		{"TerrainPopulated", nbt.Tag_Byte},
		{"xPos", nbt.Tag_Int},
		{"zPos", nbt.Tag_Int},
	}
	chunkOther = []struct {
		name      string
		tagType   nbt.TagId
		listType  nbt.TagId
		emptyByte bool
	}{
		{"Biomes", nbt.Tag_ByteArray, nbt.Tag_Byte, false},
		{"Entities", nbt.Tag_List, nbt.Tag_Compound, true},
		{"TileEntities", nbt.Tag_List, nbt.Tag_Compound, true},
		{"TileTicks", nbt.Tag_List, nbt.Tag_Compound, false},
	}
)

type chunk struct {
	sections [16]*section
	biomes   *nbt.ByteArray
	data     *nbt.Compound
	//entities     map[uint16]*nbt.Compound
	tileEntities map[uint16]*nbt.Compound
	tileTicks    map[uint16]*nbt.Compound
}

func (c *chunk) GetNBT() nbt.Tag {
	data := c.data.Copy().(*nbt.Compound)
	sections := make([]nbt.Data, 0, 16)
	for i := 0; i < 16; i++ {
		if c.sections[i] != nil {
			sections = append(sections, c.sections[i].section)
		}
	}
	sectionList := nbt.NewEmptyList(nbt.Tag_Compound)
	sectionList.Append(sections...)
	data.Set(nbt.NewTag("Sections", sectionList))
	tileEntities := make([]nbt.Data, 0)
	for _, cmp := range c.tileEntities {
		if cmp != nil {
			tileEntities = append(tileEntities, cmp)
		}
	}
	data.Set(nbt.NewTag("TileEntities", nbt.NewList(tileEntities)))
	tileTicks := make([]nbt.Data, 0)
	for _, cmp := range c.tileTicks {
		if cmp != nil {
			tileTicks = append(tileTicks, cmp)
		}
	}
	if len(tileTicks) == 0 {
		data.Remove("TileTicks")
	} else {
		data.Set(nbt.NewTag("TileTicks", nbt.NewList(tileTicks)))
	}
	return nbt.NewTag("", nbt.NewCompound([]nbt.Tag{nbt.NewTag("Level", data)}))
}

func newChunk(x, z int32, data nbt.Tag) (*chunk, error) {
	if data == nil {
		biomes := make([]int8, 256)
		for i := 0; i < 256; i++ {
			biomes[i] = -1
		}
		data = nbt.NewTag("", nbt.NewCompound([]nbt.Tag{
			nbt.NewTag("Level", nbt.NewCompound([]nbt.Tag{
				nbt.NewTag("Biomes", nbt.NewByteArray(biomes)),
				nbt.NewTag("HeightMap", nbt.NewIntArray(make([]int32, 256))),
				nbt.NewTag("InhabitedTime", nbt.NewLong(0)),
				nbt.NewTag("LastUpdate", nbt.NewLong(0)),
				nbt.NewTag("Sections", nbt.NewEmptyList(nbt.Tag_Compound)),
				nbt.NewTag("TerrainPopulated", nbt.NewByte(1)),
				nbt.NewTag("xPos", nbt.NewInt(x)),
				nbt.NewTag("zPos", nbt.NewInt(z)),
			})),
		}))
	}
	c := new(chunk)
	if data.TagId() != nbt.Tag_Compound {
		return nil, &WrongTypeError{"[Chunk Base]", nbt.Tag_Compound, data.TagId()}
	} else if tag := data.Data().(*nbt.Compound).Get("Level"); tag == nil {
		return nil, &MissingTagError{"[Chunk Base]->Level"}
	} else if tag.TagId() != nbt.Tag_Compound {
		return nil, &WrongTypeError{"[Chunk Base]->Level", nbt.Tag_Compound, tag.TagId()}
	} else {
		c.data = tag.Data().(*nbt.Compound)
	}
	for _, req := range chunkRequired {
		if tag := c.data.Get(req.name); tag == nil {
			return nil, &MissingTagError{req.name}
		} else if tagId := tag.TagId(); tagId != req.TagId {
			return nil, &WrongTypeError{req.name, req.TagId, tagId}
		}
	}

	if tX := int32(*c.data.Get("xPos").Data().(*nbt.Int)); tX != x {
		return nil, &UnexpectedValue{ "[Chunk Base]->Level->xPos", fmt.Sprintf("%d", x), fmt.Sprintf("%d", tX) }
	}
	if tZ := int32(*c.data.Get("zPos").Data().(*nbt.Int)); tZ != z {
		return nil, &UnexpectedValue{ "[Chunk Base]->Level->zPos", fmt.Sprintf("%d", z), fmt.Sprintf("%d", tZ) }
	}

	for _, co := range chunkOther {
		if tag := c.data.Get(co.name); tag == nil {
			continue
		} else if tagId := tag.TagId(); tagId != co.tagType {
			return nil, &WrongTypeError{co.name, co.tagType, tagId}
		} else if tagId == nbt.Tag_List {
			list := tag.Data().(*nbt.List)
			if list.TagType() != co.listType {
				if co.emptyByte && list.TagType() == nbt.Tag_Byte && list.Len() == 0 {
					continue
				}
				return nil, &WrongTypeError{co.name, co.listType, list.TagType()}
			}
		}
	}

	if biomes := c.data.Get("Biomes"); biomes != nil {
		c.biomes = biomes.Data().(*nbt.ByteArray)
	} else {
		biomes := make([]int8, 256)
		for i := 0; i < 256; i++ {
			biomes[i] = -1
		}
		c.biomes = nbt.NewByteArray(biomes)
		c.data.Set(nbt.NewTag("Biomes", c.biomes))
	}
	c.tileEntities = make(map[uint16]*nbt.Compound)
	if tileEntities := c.data.Get("TileEntities"); tileEntities != nil {
		if lTileEntities, ok := tileEntities.Data().(*nbt.List); ok {
			for i := 0; i < lTileEntities.Len(); i++ {
				tag := lTileEntities.Get(i).(*nbt.Compound)
				if tag == nil {
					return nil, &MissingTagError{"TileEntities->Child"}
				}
				x, y, z, err := getCoords(tag)
				if err != nil {
					return nil, err
				}
				c.tileEntities[xyz(x, y, z)] = tag
			}
		}
	}
	c.data.Remove("TileEntities")
	c.tileTicks = make(map[uint16]*nbt.Compound)
	if tileTicks := c.data.Get("TileTicks"); tileTicks != nil {
		if lTileTicks, ok := tileTicks.Data().(*nbt.List); ok {
			for i := 0; i < lTileTicks.Len(); i++ {
				tag := lTileTicks.Get(i).(*nbt.Compound)
				if tag == nil {
					return nil, &MissingTagError{"TileTicks->Child"}
				}
				x, y, z, err := getCoords(tag)
				if err != nil {
					return nil, err
				}
				c.tileTicks[xyz(x, y, z)] = tag
			}
		}
	}
	c.data.Remove("TileTicks")
	sections := c.data.Get("Sections").Data().(*nbt.List)
	for i := 0; i < sections.Len(); i++ {
		section := sections.Get(i).(*nbt.Compound)
		if yc := section.Get("Y"); yc == nil {
			return nil, &MissingTagError{"Sections->Child->Y"}
		} else if yc.TagId() != nbt.Tag_Byte {
			return nil, &WrongTypeError{"Sections->Child->Y", nbt.Tag_Byte, yc.TagId()}
		} else {
			y := int32(*yc.Data().(*nbt.Byte))
			var err error
			if c.sections[y], err = LoadSection(section); err != nil {
				return nil, err
			}
		}
	}
	c.data.Remove("Sections")
	return c, nil
}

func (c *chunk) GetBlock(x, y, z int32) (b *Block, err error) {
	ys := y >> 4
	if c.sections[ys] == nil {
		b = &Block{}
	} else {
		if b, err = c.sections[ys].GetBlock(x, y, z); err != nil {
			return
		}
		if md, ok := c.tileEntities[xyz(x, y, z)]; ok && md != nil {
			b.SetMetadata([]nbt.Tag(*md))
		}
		if tt, ok := c.tileTicks[xyz(x, y, z)]; ok && tt != nil {
			b.Tick = true
		}
	}
	return
}

func (c *chunk) SetBlock(x, y, z int32, b *Block) error {
	ys := y >> 4
	if c.sections[ys] == nil {
		if b.Equal(&Block{}) {
			return nil
		}
		c.sections[ys] = NewSection(y)
	}
	if err := c.sections[ys].SetBlock(x, y, z, b); err != nil {
		return err
	}
	pos := xyz(x, y, z)
	if b.metadata == nil {
		delete(c.tileEntities, pos)
	} else {
		c.tileEntities[pos] = nbt.NewCompound(b.GetMetadata())
		c.tileEntities[pos].Set(nbt.NewTag("x", nbt.NewInt(x)))
		c.tileEntities[pos].Set(nbt.NewTag("y", nbt.NewInt(y)))
		c.tileEntities[pos].Set(nbt.NewTag("z", nbt.NewInt(z)))
	}
	if b.Tick {
		c.tileTicks[pos] = nbt.NewCompound([]nbt.Tag{
			nbt.NewTag("i", nbt.NewInt(int32(b.BlockId))),
			nbt.NewTag("p", nbt.NewInt(1)),
			nbt.NewTag("t", nbt.NewInt(-1)),
			nbt.NewTag("x", nbt.NewInt(x)),
			nbt.NewTag("y", nbt.NewInt(y)),
			nbt.NewTag("z", nbt.NewInt(z)),
		})
	} else {
		delete(c.tileTicks, pos)
	}
	return nil
}

func (c *chunk) GetBiome(x, z int32) Biome {
	return Biome((*c.biomes)[z&15<<4|x&15])
}

func (c *chunk) SetBiome(x, z int32, b Biome) error {
	(*c.biomes)[z&15<<4|x&15] = int8(b)
	return nil
}

func (c *chunk) IsEmpty() bool {
	for i := 0; i < 16; i++ {
		if c.sections[i] != nil {
			if !c.sections[i].IsEmpty() {
				return false
			}
		}
	}
	return true
}

func xyz(x, y, z int32) uint16 {
	return (uint16(y) << 8) | (uint16(z&15) << 4) | uint16(x&15)
}

func getCoord(name string, data *nbt.Compound) (int32, error) {
	tag := data.Get(name)
	if tag == nil {
		return 0, &MissingTagError{name}
	} else if tag.TagId() != nbt.Tag_Int {
		return 0, &WrongTypeError{name, nbt.Tag_Int, tag.TagId()}
	}
	return int32(*tag.Data().(*nbt.Int)), nil
}

func getCoords(data *nbt.Compound) (x, y, z int32, err error) {
	if x, err = getCoord("x", data); err != nil {
		return
	} else if y, err = getCoord("y", data); err != nil {
		return
	}
	z, err = getCoord("z", data)
	return
}
<commit_msg>various minor changes<commit_after>// Copyright (c) 2013 - Michael Woolnough <michael.woolnough@gmail.com>
//
// 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 OWNER 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 minecraft

import (
	"fmt"
	"github.com/MJKWoolnough/minecraft/nbt"
)

var (
	chunkRequired = []struct {
		name string
		nbt.TagId
	}{
		{"HeightMap", nbt.Tag_IntArray},
		{"InhabitedTime", nbt.Tag_Long},
		{"LastUpdate", nbt.Tag_Long},
		{"Sections", nbt.Tag_List},
		{"TerrainPopulated", nbt.Tag_Byte},
		{"xPos", nbt.Tag_Int},
		{"zPos", nbt.Tag_Int},
	}
	chunkOther = []struct {
		name      string
		tagType   nbt.TagId
		listType  nbt.TagId
		emptyByte bool
	}{
		{"Biomes", nbt.Tag_ByteArray, nbt.Tag_Byte, false},
		{"Entities", nbt.Tag_List, nbt.Tag_Compound, true},
		{"TileEntities", nbt.Tag_List, nbt.Tag_Compound, true},
		{"TileTicks", nbt.Tag_List, nbt.Tag_Compound, false},
	}
)

type chunk struct {
	sections [16]*section
	biomes   *nbt.ByteArray
	data     *nbt.Compound
	//entities     map[uint16]*nbt.Compound
	tileEntities map[uint16]*nbt.Compound
	tileTicks    map[uint16]*nbt.Compound
}

func (c *chunk) GetNBT() nbt.Tag {
	data := c.data.Copy().(*nbt.Compound)
	sections := make([]nbt.Data, 0, 16)
	for i := 0; i < 16; i++ {
		if c.sections[i] != nil {
			sections = append(sections, c.sections[i].section)
		}
	}
	sectionList := nbt.NewEmptyList(nbt.Tag_Compound)
	sectionList.Append(sections...)
	data.Set(nbt.NewTag("Sections", sectionList))
	tileEntities := make([]nbt.Data, 0)
	for _, cmp := range c.tileEntities {
		if cmp != nil {
			tileEntities = append(tileEntities, cmp)
		}
	}
	data.Set(nbt.NewTag("TileEntities", nbt.NewList(tileEntities)))
	tileTicks := make([]nbt.Data, 0)
	for _, cmp := range c.tileTicks {
		if cmp != nil {
			tileTicks = append(tileTicks, cmp)
		}
	}
	if len(tileTicks) == 0 {
		data.Remove("TileTicks")
	} else {
		data.Set(nbt.NewTag("TileTicks", nbt.NewList(tileTicks)))
	}
	return nbt.NewTag("", nbt.NewCompound([]nbt.Tag{nbt.NewTag("Level", data)}))
}

func newChunk(x, z int32, data nbt.Tag) (*chunk, error) {
	if data == nil {
		biomes := make([]int8, 256)
		for i := 0; i < 256; i++ {
			biomes[i] = -1
		}
		data = nbt.NewTag("", nbt.NewCompound([]nbt.Tag{
			nbt.NewTag("Level", nbt.NewCompound([]nbt.Tag{
				nbt.NewTag("xPos", nbt.NewInt(x)),
				nbt.NewTag("zPos", nbt.NewInt(z)),
				nbt.NewTag("Biomes", nbt.NewByteArray(biomes)),
				nbt.NewTag("HeightMap", nbt.NewIntArray(make([]int32, 256))),
				nbt.NewTag("InhabitedTime", nbt.NewLong(0)),
				nbt.NewTag("LastUpdate", nbt.NewLong(0)),
				nbt.NewTag("Sections", nbt.NewEmptyList(nbt.Tag_Compound)),
				nbt.NewTag("TerrainPopulated", nbt.NewByte(1)),
			})),
		}))
	}
	c := new(chunk)
	if data.TagId() != nbt.Tag_Compound {
		return nil, &WrongTypeError{"[Chunk Base]", nbt.Tag_Compound, data.TagId()}
	} else if tag := data.Data().(*nbt.Compound).Get("Level"); tag == nil {
		return nil, &MissingTagError{"[Chunk Base]->Level"}
	} else if tag.TagId() != nbt.Tag_Compound {
		return nil, &WrongTypeError{"[Chunk Base]->Level", nbt.Tag_Compound, tag.TagId()}
	} else {
		c.data = tag.Data().(*nbt.Compound)
	}
	for _, req := range chunkRequired {
		if tag := c.data.Get(req.name); tag == nil {
			return nil, &MissingTagError{req.name}
		} else if tagId := tag.TagId(); tagId != req.TagId {
			return nil, &WrongTypeError{req.name, req.TagId, tagId}
		}
	}

	if tX := int32(*c.data.Get("xPos").Data().(*nbt.Int)); tX != x {
		return nil, &UnexpectedValue{"[Chunk Base]->Level->xPos", fmt.Sprintf("%d", x), fmt.Sprintf("%d", tX)}
	}
	if tZ := int32(*c.data.Get("zPos").Data().(*nbt.Int)); tZ != z {
		return nil, &UnexpectedValue{"[Chunk Base]->Level->zPos", fmt.Sprintf("%d", z), fmt.Sprintf("%d", tZ)}
	}

	for _, co := range chunkOther {
		if tag := c.data.Get(co.name); tag == nil {
			continue
		} else if tagId := tag.TagId(); tagId != co.tagType {
			return nil, &WrongTypeError{co.name, co.tagType, tagId}
		} else if tagId == nbt.Tag_List {
			list := tag.Data().(*nbt.List)
			if list.TagType() != co.listType {
				if co.emptyByte && list.TagType() == nbt.Tag_Byte && list.Len() == 0 {
					continue
				}
				return nil, &WrongTypeError{co.name, co.listType, list.TagType()}
			}
		}
	}

	if biomes := c.data.Get("Biomes"); biomes != nil {
		c.biomes = biomes.Data().(*nbt.ByteArray)
	} else {
		biomes := make([]int8, 256)
		for i := 0; i < 256; i++ {
			biomes[i] = -1
		}
		c.biomes = nbt.NewByteArray(biomes)
		c.data.Set(nbt.NewTag("Biomes", c.biomes))
	}
	c.tileEntities = make(map[uint16]*nbt.Compound)
	if tileEntities := c.data.Get("TileEntities"); tileEntities != nil {
		if lTileEntities, ok := tileEntities.Data().(*nbt.List); ok {
			for i := 0; i < lTileEntities.Len(); i++ {
				tag := lTileEntities.Get(i).(*nbt.Compound)
				if tag == nil {
					return nil, &MissingTagError{"TileEntities->Child"}
				}
				x, y, z, err := getCoords(tag)
				if err != nil {
					return nil, err
				}
				c.tileEntities[xyz(x, y, z)] = tag
			}
		}
	}
	c.data.Remove("TileEntities")
	c.tileTicks = make(map[uint16]*nbt.Compound)
	if tileTicks := c.data.Get("TileTicks"); tileTicks != nil {
		if lTileTicks, ok := tileTicks.Data().(*nbt.List); ok {
			for i := 0; i < lTileTicks.Len(); i++ {
				tag := lTileTicks.Get(i).(*nbt.Compound)
				if tag == nil {
					return nil, &MissingTagError{"TileTicks->Child"}
				}
				x, y, z, err := getCoords(tag)
				if err != nil {
					return nil, err
				}
				c.tileTicks[xyz(x, y, z)] = tag
			}
		}
	}
	c.data.Remove("TileTicks")
	sections := c.data.Get("Sections").Data().(*nbt.List)
	for i := 0; i < sections.Len(); i++ {
		section := sections.Get(i).(*nbt.Compound)
		if yc := section.Get("Y"); yc == nil {
			return nil, &MissingTagError{"Sections->Child->Y"}
		} else if yc.TagId() != nbt.Tag_Byte {
			return nil, &WrongTypeError{"Sections->Child->Y", nbt.Tag_Byte, yc.TagId()}
		} else {
			y := int32(*yc.Data().(*nbt.Byte))
			var err error
			if c.sections[y], err = loadSection(section); err != nil {
				return nil, err
			}
		}
	}
	c.data.Remove("Sections")
	return c, nil
}

func (c *chunk) GetBlock(x, y, z int32) (b *Block, err error) {
	ys := y >> 4
	if c.sections[ys] == nil {
		b = &Block{}
	} else {
		if b, err = c.sections[ys].GetBlock(x, y, z); err != nil {
			return
		}
		pos := xyz(x, y, z)
		if md, ok := c.tileEntities[pos]; ok && md != nil {
			b.SetMetadata([]nbt.Tag(*md))
		}
		if tt, ok := c.tileTicks[pos]; ok && tt != nil {
			b.Tick = true
		}
	}
	return
}

func (c *chunk) SetBlock(x, y, z int32, b *Block) error {
	ys := y >> 4
	if c.sections[ys] == nil {
		if b.Equal(&Block{}) {
			return nil
		}
		c.sections[ys] = newSection(y)
	}
	if err := c.sections[ys].SetBlock(x, y, z, b); err != nil {
		return err
	}
	pos := xyz(x, y, z)
	if b.metadata == nil {
		delete(c.tileEntities, pos)
	} else {
		c.tileEntities[pos] = nbt.NewCompound(b.GetMetadata())
		c.tileEntities[pos].Set(nbt.NewTag("x", nbt.NewInt(x)))
		c.tileEntities[pos].Set(nbt.NewTag("y", nbt.NewInt(y)))
		c.tileEntities[pos].Set(nbt.NewTag("z", nbt.NewInt(z)))
	}
	if b.Tick {
		c.tileTicks[pos] = nbt.NewCompound([]nbt.Tag{
			nbt.NewTag("i", nbt.NewInt(int32(b.BlockId))),
			nbt.NewTag("p", nbt.NewInt(1)),
			nbt.NewTag("t", nbt.NewInt(-1)),
			nbt.NewTag("x", nbt.NewInt(x)),
			nbt.NewTag("y", nbt.NewInt(y)),
			nbt.NewTag("z", nbt.NewInt(z)),
		})
	} else {
		delete(c.tileTicks, pos)
	}
	return nil
}

func (c *chunk) GetBiome(x, z int32) Biome {
	return Biome((*c.biomes)[z&15<<4|x&15])
}

func (c *chunk) SetBiome(x, z int32, b Biome) error {
	(*c.biomes)[z&15<<4|x&15] = int8(b)
	return nil
}

func (c *chunk) IsEmpty() bool {
	for i := 0; i < 16; i++ {
		if c.sections[i] != nil {
			if !c.sections[i].IsEmpty() {
				return false
			}
		}
	}
	return true
}

func xyz(x, y, z int32) uint16 {
	return (uint16(y) << 8) | (uint16(z&15) << 4) | uint16(x&15)
}

func getCoord(name string, data *nbt.Compound) (int32, error) {
	tag := data.Get(name)
	if tag == nil {
		return 0, &MissingTagError{name}
	} else if tag.TagId() != nbt.Tag_Int {
		return 0, &WrongTypeError{name, nbt.Tag_Int, tag.TagId()}
	}
	return int32(*tag.Data().(*nbt.Int)), nil
}

func getCoords(data *nbt.Compound) (x, y, z int32, err error) {
	if x, err = getCoord("x", data); err != nil {
		return
	} else if y, err = getCoord("y", data); err != nil {
		return
	}
	z, err = getCoord("z", data)
	return
}
<|endoftext|>