<commit_before>package base

/* CHECKLIST
 * [ ] Uses interfaces as appropriate
 * [ ] Private package variables use underscore prefix
 * [ ] All parameters validated
 * [ ] All errors handled
 * [ ] Reviewed for concurrency safety
 * [ ] Code complete
 * [ ] Full test coverage
 */

import (
	"github.com/tidepool-org/platform/app"
	"github.com/tidepool-org/platform/data"
)

const SchemaVersionCurrent = 3

type Base struct {
	Active         bool   `json:"-" bson:"_active"`
	CreatedTime    string `json:"createdTime,omitempty" bson:"createdTime,omitempty"`
	CreatedUserID  string `json:"createdUserId,omitempty" bson:"createdUserId,omitempty"`
	DeletedTime    string `json:"deletedTime,omitempty" bson:"deletedTime,omitempty"`
	DeletedUserID  string `json:"deletedUserId,omitempty" bson:"deletedUserId,omitempty"`
	GroupID        string `json:"-" bson:"_groupId,omitempty"`
	GUID           string `json:"guid,omitempty" bson:"guid,omitempty"`
	ID             string `json:"id,omitempty" bson:"id,omitempty"`
	ModifiedTime   string `json:"modifiedTime,omitempty" bson:"modifiedTime,omitempty"`
	ModifiedUserID string `json:"modifiedUserId,omitempty" bson:"modifiedUserId,omitempty"`
	SchemaVersion  int    `json:"-" bson:"_schemaVersion,omitempty"`
	Type           string `json:"type,omitempty" bson:"type,omitempty"`
	UploadID       string `json:"uploadId,omitempty" bson:"uploadId,omitempty"`
	UserID         string `json:"-" bson:"_userId,omitempty"`
	Version        int    `json:"-" bson:"_version,omitempty"`

	Annotations      *[]interface{} `json:"annotations,omitempty" bson:"annotations,omitempty"`
	ClockDriftOffset *int           `json:"clockDriftOffset,omitempty" bson:"clockDriftOffset,omitempty"`
	ConversionOffset *int           `json:"conversionOffset,omitempty" bson:"conversionOffset,omitempty"`
	DeviceID         *string        `json:"deviceId,omitempty" bson:"deviceId,omitempty"`
	DeviceTime       *string        `json:"deviceTime,omitempty" bson:"deviceTime,omitempty"`
	Payload          *interface{}   `json:"payload,omitempty" bson:"payload,omitempty"`
	Time             *string        `json:"time,omitempty" bson:"time,omitempty"`
	TimezoneOffset   *int           `json:"timezoneOffset,omitempty" bson:"timezoneOffset,omitempty"`
}

type Meta struct {
	Type string `json:"type,omitempty"`
}

func (b *Base) Init() {
	b.Active = false
	b.CreatedTime = ""
	b.CreatedUserID = ""
	b.DeletedTime = ""
	b.DeletedUserID = ""
	b.GroupID = ""
	b.GUID = app.NewUUID()
	b.ID = app.NewID() // TODO: Move calculation to Normalize to follow Jellyfish algorithm
	b.ModifiedTime = ""
	b.ModifiedUserID = ""
	b.SchemaVersion = SchemaVersionCurrent
	b.Type = ""
	b.UploadID = ""
	b.UserID = ""
	b.Version = 0

	b.Annotations = nil
	b.ClockDriftOffset = nil
	b.ConversionOffset = nil
	b.DeviceID = nil
	b.DeviceTime = nil
	b.Payload = nil
	b.Time = nil
	b.TimezoneOffset = nil
}

func (b *Base) Meta() interface{} {
	return &Meta{
		Type: b.Type,
	}
}

func (b *Base) Parse(parser data.ObjectParser) error {
	b.Annotations = parser.ParseInterfaceArray("annotations")
	b.ClockDriftOffset = parser.ParseInteger("clockDriftOffset")
	b.ConversionOffset = parser.ParseInteger("conversionOffset")
	b.DeviceID = parser.ParseString("deviceId")
	b.DeviceTime = parser.ParseString("deviceTime")
	b.Payload = parser.ParseInterface("payload")
	b.Time = parser.ParseString("time")
	b.TimezoneOffset = parser.ParseInteger("timezoneOffset")

	return nil
}

func (b *Base) Validate(validator data.Validator) error {
	// validator.ValidateInterfaceArray("annotations", b.Annotations)    // TODO: Any validations? Optional? Size?
	// validator.ValidateInteger("clockDriftOffset", b.ClockDriftOffset) // TODO: Any validations? Optional? Range?
	// validator.ValidateInteger("conversionOffset", b.ConversionOffset) // TODO: Any validations? Optional? Range?
	validator.ValidateString("deviceId", b.DeviceID).Exists().NotEmpty()
	validator.ValidateStringAsTime("deviceTime", b.DeviceTime, "2006-01-02T15:04:05") // TODO: Not in upload!  -> .Exists()
	// validator.ValidateInterface("payload", b.Payload) // TODO: Any validations? Optional? Size?
	validator.ValidateStringAsTime("time", b.Time, "2006-01-02T15:04:05Z07:00").Exists()
	// validator.ValidateInteger("timezoneOffset", b.TimezoneOffset) // TODO: Any validations? Optional? Range?

	// TODO: NOT IN UPLOAD: annotations, clockDriftOffset, deviceTime, payload

	return nil
}

func (b *Base) Normalize(normalizer data.Normalizer) error {
	return nil
}

func (b *Base) SetUserID(userID string) {
	b.UserID = userID
}

func (b *Base) SetGroupID(groupID string) {
	b.GroupID = groupID
}

func (b *Base) SetDatasetID(datasetID string) {
	b.UploadID = datasetID
}

func (b *Base) SetActive(active bool) {
	b.Active = active
}

func (b *Base) SetCreatedTime(createdTime string) {
	b.CreatedTime = createdTime
}

func (b *Base) SetCreatedUserID(createdUserID string) {
	b.CreatedUserID = createdUserID
}

func (b *Base) SetModifiedTime(modifiedTime string) {
	b.ModifiedTime = modifiedTime
}

func (b *Base) SetModifiedUserID(modifiedUserID string) {
	b.ModifiedUserID = modifiedUserID
}

func (b *Base) SetDeletedTime(deletedTime string) {
	b.DeletedTime = deletedTime
}

func (b *Base) SetDeletedUserID(deletedUserID string) {
	b.DeletedUserID = deletedUserID
}
<commit_msg>Basic validation of data base type<commit_after>package base

/* CHECKLIST
 * [ ] Uses interfaces as appropriate
 * [ ] Private package variables use underscore prefix
 * [ ] All parameters validated
 * [ ] All errors handled
 * [ ] Reviewed for concurrency safety
 * [ ] Code complete
 * [ ] Full test coverage
 */

import (
	"github.com/tidepool-org/platform/app"
	"github.com/tidepool-org/platform/data"
)

const SchemaVersionCurrent = 3

type Base struct {
	Active         bool   `json:"-" bson:"_active"`
	CreatedTime    string `json:"createdTime,omitempty" bson:"createdTime,omitempty"`
	CreatedUserID  string `json:"createdUserId,omitempty" bson:"createdUserId,omitempty"`
	DeletedTime    string `json:"deletedTime,omitempty" bson:"deletedTime,omitempty"`
	DeletedUserID  string `json:"deletedUserId,omitempty" bson:"deletedUserId,omitempty"`
	GroupID        string `json:"-" bson:"_groupId,omitempty"`
	GUID           string `json:"guid,omitempty" bson:"guid,omitempty"`
	ID             string `json:"id,omitempty" bson:"id,omitempty"`
	ModifiedTime   string `json:"modifiedTime,omitempty" bson:"modifiedTime,omitempty"`
	ModifiedUserID string `json:"modifiedUserId,omitempty" bson:"modifiedUserId,omitempty"`
	SchemaVersion  int    `json:"-" bson:"_schemaVersion,omitempty"`
	Type           string `json:"type,omitempty" bson:"type,omitempty"`
	UploadID       string `json:"uploadId,omitempty" bson:"uploadId,omitempty"`
	UserID         string `json:"-" bson:"_userId,omitempty"`
	Version        int    `json:"-" bson:"_version,omitempty"`

	Annotations      *[]interface{} `json:"annotations,omitempty" bson:"annotations,omitempty"`
	ClockDriftOffset *int           `json:"clockDriftOffset,omitempty" bson:"clockDriftOffset,omitempty"`
	ConversionOffset *int           `json:"conversionOffset,omitempty" bson:"conversionOffset,omitempty"`
	DeviceID         *string        `json:"deviceId,omitempty" bson:"deviceId,omitempty"`
	DeviceTime       *string        `json:"deviceTime,omitempty" bson:"deviceTime,omitempty"`
	Payload          *interface{}   `json:"payload,omitempty" bson:"payload,omitempty"`
	Time             *string        `json:"time,omitempty" bson:"time,omitempty"`
	TimezoneOffset   *int           `json:"timezoneOffset,omitempty" bson:"timezoneOffset,omitempty"`
}

type Meta struct {
	Type string `json:"type,omitempty"`
}

func (b *Base) Init() {
	b.Active = false
	b.CreatedTime = ""
	b.CreatedUserID = ""
	b.DeletedTime = ""
	b.DeletedUserID = ""
	b.GroupID = ""
	b.GUID = app.NewUUID()
	b.ID = app.NewID() // TODO: Move calculation to Normalize to follow Jellyfish algorithm
	b.ModifiedTime = ""
	b.ModifiedUserID = ""
	b.SchemaVersion = SchemaVersionCurrent
	b.Type = ""
	b.UploadID = ""
	b.UserID = ""
	b.Version = 0

	b.Annotations = nil
	b.ClockDriftOffset = nil
	b.ConversionOffset = nil
	b.DeviceID = nil
	b.DeviceTime = nil
	b.Payload = nil
	b.Time = nil
	b.TimezoneOffset = nil
}

func (b *Base) Meta() interface{} {
	return &Meta{
		Type: b.Type,
	}
}

func (b *Base) Parse(parser data.ObjectParser) error {
	b.Annotations = parser.ParseInterfaceArray("annotations")
	b.ClockDriftOffset = parser.ParseInteger("clockDriftOffset")
	b.ConversionOffset = parser.ParseInteger("conversionOffset")
	b.DeviceID = parser.ParseString("deviceId")
	b.DeviceTime = parser.ParseString("deviceTime")
	b.Payload = parser.ParseInterface("payload")
	b.Time = parser.ParseString("time")
	b.TimezoneOffset = parser.ParseInteger("timezoneOffset")

	return nil
}

func (b *Base) Validate(validator data.Validator) error {
	validator.ValidateString("type", &b.Type).Exists().NotEmpty()

	// validator.ValidateInterfaceArray("annotations", b.Annotations)    // TODO: Any validations? Optional? Size?
	// validator.ValidateInteger("clockDriftOffset", b.ClockDriftOffset) // TODO: Any validations? Optional? Range?
	// validator.ValidateInteger("conversionOffset", b.ConversionOffset) // TODO: Any validations? Optional? Range?
	validator.ValidateString("deviceId", b.DeviceID).Exists().NotEmpty()
	validator.ValidateStringAsTime("deviceTime", b.DeviceTime, "2006-01-02T15:04:05") // TODO: Not in upload!  -> .Exists()
	// validator.ValidateInterface("payload", b.Payload) // TODO: Any validations? Optional? Size?
	validator.ValidateStringAsTime("time", b.Time, "2006-01-02T15:04:05Z07:00").Exists()
	// validator.ValidateInteger("timezoneOffset", b.TimezoneOffset) // TODO: Any validations? Optional? Range?

	// TODO: NOT IN UPLOAD: annotations, clockDriftOffset, deviceTime, payload

	return nil
}

func (b *Base) Normalize(normalizer data.Normalizer) error {
	return nil
}

func (b *Base) SetUserID(userID string) {
	b.UserID = userID
}

func (b *Base) SetGroupID(groupID string) {
	b.GroupID = groupID
}

func (b *Base) SetDatasetID(datasetID string) {
	b.UploadID = datasetID
}

func (b *Base) SetActive(active bool) {
	b.Active = active
}

func (b *Base) SetCreatedTime(createdTime string) {
	b.CreatedTime = createdTime
}

func (b *Base) SetCreatedUserID(createdUserID string) {
	b.CreatedUserID = createdUserID
}

func (b *Base) SetModifiedTime(modifiedTime string) {
	b.ModifiedTime = modifiedTime
}

func (b *Base) SetModifiedUserID(modifiedUserID string) {
	b.ModifiedUserID = modifiedUserID
}

func (b *Base) SetDeletedTime(deletedTime string) {
	b.DeletedTime = deletedTime
}

func (b *Base) SetDeletedUserID(deletedUserID string) {
	b.DeletedUserID = deletedUserID
}
<|endoftext|>


<commit_before>// Copyright 2016 The clang-server 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 compilationdatabase

import (
	"bufio"
	"bytes"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"

	"github.com/go-clang/v3.9/clang"
	"github.com/pkg/errors"
	"github.com/pkgutil/osutil"
)

// DefaultJSONName default of compile_commands.json filename.
const DefaultJSONName = "compile_commands.json"

// ErrNotFound error of not found the compile_commands.json file.
var ErrNotFound = errors.New("couldn't find the compile_commands.json")

// CompilationDatabase represents a consist of an array of “command objects”,
// where each command object specifies one way a translation unit is compiled in the project.
type CompilationDatabase struct {
	root string

	cd             clang.CompilationDatabase
	cmds           []*CompileCommand
	CompilerConfig *CompilerConfig

	mu sync.Mutex
}

// CompilerConfig represents a clang compiler configs.
type CompilerConfig struct {
	Target              string
	ThreadModel         string
	InstalledDir        string
	DefaultFlag         []string
	Version             string
	SystemCIncludeDir   []string
	SystemCXXIncludeDir []string
	SystemFrameworkDir  []string
}

// CompileCommand represents a each command object contains the translation unit’s main file,
// the working directory of the compile run and the actual compile command.
type CompileCommand struct {
	// Directory the working directory of the compilation.
	Directory string `json:"directory"`
	// File the main translation unit source processed by this compilation step.
	File string `json:"file"`
	// Command the compile command executed.
	Command string `json:"command"`
	// Arguments the compile command executed as list of strings.
	Arguments []string `json:"arguments"`
	// Output the name of the output created by this compilation step.
	Output string `json:"output"`
}

// NewCompilationDatabase return the new CompilationDatabase.
func NewCompilationDatabase(root string) *CompilationDatabase {
	return &CompilationDatabase{
		root: root,
	}
}

// Parse parses the project root directory recursively, and cache the compile
// flags to flags map.
func (c *CompilationDatabase) Parse(jsonfile string, pathRange []string) error {
	ch := make(chan *CompilerConfig, 1)
	go func() { ch <- c.DefaultCompilerConfig() }()

	if jsonfile == "" {
		jsonfile = DefaultJSONName
	}
	dir := c.findJSONFile(jsonfile, pathRange)
	if dir == "" {
		return ErrNotFound
	}

	cErr, cd := clang.FromDirectory(dir)
	if cErr != clang.CompilationDatabase_NoError {
		return errors.WithStack(clang.CompilationDatabase_Error(cErr))
	}
	c.cd = cd
	defer c.cd.Dispose()

	c.CompilerConfig = <-ch

	if err := c.parseFlags(); err != nil {
		return errors.WithStack(err)
	}

	return nil
}

// CompileCommands return the CompileCommand struct based parse result.
func (c *CompilationDatabase) CompileCommands() []*CompileCommand {
	return c.cmds
}

// DefaultCompilerConfig gets the compiler defalut configs.
// The parse target sample:
//  clang -v -x c++ /dev/null -fsyntax-only
//
//  Apple LLVM version 8.1.0 (clang-802.0.27.2)
//  Target: x86_64-apple-darwin16.5.0
//  Thread model: posix
//  InstalledDir: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
//   "/Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.12.0 ...
//  clang -cc1 version 8.1.0 (clang-802.0.27.2) default target x86_64-apple-darwin16.5.0
//  ignoring nonexistent directory "/usr/include/c++/v1"
//  #include "..." search starts here:
//  #include <...> search starts here:
//   /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1
//   /usr/local/include
//   /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.1.0/include
//   /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
//   /usr/include
//   /System/Library/Frameworks (framework directory)
//   /Library/Frameworks (framework directory)
//  End of search list.
func (c *CompilationDatabase) DefaultCompilerConfig() *CompilerConfig {
	cc := "clang" // default is clang
	if envCC := os.Getenv("CC"); envCC != "" {
		cc = envCC
	}
	ccPath, err := exec.LookPath(cc)
	if err != nil {
		log.Fatalf("couldn't find %s", cc)
	}

	// Get C++ include directory together with -x flag
	cmd := exec.Command(ccPath, "-v", "-x", "c++", "/dev/null", "-fsyntax-only")

	var b bytes.Buffer
	cmd.Stdout = &b
	cmd.Stderr = &b
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}

	cfg := new(CompilerConfig)
	var includeSection bool
	scan := bufio.NewScanner(&b)
	for scan.Scan() {
		s := scan.Text()
		if includeSection {
			if strings.HasPrefix(s, "End") {
				includeSection = false
				break
			}
			path := strings.TrimSpace(s)
			l := len(path)
			switch {
			case path[l-6:l-3] == "c++":
				cfg.SystemCXXIncludeDir = append(cfg.SystemCXXIncludeDir, "-I"+path)
			case strings.Contains(s, "framework directory"):
				cfg.SystemFrameworkDir = append(cfg.SystemFrameworkDir, "-F"+strings.TrimSuffix(path, " (framework directory)"))
			default:
				cfg.SystemCIncludeDir = append(cfg.SystemCIncludeDir, "-I"+path)
			}
		}
		switch {
		case strings.HasPrefix(s, "Target"):
			cfg.Target = strings.TrimPrefix(s, "Target: ")
		case strings.HasPrefix(s, "Thread model"):
			cfg.ThreadModel = strings.TrimPrefix(s, "Thread model: ")
		case strings.HasPrefix(s, "InstalledDir"):
			cfg.InstalledDir = strings.TrimPrefix(s, "InstalledDir: ")
		case (strings.Contains(s, cfg.InstalledDir) && strings.Contains(s, "-cc1")):
			flags := strings.Fields(s)
			cfg.DefaultFlag = flags[1 : len(flags)-1]
		case strings.HasPrefix(s, cc):
			sep := strings.Split(s, "version ")
			version := strings.Split(sep[1], " ")[0]
			cfg.Version = version
		case strings.HasPrefix(s, "#include <...>"):
			includeSection = true
		}
	}

	return cfg
}

// findFile finds the filename on pathRange recursively.
func (c *CompilationDatabase) findJSONFile(filename string, pathRange []string) string {
	if pathRange == nil {
		parent := filepath.Dir(c.root)             // parent of projectRoot
		buildDir := filepath.Join(c.root, "build") // projectRoot/build
		outDir := filepath.Join(c.root, "out")     // projectRoot/out
		pathRange = []string{c.root, parent, buildDir, outDir}
	}

	pathCh := make(chan string, 1)
	for _, d := range pathRange {
		go func(d string) {
			if osutil.IsExist(filepath.Join(d, filename)) {
				log.Printf("found filepath: %s", filepath.Join(d, filename))
				pathCh <- d
			}
		}(d)
	}

	return <-pathCh
}

// parseFlags parses the all of project files compile flag.
func (c *CompilationDatabase) parseFlags() error {
	cmds := c.cd.AllCompileCommands()
	ncmds := cmds.Size()
	c.cmds = make([]*CompileCommand, 0, ncmds)

	var wg sync.WaitGroup
	for i := uint32(0); i < ncmds; i++ {
		wg.Add(1)
		i := i

		go func(i uint32) {
			c.mu.Lock()
			defer func() {
				wg.Done()
				c.mu.Unlock()
			}()

			cmd := cmds.Command(i)
			args := c.formatFlag(cmd)
			c.cmds = append(c.cmds, &CompileCommand{
				Directory: cmd.Directory(),
				File:      cmd.Filename(),
				Command:   strings.Join(args, " "),
				Arguments: args,
			})
		}(i)
	}

	wg.Wait()

	return nil
}

// formatFlag formats the compile flag for the libclang TranslationUnit arg.
func (c *CompilationDatabase) formatFlag(cmd clang.CompileCommand) []string {
	n := cmd.NumArgs()
	flags := make([]string, 0, n)

	for i := uint32(0); i < n; i++ {
		dir := cmd.Directory()
		f := cmd.Arg(i)

		switch {
		case strings.HasPrefix(f, "-I"): // <value>: Specified directory to the search path for include files
			includeDir := c.fixArg(strings.TrimPrefix(f, "-I"), dir)
			flags = append(flags, "-I", includeDir)

		case strings.HasPrefix(f, "-F"): // <directory>: Specified directory to the search path for framework include files
			frameworkDir := c.fixArg(strings.TrimPrefix(f, "-F"), dir)
			flags = append(flags, "-F", frameworkDir)

		case f == "-I", // <value>: Specified directory to the search path for include files
			f == "-F",                 // <directory>: Specified directory to the search path for framework include files
			f == "-D",                 // <macroname>=<value>: Adds an implicit #define into the predefines buffer which is read before the source file is preprocessed
			f == "-U",                 // <macroname>: Adds an implicit #undef into the predefines buffer which is read before the source file is preprocessed
			f == "-framework",         // <name>: Tells the linker to search for `name.framework/name' the framework search path
			f == "-x",                 // <language> Treat subsequent input files as having type language.
			f == "-arch",              // <architecture> Specify the architecture to build for.
			f == "-include",           // <file>: Adds an implicit #include into the predefines buffer which is read before the source file is preprocessed
			f == "-isysroot",          // <dir>: Add directory to SYSTEM include search path
			f == "-isystem",           // <directory>: Set the system root directory (usually /)
			f == "-iframework",        // <value>: Add directory to SYSTEM framework search path
			f == "-include-pch",       // <file>: Include precompiled header file
			f == "-isystem-after",     // <directory>: Add directory to end of the SYSTEM include search path
			f == "-idirafter",         // <value>: Add directory to AFTER include search path
			f == "-imacros",           // <file>: Include macros from file before parsing
			f == "-ivfsoverlay",       // <value>: Overlay the virtual filesystem described by file over the real file system
			f == "-iwithprefix",       // <dir>: Set directory to SYSTEM include search path with prefix
			f == "-iwithprefixbefore", // <dir>: Set directory to include search path with prefix
			f == "-iwithsysroot":      // <directory>: Add directory to SYSTEM include search path, absolute paths are relative to -isysroot
			flags = append(flags, f, c.fixArg(cmd.Arg(i+1), dir))

		case strings.HasPrefix(f, "-D"), // <macroname>=<value>: Adds an implicit #define into the predefines buffer which is read before the source file is preprocessed
			strings.HasPrefix(f, "-U"),                     // <macroname>: Adds an implicit #undef into the predefines buffer which is read before the source file is preprocessed
			strings.HasPrefix(f, "-std"),                   // <language>: Specify the language standard
			strings.HasPrefix(f, "-stdlib"),                // <library>: Specify the C++ standard library to use
			strings.HasPrefix(f, "-x"),                     // <language> Treat subsequent input files as having type language.
			strings.HasPrefix(f, "-arch"),                  // <architecture> Specify the architecture to build for.
			strings.HasPrefix(f, "-mmacosx-version-min"),   // <version>: Specify the minimum version supported by your application when building for macOS
			strings.HasPrefix(f, "-miphoneos-version-min"), // <version>: Specify the minimum version supported by your application when building for iPhone OS
			strings.HasPrefix(f, "-include"),               // <file>: Adds an implicit #include into the predefines buffer which is read before the source file is preprocessed
			strings.HasPrefix(f, "-isysroot"),              // <dir>: Add directory to SYSTEM include search path
			strings.HasPrefix(f, "-isystem"),               // <directory>: Set the system root directory (usually /)
			strings.HasPrefix(f, "-iframework"),            // <value>: Add directory to SYSTEM framework search path
			strings.HasPrefix(f, "-include-pch"),           // <file>: Include precompiled header file
			strings.HasPrefix(f, "-isystem-after"),         // <directory>: Add directory to end of the SYSTEM include search path
			strings.HasPrefix(f, "-G"),
			strings.HasPrefix(f, "-T"),
			strings.HasPrefix(f, "-V"),
			strings.HasPrefix(f, "-target"),
			strings.HasPrefix(f, "-Xanalyzer"),
			strings.HasPrefix(f, "-Xassembler"),
			strings.HasPrefix(f, "-Xclang"),
			strings.HasPrefix(f, "-Xlinker"),
			strings.HasPrefix(f, "-Xpreprocessor"),
			strings.HasPrefix(f, "-b"),
			strings.HasPrefix(f, "-gcc-toolchain"),
			strings.HasPrefix(f, "-idirafter"), // <value>: Add directory to AFTER include search path
			strings.HasPrefix(f, "-imacros"),   // <file>: Include macros from file before parsing
			strings.HasPrefix(f, "-imultilib"),
			strings.HasPrefix(f, "-iprefix"),
			strings.HasPrefix(f, "-ivfsoverlay"),       // <value>: Overlay the virtual filesystem described by file over the real file system
			strings.HasPrefix(f, "-iwithprefix"),       // <dir>: Set directory to SYSTEM include search path with prefix
			strings.HasPrefix(f, "-iwithprefixbefore"), // <dir>: Set directory to include search path with prefix
			strings.HasPrefix(f, "-iwithsysroot"):      // <directory>: Add directory to SYSTEM include search path, absolute paths are relative to -isysroot

			flags = append(flags, f)
		}
	}

	return flags
}

// fixArg return the absolube directory path based by buildDir if contains filepath.Separator.
func (c *CompilationDatabase) fixArg(arg, buildDir string) string {
	if filepath.IsAbs(arg) {
		return arg
	}

	for _, d := range []string{buildDir, c.root} {
		if dir := filepath.Join(d, arg); osutil.IsExist(dir) {
			return dir
		}
	}

	return arg
}
<commit_msg>compilationdatabase: remove ErrNotFound error variable<commit_after>// Copyright 2016 The clang-server 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 compilationdatabase

import (
	"bufio"
	"bytes"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"

	"github.com/go-clang/v3.9/clang"
	"github.com/pkg/errors"
	"github.com/pkgutil/osutil"
)

// DefaultJSONName default of compile_commands.json filename.
const DefaultJSONName = "compile_commands.json"

// CompilationDatabase represents a consist of an array of “command objects”,
// where each command object specifies one way a translation unit is compiled in the project.
type CompilationDatabase struct {
	root string

	cd             clang.CompilationDatabase
	cmds           []*CompileCommand
	CompilerConfig *CompilerConfig

	mu sync.Mutex
}

// CompilerConfig represents a clang compiler configs.
type CompilerConfig struct {
	Target              string
	ThreadModel         string
	InstalledDir        string
	DefaultFlag         []string
	Version             string
	SystemCIncludeDir   []string
	SystemCXXIncludeDir []string
	SystemFrameworkDir  []string
}

// CompileCommand represents a each command object contains the translation unit’s main file,
// the working directory of the compile run and the actual compile command.
type CompileCommand struct {
	// Directory the working directory of the compilation.
	Directory string `json:"directory"`
	// File the main translation unit source processed by this compilation step.
	File string `json:"file"`
	// Command the compile command executed.
	Command string `json:"command"`
	// Arguments the compile command executed as list of strings.
	Arguments []string `json:"arguments"`
	// Output the name of the output created by this compilation step.
	Output string `json:"output"`
}

// NewCompilationDatabase return the new CompilationDatabase.
func NewCompilationDatabase(root string) *CompilationDatabase {
	return &CompilationDatabase{
		root: root,
	}
}

// Parse parses the project root directory recursively, and cache the compile
// flags to flags map.
func (c *CompilationDatabase) Parse(jsonfile string, pathRange []string) error {
	ch := make(chan *CompilerConfig, 1)
	go func() { ch <- c.DefaultCompilerConfig() }()

	if jsonfile == "" {
		jsonfile = DefaultJSONName
	}
	dir := c.findJSONFile(jsonfile, pathRange)
	if dir == "" {
		return errors.Errorf("couldn't find the %s", jsonfile)
	}

	cErr, cd := clang.FromDirectory(dir)
	if cErr != clang.CompilationDatabase_NoError {
		return errors.WithStack(clang.CompilationDatabase_Error(cErr))
	}
	c.cd = cd
	defer c.cd.Dispose()

	c.CompilerConfig = <-ch

	if err := c.parseFlags(); err != nil {
		return errors.WithStack(err)
	}

	return nil
}

// CompileCommands return the CompileCommand struct based parse result.
func (c *CompilationDatabase) CompileCommands() []*CompileCommand {
	return c.cmds
}

// DefaultCompilerConfig gets the compiler defalut configs.
// The parse target sample:
//  clang -v -x c++ /dev/null -fsyntax-only
//
//  Apple LLVM version 8.1.0 (clang-802.0.27.2)
//  Target: x86_64-apple-darwin16.5.0
//  Thread model: posix
//  InstalledDir: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
//   "/Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.12.0 ...
//  clang -cc1 version 8.1.0 (clang-802.0.27.2) default target x86_64-apple-darwin16.5.0
//  ignoring nonexistent directory "/usr/include/c++/v1"
//  #include "..." search starts here:
//  #include <...> search starts here:
//   /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1
//   /usr/local/include
//   /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.1.0/include
//   /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
//   /usr/include
//   /System/Library/Frameworks (framework directory)
//   /Library/Frameworks (framework directory)
//  End of search list.
func (c *CompilationDatabase) DefaultCompilerConfig() *CompilerConfig {
	cc := "clang" // default is clang
	if envCC := os.Getenv("CC"); envCC != "" {
		cc = envCC
	}
	ccPath, err := exec.LookPath(cc)
	if err != nil {
		log.Fatalf("couldn't find %s", cc)
	}

	// Get C++ include directory together with -x flag
	cmd := exec.Command(ccPath, "-v", "-x", "c++", "/dev/null", "-fsyntax-only")

	var b bytes.Buffer
	cmd.Stdout = &b
	cmd.Stderr = &b
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}

	cfg := new(CompilerConfig)
	var includeSection bool
	scan := bufio.NewScanner(&b)
	for scan.Scan() {
		s := scan.Text()
		if includeSection {
			if strings.HasPrefix(s, "End") {
				includeSection = false
				break
			}
			path := strings.TrimSpace(s)
			l := len(path)
			switch {
			case path[l-6:l-3] == "c++":
				cfg.SystemCXXIncludeDir = append(cfg.SystemCXXIncludeDir, "-I"+path)
			case strings.Contains(s, "framework directory"):
				cfg.SystemFrameworkDir = append(cfg.SystemFrameworkDir, "-F"+strings.TrimSuffix(path, " (framework directory)"))
			default:
				cfg.SystemCIncludeDir = append(cfg.SystemCIncludeDir, "-I"+path)
			}
		}
		switch {
		case strings.HasPrefix(s, "Target"):
			cfg.Target = strings.TrimPrefix(s, "Target: ")
		case strings.HasPrefix(s, "Thread model"):
			cfg.ThreadModel = strings.TrimPrefix(s, "Thread model: ")
		case strings.HasPrefix(s, "InstalledDir"):
			cfg.InstalledDir = strings.TrimPrefix(s, "InstalledDir: ")
		case (strings.Contains(s, cfg.InstalledDir) && strings.Contains(s, "-cc1")):
			flags := strings.Fields(s)
			cfg.DefaultFlag = flags[1 : len(flags)-1]
		case strings.HasPrefix(s, cc):
			sep := strings.Split(s, "version ")
			version := strings.Split(sep[1], " ")[0]
			cfg.Version = version
		case strings.HasPrefix(s, "#include <...>"):
			includeSection = true
		}
	}

	return cfg
}

// findFile finds the filename on pathRange recursively.
func (c *CompilationDatabase) findJSONFile(filename string, pathRange []string) string {
	if pathRange == nil {
		parent := filepath.Dir(c.root)             // parent of projectRoot
		buildDir := filepath.Join(c.root, "build") // projectRoot/build
		outDir := filepath.Join(c.root, "out")     // projectRoot/out
		pathRange = []string{c.root, parent, buildDir, outDir}
	}

	pathCh := make(chan string, 1)
	for _, d := range pathRange {
		go func(d string) {
			if osutil.IsExist(filepath.Join(d, filename)) {
				log.Printf("found filepath: %s", filepath.Join(d, filename))
				pathCh <- d
			}
		}(d)
	}

	return <-pathCh
}

// parseFlags parses the all of project files compile flag.
func (c *CompilationDatabase) parseFlags() error {
	cmds := c.cd.AllCompileCommands()
	ncmds := cmds.Size()
	c.cmds = make([]*CompileCommand, 0, ncmds)

	var wg sync.WaitGroup
	for i := uint32(0); i < ncmds; i++ {
		wg.Add(1)
		i := i

		go func(i uint32) {
			c.mu.Lock()
			defer func() {
				wg.Done()
				c.mu.Unlock()
			}()

			cmd := cmds.Command(i)
			args := c.formatFlag(cmd)
			c.cmds = append(c.cmds, &CompileCommand{
				Directory: cmd.Directory(),
				File:      cmd.Filename(),
				Command:   strings.Join(args, " "),
				Arguments: args,
			})
		}(i)
	}

	wg.Wait()

	return nil
}

// formatFlag formats the compile flag for the libclang TranslationUnit arg.
func (c *CompilationDatabase) formatFlag(cmd clang.CompileCommand) []string {
	n := cmd.NumArgs()
	flags := make([]string, 0, n)

	for i := uint32(0); i < n; i++ {
		dir := cmd.Directory()
		f := cmd.Arg(i)

		switch {
		case strings.HasPrefix(f, "-I"): // <value>: Specified directory to the search path for include files
			includeDir := c.fixArg(strings.TrimPrefix(f, "-I"), dir)
			flags = append(flags, "-I", includeDir)

		case strings.HasPrefix(f, "-F"): // <directory>: Specified directory to the search path for framework include files
			frameworkDir := c.fixArg(strings.TrimPrefix(f, "-F"), dir)
			flags = append(flags, "-F", frameworkDir)

		case f == "-I", // <value>: Specified directory to the search path for include files
			f == "-F",                 // <directory>: Specified directory to the search path for framework include files
			f == "-D",                 // <macroname>=<value>: Adds an implicit #define into the predefines buffer which is read before the source file is preprocessed
			f == "-U",                 // <macroname>: Adds an implicit #undef into the predefines buffer which is read before the source file is preprocessed
			f == "-framework",         // <name>: Tells the linker to search for `name.framework/name' the framework search path
			f == "-x",                 // <language> Treat subsequent input files as having type language.
			f == "-arch",              // <architecture> Specify the architecture to build for.
			f == "-include",           // <file>: Adds an implicit #include into the predefines buffer which is read before the source file is preprocessed
			f == "-isysroot",          // <dir>: Add directory to SYSTEM include search path
			f == "-isystem",           // <directory>: Set the system root directory (usually /)
			f == "-iframework",        // <value>: Add directory to SYSTEM framework search path
			f == "-include-pch",       // <file>: Include precompiled header file
			f == "-isystem-after",     // <directory>: Add directory to end of the SYSTEM include search path
			f == "-idirafter",         // <value>: Add directory to AFTER include search path
			f == "-imacros",           // <file>: Include macros from file before parsing
			f == "-ivfsoverlay",       // <value>: Overlay the virtual filesystem described by file over the real file system
			f == "-iwithprefix",       // <dir>: Set directory to SYSTEM include search path with prefix
			f == "-iwithprefixbefore", // <dir>: Set directory to include search path with prefix
			f == "-iwithsysroot":      // <directory>: Add directory to SYSTEM include search path, absolute paths are relative to -isysroot
			flags = append(flags, f, c.fixArg(cmd.Arg(i+1), dir))

		case strings.HasPrefix(f, "-D"), // <macroname>=<value>: Adds an implicit #define into the predefines buffer which is read before the source file is preprocessed
			strings.HasPrefix(f, "-U"),                     // <macroname>: Adds an implicit #undef into the predefines buffer which is read before the source file is preprocessed
			strings.HasPrefix(f, "-std"),                   // <language>: Specify the language standard
			strings.HasPrefix(f, "-stdlib"),                // <library>: Specify the C++ standard library to use
			strings.HasPrefix(f, "-x"),                     // <language> Treat subsequent input files as having type language.
			strings.HasPrefix(f, "-arch"),                  // <architecture> Specify the architecture to build for.
			strings.HasPrefix(f, "-mmacosx-version-min"),   // <version>: Specify the minimum version supported by your application when building for macOS
			strings.HasPrefix(f, "-miphoneos-version-min"), // <version>: Specify the minimum version supported by your application when building for iPhone OS
			strings.HasPrefix(f, "-include"),               // <file>: Adds an implicit #include into the predefines buffer which is read before the source file is preprocessed
			strings.HasPrefix(f, "-isysroot"),              // <dir>: Add directory to SYSTEM include search path
			strings.HasPrefix(f, "-isystem"),               // <directory>: Set the system root directory (usually /)
			strings.HasPrefix(f, "-iframework"),            // <value>: Add directory to SYSTEM framework search path
			strings.HasPrefix(f, "-include-pch"),           // <file>: Include precompiled header file
			strings.HasPrefix(f, "-isystem-after"),         // <directory>: Add directory to end of the SYSTEM include search path
			strings.HasPrefix(f, "-G"),
			strings.HasPrefix(f, "-T"),
			strings.HasPrefix(f, "-V"),
			strings.HasPrefix(f, "-target"),
			strings.HasPrefix(f, "-Xanalyzer"),
			strings.HasPrefix(f, "-Xassembler"),
			strings.HasPrefix(f, "-Xclang"),
			strings.HasPrefix(f, "-Xlinker"),
			strings.HasPrefix(f, "-Xpreprocessor"),
			strings.HasPrefix(f, "-b"),
			strings.HasPrefix(f, "-gcc-toolchain"),
			strings.HasPrefix(f, "-idirafter"), // <value>: Add directory to AFTER include search path
			strings.HasPrefix(f, "-imacros"),   // <file>: Include macros from file before parsing
			strings.HasPrefix(f, "-imultilib"),
			strings.HasPrefix(f, "-iprefix"),
			strings.HasPrefix(f, "-ivfsoverlay"),       // <value>: Overlay the virtual filesystem described by file over the real file system
			strings.HasPrefix(f, "-iwithprefix"),       // <dir>: Set directory to SYSTEM include search path with prefix
			strings.HasPrefix(f, "-iwithprefixbefore"), // <dir>: Set directory to include search path with prefix
			strings.HasPrefix(f, "-iwithsysroot"):      // <directory>: Add directory to SYSTEM include search path, absolute paths are relative to -isysroot

			flags = append(flags, f)
		}
	}

	return flags
}

// fixArg return the absolube directory path based by buildDir if contains filepath.Separator.
func (c *CompilationDatabase) fixArg(arg, buildDir string) string {
	if filepath.IsAbs(arg) {
		return arg
	}

	for _, d := range []string{buildDir, c.root} {
		if dir := filepath.Join(d, arg); osutil.IsExist(dir) {
			return dir
		}
	}

	return arg
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Zhen, LLC. http://zhen.io. All rights reserved.
 * Use of this source code is governed by the Apache 2.0 license.
 *
 */

package samples

import (
	"testing"
	"log"
	"os"
	"bufio"
	"encoding/binary"
	"compress/gzip"
	"strconv"
	"runtime"
	"github.com/reducedb/encoding"
	"github.com/reducedb/encoding/composition"
	"github.com/reducedb/encoding/bp32"
	"github.com/reducedb/encoding/variablebyte"
)

func TestEncoding(t *testing.T) {
	testEncodingWithFile("ts.txt.gz", t)
	//testEncodingWithFile("srcip.txt.gz", t)
	//testEncodingWithFile("dstip.txt.gz", t)
	testEncodingWithFile("latency.txt.gz", t)
}

func testEncodingWithFile(path string, t *testing.T) {
	data, err := readFileOfIntegers(path)
	if err == nil {
		log.Printf("encoding/TestEncoding: Read %d integers (%d bytes) from %s.\n", len(data), len(data)*4, path)
	} else {
		log.Printf("encoding/TestEncoding: Error opening ts.txt.gz: %v\n", err)
	}

	log.Printf("encoding/TestEncoding: Testing comprssion BP32+VariableByte\n")
	encoding.TestCodec(composition.NewIntegratedComposition(bp32.NewIntegratedBP32(), variablebyte.NewIntegratedVariableByte()), data, []int{len(data)}, t)

	log.Printf("encoding/TestEncoding: Testing comprssion VariableByte\n")
	encoding.TestCodec(variablebyte.NewIntegratedVariableByte(), data, []int{len(data)}, t)

	b := make([]byte, len(data)*4)
	for i := 0; i < len(data); i++ {
		binary.LittleEndian.PutUint32(b[i*4:], data[i])
	}

	encoding.TestGzip(b, t)
	encoding.TestLZW(b, t)
}

func readFileOfIntegers(path string) ([]uint32, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	gunzip, err := gzip.NewReader(file)
	if err != nil {
		log.Printf("encoding/readTimestamps: Error opening gzip reader: %v\n", err)
		return nil, err
	}

	result := make([]uint32, 0, 50000000)
	scanner := bufio.NewScanner(gunzip)
	for scanner.Scan() {
		i, e := strconv.ParseUint(scanner.Text(), 10, 32)
		if e != nil {
			log.Printf("encoding/readTimestamps: Error reading from %s. %v\n", path, e)
		} else {
			result = append(result, uint32(i))
		}
	}

	// Run the garbage collector to get rid of all the strings that's been allocated
	// during the file read
	runtime.GC()

	return result, scanner.Err()
}
<commit_msg>Added tests for both integrated and non-integrated encoders<commit_after>/*
 * Copyright (c) 2013 Zhen, LLC. http://zhen.io. All rights reserved.
 * Use of this source code is governed by the Apache 2.0 license.
 *
 */

package samples

import (
	"testing"
	"log"
	"os"
	"bufio"
	"encoding/binary"
	"compress/gzip"
	"strconv"
	"runtime"
	"github.com/reducedb/encoding"
	"github.com/reducedb/encoding/composition"
	"github.com/reducedb/encoding/bp32"
	"github.com/reducedb/encoding/variablebyte"
)

func TestEncoding(t *testing.T) {
	testEncodingWithFile("ts.txt.gz", t)
	//testEncodingWithFile("srcip.txt.gz", t)
	//testEncodingWithFile("dstip.txt.gz", t)
	testEncodingWithFile("latency.txt.gz", t)
}

func testEncodingWithFile(path string, t *testing.T) {
	data, err := readFileOfIntegers(path)
	if err == nil {
		log.Printf("encoding/testEncodingWithFile: Read %d integers (%d bytes) from %s.\n", len(data), len(data)*4, path)
	} else {
		log.Printf("encoding/testEncodingWithFile: Error opening ts.txt.gz: %v\n", err)
	}

	log.Printf("encoding/testEncodingWithFile: Testing comprssion Integrated BP32+VariableByte\n")
	encoding.TestCodec(composition.NewComposition(bp32.NewIntegratedBP32(), variablebyte.NewIntegratedVariableByte()), data, []int{len(data)}, t)

	log.Printf("encoding/testEncodingWithFile: Testing comprssion Integrated VariableByte\n")
	encoding.TestCodec(variablebyte.NewIntegratedVariableByte(), data, []int{len(data)}, t)

	log.Printf("encoding/testEncodingWithFile: Testing comprssion BP32+VariableByte\n")
	encoding.TestCodec(composition.NewComposition(bp32.NewBP32(), variablebyte.NewVariableByte()), data, []int{len(data)}, t)

	log.Printf("encoding/testEncodingWithFile: Testing comprssion VariableByte\n")
	encoding.TestCodec(variablebyte.NewVariableByte(), data, []int{len(data)}, t)

	b := make([]byte, len(data)*4)
	for i := 0; i < len(data); i++ {
		binary.LittleEndian.PutUint32(b[i*4:], data[i])
	}

	encoding.TestGzip(b, t)
	encoding.TestLZW(b, t)
}

func readFileOfIntegers(path string) ([]uint32, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	gunzip, err := gzip.NewReader(file)
	if err != nil {
		log.Printf("encoding/readTimestamps: Error opening gzip reader: %v\n", err)
		return nil, err
	}

	result := make([]uint32, 0, 50000000)
	scanner := bufio.NewScanner(gunzip)
	for scanner.Scan() {
		i, e := strconv.ParseUint(scanner.Text(), 10, 32)
		if e != nil {
			log.Printf("encoding/readTimestamps: Error reading from %s. %v\n", path, e)
		} else {
			result = append(result, uint32(i))
		}
	}

	// Run the garbage collector to get rid of all the strings that's been allocated
	// during the file read
	runtime.GC()

	return result, scanner.Err()
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package ssystem

import (
	"path/filepath"
	"strings"
	"time"

	"github.com/admpub/log"
	"github.com/webx-top/echo"
)

type System struct {
	Env                     string            `json:"env"` // prod/dev/test
	VhostsfileDir           string            `json:"vhostsfileDir"`
	AllowIP                 []string          `json:"allowIP"`
	SSLAuto                 bool              `json:"sslAuto"`
	SSLEmail                string            `json:"sslEmail"`
	SSLHosts                []string          `json:"sslHosts"`
	SSLCacheDir             string            `json:"sslCacheDir"`
	SSLKeyFile              string            `json:"sslKeyFile"`
	SSLCertFile             string            `json:"sslCertFile"`
	EditableFileExtensions  map[string]string `json:"editableFileExtensions"`
	EditableFileMaxSize     string            `json:"editableFileMaxSize"`
	editableFileMaxBytes    int               `json:"editableFileMaxBytes"`
	PlayableFileExtensions  map[string]string `json:"playableFileExtensions"`
	ErrorPages              map[int]string    `json:"errorPages"`
	CmdTimeout              string            `json:"cmdTimeout"`
	CmdTimeoutDuration      time.Duration     `json:"-"`
	ShowExpirationTime      int64             `json:"showExpirationTime"` //显示过期时间：0为始终显示；大于0为距离剩余到期时间多少秒的时候显示；小于0为不显示
	SessionName             string            `json:"sessionName"`
	SessionEngine           string            `json:"sessionEngine"`
	SessionConfig           echo.H            `json:"sessionConfig"`
	MaxRequestBodySize      string            `json:"maxRequestBodySize"`
	maxRequestBodySizeBytes int
}

func (sys *System) Init() {
	if len(sys.MaxRequestBodySize) == 0 {
		sys.maxRequestBodySizeBytes = 0
		return
	}
	sys.maxRequestBodySizeBytes, _ = ParseBytes(sys.MaxRequestBodySize)
	if sys.editableFileMaxBytes < 1 && len(sys.EditableFileMaxSize) > 0 {
		var err error
		sys.editableFileMaxBytes, err = ParseBytes(sys.EditableFileMaxSize)
		if err != nil {
			log.Error(err.Error())
		}
	}
	sys.CmdTimeoutDuration = ParseTimeDuration(sys.CmdTimeout)
	if sys.CmdTimeoutDuration <= 0 {
		sys.CmdTimeoutDuration = time.Second * 30
	}
	if len(sys.SSLCacheDir) == 0 {
		sys.SSLCacheDir = `data` + echo.FilePathSeparator + `cache` + echo.FilePathSeparator + `autocert`
	}
}

func (sys *System) EditableFileMaxBytes() int {
	return sys.editableFileMaxBytes
}

func (sys *System) MaxRequestBodySizeBytes() int {
	if sys.maxRequestBodySizeBytes <= 0 {
		return sys.maxRequestBodySizeBytes
	}
	return sys.maxRequestBodySizeBytes
}

func (sys *System) Editable(fileName string) (string, bool) {
	if sys.EditableFileExtensions == nil {
		return "", false
	}
	ext := strings.TrimPrefix(filepath.Ext(fileName), `.`)
	ext = strings.ToLower(ext)
	typ, ok := sys.EditableFileExtensions[ext]
	return typ, ok
}

func (sys *System) IsEnv(name string) bool {
	if name == `prod` {
		if len(sys.Env) == 0 {
			return true
		}
	}
	return sys.Env == name
}

func (sys *System) Playable(fileName string) (string, bool) {
	if sys.PlayableFileExtensions == nil {
		sys.PlayableFileExtensions = map[string]string{
			`mp4`:  `video/mp4`,
			`m3u8`: `application/x-mpegURL`,
			//`ts`:   `video/MP2T`,
		}
	}
	ext := strings.TrimPrefix(filepath.Ext(fileName), `.`)
	ext = strings.ToLower(ext)
	typ, ok := sys.PlayableFileExtensions[ext]
	return typ, ok
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package ssystem

import (
	"path/filepath"
	"strings"
	"time"

	"github.com/admpub/log"
	"github.com/webx-top/echo"
)

type System struct {
	Env                     string            `json:"env"` // prod/dev/test
	VhostsfileDir           string            `json:"vhostsfileDir"`
	AllowIP                 []string          `json:"allowIP"`
	SSLAuto                 bool              `json:"sslAuto"`
	SSLEmail                string            `json:"sslEmail"`
	SSLHosts                []string          `json:"sslHosts"`
	SSLCacheDir             string            `json:"sslCacheDir"`
	SSLKeyFile              string            `json:"sslKeyFile"`
	SSLCertFile             string            `json:"sslCertFile"`
	EditableFileExtensions  map[string]string `json:"editableFileExtensions"`
	EditableFileMaxSize     string            `json:"editableFileMaxSize"`
	editableFileMaxBytes    int               `json:"editableFileMaxBytes"`
	PlayableFileExtensions  map[string]string `json:"playableFileExtensions"`
	ErrorPages              map[int]string    `json:"errorPages"`
	CmdTimeout              string            `json:"cmdTimeout"`
	CmdTimeoutDuration      time.Duration     `json:"-"`
	ShowExpirationTime      int64             `json:"showExpirationTime"` //显示过期时间：0为始终显示；大于0为距离剩余到期时间多少秒的时候显示；小于0为不显示
	SessionName             string            `json:"sessionName"`
	SessionEngine           string            `json:"sessionEngine"`
	SessionConfig           echo.H            `json:"sessionConfig"`
	MaxRequestBodySize      string            `json:"maxRequestBodySize"`
	maxRequestBodySizeBytes int
}

func (sys *System) Init() {
	var err error
	if len(sys.MaxRequestBodySize) > 0 {
		sys.maxRequestBodySizeBytes, err = ParseBytes(sys.MaxRequestBodySize)
		if err != nil {
			log.Error(err.Error())
		}
	}
	if sys.editableFileMaxBytes < 1 && len(sys.EditableFileMaxSize) > 0 {
		sys.editableFileMaxBytes, err = ParseBytes(sys.EditableFileMaxSize)
		if err != nil {
			log.Error(err.Error())
		}
	}
	sys.CmdTimeoutDuration = ParseTimeDuration(sys.CmdTimeout)
	if sys.CmdTimeoutDuration <= 0 {
		sys.CmdTimeoutDuration = time.Second * 30
	}
	if len(sys.SSLCacheDir) == 0 {
		sys.SSLCacheDir = `data` + echo.FilePathSeparator + `cache` + echo.FilePathSeparator + `autocert`
	}
}

func (sys *System) EditableFileMaxBytes() int {
	return sys.editableFileMaxBytes
}

func (sys *System) MaxRequestBodySizeBytes() int {
	if sys.maxRequestBodySizeBytes <= 0 {
		return defaultMaxRequestBodyBytes
	}
	return sys.maxRequestBodySizeBytes
}

func (sys *System) Editable(fileName string) (string, bool) {
	if sys.EditableFileExtensions == nil {
		return "", false
	}
	ext := strings.TrimPrefix(filepath.Ext(fileName), `.`)
	ext = strings.ToLower(ext)
	typ, ok := sys.EditableFileExtensions[ext]
	return typ, ok
}

func (sys *System) IsEnv(name string) bool {
	if name == `prod` {
		if len(sys.Env) == 0 {
			return true
		}
	}
	return sys.Env == name
}

func (sys *System) Playable(fileName string) (string, bool) {
	if sys.PlayableFileExtensions == nil {
		sys.PlayableFileExtensions = map[string]string{
			`mp4`:  `video/mp4`,
			`m3u8`: `application/x-mpegURL`,
			//`ts`:   `video/MP2T`,
		}
	}
	ext := strings.TrimPrefix(filepath.Ext(fileName), `.`)
	ext = strings.ToLower(ext)
	typ, ok := sys.PlayableFileExtensions[ext]
	return typ, ok
}
<|endoftext|>


<commit_before>package rpc

import (
	"errors"
	"fmt"
	"io"
	"net"
	"time"

	"golang.org/x/net/context"
)

type testConnectionHandler struct{}

var _ ConnectionHandler = testConnectionHandler{}

func (testConnectionHandler) OnConnect(context.Context, *Connection, GenericClient, *Server) error {
	return nil
}

func (testConnectionHandler) OnConnectError(err error, reconnectThrottleDuration time.Duration) {
}

func (testConnectionHandler) OnDoCommandError(err error, nextTime time.Duration) {
}

func (testConnectionHandler) OnDisconnected(ctx context.Context, status DisconnectStatus) {
}

func (testConnectionHandler) ShouldRetry(name string, err error) bool {
	return false
}

func (testConnectionHandler) ShouldRetryOnConnect(err error) bool {
	return false
}

func (testConnectionHandler) HandlerName() string {
	return "testConnectionHandler"
}

type singleTransport struct {
	t Transporter
}

var _ ConnectionTransport = singleTransport{}

// Dial is an implementation of the ConnectionTransport interface.
func (st singleTransport) Dial(ctx context.Context) (Transporter, error) {
	if !st.t.IsConnected() {
		return nil, io.EOF
	}
	return st.t, nil
}

// IsConnected is an implementation of the ConnectionTransport interface.
func (st singleTransport) IsConnected() bool {
	return st.t.IsConnected()
}

// Finalize is an implementation of the ConnectionTransport interface.
func (st singleTransport) Finalize() {}

// Close is an implementation of the ConnectionTransport interface.
func (st singleTransport) Close() {}

type testStatus struct {
	Code int
}
type testUnwrapper struct{}

func testWrapError(err error) interface{} {
	return &testStatus{}
}

func testLogTags(ctx context.Context) (map[interface{}]string, bool) {
	return nil, false
}

type throttleError struct {
	Err error
}

func (e throttleError) ToStatus() (s testStatus) {
	s.Code = 15
	return
}

func (e throttleError) Error() string {
	return e.Err.Error()
}

type testErrorUnwrapper struct{}

var _ ErrorUnwrapper = testErrorUnwrapper{}

func (eu testErrorUnwrapper) MakeArg() interface{} {
	return &testStatus{}
}

func (eu testErrorUnwrapper) UnwrapError(arg interface{}) (appError error, dispatchError error) {
	s, ok := arg.(*testStatus)
	if !ok {
		return nil, errors.New("Error converting arg to testStatus object")
	}
	if s == nil || s.Code == 0 {
		return nil, nil
	}

	switch s.Code {
	case 15:
		appError = throttleError{errors.New("throttle")}
		break
	default:
		panic("Unknown testing error")
	}
	return appError, nil
}

// TestLogger is an interface for things, like *testing.T, that have a
// Logf function.
type TestLogger interface {
	Logf(format string, args ...interface{})
}

type testLogOutput struct {
	t TestLogger
}

func (t testLogOutput) log(ch string, fmts string, args []interface{}) {
	fmts = fmt.Sprintf("[%s] %s", ch, fmts)
	t.t.Logf(fmts, args...)
}

func (t testLogOutput) Info(fmt string, args ...interface{})    { t.log("I", fmt, args) }
func (t testLogOutput) Error(fmt string, args ...interface{})   { t.log("E", fmt, args) }
func (t testLogOutput) Debug(fmt string, args ...interface{})   { t.log("D", fmt, args) }
func (t testLogOutput) Warning(fmt string, args ...interface{}) { t.log("W", fmt, args) }
func (t testLogOutput) Profile(fmt string, args ...interface{}) { t.log("P", fmt, args) }

// MakeConnectionForTest returns a Connection object, and a net.Conn
// object representing the other end of that connection.
func MakeConnectionForTest(t TestLogger) (net.Conn, *Connection) {
	clientConn, serverConn := net.Pipe()
	transporter := NewTransport(clientConn, nil, testWrapError)
	st := singleTransport{transporter}
	conn := NewConnectionWithTransport(testConnectionHandler{}, st,
		testErrorUnwrapper{}, true, testWrapError,
		testLogOutput{t}, testLogTags)
	return serverConn, conn
}
<commit_msg>Pass correct log factory to NewTransport (#93)<commit_after>package rpc

import (
	"errors"
	"fmt"
	"io"
	"net"
	"time"

	"golang.org/x/net/context"
)

type testConnectionHandler struct{}

var _ ConnectionHandler = testConnectionHandler{}

func (testConnectionHandler) OnConnect(context.Context, *Connection, GenericClient, *Server) error {
	return nil
}

func (testConnectionHandler) OnConnectError(err error, reconnectThrottleDuration time.Duration) {
}

func (testConnectionHandler) OnDoCommandError(err error, nextTime time.Duration) {
}

func (testConnectionHandler) OnDisconnected(ctx context.Context, status DisconnectStatus) {
}

func (testConnectionHandler) ShouldRetry(name string, err error) bool {
	return false
}

func (testConnectionHandler) ShouldRetryOnConnect(err error) bool {
	return false
}

func (testConnectionHandler) HandlerName() string {
	return "testConnectionHandler"
}

type singleTransport struct {
	t Transporter
}

var _ ConnectionTransport = singleTransport{}

// Dial is an implementation of the ConnectionTransport interface.
func (st singleTransport) Dial(ctx context.Context) (Transporter, error) {
	if !st.t.IsConnected() {
		return nil, io.EOF
	}
	return st.t, nil
}

// IsConnected is an implementation of the ConnectionTransport interface.
func (st singleTransport) IsConnected() bool {
	return st.t.IsConnected()
}

// Finalize is an implementation of the ConnectionTransport interface.
func (st singleTransport) Finalize() {}

// Close is an implementation of the ConnectionTransport interface.
func (st singleTransport) Close() {}

type testStatus struct {
	Code int
}
type testUnwrapper struct{}

func testWrapError(err error) interface{} {
	return &testStatus{}
}

func testLogTags(ctx context.Context) (map[interface{}]string, bool) {
	return nil, false
}

type throttleError struct {
	Err error
}

func (e throttleError) ToStatus() (s testStatus) {
	s.Code = 15
	return
}

func (e throttleError) Error() string {
	return e.Err.Error()
}

type testErrorUnwrapper struct{}

var _ ErrorUnwrapper = testErrorUnwrapper{}

func (eu testErrorUnwrapper) MakeArg() interface{} {
	return &testStatus{}
}

func (eu testErrorUnwrapper) UnwrapError(arg interface{}) (appError error, dispatchError error) {
	s, ok := arg.(*testStatus)
	if !ok {
		return nil, errors.New("Error converting arg to testStatus object")
	}
	if s == nil || s.Code == 0 {
		return nil, nil
	}

	switch s.Code {
	case 15:
		appError = throttleError{errors.New("throttle")}
		break
	default:
		panic("Unknown testing error")
	}
	return appError, nil
}

// TestLogger is an interface for things, like *testing.T, that have a
// Logf function.
type TestLogger interface {
	Logf(format string, args ...interface{})
}

type testLogOutput struct {
	t TestLogger
}

func (t testLogOutput) log(ch string, fmts string, args []interface{}) {
	fmts = fmt.Sprintf("[%s] %s", ch, fmts)
	t.t.Logf(fmts, args...)
}

func (t testLogOutput) Info(fmt string, args ...interface{})    { t.log("I", fmt, args) }
func (t testLogOutput) Error(fmt string, args ...interface{})   { t.log("E", fmt, args) }
func (t testLogOutput) Debug(fmt string, args ...interface{})   { t.log("D", fmt, args) }
func (t testLogOutput) Warning(fmt string, args ...interface{}) { t.log("W", fmt, args) }
func (t testLogOutput) Profile(fmt string, args ...interface{}) { t.log("P", fmt, args) }

// MakeConnectionForTest returns a Connection object, and a net.Conn
// object representing the other end of that connection.
func MakeConnectionForTest(t TestLogger) (net.Conn, *Connection) {
	clientConn, serverConn := net.Pipe()
	logOutput := testLogOutput{t}
	logFactory := NewSimpleLogFactory(logOutput, nil)
	transporter := NewTransport(clientConn, logFactory, testWrapError)
	st := singleTransport{transporter}
	conn := NewConnectionWithTransport(testConnectionHandler{}, st,
		testErrorUnwrapper{}, true, testWrapError,
		logOutput, testLogTags)
	return serverConn, conn
}
<|endoftext|>


<commit_before>package db

import (
	"sync"

	"github.com/concourse/atc"
	"github.com/concourse/atc/event"
)

func newSQLDBBuildEventSource(
	buildID int,
	table string,
	conn Conn,
	notifier Notifier,
	from uint,
) *sqldbBuildEventSource {
	wg := new(sync.WaitGroup)

	source := &sqldbBuildEventSource{
		buildID: buildID,
		table:   table,

		conn: conn,

		notifier: notifier,

		events: make(chan atc.Event, 20),
		stop:   make(chan struct{}),
		wg:     wg,
	}

	wg.Add(1)
	go source.collectEvents(from)

	return source
}

type sqldbBuildEventSource struct {
	buildID int
	table   string

	conn     Conn
	notifier Notifier

	events chan atc.Event
	stop   chan struct{}
	err    error
	wg     *sync.WaitGroup
}

func (source *sqldbBuildEventSource) Next() (atc.Event, error) {
	select {
	case e, ok := <-source.events:
		if !ok {
			return nil, source.err
		}

		return e, nil
	}
}

func (source *sqldbBuildEventSource) Close() error {
	select {
	case <-source.stop:
		return nil
	default:
		close(source.stop)
	}

	source.wg.Wait()

	return source.notifier.Close()
}

func (source *sqldbBuildEventSource) collectEvents(cursor uint) {
	defer source.wg.Done()

	var batchSize = cap(source.events)

	for {
		select {
		case <-source.stop:
			source.err = ErrBuildEventStreamClosed
			close(source.events)
			return
		default:
		}

		completed := false

		err := source.conn.QueryRow(`
			SELECT builds.completed
			FROM builds
			WHERE builds.id = $1
		`, source.buildID).Scan(&completed)
		if err != nil {
			source.err = err
			close(source.events)
			return
		}

		rows, err := source.conn.Query(`
			SELECT type, version, payload
			FROM `+source.table+`
			WHERE build_id = $1
			ORDER BY event_id ASC
			OFFSET $2
			LIMIT $3
		`, source.buildID, cursor, batchSize)
		if err != nil {
			source.err = err
			close(source.events)
			return
		}

		rowsReturned := 0

		for rows.Next() {
			rowsReturned++

			cursor++

			var t, v, p string
			err := rows.Scan(&t, &v, &p)
			if err != nil {
				rows.Close()

				source.err = err
				close(source.events)
				return
			}

			ev, err := event.ParseEvent(atc.EventVersion(v), atc.EventType(t), []byte(p))
			if err != nil {
				rows.Close()

				source.err = err
				close(source.events)
				return
			}

			select {
			case source.events <- ev:
			case <-source.stop:
				rows.Close()

				source.err = ErrBuildEventStreamClosed
				close(source.events)
				return
			}
		}

		if rowsReturned == batchSize {
			// still more events
			continue
		}

		if completed {
			source.err = ErrEndOfBuildEventStream
			close(source.events)
			return
		}

		select {
		case <-source.notifier.Notify():
		case <-source.stop:
			source.err = ErrBuildEventStreamClosed
			close(source.events)
			return
		}
	}
}
<commit_msg>100x larger events batch size<commit_after>package db

import (
	"sync"

	"github.com/concourse/atc"
	"github.com/concourse/atc/event"
)

func newSQLDBBuildEventSource(
	buildID int,
	table string,
	conn Conn,
	notifier Notifier,
	from uint,
) *sqldbBuildEventSource {
	wg := new(sync.WaitGroup)

	source := &sqldbBuildEventSource{
		buildID: buildID,
		table:   table,

		conn: conn,

		notifier: notifier,

		events: make(chan atc.Event, 2000),
		stop:   make(chan struct{}),
		wg:     wg,
	}

	wg.Add(1)
	go source.collectEvents(from)

	return source
}

type sqldbBuildEventSource struct {
	buildID int
	table   string

	conn     Conn
	notifier Notifier

	events chan atc.Event
	stop   chan struct{}
	err    error
	wg     *sync.WaitGroup
}

func (source *sqldbBuildEventSource) Next() (atc.Event, error) {
	select {
	case e, ok := <-source.events:
		if !ok {
			return nil, source.err
		}

		return e, nil
	}
}

func (source *sqldbBuildEventSource) Close() error {
	select {
	case <-source.stop:
		return nil
	default:
		close(source.stop)
	}

	source.wg.Wait()

	return source.notifier.Close()
}

func (source *sqldbBuildEventSource) collectEvents(cursor uint) {
	defer source.wg.Done()

	var batchSize = cap(source.events)

	for {
		select {
		case <-source.stop:
			source.err = ErrBuildEventStreamClosed
			close(source.events)
			return
		default:
		}

		completed := false

		err := source.conn.QueryRow(`
			SELECT builds.completed
			FROM builds
			WHERE builds.id = $1
		`, source.buildID).Scan(&completed)
		if err != nil {
			source.err = err
			close(source.events)
			return
		}

		rows, err := source.conn.Query(`
			SELECT type, version, payload
			FROM `+source.table+`
			WHERE build_id = $1
			ORDER BY event_id ASC
			OFFSET $2
			LIMIT $3
		`, source.buildID, cursor, batchSize)
		if err != nil {
			source.err = err
			close(source.events)
			return
		}

		rowsReturned := 0

		for rows.Next() {
			rowsReturned++

			cursor++

			var t, v, p string
			err := rows.Scan(&t, &v, &p)
			if err != nil {
				rows.Close()

				source.err = err
				close(source.events)
				return
			}

			ev, err := event.ParseEvent(atc.EventVersion(v), atc.EventType(t), []byte(p))
			if err != nil {
				rows.Close()

				source.err = err
				close(source.events)
				return
			}

			select {
			case source.events <- ev:
			case <-source.stop:
				rows.Close()

				source.err = ErrBuildEventStreamClosed
				close(source.events)
				return
			}
		}

		if rowsReturned == batchSize {
			// still more events
			continue
		}

		if completed {
			source.err = ErrEndOfBuildEventStream
			close(source.events)
			return
		}

		select {
		case <-source.notifier.Notify():
		case <-source.stop:
			source.err = ErrBuildEventStreamClosed
			close(source.events)
			return
		}
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"

	"github.com/techjanitor/pram-post/config"
	e "github.com/techjanitor/pram-post/errors"
	"github.com/techjanitor/pram-post/models"
	u "github.com/techjanitor/pram-post/utils"
)

// Add a favorite
type favoriteForm struct {
	Image uint `json:"image" binding:"required"`
}

// FavoriteController handles the creation of new threads
func FavoriteController(c *gin.Context) {
	var err error
	var ff favoriteForm

	// get userdata from session middleware
	userdata := c.MustGet("userdata").(u.User)

	err = c.Bind(&ff)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInvalidParam))
		c.Error(err)
		return
	}

	// Set parameters to FavoriteModel
	m := models.FavoriteModel{
		Uid:   userdata.Id,
		Ip:    c.ClientIP(),
		Image: atf.Image,
	}

	// Validate input parameters
	err = m.ValidateInput()
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
		c.Error(err)
		return
	}

	// Check fav, if its there delete it because i dont want this to be too complicated
	err = m.Status()
	if err == e.ErrFavoriteRemoved {
		c.JSON(http.StatusOK, gin.H{"success_message": e.ErrFavoriteRemoved})
		return
	}
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	// Post data
	err = m.Post()
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	c.JSON(http.StatusOK, gin.H{"success_message": "Favorite added"})

	return

}
<commit_msg>add user favorites<commit_after>package controllers

import (
	"github.com/gin-gonic/gin"
	"net/http"

	e "github.com/techjanitor/pram-post/errors"
	"github.com/techjanitor/pram-post/models"
	u "github.com/techjanitor/pram-post/utils"
)

// Add a favorite
type favoriteForm struct {
	Image uint `json:"image" binding:"required"`
}

// FavoriteController handles the creation of new threads
func FavoriteController(c *gin.Context) {
	var err error
	var ff favoriteForm

	// get userdata from session middleware
	userdata := c.MustGet("userdata").(u.User)

	err = c.Bind(&ff)
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInvalidParam))
		c.Error(err)
		return
	}

	// Set parameters to FavoriteModel
	m := models.FavoriteModel{
		Uid:   userdata.Id,
		Ip:    c.ClientIP(),
		Image: ff.Image,
	}

	// Validate input parameters
	err = m.ValidateInput()
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error_message": err.Error()})
		c.Error(err)
		return
	}

	// Check fav, if its there delete it because i dont want this to be too complicated
	err = m.Status()
	if err == e.ErrFavoriteRemoved {
		c.JSON(http.StatusOK, gin.H{"success_message": e.ErrFavoriteRemoved})
		return
	}
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	// Post data
	err = m.Post()
	if err != nil {
		c.JSON(e.ErrorMessage(e.ErrInternalError))
		c.Error(err)
		return
	}

	c.JSON(http.StatusOK, gin.H{"success_message": "Favorite added"})

	return

}
<|endoftext|>


<commit_before><commit_msg>Whitelist common Wolf RPG database contexts<commit_after><|endoftext|>


<commit_before>/*
Package scache provides a simple in-memory cache, supports concurrent access and expirable key/value pairs.

Basic Usage:

	// New a cache with auto clean interval set to 30 seconds
	cache := scache.New(30 * time.Second)

	cache.Set("1", []byte("Hello World"))

	// set key/value and would be expired after 2 seconds
	cache.Set("2", []byte("Hello World 2", 2 * time.Second)

	data, ok := cache.Get("1") // ok stands for the key existing
	cache.Del("1") // will delete the key=1

	// Renew a key to be expired after 15 seconds
	cache.Renew("2", 15 * time.Second)

Expired clear stragety:
1) A key would be cleared when client calls .Get and the key is expired;
2) A goroutine would be running to clear the expired keys and will guarantee the clear would cost less than 50 milliseconds.
*/
package scache

import (
	"log"
	"sync"
	"time"
)

const (
	kDefaultCleanInterval = 30 * time.Second
	kMaxCleanupDuration   = 50 // in milliseconds
)

type Cache struct {
	// will lock the whole object
	sync.RWMutex

	items map[string][]byte
	exps  map[string]int64 // in milliseconds
}

// Save a key/value pair inside cache with or without expiration
func (c *Cache) Set(key string, value []byte, expirations ...time.Duration) {
	expiration := expireSeconds(expirations)
	if expiration == 0 || value == nil {
		c.Del(key)
		return
	}

	c.Lock()
	c.items[key] = value
	if expiration > 0 {
		c.exps[key] = expiration
	}
	c.Unlock()
}

// Get data for a key: data, ok := cache.Get(...), 'ok' would be stand for the key existing.
// If the key is expired at the moment, it would be cleared from the cache.
func (c *Cache) Get(key string) ([]byte, bool) {
	now := time.Now().UnixNano() / 1e6
	var (
		mayExpire bool   = false
		result    []byte = nil
		getit     bool   = false
	)

	c.RLock()
	if exp, ok := c.exps[key]; ok && exp <= now {
		mayExpire = true
	} else {
		result, getit = c.items[key]
	}
	c.RUnlock()

	if mayExpire {
		c.Lock()
		if exp, ok := c.exps[key]; ok && exp <= now {
			delete(c.items, key)
			delete(c.exps, key)
			if IsDebug {
				log.Printf("Lazy clean up for key=%q", key)
			}
		}
		c.Unlock()
	}

	return result, getit
}

// Del key/value from cache
func (c *Cache) Del(key string) {
	c.Lock()
	defer c.Unlock()
	delete(c.items, key)
	delete(c.exps, key)
}

// Renew a key/value for a new expiration time: cache.Renew("test", 0) will delete the key,
// cache.Renew("test") will persist the key/value in the memory
func (c *Cache) Renew(key string, expirations ...time.Duration) {
	expiration := expireSeconds(expirations)
	if expiration == 0 {
		c.Del(key)
		return
	}

	c.Lock()
	defer c.Unlock()
	if _, ok := c.items[key]; ok {
		if expiration > 0 {
			c.exps[key] = expiration
		} else {
			delete(c.exps, key)
		}
	}
}

func expireSeconds(expirations []time.Duration) int64 {
	// all in milliseconds
	var expiration int64 = -1
	if len(expirations) > 0 {
		expiration = int64(expirations[0]) / 1e6
	}
	if expiration == 0 {
		return 0
	} else if expiration > 0 {
		expiration = expiration + time.Now().UnixNano()/1e6
	} else {
		expiration = -1
	}
	return expiration
}

// This will be running inside a goroutine
func (c *Cache) initCleanup(interval time.Duration) {
	cTick := time.Tick(interval)
	for tick := range cTick {
		now := tick.UnixNano() / 1e6
		expireKeys, counter := 0, 0
		c.Lock()
		if len(c.exps) > 0 {
			// since the go map is unordered, the key would be randomized which is great
			for key, expire := range c.exps {
				if expire <= now {
					delete(c.items, key)
					delete(c.exps, key)
					expireKeys++
				}
				counter++
				if counter >= 10000 {
					lasting := time.Now().UnixNano()/1e6 - now
					if lasting > kMaxCleanupDuration {
						break
					}
					counter = 0
				}
			}
			if IsDebug && expireKeys > 0 {
				log.Printf("Cache cleanup, cleaned=%d, size=%d/%d, duration=%s",
					expireKeys, len(c.items), len(c.exps), time.Since(tick))
			}
		}
		c.Unlock()
	}
}

// New a cache object with a user defined clean interval, must be more than 10 seconds.
func New(cleanInterval ...time.Duration) *Cache {
	ci := kDefaultCleanInterval
	if len(cleanInterval) > 0 && cleanInterval[0] >= 10*time.Second {
		ci = cleanInterval[0]
	}

	cache := &Cache{
		items: make(map[string][]byte),
		exps:  make(map[string]int64),
	}
	go func() {
		cache.initCleanup(ci)
	}()
	return cache
}

// Debug switch on the background routine cleanning information, you can ignore this.
var IsDebug bool = false
<commit_msg>Update godoc<commit_after>/*
Package scache provides a simple in-memory cache, supports concurrent access and expirable key/value pairs.

Basic Usage:

	// New a cache with auto clean interval set to 30 seconds
	cache := scache.New(30 * time.Second)

	cache.Set("1", []byte("Hello World"))

	// set key/value and would be expired after 2 seconds
	cache.Set("2", []byte("Hello World 2", 2 * time.Second)

	if data, ok := cache.Get("1"); ok {
		// deal with the data
	}

	cache.Del("1") // will delete the key=1

	// Renew a key to be expired after 15 seconds
	cache.Renew("2", 15 * time.Second)

Expired clear stragety:
1) A key would be cleared when client calls .Get and the key is expired;
2) A goroutine would be running to clear the expired keys and will guarantee the clear would cost less than 50 milliseconds.
*/
package scache

import (
	"log"
	"sync"
	"time"
)

const (
	kDefaultCleanInterval = 30 * time.Second
	kMaxCleanupDuration   = 50 // in milliseconds
)

type Cache struct {
	// will lock the whole object
	sync.RWMutex

	items map[string][]byte
	exps  map[string]int64 // in milliseconds
}

// Save a key/value pair inside cache with or without expiration
func (c *Cache) Set(key string, value []byte, expirations ...time.Duration) {
	expiration := expireSeconds(expirations)
	if expiration == 0 || value == nil {
		c.Del(key)
		return
	}

	c.Lock()
	c.items[key] = value
	if expiration > 0 {
		c.exps[key] = expiration
	}
	c.Unlock()
}

// Get data for a key: data, ok := cache.Get(...), 'ok' would be stand for the key existing.
// If the key is expired at the moment, it would be cleared from the cache.
func (c *Cache) Get(key string) ([]byte, bool) {
	now := time.Now().UnixNano() / 1e6
	var (
		mayExpire bool   = false
		result    []byte = nil
		getit     bool   = false
	)

	c.RLock()
	if exp, ok := c.exps[key]; ok && exp <= now {
		mayExpire = true
	} else {
		result, getit = c.items[key]
	}
	c.RUnlock()

	if mayExpire {
		c.Lock()
		if exp, ok := c.exps[key]; ok && exp <= now {
			delete(c.items, key)
			delete(c.exps, key)
			if IsDebug {
				log.Printf("Lazy clean up for key=%q", key)
			}
		}
		c.Unlock()
	}

	return result, getit
}

// Del key/value from cache
func (c *Cache) Del(key string) {
	c.Lock()
	defer c.Unlock()
	delete(c.items, key)
	delete(c.exps, key)
}

// Renew a key/value for a new expiration time: cache.Renew("test", 0) will delete the key,
// cache.Renew("test") will persist the key/value in the memory
func (c *Cache) Renew(key string, expirations ...time.Duration) {
	expiration := expireSeconds(expirations)
	if expiration == 0 {
		c.Del(key)
		return
	}

	c.Lock()
	defer c.Unlock()
	if _, ok := c.items[key]; ok {
		if expiration > 0 {
			c.exps[key] = expiration
		} else {
			delete(c.exps, key)
		}
	}
}

func expireSeconds(expirations []time.Duration) int64 {
	// all in milliseconds
	var expiration int64 = -1
	if len(expirations) > 0 {
		expiration = int64(expirations[0]) / 1e6
	}
	if expiration == 0 {
		return 0
	} else if expiration > 0 {
		expiration = expiration + time.Now().UnixNano()/1e6
	} else {
		expiration = -1
	}
	return expiration
}

// This will be running inside a goroutine
func (c *Cache) initCleanup(interval time.Duration) {
	cTick := time.Tick(interval)
	for tick := range cTick {
		now := tick.UnixNano() / 1e6
		expireKeys, counter := 0, 0
		c.Lock()
		if len(c.exps) > 0 {
			// since the go map is unordered, the key would be randomized which is great
			for key, expire := range c.exps {
				if expire <= now {
					delete(c.items, key)
					delete(c.exps, key)
					expireKeys++
				}
				counter++
				if counter >= 10000 {
					lasting := time.Now().UnixNano()/1e6 - now
					if lasting > kMaxCleanupDuration {
						break
					}
					counter = 0
				}
			}
			if IsDebug && expireKeys > 0 {
				log.Printf("Cache cleanup, cleaned=%d, size=%d/%d, duration=%s",
					expireKeys, len(c.items), len(c.exps), time.Since(tick))
			}
		}
		c.Unlock()
	}
}

// New a cache object with a user defined clean interval, must be more than 10 seconds.
func New(cleanInterval ...time.Duration) *Cache {
	ci := kDefaultCleanInterval
	if len(cleanInterval) > 0 && cleanInterval[0] >= 10*time.Second {
		ci = cleanInterval[0]
	}

	cache := &Cache{
		items: make(map[string][]byte),
		exps:  make(map[string]int64),
	}
	go func() {
		cache.initCleanup(ci)
	}()
	return cache
}

// Debug switch on the background routine cleanning information, you can ignore this.
var IsDebug bool = false
<|endoftext|>


<commit_before>package tests

import (
	check "gopkg.in/check.v1"

	"github.com/redhatanalytics/oshinko-rest/client/clusters"
	"github.com/redhatanalytics/oshinko-rest/models"
)

func (s *OshinkoRestTestSuite) TestCreateCluster(c *check.C) {
	cname := "TestCluster"
	mcount := int64(1)
	wcount := int64(3)
	cdetails := models.NewCluster{Name: &cname, MasterCount: &mcount, WorkerCount: &wcount}
	params := clusters.NewCreateClusterParams().WithCluster(&cdetails)

	_, err := s.cli.Clusters.CreateCluster(params)

	expectedStatusCode := 501
	observedStatusCode := err.(*clusters.CreateClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestDeleteSingleCluster(c *check.C) {
	params := clusters.NewDeleteSingleClusterParams().WithName("TestCluster")

	_, err := s.cli.Clusters.DeleteSingleCluster(params)

	expectedStatusCode := 501
	observedStatusCode := err.(*clusters.DeleteSingleClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestFindClusters(c *check.C) {
	_, err := s.cli.Clusters.FindClusters(nil)

	expectedStatusCode := 501
	observedStatusCode := err.(*clusters.FindClustersDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestFindSingleCluster(c *check.C) {
	params := clusters.NewFindSingleClusterParams().WithName("TestCluster")

	_, err := s.cli.Clusters.FindSingleCluster(params)

	expectedStatusCode := 501
	observedStatusCode := err.(*clusters.FindSingleClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestUpdateSingleCluster(c *check.C) {
	cname := "TestCluster"
	mcount := int64(1)
	wcount := int64(3)
	cdetails := models.NewCluster{Name: &cname, MasterCount: &mcount, WorkerCount: &wcount}
	params := clusters.NewUpdateSingleClusterParams().WithName("TestCluster").WithCluster(&cdetails)

	_, err := s.cli.Clusters.UpdateSingleCluster(params)

	expectedStatusCode := 501
	observedStatusCode := err.(*clusters.UpdateSingleClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}
<commit_msg>fix cluster tests (#24)<commit_after>package tests

import (
	check "gopkg.in/check.v1"

	"github.com/redhatanalytics/oshinko-rest/client/clusters"
	"github.com/redhatanalytics/oshinko-rest/models"
)

func (s *OshinkoRestTestSuite) TestCreateCluster(c *check.C) {
	cname := "TestCluster"
	mcount := int64(1)
	wcount := int64(3)
	cdetails := models.NewCluster{Name: &cname, MasterCount: &mcount, WorkerCount: &wcount}
	params := clusters.NewCreateClusterParams().WithCluster(&cdetails)

	_, err := s.cli.Clusters.CreateCluster(params)

	expectedStatusCode := 500
	observedStatusCode := err.(*clusters.CreateClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestDeleteSingleCluster(c *check.C) {
	params := clusters.NewDeleteSingleClusterParams().WithName("TestCluster")

	_, err := s.cli.Clusters.DeleteSingleCluster(params)

	expectedStatusCode := 500
	observedStatusCode := err.(*clusters.DeleteSingleClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestFindClusters(c *check.C) {
	_, err := s.cli.Clusters.FindClusters(nil)

	expectedStatusCode := 500
	observedStatusCode := err.(*clusters.FindClustersDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestFindSingleCluster(c *check.C) {
	params := clusters.NewFindSingleClusterParams().WithName("TestCluster")

	_, err := s.cli.Clusters.FindSingleCluster(params)

	expectedStatusCode := 500
	observedStatusCode := err.(*clusters.FindSingleClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}

func (s *OshinkoRestTestSuite) TestUpdateSingleCluster(c *check.C) {
	cname := "TestCluster"
	mcount := int64(1)
	wcount := int64(3)
	cdetails := models.NewCluster{Name: &cname, MasterCount: &mcount, WorkerCount: &wcount}
	params := clusters.NewUpdateSingleClusterParams().WithName("TestCluster").WithCluster(&cdetails)

	_, err := s.cli.Clusters.UpdateSingleCluster(params)

	expectedStatusCode := 500
	observedStatusCode := err.(*clusters.UpdateSingleClusterDefault).Code()

	c.Assert(observedStatusCode, check.Equals, expectedStatusCode)
}
<|endoftext|>


<commit_before>// Go language actor framework.
//
// Stability: 1 - Experimental
// (see: https://github.com/tristanls/stability-index#stability-1---experimental)
//
//    package main
//
//    import "fmt"
//    import "github.com/tristanls/go-actor"
//
//    // create an actor behavior
//    func Print(context actor.Context, msg actor.Message) {
//      for _, param := range msg.Params {
//        fmt.Println(param.(string))
//      }
//      // starting go routines within an actor behavior is *NOT SAFE*
//      // create actors instead, that's what they're for :)
//    }
//
//    // create a new actor configuration
//    func main() {
//      config := actor.Configuration()
//      config.Trace = true // trace message deliveries
//
//      // create a new actor
//      printer := config.Create(Print)
//
//      // send a message to an actor
//      // both ways of creating a message are identical
//      printer <- config.CreateMessage("hello world")
//      printer <- actor.CreateMessage("hello world")
//      printer <- actor.Message{Params: []interface{}{"hello world"}}
//
//      // wait for actor configuration to finish
//      config.Wait()
//    }
package actor

import (
  "fmt"
  "sync"
)

// Become is a function that takes the behavior to handle the next message
type Become func(Behavior)

// Behavior describes how an actor will respond to a message. Actors are created
// with behaviors. Each behavior takes a context and a message to respond to.
// These behaviors are invoked by the library when executing an actor
// configuration.
type Behavior func(Context, Message)

// Message is just a slice of data
type Message struct {
  Params []interface{}
}

// Reference to an actor is a channel that accepts messages
type Reference chan<- Message

// Send is here for completeness, use `Reference <- Message` notation instead
func (reference Reference) Send(msg Message) {
  reference <- msg
}

// ActorConfiguration holds the state of an actor configuration
// To wait for all actors to finish call ActorConfiguration.Wait(), for example:
//    config := actor.Configuration()
//    // send some messages to actors
//    config.Wait() // wait for all actors to finish
// "finish" in this case means that all actors finished execution and there are
// no more messages in flight
type ActorConfiguration struct {
  Trace bool
  waitGroup sync.WaitGroup
}

// Create creates a new actor as part of the actor configuration
func (configuration *ActorConfiguration) Create(behavior Behavior) Reference {
  instrumentedReference := make(chan Message)
  reference := make(chan Message)
  go actorBehavior(configuration, behavior, reference, instrumentedReference)
  go func() {
    buffer := []Message{}
    receiveLoop:
    for {
      if len(buffer) == 0 {
        message, ok := <-instrumentedReference
        if !ok { break }
        configuration.waitGroup.Add(1)
        buffer = append(buffer, message)
      }

      select {
      case message, ok := <-instrumentedReference:
        if !ok { break receiveLoop }
        configuration.waitGroup.Add(1)
        buffer = append(buffer, message)
      case reference <- buffer[0]:
        buffer = buffer[1:]
      }
    }
    for _, message := range buffer {
      reference <- message
    }
  }()
  if configuration.Trace {
    fmt.Println(instrumentedReference, "created")
  }
  return instrumentedReference
}

// CreateMessage is syntactic sugar to create a Message
func (configuration *ActorConfiguration) CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// Wait blocks until the actor configuration finishes executing
func (configuration *ActorConfiguration) Wait() {
  configuration.waitGroup.Wait()
}

// Context is passed to an actor behavior when it is invoked upon a message receive
type Context struct {
	Become Become
	Create func(Behavior) Reference
	Self Reference
}

// Configuration creates a new actor configuration
func Configuration() ActorConfiguration {
  return ActorConfiguration{}
}

// CreateMessage is syntactic sugar to create a Message
func CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// actorBehavior implements execution of actor behaviors, become semantics,
// and making sure that the configuration doesn't exit while messages are still
// in flight.
// func actorBehavior(configuration *ActorConfiguration, behavior Behavior, reference chan Message) {
//   var become func(Behavior)
//   become = func(nextBehavior Behavior) {
//     become = func(Behavior){} // become can only be called once
//     go actorBehavior(configuration, nextBehavior, reference)
//     behavior = nil
//   }
//   for {
//     if behavior != nil {
//       messageCounter := make(chan Message)
//       self := make(chan Message)
//       go func() {
//         for msg := range messageCounter {
//           configuration.countChan <- 1
//           self <- msg
//         }
//         close(self)
//       }()
//       msg := <-reference
//       // fmt.Println("trace: <-", msg.Params)
//       context := Context{Become: become, Create: configuration.Create, Self: messageCounter}
//       behavior(context, msg)
//       close(messageCounter)
//       go func() {
//         for selfMsg := range self {
//           // fmt.Println("trace: self <-", selfMsg.Params)
//           reference <- selfMsg
//         }
//         configuration.countChan <- -1
//       }()
//     } else {
//       return
//     }
//   }
// }
func actorBehavior(configuration *ActorConfiguration, behavior Behavior, reference chan Message, instrumentedReference chan Message) {
  var become func(Behavior)
  become = func(nextBehavior Behavior) {
    become = func(Behavior){} // become can only be called once
    go actorBehavior(configuration, nextBehavior, reference, instrumentedReference)
    behavior = nil
  }
  for {
    if behavior != nil {
      message := <-reference
      if configuration.Trace {
        fmt.Println(instrumentedReference, "<-", message)
      }
      context := Context{Become: become, Create: configuration.Create, Self: instrumentedReference}
      behavior(context, message)
      configuration.waitGroup.Done() // one message has been delivered
      if configuration.Trace {
        fmt.Println(instrumentedReference, "completed", message)
      }
    } else {
      return // this actor behavior has been replace.. go away
    }
  }
}<commit_msg>added queue trace statements<commit_after>// Go language actor framework.
//
// Stability: 1 - Experimental
// (see: https://github.com/tristanls/stability-index#stability-1---experimental)
//
//    package main
//
//    import "fmt"
//    import "github.com/tristanls/go-actor"
//
//    // create an actor behavior
//    func Print(context actor.Context, msg actor.Message) {
//      for _, param := range msg.Params {
//        fmt.Println(param.(string))
//      }
//      // starting go routines within an actor behavior is *NOT SAFE*
//      // create actors instead, that's what they're for :)
//    }
//
//    // create a new actor configuration
//    func main() {
//      config := actor.Configuration()
//      config.Trace = true // trace message deliveries
//
//      // create a new actor
//      printer := config.Create(Print)
//
//      // send a message to an actor
//      // both ways of creating a message are identical
//      printer <- config.CreateMessage("hello world")
//      printer <- actor.CreateMessage("hello world")
//      printer <- actor.Message{Params: []interface{}{"hello world"}}
//
//      // wait for actor configuration to finish
//      config.Wait()
//    }
package actor

import (
  "fmt"
  "sync"
)

// Become is a function that takes the behavior to handle the next message
type Become func(Behavior)

// Behavior describes how an actor will respond to a message. Actors are created
// with behaviors. Each behavior takes a context and a message to respond to.
// These behaviors are invoked by the library when executing an actor
// configuration.
type Behavior func(Context, Message)

// Message is just a slice of data
type Message struct {
  Params []interface{}
}

// Reference to an actor is a channel that accepts messages
type Reference chan<- Message

// Send is here for completeness, use `Reference <- Message` notation instead
func (reference Reference) Send(msg Message) {
  reference <- msg
}

// ActorConfiguration holds the state of an actor configuration
// To wait for all actors to finish call ActorConfiguration.Wait(), for example:
//    config := actor.Configuration()
//    // send some messages to actors
//    config.Wait() // wait for all actors to finish
// "finish" in this case means that all actors finished execution and there are
// no more messages in flight
type ActorConfiguration struct {
  Trace bool
  waitGroup sync.WaitGroup
}

// Create creates a new actor as part of the actor configuration
func (configuration *ActorConfiguration) Create(behavior Behavior) Reference {
  instrumentedReference := make(chan Message)
  reference := make(chan Message)
  go actorBehavior(configuration, behavior, reference, instrumentedReference)
  go func() {
    buffer := []Message{}
    receiveLoop:
    for {
      if len(buffer) == 0 {
        message, ok := <-instrumentedReference
        if !ok { break }
        configuration.waitGroup.Add(1)
        if configuration.Trace {
          fmt.Println(instrumentedReference, "queued", message)
        }
        buffer = append(buffer, message)
      }

      select {
      case message, ok := <-instrumentedReference:
        if !ok { break receiveLoop }
        configuration.waitGroup.Add(1)
        if configuration.Trace {
          fmt.Println(instrumentedReference, "queued", message)
        }
        buffer = append(buffer, message)
      case reference <- buffer[0]:
        buffer = buffer[1:]
      }
    }
    for _, message := range buffer {
      reference <- message
    }
  }()
  if configuration.Trace {
    fmt.Println(instrumentedReference, "created")
  }
  return instrumentedReference
}

// CreateMessage is syntactic sugar to create a Message
func (configuration *ActorConfiguration) CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// Wait blocks until the actor configuration finishes executing
func (configuration *ActorConfiguration) Wait() {
  configuration.waitGroup.Wait()
}

// Context is passed to an actor behavior when it is invoked upon a message receive
type Context struct {
	Become Become
	Create func(Behavior) Reference
	Self Reference
}

// Configuration creates a new actor configuration
func Configuration() ActorConfiguration {
  return ActorConfiguration{}
}

// CreateMessage is syntactic sugar to create a Message
func CreateMessage(params ...interface{}) Message {
  message := Message{Params: make([]interface{}, len(params), len(params))}
  for index, param := range params {
    message.Params[index] = param
  }
  return message
}

// actorBehavior implements execution of actor behaviors, become semantics,
// and making sure that the configuration doesn't exit while messages are still
// in flight.
// func actorBehavior(configuration *ActorConfiguration, behavior Behavior, reference chan Message) {
//   var become func(Behavior)
//   become = func(nextBehavior Behavior) {
//     become = func(Behavior){} // become can only be called once
//     go actorBehavior(configuration, nextBehavior, reference)
//     behavior = nil
//   }
//   for {
//     if behavior != nil {
//       messageCounter := make(chan Message)
//       self := make(chan Message)
//       go func() {
//         for msg := range messageCounter {
//           configuration.countChan <- 1
//           self <- msg
//         }
//         close(self)
//       }()
//       msg := <-reference
//       // fmt.Println("trace: <-", msg.Params)
//       context := Context{Become: become, Create: configuration.Create, Self: messageCounter}
//       behavior(context, msg)
//       close(messageCounter)
//       go func() {
//         for selfMsg := range self {
//           // fmt.Println("trace: self <-", selfMsg.Params)
//           reference <- selfMsg
//         }
//         configuration.countChan <- -1
//       }()
//     } else {
//       return
//     }
//   }
// }
func actorBehavior(configuration *ActorConfiguration, behavior Behavior, reference chan Message, instrumentedReference chan Message) {
  var become func(Behavior)
  become = func(nextBehavior Behavior) {
    become = func(Behavior){} // become can only be called once
    go actorBehavior(configuration, nextBehavior, reference, instrumentedReference)
    behavior = nil
  }
  for {
    if behavior != nil {
      message := <-reference
      if configuration.Trace {
        fmt.Println(instrumentedReference, "<-", message)
      }
      context := Context{Become: become, Create: configuration.Create, Self: instrumentedReference}
      behavior(context, message)
      configuration.waitGroup.Done() // one message has been delivered
      if configuration.Trace {
        fmt.Println(instrumentedReference, "completed", message)
      }
    } else {
      return // this actor behavior has been replaced.. go away
    }
  }
}<|endoftext|>


<commit_before>// $G $D/$F.go

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

package main

type T struct {
  s string;
}

func main() {
  s := "";
  l1 := len(s);
  var t T;
  l2 := len(T.s);  // BUG: cannot take len() of a string field
}

/*
uetli:/home/gri/go/test/bugs gri$ 6g bug057.go
bug057.go:14: syntax error
*/
<commit_msg>really fix it this time<commit_after>// $G $D/$F.go

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

package main

type T struct {
  s string;
}

func main() {
  s := "";
  l1 := len(s);
  var t T;
  l2 := len(t.s);  // BUG: cannot take len() of a string field
}

/*
uetli:/home/gri/go/test/bugs gri$ 6g bug057.go
bug057.go:14: syntax error
*/
<|endoftext|>