<commit_before>package io

import (
	"fmt"
	"strings"

	"gopkg.in/gcfg.v1"
	"github.com/phil-mansfield/gotetra/render/density"
)

const (
	ExampleConvertSnapshotFile = `[ConvertSnapshot]

#######################
# Required Parameters #
#######################

Input = path/to/input/dir
Output = path/to/output/dir

InputFormat = LGadget-2

Cells = 8 # It's unlikely that you will want to change this.

#######################
# Optional Parameters #
#######################

# ProfileFile = pprof.out
# LogFile = log.out

# IteratedInput = path/to/iterated/input_with_single_%d_format
# IteratedOutput = path/to/iterated/input_with_single_%d_format

# IterationStart = 0
# IterationEnd = 100
# Inclusive. If IterationEnd isn't set, folders will be iterated through until
# an invalid one is found.`
	ExampleRenderFile = `[Render]

######################
# RequiredParameters #
######################

# Quantity can be set to one of:
# [ Density | Velocity | DensityGradient | VelocityCurl | VelocityDivergence ].
Quantity = Density

Input  = path/to/input/dir
Output = path/to/output/dir

# Default way of specifying pixel size: the number of pixels which would
# be required to render the entire box. All rendered boxes will have the same
# pixel size.
TotalPixels = 500

# Default way of specifying particle count: the number of particles per
# tetrahedron.
Particles = 25

#####################
# OptionalParamters #
#####################

# Alternative way of specifying pixel size: the number of pixels required to
# render the longest axis of each box. All rendered boxes will, in general,
# not have the same number of pixels.
# ImagePixels = 100

# Alternative way of specifying the particle count. gotetra will (attempt to)
# automatically calculate how many particles are needed so that all projections
# rendered from the resulting grid will have enough particles-per-tetrahedron
# to avoid artifacts.
# AutoParticles = true

# Alternative way of specifying the particle count. Identical to specifying
# AutoParticles, except that projections with a depth below ProjectionDepth
# may contain artifacts.
# ProjectionDepth = 3

# ProfileFile = prof.out
# LogFile = log.out

# SubsampleLength = 2

# Will result in files named pre_*foo*_app.gtet:
# PrependName = pre_
# AppendName  = _app`
	ExampleBoundsFile = `[Box "my_z_slice"]
# A thin slice containing a big halo for the L0125 box.

#######################
# Required Parameters #
#######################

# Location of lowermost corner:
X = 107.9
Y = 79
Z = 78.5

XWidth = 42.14
YWidth = 42.14
ZWidth = 4.21

#######################
# Optional Parameters #
#######################

# Given axis must be one of [ X | Y | Z ].
# ProjectionAxis = Z

[Ball "my_halo"]
Quantity = VelocityCurl

# A bounding box around a sphere whose radius is three times larger than
# the halo's R_vir.

X = 4.602
Y = 100.7
Z = 80.7

Radius = 2.17
RadiusMultiplier = 3 # optional`
	ExampleTetraHist = `[TetraHist]

#######################
# Required Parameters #
#######################

# The property which a distribution is being measured for. Supported properties:
# Density: The mass-weighted density.
Quantity = Density

HistMin = 1e4
HistMax = 1e-2
HistBins = 200

# Must be "Log" or "Linear"
HistScale = Log

Particles = 50

Input = path/to/grid.gtet
Output = path/to/outut/dir/

#######################
# Optional Parameters #
#######################

# SubsampleLength = 1

# Will result in files named pre_*foo*_app.txt:
# PrependName = pre_
# AppendName  = _app

# ProfileFile = prof.out
# LogFile = log.out`
)

type SharedConfig struct {
	// Required
	Input, Output string
	// Optional
	LogFile, ProfileFile string
}

func (con *SharedConfig) ValidInput() bool {
	return con.Input != ""
}
func (con *SharedConfig) ValidOutput() bool {
	return con.Output != ""
}
func (con *SharedConfig) ValidLogFile() bool {
	return con.LogFile != ""
}
func (con *SharedConfig) ValidProfileFile() bool {
	return con.ProfileFile != ""
}

type ConvertSnapshotConfig struct {
	SharedConfig
	// Required
	Cells int
	InputFormat string

	// Optional
	IteratedInput, IteratedOutput string
	IterationStart, IterationEnd int
}

func DefaultConvertSnapshotWrapper() *ConvertSnapshotWrapper {
	con := ConvertSnapshotConfig{}
	con.IterationStart = 0
	con.IterationEnd = -1
	return &ConvertSnapshotWrapper{con}
}

func (con *ConvertSnapshotConfig) ValidCells() bool {
	return con.Cells > 0
}
func (con *ConvertSnapshotConfig) ValidInputFormat() bool {
	return con.InputFormat != ""
}
func (con *ConvertSnapshotConfig) ValidIteratedInput() bool {
	return con.IteratedInput != ""
}
func (con *ConvertSnapshotConfig) ValidIteratedOutput() bool {
	return con.IteratedOutput != ""
}
func (con *ConvertSnapshotConfig) ValidIterationStart() bool {
	return con.IterationStart >= 0
}
func (con *ConvertSnapshotConfig) ValidIterationEnd() bool {
	return con.IterationEnd >= 0
}

type RenderConfig struct {
	SharedConfig

	// Required
	Quantity string
	TotalPixels, Particles int

	// Optional
	AutoParticles bool
	ImagePixels, ProjectionDepth int
	SubsampleLength int
	AppendName, PrependName string
}

func DefaultRenderWrapper() *RenderWrapper {
	rc := RenderConfig{ }
	rc.SubsampleLength = 1
	return &RenderWrapper{rc}
}

func (b *RenderConfig) ValidQuantity() bool {
	var q density.Quantity
	for q = 0; q < density.EndQuantity; q++ {
		if strings.ToLower(q.String()) == strings.ToLower(b.Quantity) {
			return true
		}
	}
	return false
}

func (con *RenderConfig) ValidTotalPixels() bool {
	return con.TotalPixels > 0
}

func (con *RenderConfig) ValidParticles() bool {
	return con.Particles > 0
}
func (con *RenderConfig) ValidSubsampleLength() bool {
	return con.SubsampleLength > 0
}
func (con *RenderConfig) ValidImagePixels() bool {
	return con.ImagePixels > 0
}
func (con *RenderConfig) ValidProjectionDepth() bool {
	return con.ProjectionDepth > 0
}

type ConvertSnapshotWrapper struct {
	ConvertSnapshot ConvertSnapshotConfig
}

type RenderWrapper struct {
	Render RenderConfig
}

type BallConfig struct {
	// Required
	X, Y, Z, Radius float64

	// Optional
	RadiusMultiplier float64
	Name string
}

func (ball *BallConfig) CheckInit(name string, totalWidth float64) error {
	if ball.Radius == 0 {
		return fmt.Errorf(
			"Need to specify a positive radius for Ball '%s'.", name,
		)
	}

	if ball.X >= totalWidth || ball.X < 0 {
		return fmt.Errorf(
			"X center of Ball '%s' must be in range [0, %g), but is %g",
			name, totalWidth, ball.X,
		)
	} else if ball.Y >= totalWidth || ball.Y < 0 {
		return fmt.Errorf(
			"Y center of Ball '%s' must be in range [0, %g), but is %g",
			name, totalWidth, ball.Y,
		)
	} else if ball.Z >= totalWidth || ball.Z < 0 {
		return fmt.Errorf(
			"Z center of Ball '%s' must be in range [0, %g), but is %g",
			name, totalWidth, ball.Z,
		)
	}

	ball.Name = name
	if ball.RadiusMultiplier == 0 {
		ball.RadiusMultiplier = 1
	} else if ball.RadiusMultiplier < 0 {
		return fmt.Errorf(
			"Ball '%s' given a negative radius multiplier, %g.",
			name, ball.RadiusMultiplier,
		)
	}

	return nil
}

func (ball *BallConfig) Box(totalWidth float64) *BoxConfig {
	box := &BoxConfig{}
	rad := ball.Radius * ball.RadiusMultiplier

	box.XWidth, box.YWidth, box.ZWidth = 2 * rad, 2 * rad, 2 * rad

	if ball.X > rad {
		box.X = ball.X - rad
	} else {
		box.X = ball.X - rad + totalWidth
	}

	if ball.Y > rad {
		box.Y = ball.Y - rad
	} else {
		box.Y = ball.Y - rad + totalWidth
	}

	if ball.Z > rad {
		box.Z = ball.Z - rad
	} else {
		box.Z = ball.Z - rad + totalWidth
	}

	box.Name = ball.Name

	return box
}

type BoxConfig struct {
	// Required
	X, Y, Z float64
	XWidth, YWidth, ZWidth float64

	// Optional
	ProjectionAxis string

	// Optional, "undocumented"
	Name string
}

func (box *BoxConfig) CheckInit(name string, totalWidth float64) error {
	if box.XWidth <= 0 {
		return fmt.Errorf(
			"Need to specify a positive XWidth for Box '%s'", name,
		)
	} else if box.YWidth <= 0 {
		return fmt.Errorf(
			"Need to specify a positive YWidth for Box '%s'", name,
		)
	} else if box.ZWidth <= 0 {
		return fmt.Errorf(
			"Need to specify a positive ZWidth for Box '%s'", name,
		)
	}

	if box.X >= totalWidth || box.X < 0 {
		return fmt.Errorf(
			"X origin of Box '%s' must be in range [0, %g), but is %g",
			name, totalWidth, box.X,
		)
	} else if box.Y >= totalWidth || box.Y < 0 {
		return fmt.Errorf(
			"Y origin of Box '%s' must be in range [0, %g), but is %g",
			name, totalWidth, box.Y,
		)
	} else if box.Z >= totalWidth || box.Z < 0 {
		return fmt.Errorf(
			"Z origin of Box '%s' must be in range [0, %g), but is %g",
			name, totalWidth, box.Z,
		)
	}

	tmp := box.ProjectionAxis
	box.ProjectionAxis = strings.Trim(strings.ToUpper(box.ProjectionAxis), " ")
	if box.ProjectionAxis != "" &&
		box.ProjectionAxis != "X" &&
		box.ProjectionAxis != "Y" &&
		box.ProjectionAxis != "Z" {

		return fmt.Errorf(
			"ProjectionAxis of Box '%s' must be one of [X | Y | Z]. '%s' is " +
				"not recognized.", name, tmp,
		)
	}

	box.Name = name

	return nil
}

func (box *BoxConfig) IsProjection() bool { return box.ProjectionAxis != "" }

type BoundsConfig struct {
	Ball map[string]*BallConfig
	Box  map[string]*BoxConfig
}

func ReadBoundsConfig(fname string, totalWidth float64) ([]BoxConfig, error) {
	bc := BoundsConfig{}

	if err := gcfg.ReadFileInto(&bc, fname); err != nil {
		return nil, err
	}

	boxes := []BoxConfig{}
	for name, ball := range bc.Ball {
		if err := ball.CheckInit(name, totalWidth); err != nil {
			return nil, err
		}
		boxes = append(boxes, *ball.Box(totalWidth))
	}
	for name, box := range bc.Box {
		if err := box.CheckInit(name, totalWidth); err != nil {
			return nil, err
		}
		boxes = append(boxes, *box)
	}

	return boxes, nil
}

type TetraHistConfig struct {
	SharedConfig

	Quantity string

	HistMin, HistMax float64
	HistBins int
	HistScale string

	Particles int

	SubsampleLength int

	AppendName, PrependName string
}

func DefaultTetraHistConfig() *TetraHistConfig {
	return &TetraHistConfig{ SubsampleLength: 1 }
}

func (con *TetraHistConfig) QuantityValid() bool {
	switch con.Quantity {
	case "Density":
		return true
	}
	return false
}

func (con *TetraHistConfig) HistMinValid() bool {
	return (con.HistScale == "Linear" ||
		(con.HistScale == "Log" && con.HistMin > 0)) &&
		con.HistMin < con.HistMax
}

func (con *TetraHistConfig) HistMaxValid() bool {
	return (con.HistScale == "Linear" ||
		(con.HistScale == "Log" && con.HistMax > 0)) &&
		con.HistMin < con.HistMax
}

func (con *TetraHistConfig) HistBinsValid() bool {
	return con.HistBins > 0
}

func (con *TetraHistConfig) HistScaleValid() bool {
	return con.HistScale == "Linear" || con.HistScale == "Log"
}

<commit_msg>updated config file comments<commit_after>package io

import (
	"fmt"
	"strings"

	"gopkg.in/gcfg.v1"
	"github.com/phil-mansfield/gotetra/render/density"
)

const (
	ExampleConvertSnapshotFile = `[ConvertSnapshot]

#######################
# Required Parameters #
#######################

# Directory containing the input files.
Input = path/to/input/dir
# Directory which output files will be written to.
Output = path/to/output/dir

# The format of the input files. LGadget-2 is currently the only format
# supported. I think vanilla Gadget-2 files should also work here, but keep
# an eye out for any bugs. Support for other file formats will come in the
# future.
InputFormat = LGadget-2

# Specifies the geometry of the output files. It's unlikely that you will want
# to change this.
Cells = 8

#######################
# Optional Parameters #
#######################

# Output files which are useful for profiling and debugging. Generally, there
# isn't a reason to use these unless something goes wrong.
# ProfileFile = prof.out
# LogFile = log.out

# In some cases, you might want to convert several snapshots at once (e.g.
# sims/output/snapdir001, sims/output/snapdir002, etc.) This can be done with
# IteratedInput and IteratedOutput, which are printf format strings that
# describe your output format (e.g. sims/output/snapdir%03d). You must create
# all the directories which IteratedOutput will use before running, which is
# easiest to do with a Python loop over os.system("mkdir ...") calls.
# IteratedInput = path/to/iterated/input_with_single_%d_format
# IteratedOutput = path/to/iterated/input_with_single_%d_format

# (Inclusive) range for this iteration. If IterationEnd isn't set, folders will
# be iterated through until an invalid one is found.
# IterationStart = 0
# IterationEnd = 100`
	ExampleRenderFile = `[Render]

# Render specified

######################
# RequiredParameters #
######################

# Quantity can be set to one of:
# [ Density ]
# In the future,
# [ Velocity | DensityGradient | VelocityCurl | VelocityDivergence ]
# will be supported.
Quantity = Density

# Directory containing the input files.
Input  = path/to/input/dir
# Directory where the output .gtet files will be written to.
Output = path/to/output/dir

# Default way of specifying pixel size. TotalPixels gives the number of pixels
# across one side of the entire simulation box. This is useful if you are
# rendering many images with various sizes and want a fixed pixel size. You
# do not need to specify this if you use ImagePixels instead.
TotalPixels = 500

# Alternative way of specifying pixel size: the number of pixels required to
# render the longest axis of eaxh boxing box. This is a more natural way to
# specify pixl size if you are only rendering a single image at a time.
# ImagePixels = 100

# Default way of specifying redering resolution. Rendering is performed by
# populating tetrahedra with points, and this variable to controls the number
# of points used. You can think of this as "increasing" the number of particles
# in the simulation by a factor of 6*Particles.
# Expect to rerun the rendering a couple times to get this number right.
Particles = 25

#####################
# OptionalParamters #
#####################

# SubsampleLength allows you to render the image/volume using a subselection of
# the particles in the snapshot files. This is useful for resolution tests and
# some science applications. SubsampleLength must be a power of 2.
# SubsampleLength = 2

# Rendering output files are named after the bounding box. For example, a
# bounding box with the header [Box "halo_1"] will be written to halo_1.gtet.
# You can add leading and ending text to these files names using the following
# two variables (e.g. pre_halo_1_app.gtet). This is useful if you want to
# annotate file names with rendering information, like pixel size or particle
# count.
# PrependName = pre_
# AppendName  = _app

# Output files which are useful for profiling and debugging. Generally, there
# isn't a reason to use these unless something goes wrong.
# ProfileFile = prof.out
# LogFile = log.out

# Use these next two options with caution: I need to do much more testing.

# Alternative way of specifying the particle count. gotetra will (attempt to)
# automatically calculate how many particles are needed so that all projections
# rendered from the resulting grid will have enough particles-per-tetrahedron
# to avoid artifacts.
# AutoParticles = true

# Alternative way of specifying the particle count. Identical to specifying
# AutoParticles, except that projections with a depth below ProjectionDepth
# may contain artifacts.
# ProjectionDepth = 3`
	ExampleBoxFile = `[Box "my_z_slice"]
# This file creates a bounding box which specified a volume or image that will
# be rendered. It is paired with a Render config file. If ProjectionAxis is set,
# an image will be rendered. If ProjectionAxis is not set, a 3D volume will
# be rendered.

# Location of lowermost corner:
X = 107.9
Y = 79
Z = 78.5

# Width of the box in each dimension:
XWidth = 42.14
YWidth = 42.14
ZWidth = 4.21

#######################
# Optional Parameters #
#######################

# Projection axis must be one of [ X | Y | Z ].
# ProjectionAxis = Z`
	ExampleBallFile = `[Ball "my_halo"]
# This file creates a bounding box defined by a sphere with a given radius.
# This is an alternative to using Box to specify rendering regions and is
# convenient when you're looking at individual haloes.

X = 4.602
Y = 100.7
Z = 80.7

Radius = 2.17

#######################
# Optional Parameters #
#######################

# Creates an image instead of a volume rendering when set. It must be one of
# [ X | Y | Z ].
# ProjectionAxis = Z

# Multiplies Radius by a constant.
# RadiusMultiplier = 3`
	ExampleTetraHistFile = `[TetraHist]

#######################
# Required Parameters #
#######################

# The property which a distribution is being measured for. Supported properties:
# Density: The mass-weighted density.
Quantity = Density

HistMin = 1e4
HistMax = 1e-2
HistBins = 200

# Must be "Log" or "Linear"
HistScale = Log

Particles = 50

Input = path/to/grid.gtet
Output = path/to/outut/dir/

#######################
# Optional Parameters #
#######################

# SubsampleLength = 1

# Will result in files named pre_*foo*_app.txt:
# PrependName = pre_
# AppendName  = _app

# ProfileFile = prof.out
# LogFile = log.out`
)

type SharedConfig struct {
	// Required
	Input, Output string
	// Optional
	LogFile, ProfileFile string
}

func (con *SharedConfig) ValidInput() bool {
	return con.Input != ""
}
func (con *SharedConfig) ValidOutput() bool {
	return con.Output != ""
}
func (con *SharedConfig) ValidLogFile() bool {
	return con.LogFile != ""
}
func (con *SharedConfig) ValidProfileFile() bool {
	return con.ProfileFile != ""
}

type ConvertSnapshotConfig struct {
	SharedConfig
	// Required
	Cells int
	InputFormat string

	// Optional
	IteratedInput, IteratedOutput string
	IterationStart, IterationEnd int
}

func DefaultConvertSnapshotWrapper() *ConvertSnapshotWrapper {
	con := ConvertSnapshotConfig{}
	con.IterationStart = 0
	con.IterationEnd = -1
	return &ConvertSnapshotWrapper{con}
}

func (con *ConvertSnapshotConfig) ValidCells() bool {
	return con.Cells > 0
}
func (con *ConvertSnapshotConfig) ValidInputFormat() bool {
	return con.InputFormat != ""
}
func (con *ConvertSnapshotConfig) ValidIteratedInput() bool {
	return con.IteratedInput != ""
}
func (con *ConvertSnapshotConfig) ValidIteratedOutput() bool {
	return con.IteratedOutput != ""
}
func (con *ConvertSnapshotConfig) ValidIterationStart() bool {
	return con.IterationStart >= 0
}
func (con *ConvertSnapshotConfig) ValidIterationEnd() bool {
	return con.IterationEnd >= 0
}

type RenderConfig struct {
	SharedConfig

	// Required
	Quantity string
	TotalPixels, Particles int

	// Optional
	AutoParticles bool
	ImagePixels, ProjectionDepth int
	SubsampleLength int
	AppendName, PrependName string
}

func DefaultRenderWrapper() *RenderWrapper {
	rc := RenderConfig{ }
	rc.SubsampleLength = 1
	return &RenderWrapper{rc}
}

func (b *RenderConfig) ValidQuantity() bool {
	var q density.Quantity
	for q = 0; q < density.EndQuantity; q++ {
		if strings.ToLower(q.String()) == strings.ToLower(b.Quantity) {
			return true
		}
	}
	return false
}

func (con *RenderConfig) ValidTotalPixels() bool {
	return con.TotalPixels > 0
}

func (con *RenderConfig) ValidParticles() bool {
	return con.Particles > 0
}
func (con *RenderConfig) ValidSubsampleLength() bool {
	return con.SubsampleLength > 0
}
func (con *RenderConfig) ValidImagePixels() bool {
	return con.ImagePixels > 0
}
func (con *RenderConfig) ValidProjectionDepth() bool {
	return con.ProjectionDepth > 0
}

type ConvertSnapshotWrapper struct {
	ConvertSnapshot ConvertSnapshotConfig
}

type RenderWrapper struct {
	Render RenderConfig
}

type BallConfig struct {
	// Required
	X, Y, Z, Radius float64

	// Optional
	RadiusMultiplier float64
	Name string
}

func (ball *BallConfig) CheckInit(name string, totalWidth float64) error {
	if ball.Radius == 0 {
		return fmt.Errorf(
			"Need to specify a positive radius for Ball '%s'.", name,
		)
	}

	if ball.X >= totalWidth || ball.X < 0 {
		return fmt.Errorf(
			"X center of Ball '%s' must be in range [0, %g), but is %g",
			name, totalWidth, ball.X,
		)
	} else if ball.Y >= totalWidth || ball.Y < 0 {
		return fmt.Errorf(
			"Y center of Ball '%s' must be in range [0, %g), but is %g",
			name, totalWidth, ball.Y,
		)
	} else if ball.Z >= totalWidth || ball.Z < 0 {
		return fmt.Errorf(
			"Z center of Ball '%s' must be in range [0, %g), but is %g",
			name, totalWidth, ball.Z,
		)
	}

	ball.Name = name
	if ball.RadiusMultiplier == 0 {
		ball.RadiusMultiplier = 1
	} else if ball.RadiusMultiplier < 0 {
		return fmt.Errorf(
			"Ball '%s' given a negative radius multiplier, %g.",
			name, ball.RadiusMultiplier,
		)
	}

	return nil
}

func (ball *BallConfig) Box(totalWidth float64) *BoxConfig {
	box := &BoxConfig{}
	rad := ball.Radius * ball.RadiusMultiplier

	box.XWidth, box.YWidth, box.ZWidth = 2 * rad, 2 * rad, 2 * rad

	if ball.X > rad {
		box.X = ball.X - rad
	} else {
		box.X = ball.X - rad + totalWidth
	}

	if ball.Y > rad {
		box.Y = ball.Y - rad
	} else {
		box.Y = ball.Y - rad + totalWidth
	}

	if ball.Z > rad {
		box.Z = ball.Z - rad
	} else {
		box.Z = ball.Z - rad + totalWidth
	}

	box.Name = ball.Name

	return box
}

type BoxConfig struct {
	// Required
	X, Y, Z float64
	XWidth, YWidth, ZWidth float64

	// Optional
	ProjectionAxis string

	// Optional, "undocumented"
	Name string
}

func (box *BoxConfig) CheckInit(name string, totalWidth float64) error {
	if box.XWidth <= 0 {
		return fmt.Errorf(
			"Need to specify a positive XWidth for Box '%s'", name,
		)
	} else if box.YWidth <= 0 {
		return fmt.Errorf(
			"Need to specify a positive YWidth for Box '%s'", name,
		)
	} else if box.ZWidth <= 0 {
		return fmt.Errorf(
			"Need to specify a positive ZWidth for Box '%s'", name,
		)
	}

	if box.X >= totalWidth || box.X < 0 {
		return fmt.Errorf(
			"X origin of Box '%s' must be in range [0, %g), but is %g",
			name, totalWidth, box.X,
		)
	} else if box.Y >= totalWidth || box.Y < 0 {
		return fmt.Errorf(
			"Y origin of Box '%s' must be in range [0, %g), but is %g",
			name, totalWidth, box.Y,
		)
	} else if box.Z >= totalWidth || box.Z < 0 {
		return fmt.Errorf(
			"Z origin of Box '%s' must be in range [0, %g), but is %g",
			name, totalWidth, box.Z,
		)
	}

	tmp := box.ProjectionAxis
	box.ProjectionAxis = strings.Trim(strings.ToUpper(box.ProjectionAxis), " ")
	if box.ProjectionAxis != "" &&
		box.ProjectionAxis != "X" &&
		box.ProjectionAxis != "Y" &&
		box.ProjectionAxis != "Z" {

		return fmt.Errorf(
			"ProjectionAxis of Box '%s' must be one of [X | Y | Z]. '%s' is " +
				"not recognized.", name, tmp,
		)
	}

	box.Name = name

	return nil
}

func (box *BoxConfig) IsProjection() bool { return box.ProjectionAxis != "" }

type BoundsConfig struct {
	Ball map[string]*BallConfig
	Box  map[string]*BoxConfig
}

func ReadBoundsConfig(fname string, totalWidth float64) ([]BoxConfig, error) {
	bc := BoundsConfig{}

	if err := gcfg.ReadFileInto(&bc, fname); err != nil {
		return nil, err
	}

	boxes := []BoxConfig{}
	for name, ball := range bc.Ball {
		if err := ball.CheckInit(name, totalWidth); err != nil {
			return nil, err
		}
		boxes = append(boxes, *ball.Box(totalWidth))
	}
	for name, box := range bc.Box {
		if err := box.CheckInit(name, totalWidth); err != nil {
			return nil, err
		}
		boxes = append(boxes, *box)
	}

	return boxes, nil
}

type TetraHistConfig struct {
	SharedConfig

	Quantity string

	HistMin, HistMax float64
	HistBins int
	HistScale string

	Particles int

	SubsampleLength int

	AppendName, PrependName string
}

func DefaultTetraHistConfig() *TetraHistConfig {
	return &TetraHistConfig{ SubsampleLength: 1 }
}

func (con *TetraHistConfig) QuantityValid() bool {
	switch con.Quantity {
	case "Density":
		return true
	}
	return false
}

func (con *TetraHistConfig) HistMinValid() bool {
	return (con.HistScale == "Linear" ||
		(con.HistScale == "Log" && con.HistMin > 0)) &&
		con.HistMin < con.HistMax
}

func (con *TetraHistConfig) HistMaxValid() bool {
	return (con.HistScale == "Linear" ||
		(con.HistScale == "Log" && con.HistMax > 0)) &&
		con.HistMin < con.HistMax
}

func (con *TetraHistConfig) HistBinsValid() bool {
	return con.HistBins > 0
}

func (con *TetraHistConfig) HistScaleValid() bool {
	return con.HistScale == "Linear" || con.HistScale == "Log"
}

<|endoftext|>


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

package filesystem

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/go-fswatch"
)

type updateHubCallbacks map[string]func() fswatch.Watcher
type updateHubWatchers map[string]fswatch.Watcher

type watcherRegistry map[string]updateHubWatchers
type callbackRegistry map[string]updateHubCallbacks

func newUpdateHub(logger logger.Logger) *UpdateHub {
	return &UpdateHub{
		logger: logger,

		callbacks: make(callbackRegistry),
		watchers:  make(watcherRegistry),
	}
}

type UpdateHub struct {
	logger logger.Logger

	callbacks callbackRegistry
	watchers  watcherRegistry
}

func (hub *UpdateHub) StartWatching(route route.Route) {

	hub.logger.Debug("Watchers (Folder: %s, File: %s)", fswatch.NumberOfFolderWatchers(), fswatch.NumberOfFileWatchers())

	hub.logger.Debug("Starting callbacks for route %q", route.String())

	for callbackType, callback := range hub.callbacks[routeToKey(route)] {

		if hub.watcherExists(route, callbackType) {
			hub.logger.Debug("Callback %q for route %q is already running", callbackType, route.String())
			continue
		}

		hub.logger.Debug("Starting callback %q for route %q", callbackType, route.String())

		// execute the callback
		watcher := callback()

		if watchers, exists := hub.watchers[routeToKey(route)]; !exists {
			watchers := make(updateHubWatchers)
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		} else {
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		}
	}

	hub.logger.Debug("Watchers (Folder: %s, File: %s)", fswatch.NumberOfFolderWatchers(), fswatch.NumberOfFileWatchers())
}

func (hub *UpdateHub) StopWatching(route route.Route) {

	hub.logger.Debug(fmt.Sprintf("Stopping callbacks for route %q", route.String()))

	watchers, exists := hub.watchers[routeToKey(route)]
	if !exists {
		hub.logger.Debug("There is no running watcher for route %q", route.String())
		return
	}

	callbackTypes := make([]string, 0)
	for callbackType, _ := range watchers {
		callbackTypes = append(callbackTypes, callbackType)
	}

	for _, callbackType := range callbackTypes {
		hub.stopWatcher(route, callbackType)
	}
}

func (hub *UpdateHub) Detach(route route.Route) {
	hub.logger.Debug("Detaching callbacks %q for route %q", route.String())
	hub.StopWatching(route)
	delete(hub.watchers, route.Value())
}

func (hub *UpdateHub) Attach(route route.Route, callbackType string, callback func() fswatch.Watcher) {
	hub.logger.Debug("Attaching callback %q for route %q", callbackType, route.String())

	if callbacks, exists := hub.callbacks[routeToKey(route)]; !exists {

		// create a new callback map
		callbacks := make(updateHubCallbacks)

		// attach the callback
		callbacks[callbackType] = callback

		hub.callbacks[routeToKey(route)] = callbacks
	} else {

		// stop any existing callbacks
		hub.stopWatcher(route, callbackType)

		// attach the callback
		callbacks[callbackType] = callback

	}
}

func (hub *UpdateHub) watcherExists(route route.Route, callbackType string) bool {
	watchers, exists := hub.watchers[routeToKey(route)]
	if !exists {
		return false
	}

	_, watcherExists := watchers[callbackType]
	return watcherExists
}

func (hub *UpdateHub) stopWatcher(route route.Route, callbackType string) {

	hub.logger.Debug(fmt.Sprintf("Stopping callbacks for route %q", route.String()))

	key := routeToKey(route)
	watchers, exists := hub.watchers[key]
	if !exists {
		hub.logger.Debug("There is no running watcher for route %q", route.String())
		return
	}

	if watcher, exists := watchers[callbackType]; exists {
		hub.logger.Debug("Stopping watcher %q for route %q", callbackType, route.String())
		watcher.Stop()
		delete(watchers, callbackType)
	}

	delete(hub.watchers, key)
}

func routeToKey(route route.Route) string {
	return route.Value()
}
<commit_msg>Fixed the issue that not all watchers were stopped on subscribe, but only the first one. The reason was that the stopWatcher method removed all watchers for the given route - this should only be done by the StopWatching method which stops all watchers for a given route.<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package filesystem

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/go-fswatch"
)

type updateHubCallbacks map[string]func() fswatch.Watcher
type updateHubWatchers map[string]fswatch.Watcher

type watcherRegistry map[string]updateHubWatchers
type callbackRegistry map[string]updateHubCallbacks

func newUpdateHub(logger logger.Logger) *UpdateHub {
	return &UpdateHub{
		logger: logger,

		callbacks: make(callbackRegistry),
		watchers:  make(watcherRegistry),
	}
}

type UpdateHub struct {
	logger logger.Logger

	callbacks callbackRegistry
	watchers  watcherRegistry
}

func (hub *UpdateHub) StartWatching(route route.Route) {

	hub.logger.Debug("Watchers (Folder: %s, File: %s)", fswatch.NumberOfFolderWatchers(), fswatch.NumberOfFileWatchers())

	hub.logger.Debug("Starting callbacks for route %q", route.String())

	for callbackType, callback := range hub.callbacks[routeToKey(route)] {

		if hub.watcherExists(route, callbackType) {
			hub.logger.Debug("Callback %q for route %q is already running", callbackType, route.String())
			continue
		}

		hub.logger.Debug("Starting callback %q for route %q", callbackType, route.String())

		// execute the callback
		watcher := callback()

		if watchers, exists := hub.watchers[routeToKey(route)]; !exists {
			watchers := make(updateHubWatchers)
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		} else {
			watchers[callbackType] = watcher
			hub.watchers[routeToKey(route)] = watchers
		}
	}

	hub.logger.Debug("Watchers (Folder: %s, File: %s)", fswatch.NumberOfFolderWatchers(), fswatch.NumberOfFileWatchers())
}

func (hub *UpdateHub) StopWatching(route route.Route) {

	hub.logger.Debug(fmt.Sprintf("Stopping callbacks for route %q", route.String()))

	routeKey := routeToKey(route)
	watchers, exists := hub.watchers[routeKey]
	if !exists {
		hub.logger.Debug("There is no running watcher for route %q", route.String())
		return
	}

	callbackTypes := make([]string, 0)
	for callbackType, _ := range watchers {
		callbackTypes = append(callbackTypes, callbackType)
	}

	for _, callbackType := range callbackTypes {
		hub.stopWatcher(route, callbackType)
	}

	delete(hub.watchers, routeKey)
}

func (hub *UpdateHub) Detach(route route.Route) {
	hub.logger.Debug("Detaching callbacks %q for route %q", route.String())
	hub.StopWatching(route)
}

func (hub *UpdateHub) Attach(route route.Route, callbackType string, callback func() fswatch.Watcher) {
	hub.logger.Debug("Attaching callback %q for route %q", callbackType, route.String())

	if callbacks, exists := hub.callbacks[routeToKey(route)]; !exists {

		// create a new callback map
		callbacks := make(updateHubCallbacks)

		// attach the callback
		callbacks[callbackType] = callback

		hub.callbacks[routeToKey(route)] = callbacks
	} else {

		// stop any existing callbacks
		hub.stopWatcher(route, callbackType)

		// attach the callback
		callbacks[callbackType] = callback

	}
}

func (hub *UpdateHub) watcherExists(route route.Route, callbackType string) bool {
	watchers, exists := hub.watchers[routeToKey(route)]
	if !exists {
		return false
	}

	watcher, watcherExists := watchers[callbackType]
	return watcherExists && watcher.IsRunning()
}

func (hub *UpdateHub) stopWatcher(route route.Route, callbackType string) {

	hub.logger.Debug(fmt.Sprintf("Stopping callbacks for route %q", route.String()))

	key := routeToKey(route)
	watchers, exists := hub.watchers[key]
	if !exists {
		hub.logger.Debug("There is no running watcher for route %q", route.String())
		return
	}

	if watcher, exists := watchers[callbackType]; exists {
		hub.logger.Debug("Stopping watcher %q for route %q", callbackType, route.String())
		watcher.Stop()
		delete(watchers, callbackType)
	}
}

func routeToKey(route route.Route) string {
	return route.Value()
}
<|endoftext|>


<commit_before>// Copyright 2012 Samuel Stauffer. All rights reserved.
// Use of this source code is governed by a 3-clause BSD
// license that can be found in the LICENSE file.

package reporter

import (
	"log"
	"strings"
	"time"

	"github.com/samuel/go-librato"
	"github.com/samuel/go-metrics/metrics"
)

type libratoReporter struct {
	source          string
	lib             *librato.Metrics
	percentiles     []float64
	percentileNames []string
	counterCache    *counterDeltaCache
}

func NewLibratoReporter(registry metrics.Registry, interval time.Duration, username, token, source string, percentiles map[string]float64) *PeriodicReporter {
	per := metrics.DefaultPercentiles
	perNames := metrics.DefaultPercentileNames

	if percentiles != nil {
		per = make([]float64, 0)
		perNames = make([]string, 0)
		for name, p := range percentiles {
			per = append(per, p)
			perNames = append(perNames, name)
		}
	}

	lr := &libratoReporter{
		source:          source,
		lib:             &librato.Metrics{username, token},
		percentiles:     per,
		percentileNames: perNames,
		counterCache:    &counterDeltaCache{},
	}
	return NewPeriodicReporter(registry, interval, true, lr)
}

func (r *libratoReporter) Report(registry metrics.Registry) {
	mets := &librato.MetricsFormat{Source: r.source}
	count := 0

	registry.Do(func(name string, metric interface{}) error {
		count++
		name = strings.Replace(name, "/", ".", -1)
		switch m := metric.(type) {
		case metrics.CounterValue:
			mets.Counters = append(mets.Counters,
				librato.Metric{
					Name:  name,
					Value: float64(m),
				})
		case metrics.GaugeValue:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name,
					Value: float64(m),
				})
		case metrics.Counter:
			mets.Counters = append(mets.Counters,
				librato.Metric{
					Name:  name,
					Value: float64(m.Count()),
				})
		case *metrics.EWMA:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name,
					Value: m.Rate(),
				})
		case *metrics.EWMAGauge:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name,
					Value: m.Mean(),
				})
		case *metrics.Meter:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name + ".1m",
					Value: m.OneMinuteRate(),
				},
				librato.Metric{
					Name:  name + ".5m",
					Value: m.FiveMinuteRate(),
				},
				librato.Metric{
					Name:  name + ".15m",
					Value: m.FifteenMinuteRate(),
				})
		case metrics.Histogram:
			count := m.Count()
			if count > 0 {
				deltaCount := r.counterCache.delta(name+".count", int64(count))
				if deltaCount > 0 {
					deltaSum := r.counterCache.delta(name+".sum", m.Sum())
					mets.Gauges = append(mets.Gauges,
						librato.Gauge{
							Name:  name,
							Count: uint64(deltaCount),
							Sum:   float64(deltaSum),
						})
				}
				percentiles := m.Percentiles(r.percentiles)
				for i, perc := range percentiles {
					mets.Gauges = append(mets.Gauges,
						librato.Metric{
							Name:  name + "." + r.percentileNames[i],
							Value: float64(perc),
						})
				}
			}
		default:
			log.Printf("Unrecognized metric type for %s: %+v", name, m)
		}
		return nil
	})

	if count > 0 {
		if err := r.lib.SendMetrics(mets); err != nil {
			log.Printf("ERR librato.SendMetrics: %+v", err)
		}
	}
}
<commit_msg>Fix reporter<commit_after>// Copyright 2012 Samuel Stauffer. All rights reserved.
// Use of this source code is governed by a 3-clause BSD
// license that can be found in the LICENSE file.

package reporter

import (
	"log"
	"strings"
	"time"

	"github.com/samuel/go-librato/librato"
	"github.com/samuel/go-metrics/metrics"
)

type libratoReporter struct {
	source          string
	lib             *librato.Metrics
	percentiles     []float64
	percentileNames []string
	counterCache    *counterDeltaCache
}

func NewLibratoReporter(registry metrics.Registry, interval time.Duration, username, token, source string, percentiles map[string]float64) *PeriodicReporter {
	per := metrics.DefaultPercentiles
	perNames := metrics.DefaultPercentileNames

	if percentiles != nil {
		per = make([]float64, 0)
		perNames = make([]string, 0)
		for name, p := range percentiles {
			per = append(per, p)
			perNames = append(perNames, name)
		}
	}

	lr := &libratoReporter{
		source:          source,
		lib:             &librato.Metrics{Username: username, Token: token},
		percentiles:     per,
		percentileNames: perNames,
		counterCache:    &counterDeltaCache{},
	}
	return NewPeriodicReporter(registry, interval, true, lr)
}

func (r *libratoReporter) Report(registry metrics.Registry) {
	mets := &librato.MetricsFormat{Source: r.source}
	count := 0

	registry.Do(func(name string, metric interface{}) error {
		count++
		name = strings.Replace(name, "/", ".", -1)
		switch m := metric.(type) {
		case metrics.CounterValue:
			mets.Counters = append(mets.Counters,
				librato.Metric{
					Name:  name,
					Value: float64(m),
				})
		case metrics.GaugeValue:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name,
					Value: float64(m),
				})
		case metrics.Counter:
			mets.Counters = append(mets.Counters,
				librato.Metric{
					Name:  name,
					Value: float64(m.Count()),
				})
		case *metrics.EWMA:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name,
					Value: m.Rate(),
				})
		case *metrics.EWMAGauge:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name,
					Value: m.Mean(),
				})
		case *metrics.Meter:
			mets.Gauges = append(mets.Gauges,
				librato.Metric{
					Name:  name + ".1m",
					Value: m.OneMinuteRate(),
				},
				librato.Metric{
					Name:  name + ".5m",
					Value: m.FiveMinuteRate(),
				},
				librato.Metric{
					Name:  name + ".15m",
					Value: m.FifteenMinuteRate(),
				})
		case metrics.Histogram:
			count := m.Count()
			if count > 0 {
				deltaCount := r.counterCache.delta(name+".count", int64(count))
				if deltaCount > 0 {
					deltaSum := r.counterCache.delta(name+".sum", m.Sum())
					mets.Gauges = append(mets.Gauges,
						librato.Gauge{
							Name:  name,
							Count: uint64(deltaCount),
							Sum:   float64(deltaSum),
						})
				}
				percentiles := m.Percentiles(r.percentiles)
				for i, perc := range percentiles {
					mets.Gauges = append(mets.Gauges,
						librato.Metric{
							Name:  name + "." + r.percentileNames[i],
							Value: float64(perc),
						})
				}
			}
		default:
			log.Printf("Unrecognized metric type for %s: %+v", name, m)
		}
		return nil
	})

	if count > 0 {
		if err := r.lib.SendMetrics(mets); err != nil {
			log.Printf("ERR librato.SendMetrics: %+v", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package transport

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
)

// New returns an http.RoundTripper that will provide the authentication
// or transport level security defined by the provided Config.
func New(config *Config) (http.RoundTripper, error) {
	// Set transport level security
	if config.Transport != nil && (config.HasCA() || config.HasCertAuth() || config.TLS.Insecure) {
		return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
	}

	var (
		rt  http.RoundTripper
		err error
	)

	if config.Transport != nil {
		rt = config.Transport
	} else {
		rt, err = tlsCache.get(config)
		if err != nil {
			return nil, err
		}
	}

	return HTTPWrappersForConfig(config, rt)
}

// TLSConfigFor returns a tls.Config that will provide the transport level security defined
// by the provided Config. Will return nil if no transport level security is requested.
func TLSConfigFor(c *Config) (*tls.Config, error) {
	if !(c.HasCA() || c.HasCertAuth() || c.TLS.Insecure) {
		return nil, nil
	}
	if c.HasCA() && c.TLS.Insecure {
		return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")
	}
	if err := loadTLSFiles(c); err != nil {
		return nil, err
	}

	tlsConfig := &tls.Config{
		// Can't use SSLv4 because of POODLE and BEAST
		// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
		// Can't use TLSv1.1 because of RC4 cipher usage
		MinVersion:         tls.VersionTLS12,
		InsecureSkipVerify: c.TLS.Insecure,
	}

	if c.HasCA() {
		tlsConfig.RootCAs = rootCertPool(c.TLS.CAData)
	}

	if c.HasCertAuth() {
		cert, err := tls.X509KeyPair(c.TLS.CertData, c.TLS.KeyData)
		if err != nil {
			return nil, err
		}
		tlsConfig.Certificates = []tls.Certificate{cert}
	}

	return tlsConfig, nil
}

// loadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
// either populated or were empty to start.
func loadTLSFiles(c *Config) error {
	var err error
	c.TLS.CAData, err = dataFromSliceOrFile(c.TLS.CAData, c.TLS.CAFile)
	if err != nil {
		return err
	}

	c.TLS.CertData, err = dataFromSliceOrFile(c.TLS.CertData, c.TLS.CertFile)
	if err != nil {
		return err
	}

	c.TLS.KeyData, err = dataFromSliceOrFile(c.TLS.KeyData, c.TLS.KeyFile)
	if err != nil {
		return err
	}
	return nil
}

// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
// or an error if an error occurred reading the file
func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
	if len(data) > 0 {
		return data, nil
	}
	if len(file) > 0 {
		fileData, err := ioutil.ReadFile(file)
		if err != nil {
			return []byte{}, err
		}
		return fileData, nil
	}
	return nil, nil
}

// rootCertPool returns nil if caData is empty.  When passed along, this will mean "use system CAs".
// When caData is not empty, it will be the ONLY information used in the CertPool.
func rootCertPool(caData []byte) *x509.CertPool {
	// What we really want is a copy of x509.systemRootsPool, but that isn't exposed.  It's difficult to build (see the go
	// code for a look at the platform specific insanity), so we'll use the fact that RootCAs == nil gives us the system values
	// It doesn't allow trusting either/or, but hopefully that won't be an issue
	if len(caData) == 0 {
		return nil
	}

	// if we have caData, use it
	certPool := x509.NewCertPool()
	certPool.AppendCertsFromPEM(caData)
	return certPool
}
<commit_msg>Minor typo in comment, SSLv3 instead of SSLv4<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package transport

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
)

// New returns an http.RoundTripper that will provide the authentication
// or transport level security defined by the provided Config.
func New(config *Config) (http.RoundTripper, error) {
	// Set transport level security
	if config.Transport != nil && (config.HasCA() || config.HasCertAuth() || config.TLS.Insecure) {
		return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
	}

	var (
		rt  http.RoundTripper
		err error
	)

	if config.Transport != nil {
		rt = config.Transport
	} else {
		rt, err = tlsCache.get(config)
		if err != nil {
			return nil, err
		}
	}

	return HTTPWrappersForConfig(config, rt)
}

// TLSConfigFor returns a tls.Config that will provide the transport level security defined
// by the provided Config. Will return nil if no transport level security is requested.
func TLSConfigFor(c *Config) (*tls.Config, error) {
	if !(c.HasCA() || c.HasCertAuth() || c.TLS.Insecure) {
		return nil, nil
	}
	if c.HasCA() && c.TLS.Insecure {
		return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")
	}
	if err := loadTLSFiles(c); err != nil {
		return nil, err
	}

	tlsConfig := &tls.Config{
		// Can't use SSLv3 because of POODLE and BEAST
		// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
		// Can't use TLSv1.1 because of RC4 cipher usage
		MinVersion:         tls.VersionTLS12,
		InsecureSkipVerify: c.TLS.Insecure,
	}

	if c.HasCA() {
		tlsConfig.RootCAs = rootCertPool(c.TLS.CAData)
	}

	if c.HasCertAuth() {
		cert, err := tls.X509KeyPair(c.TLS.CertData, c.TLS.KeyData)
		if err != nil {
			return nil, err
		}
		tlsConfig.Certificates = []tls.Certificate{cert}
	}

	return tlsConfig, nil
}

// loadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
// either populated or were empty to start.
func loadTLSFiles(c *Config) error {
	var err error
	c.TLS.CAData, err = dataFromSliceOrFile(c.TLS.CAData, c.TLS.CAFile)
	if err != nil {
		return err
	}

	c.TLS.CertData, err = dataFromSliceOrFile(c.TLS.CertData, c.TLS.CertFile)
	if err != nil {
		return err
	}

	c.TLS.KeyData, err = dataFromSliceOrFile(c.TLS.KeyData, c.TLS.KeyFile)
	if err != nil {
		return err
	}
	return nil
}

// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
// or an error if an error occurred reading the file
func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
	if len(data) > 0 {
		return data, nil
	}
	if len(file) > 0 {
		fileData, err := ioutil.ReadFile(file)
		if err != nil {
			return []byte{}, err
		}
		return fileData, nil
	}
	return nil, nil
}

// rootCertPool returns nil if caData is empty.  When passed along, this will mean "use system CAs".
// When caData is not empty, it will be the ONLY information used in the CertPool.
func rootCertPool(caData []byte) *x509.CertPool {
	// What we really want is a copy of x509.systemRootsPool, but that isn't exposed.  It's difficult to build (see the go
	// code for a look at the platform specific insanity), so we'll use the fact that RootCAs == nil gives us the system values
	// It doesn't allow trusting either/or, but hopefully that won't be an issue
	if len(caData) == 0 {
		return nil
	}

	// if we have caData, use it
	certPool := x509.NewCertPool()
	certPool.AppendCertsFromPEM(caData)
	return certPool
}
<|endoftext|>


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

package cbft

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

// A PIndex represents a "physical" index or a index "partition".

const PINDEX_META_FILENAME string = "PINDEX_META"
const pindexPathSuffix string = ".pindex"

type PIndex struct {
	Name             string     `json:"name"`
	UUID             string     `json:"uuid"`
	IndexType        string     `json:"indexType"`
	IndexName        string     `json:"indexName"`
	IndexUUID        string     `json:"indexUUID"`
	IndexParams      string     `json:"indexParams"`
	SourceType       string     `json:"sourceType"`
	SourceName       string     `json:"sourceName"`
	SourceUUID       string     `json:"sourceUUID"`
	SourceParams     string     `json:"sourceParams"`
	SourcePartitions string     `json:"sourcePartitions"`
	Path             string     `json:"-"` // Transient, not persisted.
	Impl             PIndexImpl `json:"-"` // Transient, not persisted.
	Dest             Dest       `json:"-"` // Transient, not persisted.

	sourcePartitionsArr []string // Non-persisted memoization.
}

func (p *PIndex) Close(remove bool) error {
	if p.Dest != nil {
		err := p.Dest.Close()
		if err != nil {
			return err
		}
	}

	if p.Impl != nil {
		err := p.Impl.Close()
		if err != nil {
			return err
		}
	}

	if remove {
		os.RemoveAll(p.Path)
	}

	return nil
}

func NewPIndex(mgr *Manager, name, uuid,
	indexType, indexName, indexUUID, indexParams,
	sourceType, sourceName, sourceUUID, sourceParams, sourcePartitions string,
	path string) (*PIndex, error) {
	var pindex *PIndex

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := NewPIndexImpl(indexType, indexParams, path, restart)
	if err != nil {
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: new indexType: %s, indexParams: %s,"+
			" path: %s, err: %s", indexType, indexParams, path, err)
	}

	pindex = &PIndex{
		Name:             name,
		UUID:             uuid,
		IndexType:        indexType,
		IndexName:        indexName,
		IndexUUID:        indexUUID,
		IndexParams:      indexParams,
		SourceType:       sourceType,
		SourceName:       sourceName,
		SourceUUID:       sourceUUID,
		SourceParams:     sourceParams,
		SourcePartitions: sourcePartitions,
		Path:             path,
		Impl:             impl,
		Dest:             dest,

		sourcePartitionsArr: strings.Split(sourcePartitions, ","),
	}
	buf, err := json.Marshal(pindex)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, err
	}

	err = ioutil.WriteFile(path+string(os.PathSeparator)+PINDEX_META_FILENAME,
		buf, 0600)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: could not save PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	return pindex, nil
}

// NOTE: Path argument must be a directory.
func OpenPIndex(mgr *Manager, path string) (*PIndex, error) {
	buf, err := ioutil.ReadFile(path + string(os.PathSeparator) + PINDEX_META_FILENAME)
	if err != nil {
		return nil, fmt.Errorf("error: could not load PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	pindex := &PIndex{}
	err = json.Unmarshal(buf, pindex)
	if err != nil {
		return nil, fmt.Errorf("error: could not parse pindex json,"+
			" path: %s, err: %v", path, err)
	}

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := OpenPIndexImpl(pindex.IndexType, path, restart)
	if err != nil {
		return nil, fmt.Errorf("error: could not open indexType: %s, path: %s, err: %v",
			pindex.IndexType, path, err)
	}

	pindex.Path = path
	pindex.Impl = impl
	pindex.Dest = dest
	pindex.sourcePartitionsArr = strings.Split(pindex.SourcePartitions, ",")

	return pindex, nil
}

func PIndexPath(dataDir, pindexName string) string {
	// TODO: path security checks / mapping here; ex: "../etc/pswd"
	return dataDir + string(os.PathSeparator) + pindexName + pindexPathSuffix
}

func ParsePIndexPath(dataDir, pindexPath string) (string, bool) {
	if !strings.HasSuffix(pindexPath, pindexPathSuffix) {
		return "", false
	}
	prefix := dataDir + string(os.PathSeparator)
	if !strings.HasPrefix(pindexPath, prefix) {
		return "", false
	}
	pindexName := pindexPath[len(prefix):]
	pindexName = pindexName[0 : len(pindexName)-len(pindexPathSuffix)]
	return pindexName, true
}

// ---------------------------------------------------------

type RemotePlanPIndex struct {
	PlanPIndex *PlanPIndex
	NodeDef    *NodeDef
}

// Returns a non-overlapping, disjoint set (or cut) of PIndexes
// (either local or remote) that cover all the partitons of an index
// so that the caller can perform scatter/gather queries, etc.  Only
// PlanPIndexes on wanted nodes that pass the wantNode filter will be
// returned.
//
// TODO: Perhaps need a tighter check around indexUUID, as the current
// implementation might have a race where old pindexes with a matching
// (but outdated) indexUUID might be chosen.
//
// TODO: This implementation currently always favors the local node's
// pindex, but should it?  Perhaps a remote node is more up-to-date
// than the local pindex?
//
// TODO: We should favor the most up-to-date node rather than
// the first one that we run into here?  But, perhaps the most
// up-to-date node is also the most overloaded?  Or, perhaps
// the planner may be trying to rebalance away the most
// up-to-date node and hitting it with load just makes the
// rebalance take longer?
func (mgr *Manager) CoveringPIndexes(indexName, indexUUID string,
	wantNode func(*PlanPIndexNode) bool) (
	localPIndexes []*PIndex, remotePlanPIndexes []*RemotePlanPIndex, err error) {
	nodeDefs, _, err := CfgGetNodeDefs(mgr.Cfg(), NODE_DEFS_WANTED)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve wanted nodeDefs, err: %v", err)
	}

	// Returns true if the node has the "pindex" tag.
	nodeDoesPIndexes := func(nodeUUID string) (*NodeDef, bool) {
		for _, nodeDef := range nodeDefs.NodeDefs {
			if nodeDef.UUID == nodeUUID {
				if len(nodeDef.Tags) <= 0 {
					return nodeDef, true
				}
				for _, tag := range nodeDef.Tags {
					if tag == "pindex" {
						return nodeDef, true
					}
				}
			}
		}
		return nil, false
	}

	_, allPlanPIndexes, err := mgr.GetPlanPIndexes(false)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve allPlanPIndexes, err: %v", err)
	}

	planPIndexes, exists := allPlanPIndexes[indexName]
	if !exists || len(planPIndexes) <= 0 {
		return nil, nil, fmt.Errorf("no planPIndexes for indexName: %s", indexName)
	}

	localPIndexes = make([]*PIndex, 0)
	remotePlanPIndexes = make([]*RemotePlanPIndex, 0)

	_, pindexes := mgr.CurrentMaps()

	selfUUID := mgr.UUID()
	_, selfDoesPIndexes := nodeDoesPIndexes(selfUUID)

build_alias_loop:
	for _, planPIndex := range planPIndexes {
		// First check whether this local node serves that planPIndex.
		if selfDoesPIndexes &&
			wantNode(planPIndex.Nodes[selfUUID]) {
			localPIndex, exists := pindexes[planPIndex.Name]
			if exists &&
				localPIndex != nil &&
				localPIndex.Name == planPIndex.Name &&
				localPIndex.IndexName == indexName &&
				(indexUUID == "" || localPIndex.IndexUUID == indexUUID) {
				localPIndexes = append(localPIndexes, localPIndex)
				continue build_alias_loop
			}
		}

		// Otherwise, look for a remote node that serves that planPIndex.
		for nodeUUID, planPIndexNode := range planPIndex.Nodes {
			if nodeUUID != selfUUID {
				nodeDef, ok := nodeDoesPIndexes(nodeUUID)
				if ok && wantNode(planPIndexNode) {
					remotePlanPIndexes = append(remotePlanPIndexes, &RemotePlanPIndex{
						PlanPIndex: planPIndex,
						NodeDef:    nodeDef,
					})
					continue build_alias_loop
				}
			}
		}

		return nil, nil, fmt.Errorf("no node covers planPIndex: %#v", planPIndex)
	}

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

package cbft

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

// A PIndex represents a "physical" index or a index "partition".

const PINDEX_META_FILENAME string = "PINDEX_META"
const pindexPathSuffix string = ".pindex"

type PIndex struct {
	Name             string     `json:"name"`
	UUID             string     `json:"uuid"`
	IndexType        string     `json:"indexType"`
	IndexName        string     `json:"indexName"`
	IndexUUID        string     `json:"indexUUID"`
	IndexParams      string     `json:"indexParams"`
	SourceType       string     `json:"sourceType"`
	SourceName       string     `json:"sourceName"`
	SourceUUID       string     `json:"sourceUUID"`
	SourceParams     string     `json:"sourceParams"`
	SourcePartitions string     `json:"sourcePartitions"`
	Path             string     `json:"-"` // Transient, not persisted.
	Impl             PIndexImpl `json:"-"` // Transient, not persisted.
	Dest             Dest       `json:"-"` // Transient, not persisted.

	sourcePartitionsArr []string // Non-persisted memoization.
}

func (p *PIndex) Close(remove bool) error {
	if p.Dest != nil {
		err := p.Dest.Close()
		if err != nil {
			return err
		}
	}

	if p.Impl != nil {
		err := p.Impl.Close()
		if err != nil {
			return err
		}
	}

	if remove {
		os.RemoveAll(p.Path)
	}

	return nil
}

func NewPIndex(mgr *Manager, name, uuid,
	indexType, indexName, indexUUID, indexParams,
	sourceType, sourceName, sourceUUID, sourceParams, sourcePartitions string,
	path string) (*PIndex, error) {
	var pindex *PIndex

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := NewPIndexImpl(indexType, indexParams, path, restart)
	if err != nil {
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: new indexType: %s, indexParams: %s,"+
			" path: %s, err: %s", indexType, indexParams, path, err)
	}

	pindex = &PIndex{
		Name:             name,
		UUID:             uuid,
		IndexType:        indexType,
		IndexName:        indexName,
		IndexUUID:        indexUUID,
		IndexParams:      indexParams,
		SourceType:       sourceType,
		SourceName:       sourceName,
		SourceUUID:       sourceUUID,
		SourceParams:     sourceParams,
		SourcePartitions: sourcePartitions,
		Path:             path,
		Impl:             impl,
		Dest:             dest,

		sourcePartitionsArr: strings.Split(sourcePartitions, ","),
	}
	buf, err := json.Marshal(pindex)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, err
	}

	err = ioutil.WriteFile(path+string(os.PathSeparator)+PINDEX_META_FILENAME,
		buf, 0600)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: could not save PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	return pindex, nil
}

// NOTE: Path argument must be a directory.
func OpenPIndex(mgr *Manager, path string) (*PIndex, error) {
	buf, err := ioutil.ReadFile(path + string(os.PathSeparator) + PINDEX_META_FILENAME)
	if err != nil {
		return nil, fmt.Errorf("error: could not load PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	pindex := &PIndex{}
	err = json.Unmarshal(buf, pindex)
	if err != nil {
		return nil, fmt.Errorf("error: could not parse pindex json,"+
			" path: %s, err: %v", path, err)
	}

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := OpenPIndexImpl(pindex.IndexType, path, restart)
	if err != nil {
		return nil, fmt.Errorf("error: could not open indexType: %s, path: %s, err: %v",
			pindex.IndexType, path, err)
	}

	pindex.Path = path
	pindex.Impl = impl
	pindex.Dest = dest
	pindex.sourcePartitionsArr = strings.Split(pindex.SourcePartitions, ",")

	return pindex, nil
}

func PIndexPath(dataDir, pindexName string) string {
	// TODO: path security checks / mapping here; ex: "../etc/pswd"
	return dataDir + string(os.PathSeparator) + pindexName + pindexPathSuffix
}

func ParsePIndexPath(dataDir, pindexPath string) (string, bool) {
	if !strings.HasSuffix(pindexPath, pindexPathSuffix) {
		return "", false
	}
	prefix := dataDir + string(os.PathSeparator)
	if !strings.HasPrefix(pindexPath, prefix) {
		return "", false
	}
	pindexName := pindexPath[len(prefix):]
	pindexName = pindexName[0 : len(pindexName)-len(pindexPathSuffix)]
	return pindexName, true
}

// ---------------------------------------------------------

type RemotePlanPIndex struct {
	PlanPIndex *PlanPIndex
	NodeDef    *NodeDef
}

// Returns a non-overlapping, disjoint set (or cut) of PIndexes
// (either local or remote) that cover all the partitons of an index
// so that the caller can perform scatter/gather queries, etc.  Only
// PlanPIndexes on wanted nodes that pass the wantNode filter will be
// returned.
//
// TODO: Perhaps need a tighter check around indexUUID, as the current
// implementation might have a race where old pindexes with a matching
// (but outdated) indexUUID might be chosen.
//
// TODO: This implementation currently always favors the local node's
// pindex, but should it?  Perhaps a remote node is more up-to-date
// than the local pindex?
//
// TODO: We should favor the most up-to-date node rather than
// the first one that we run into here?  But, perhaps the most
// up-to-date node is also the most overloaded?  Or, perhaps
// the planner may be trying to rebalance away the most
// up-to-date node and hitting it with load just makes the
// rebalance take longer?
func (mgr *Manager) CoveringPIndexes(indexName, indexUUID string,
	wantNode func(*PlanPIndexNode) bool) (
	localPIndexes []*PIndex, remotePlanPIndexes []*RemotePlanPIndex, err error) {
	nodeDefs, _, err := CfgGetNodeDefs(mgr.Cfg(), NODE_DEFS_WANTED)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve wanted nodeDefs, err: %v", err)
	}

	// Returns true if the node has the "pindex" tag.
	nodeDoesPIndexes := func(nodeUUID string) (*NodeDef, bool) {
		for _, nodeDef := range nodeDefs.NodeDefs {
			if nodeDef.UUID == nodeUUID {
				if len(nodeDef.Tags) <= 0 {
					return nodeDef, true
				}
				for _, tag := range nodeDef.Tags {
					if tag == "pindex" {
						return nodeDef, true
					}
				}
			}
		}
		return nil, false
	}

	_, allPlanPIndexes, err := mgr.GetPlanPIndexes(false)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve allPlanPIndexes, err: %v", err)
	}

	planPIndexes, exists := allPlanPIndexes[indexName]
	if !exists || len(planPIndexes) <= 0 {
		return nil, nil, fmt.Errorf("no planPIndexes for indexName: %s", indexName)
	}

	localPIndexes = make([]*PIndex, 0)
	remotePlanPIndexes = make([]*RemotePlanPIndex, 0)

	_, pindexes := mgr.CurrentMaps()

	selfUUID := mgr.UUID()
	_, selfDoesPIndexes := nodeDoesPIndexes(selfUUID)

build_loop:
	for _, planPIndex := range planPIndexes {
		// First check whether this local node serves that planPIndex.
		if selfDoesPIndexes &&
			wantNode(planPIndex.Nodes[selfUUID]) {
			localPIndex, exists := pindexes[planPIndex.Name]
			if exists &&
				localPIndex != nil &&
				localPIndex.Name == planPIndex.Name &&
				localPIndex.IndexName == indexName &&
				(indexUUID == "" || localPIndex.IndexUUID == indexUUID) {
				localPIndexes = append(localPIndexes, localPIndex)
				continue build_loop
			}
		}

		// Otherwise, look for a remote node that serves that planPIndex.
		for nodeUUID, planPIndexNode := range planPIndex.Nodes {
			if nodeUUID != selfUUID {
				nodeDef, ok := nodeDoesPIndexes(nodeUUID)
				if ok && wantNode(planPIndexNode) {
					remotePlanPIndexes = append(remotePlanPIndexes, &RemotePlanPIndex{
						PlanPIndex: planPIndex,
						NodeDef:    nodeDef,
					})
					continue build_loop
				}
			}
		}

		return nil, nil, fmt.Errorf("no node covers planPIndex: %#v", planPIndex)
	}

	return localPIndexes, remotePlanPIndexes, nil
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "strings"
    "github.com/glendc/cgreader"
)

func main() {
    cgreader.RunAndValidateManualPrograms(
        []string{
            "../../input/stock_exchange_losses_1.txt",
            "../../input/stock_exchange_losses_2.txt",
            "../../input/stock_exchange_losses_3.txt",
            "../../input/stock_exchange_losses_4.txt",
            "../../input/stock_exchange_losses_5.txt"},
        []string{
            "../../output/stock_exchange_losses_1.txt",
            "../../output/stock_exchange_losses_2.txt",
            "../../output/stock_exchange_losses_3.txt",
            "../../output/stock_exchange_losses_4.txt",
            "../../output/stock_exchange_losses_5.txt"},
        true,
        func(input <-chan string, output chan string) {
            var n int
            fmt.Sscanf(<-input, "%d", &n)

            s := strings.Split(<-input, " ")
            stocks := make([]int, n)
            for i := range s {
                fmt.Sscanf(s[i], "%d", &stocks[i])
            }

            var difference int
            for i := 0; i < len(stocks) - 1; i++ {
                for u := i+1; u < len(stocks); u++ {
                    if i != u {
                        if d := stocks[u] - stocks[i]; d < difference {
                            difference = d
                        }
                    }
                }
            }

            output <- fmt.Sprintf("%d", difference)
        })
}
<commit_msg>stock exchange go solution improved<commit_after>package main

import (
	"fmt"
	"github.com/glendc/cgreader"
	"strings"
)

func main() {
	cgreader.RunAndValidateManualPrograms(
		[]string{
			"../../input/stock_exchange_losses_1.txt",
			"../../input/stock_exchange_losses_2.txt",
			"../../input/stock_exchange_losses_3.txt",
			"../../input/stock_exchange_losses_4.txt",
			"../../input/stock_exchange_losses_5.txt"},
		[]string{
			"../../output/stock_exchange_losses_1.txt",
			"../../output/stock_exchange_losses_2.txt",
			"../../output/stock_exchange_losses_3.txt",
			"../../output/stock_exchange_losses_4.txt",
			"../../output/stock_exchange_losses_5.txt"},
		true,
		func(input <-chan string, output chan string) {
			var ml, cm, v int64 = 0, 0, 0
			var n, i int
			fmt.Sscanf(<-input, "%d", &n)

			if n == 0 {
				output <- fmt.Sprint("0")
				return
			}

			stocks := strings.Split(<-input, " ")
			fmt.Sscanf(stocks[i], "%d", &v)
			cm = v

			for i = 1; i < n; i++ {
				fmt.Sscanf(stocks[i], "%d", &v)

				if d := v - cm; d < ml {
					ml = d
				}

				if v > cm {
					cm = v
				}
			}

			output <- fmt.Sprintf("%d", ml)
		})
}
<|endoftext|>


<commit_before>// Package pkcs11 is a thin wrapper around the PKCS#11 crypto library.
package pkcs11

/*
#cgo LDFLAGS: -lltdl
#define CK_PTR *
#ifndef NULL_PTR
#define NULL_PTR 0
#endif
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)

#include <stdlib.h>
#include <stdio.h>
#include <ltdl.h>
#include <unistd.h>
#include "pkcs11.h"

struct ctx {
        lt_dlhandle handle;
        CK_FUNCTION_LIST_PTR sym;
};

// New initializes a ctx and fills the symbol table.
struct ctx * New(const char *module) {
        if (lt_dlinit() != 0) {
                return NULL;
        }
        CK_C_GetFunctionList list;
        struct ctx *c=  calloc(1, sizeof(struct ctx));
        c->handle = lt_dlopen(module);
        if (c->handle == NULL) {
                free(c);
                return NULL;
        }
        list = (CK_C_GetFunctionList) lt_dlsym(c->handle, "C_GetFunctionList");
        if (list == NULL) {
                free(c);
                return NULL;
        }
        list(&c->sym);
        return c;
}

// Destroy cleans up a ctx.
void Destroy(struct ctx *c) {
        if (!c) {
                return;
        }
        if (c->handle == NULL) {
                return;
        }
        if (lt_dlclose(c->handle) < 0) {
                return;
        }
        lt_dlexit();
        free(c);
}

CK_RV Initialize(struct ctx* c, CK_VOID_PTR initArgs) {
	return c->sym->C_Initialize(initArgs);
}

CK_RV Finalize(struct ctx* c) {
	return c->sym->C_Finalize(NULL_PTR);
}

CK_RV GetSlotList(struct ctx* c, CK_BBOOL tokenPresent, CK_ULONG_PTR *slotList, CK_ULONG_PTR ulCount) {
	CK_RV e = c->sym->C_GetSlotList(tokenPresent, NULL_PTR, ulCount);
	if (e != CKR_OK) {
		return e;
	}
	*slotList = calloc(1, sizeof(CK_SLOT_ID) * *ulCount);
	e = c->sym->C_GetSlotList(tokenPresent, *slotList, ulCount);
	return e;
}

CK_RV OpenSession(struct ctx* c, CK_ULONG slotID, CK_ULONG flags, CK_SESSION_HANDLE_PTR session) {
	CK_RV e = c->sym->C_OpenSession((CK_SLOT_ID)slotID, (CK_FLAGS)flags, NULL_PTR, NULL_PTR, session);
	return e;
}

CK_RV CloseSession(struct ctx *c, CK_SESSION_HANDLE session) {
	CK_RV e = c->sym->C_CloseSession(session);
	return e;
}

CK_RV CloseAllSessions(struct ctx *c, CK_ULONG slotID) {
	CK_RV e = c->sym->C_CloseAllSessions(slotID);
	return e;
}

CK_RV Login(struct ctx* c, CK_SESSION_HANDLE session, CK_USER_TYPE userType, char* pin, CK_ULONG pinLen) {
	CK_RV e = c->sym->C_Login(session, userType, (CK_UTF8CHAR_PTR)pin, pinLen);
	return e;
}

CK_RV Logout(struct ctx *c, CK_SESSION_HANDLE session) {
	CK_RV e = c->sym->C_Logout(session);
	return e;
}

CK_RV DigestInit(struct ctx *c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism) {
	CK_RV e = c->sym->C_DigestInit(session, mechanism);
	return e;
}

CK_RV Digest(struct ctx *c, CK_SESSION_HANDLE session, CK_BYTE_PTR message, CK_ULONG mlen, CK_BYTE_PTR *hash, CK_ULONG_PTR hashlen) {
        CK_RV rv = c->sym->C_Digest(session, message, mlen, NULL, hashlen);
        if (rv != CKR_OK) {
                return rv;
        }
        *hash = malloc(*hashlen * sizeof(CK_BYTE));
	if (*hash == NULL) {
		return CKR_HOST_MEMORY;
	}
        rv = c->sym->C_Digest(session, message, mlen, *hash, hashlen);
	return rv;
}

CK_RV GenerateKey(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
	CK_ATTRIBUTE_PTR temp, CK_ULONG tempCount, CK_OBJECT_HANDLE_PTR key) {
	CK_RV e = c->sym->C_GenerateKey(session, mechanism, temp, tempCount, key);
	return e;
}

CK_RV GenerateKeyPair(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
	CK_ATTRIBUTE_PTR pub, CK_ULONG pubCount, CK_ATTRIBUTE_PTR priv, CK_ULONG privCount,
	CK_OBJECT_HANDLE_PTR pubkey, CK_OBJECT_HANDLE_PTR privkey) {
	CK_RV e = c->sym->C_GenerateKeyPair(session, mechanism, pub, pubCount, priv, privCount,
					pubkey, privkey);
	return e;
}

CK_RV SignInit(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
	CK_RV e = c->sym->C_SignInit(session, mechanism, key);
	return e;
}

CK_RV Sign(struct ctx *c, CK_SESSION_HANDLE session, CK_BYTE_PTR message, CK_ULONG mlen, CK_BYTE_PTR *sig, CK_ULONG_PTR siglen) {
        CK_RV rv = c->sym->C_Sign(session, message, mlen, NULL, siglen);
        if (rv != CKR_OK) {
                return rv;
        }
        *sig = malloc(*siglen * sizeof(CK_BYTE));
	if (*sig == NULL) {
		return CKR_HOST_MEMORY;
	}
        rv = c->sym->C_Sign(session, message, mlen, *sig, siglen);
	return rv;
}

CK_RV EncryptInit(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
	CK_RV e = c->sym->C_EncryptInit(session, mechanism, key);
	return e;
}

CK_RV Encrypt(struct ctx *c, CK_SESSION_HANDLE session, CK_BYTE_PTR message, CK_ULONG mlen, CK_BYTE_PTR *enc, CK_ULONG_PTR enclen) {
        CK_RV rv = c->sym->C_Encrypt(session, message, mlen, NULL, enclen);
        if (rv != CKR_OK) {
                return rv;
        }
        *enc = malloc(*enclen * sizeof(CK_BYTE));
	if (*enc == NULL) {
		return CKR_HOST_MEMORY;
	}
        rv = c->sym->C_Encrypt(session, message, mlen, *enc, enclen);
	return rv;
}

*/
import "C"

import "unsafe"

// Ctx contains the current pkcs11 context.
type Ctx struct {
	ctx         *C.struct_ctx
	initialized bool
}

// New creates a new context.
func New(module string) *Ctx {
	c := new(Ctx)
	mod := C.CString(module)
	defer C.free(unsafe.Pointer(mod))
	c.ctx = C.New(mod)
	if c.ctx == nil {
		return nil
	}
	return c
}

// Destroy unload the module and frees any remaining memory.
func (c *Ctx) Destroy() {
	if c == nil {
		return
	}
	C.Destroy(c.ctx)
}

/* Initialize initializes the Cryptoki library. */
func (c *Ctx) Initialize() error {
	args := &C.CK_C_INITIALIZE_ARGS{nil, nil, nil, nil, C.CKF_OS_LOCKING_OK, nil}
	e := C.Initialize(c.ctx, C.CK_VOID_PTR(args))
	if e == C.CKR_OK {
		c.initialized = true // TODO(miek): keep?
	}
	return toError(e)
}

/* Finalize indicates that an application is done with the Cryptoki library. */
func (c *Ctx) Finalize() error {
	if !c.initialized {
		panic("pkcs11: context not initialized")
	}
	e := C.Finalize(c.ctx)
	return toError(e)
}

/* GetSlotList obtains a list of slots in the system. */
func (c *Ctx) GetSlotList(tokenPresent bool) (List, error) {
	var (
		slotList C.CK_ULONG_PTR
		ulCount  C.CK_ULONG
	)
	e := C.GetSlotList(c.ctx, cBBool(tokenPresent), &slotList, &ulCount)
	if toError(e) == nil {
		l := toList(slotList, ulCount)
		return l, nil
	}
	return nil, toError(e)
}

/* OpenSession opens a session between an application and a token. */
func (c *Ctx) OpenSession(slotID uint, flags uint) (SessionHandle, error) {
	var s C.CK_SESSION_HANDLE
	e := C.OpenSession(c.ctx, C.CK_ULONG(slotID), C.CK_ULONG(flags), C.CK_SESSION_HANDLE_PTR(&s))
	return SessionHandle(s), toError(e)
}

/* CloseSession closes a session between an application and a token. */
func (c *Ctx) CloseSession(sh SessionHandle) error {
	e := C.CloseSession(c.ctx, C.CK_SESSION_HANDLE(sh))
	return toError(e)
}

/* CloseAllSessions closes all sessions with a token. */
func (c *Ctx) CloseAllSessions(slotID uint) error {
	e := C.CloseAllSessions(c.ctx, C.CK_ULONG(slotID))
	return toError(e)
}

/* Login logs a user into a token. */
func (c *Ctx) Login(sh SessionHandle, userType uint, pin string) error {
	p := C.CString(pin)
	defer C.free(unsafe.Pointer(p))
	e := C.Login(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_USER_TYPE(userType), p, C.CK_ULONG(len(pin)))
	return toError(e)
}

/* Logout logs a user out from a token. */
func (c *Ctx) Logout(sh SessionHandle) error {
	e := C.Logout(c.ctx, C.CK_SESSION_HANDLE(sh))
	return toError(e)
}

/* GenerateKey generates a secret key, creating a new key object. */
func (c *Ctx) GenerateKey(sh SessionHandle, m []*Mechanism, temp []*Attribute) (ObjectHandle, error) {
	var key  C.CK_OBJECT_HANDLE
	t, tcount := cAttributeList(temp)
	mech, _ := cMechanismList(m)
	e := C.GenerateKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, t, tcount, C.CK_OBJECT_HANDLE_PTR(&key))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(key), nil
	}
	return 0, e1
}

/* GenerateKeyPair generates a public-key/private-key pair creating new key objects. */
func (c *Ctx) GenerateKeyPair(sh SessionHandle, m []*Mechanism, public, private []*Attribute) (ObjectHandle, ObjectHandle, error) {
	var (
		pubkey  C.CK_OBJECT_HANDLE
		privkey C.CK_OBJECT_HANDLE
	)
	pub, pubcount := cAttributeList(public)
	priv, privcount := cAttributeList(private)
	mech, _ := cMechanismList(m)
	e := C.GenerateKeyPair(c.ctx, C.CK_SESSION_HANDLE(sh), mech, pub, pubcount, priv, privcount, C.CK_OBJECT_HANDLE_PTR(&pubkey), C.CK_OBJECT_HANDLE_PTR(&privkey))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(pubkey), ObjectHandle(privkey), nil
	}
	return 0, 0, e1
}

func (c *Ctx) DigestInit(sh SessionHandle, m []*Mechanism) error {
	mech, _ := cMechanismList(m)
	e := C.DigestInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech)
	return toError(e)
}

func (c *Ctx) Digest(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		hash    C.CK_BYTE_PTR
		hashlen C.CK_ULONG
	)
	e := C.Digest(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &hash, &hashlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(hash), C.int(hashlen))
	return h, nil
}

func (c *Ctx) SignInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
	mech, _ := cMechanismList(m) // Only the first is used, but still use a list.
	e := C.SignInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
	return toError(e)
}

func (c *Ctx) Sign(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		sig    C.CK_BYTE_PTR
		siglen C.CK_ULONG
	)
	e := C.Sign(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &sig, &siglen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	s := C.GoBytes(unsafe.Pointer(sig), C.int(siglen))
	return s, nil
}

/* EncryptInit initializes an encryption operation. */
func (c *Ctx) EncryptInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
	mech, _ := cMechanismList(m)
	e := C.EncryptInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
	return toError(e)
}

/* Encrypt encrypts single-part data. */
func (c *Ctx) Encrypt(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		enc    C.CK_BYTE_PTR
		enclen C.CK_ULONG
	)
	e := C.Encrypt(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &enc, &enclen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	s := C.GoBytes(unsafe.Pointer(enc), C.int(enclen))
	return s, nil
}
<commit_msg>more docs<commit_after>// Package pkcs11 is a thin wrapper around the PKCS#11 crypto library.
package pkcs11

/*
#cgo LDFLAGS: -lltdl
#define CK_PTR *
#ifndef NULL_PTR
#define NULL_PTR 0
#endif
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)

#include <stdlib.h>
#include <stdio.h>
#include <ltdl.h>
#include <unistd.h>
#include "pkcs11.h"

struct ctx {
        lt_dlhandle handle;
        CK_FUNCTION_LIST_PTR sym;
};

// New initializes a ctx and fills the symbol table.
struct ctx * New(const char *module) {
        if (lt_dlinit() != 0) {
                return NULL;
        }
        CK_C_GetFunctionList list;
        struct ctx *c=  calloc(1, sizeof(struct ctx));
        c->handle = lt_dlopen(module);
        if (c->handle == NULL) {
                free(c);
                return NULL;
        }
        list = (CK_C_GetFunctionList) lt_dlsym(c->handle, "C_GetFunctionList");
        if (list == NULL) {
                free(c);
                return NULL;
        }
        list(&c->sym);
        return c;
}

// Destroy cleans up a ctx.
void Destroy(struct ctx *c) {
        if (!c) {
                return;
        }
        if (c->handle == NULL) {
                return;
        }
        if (lt_dlclose(c->handle) < 0) {
                return;
        }
        lt_dlexit();
        free(c);
}

CK_RV Initialize(struct ctx* c, CK_VOID_PTR initArgs) {
	return c->sym->C_Initialize(initArgs);
}

CK_RV Finalize(struct ctx* c) {
	return c->sym->C_Finalize(NULL_PTR);
}

CK_RV GetSlotList(struct ctx* c, CK_BBOOL tokenPresent, CK_ULONG_PTR *slotList, CK_ULONG_PTR ulCount) {
	CK_RV e = c->sym->C_GetSlotList(tokenPresent, NULL_PTR, ulCount);
	if (e != CKR_OK) {
		return e;
	}
	*slotList = calloc(1, sizeof(CK_SLOT_ID) * *ulCount);
	e = c->sym->C_GetSlotList(tokenPresent, *slotList, ulCount);
	return e;
}

CK_RV OpenSession(struct ctx* c, CK_ULONG slotID, CK_ULONG flags, CK_SESSION_HANDLE_PTR session) {
	CK_RV e = c->sym->C_OpenSession((CK_SLOT_ID)slotID, (CK_FLAGS)flags, NULL_PTR, NULL_PTR, session);
	return e;
}

CK_RV CloseSession(struct ctx *c, CK_SESSION_HANDLE session) {
	CK_RV e = c->sym->C_CloseSession(session);
	return e;
}

CK_RV CloseAllSessions(struct ctx *c, CK_ULONG slotID) {
	CK_RV e = c->sym->C_CloseAllSessions(slotID);
	return e;
}

CK_RV Login(struct ctx* c, CK_SESSION_HANDLE session, CK_USER_TYPE userType, char* pin, CK_ULONG pinLen) {
	CK_RV e = c->sym->C_Login(session, userType, (CK_UTF8CHAR_PTR)pin, pinLen);
	return e;
}

CK_RV Logout(struct ctx *c, CK_SESSION_HANDLE session) {
	CK_RV e = c->sym->C_Logout(session);
	return e;
}

CK_RV DigestInit(struct ctx *c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism) {
	CK_RV e = c->sym->C_DigestInit(session, mechanism);
	return e;
}

CK_RV Digest(struct ctx *c, CK_SESSION_HANDLE session, CK_BYTE_PTR message, CK_ULONG mlen, CK_BYTE_PTR *hash, CK_ULONG_PTR hashlen) {
        CK_RV rv = c->sym->C_Digest(session, message, mlen, NULL, hashlen);
        if (rv != CKR_OK) {
                return rv;
        }
        *hash = malloc(*hashlen * sizeof(CK_BYTE));
	if (*hash == NULL) {
		return CKR_HOST_MEMORY;
	}
        rv = c->sym->C_Digest(session, message, mlen, *hash, hashlen);
	return rv;
}

CK_RV GenerateKey(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
	CK_ATTRIBUTE_PTR temp, CK_ULONG tempCount, CK_OBJECT_HANDLE_PTR key) {
	CK_RV e = c->sym->C_GenerateKey(session, mechanism, temp, tempCount, key);
	return e;
}

CK_RV GenerateKeyPair(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism,
	CK_ATTRIBUTE_PTR pub, CK_ULONG pubCount, CK_ATTRIBUTE_PTR priv, CK_ULONG privCount,
	CK_OBJECT_HANDLE_PTR pubkey, CK_OBJECT_HANDLE_PTR privkey) {
	CK_RV e = c->sym->C_GenerateKeyPair(session, mechanism, pub, pubCount, priv, privCount,
					pubkey, privkey);
	return e;
}

CK_RV SignInit(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
	CK_RV e = c->sym->C_SignInit(session, mechanism, key);
	return e;
}

CK_RV Sign(struct ctx *c, CK_SESSION_HANDLE session, CK_BYTE_PTR message, CK_ULONG mlen, CK_BYTE_PTR *sig, CK_ULONG_PTR siglen) {
        CK_RV rv = c->sym->C_Sign(session, message, mlen, NULL, siglen);
        if (rv != CKR_OK) {
                return rv;
        }
        *sig = malloc(*siglen * sizeof(CK_BYTE));
	if (*sig == NULL) {
		return CKR_HOST_MEMORY;
	}
        rv = c->sym->C_Sign(session, message, mlen, *sig, siglen);
	return rv;
}

CK_RV EncryptInit(struct ctx* c, CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) {
	CK_RV e = c->sym->C_EncryptInit(session, mechanism, key);
	return e;
}

CK_RV Encrypt(struct ctx *c, CK_SESSION_HANDLE session, CK_BYTE_PTR message, CK_ULONG mlen, CK_BYTE_PTR *enc, CK_ULONG_PTR enclen) {
        CK_RV rv = c->sym->C_Encrypt(session, message, mlen, NULL, enclen);
        if (rv != CKR_OK) {
                return rv;
        }
        *enc = malloc(*enclen * sizeof(CK_BYTE));
	if (*enc == NULL) {
		return CKR_HOST_MEMORY;
	}
        rv = c->sym->C_Encrypt(session, message, mlen, *enc, enclen);
	return rv;
}

// Random functions

*/
import "C"

import "unsafe"

// Ctx contains the current pkcs11 context.
type Ctx struct {
	ctx         *C.struct_ctx
	initialized bool
}

// New creates a new context.
func New(module string) *Ctx {
	c := new(Ctx)
	mod := C.CString(module)
	defer C.free(unsafe.Pointer(mod))
	c.ctx = C.New(mod)
	if c.ctx == nil {
		return nil
	}
	return c
}

// Destroy unload the module and frees any remaining memory.
func (c *Ctx) Destroy() {
	if c == nil {
		return
	}
	C.Destroy(c.ctx)
}

/* Initialize initializes the Cryptoki library. */
func (c *Ctx) Initialize() error {
	args := &C.CK_C_INITIALIZE_ARGS{nil, nil, nil, nil, C.CKF_OS_LOCKING_OK, nil}
	e := C.Initialize(c.ctx, C.CK_VOID_PTR(args))
	if e == C.CKR_OK {
		c.initialized = true // TODO(miek): keep?
	}
	return toError(e)
}

/* Finalize indicates that an application is done with the Cryptoki library. */
func (c *Ctx) Finalize() error {
	if !c.initialized {
		panic("pkcs11: context not initialized")
	}
	e := C.Finalize(c.ctx)
	return toError(e)
}

/* GetSlotList obtains a list of slots in the system. */
func (c *Ctx) GetSlotList(tokenPresent bool) (List, error) {
	var (
		slotList C.CK_ULONG_PTR
		ulCount  C.CK_ULONG
	)
	e := C.GetSlotList(c.ctx, cBBool(tokenPresent), &slotList, &ulCount)
	if toError(e) == nil {
		l := toList(slotList, ulCount)
		return l, nil
	}
	return nil, toError(e)
}

/* OpenSession opens a session between an application and a token. */
func (c *Ctx) OpenSession(slotID uint, flags uint) (SessionHandle, error) {
	var s C.CK_SESSION_HANDLE
	e := C.OpenSession(c.ctx, C.CK_ULONG(slotID), C.CK_ULONG(flags), C.CK_SESSION_HANDLE_PTR(&s))
	return SessionHandle(s), toError(e)
}

/* CloseSession closes a session between an application and a token. */
func (c *Ctx) CloseSession(sh SessionHandle) error {
	e := C.CloseSession(c.ctx, C.CK_SESSION_HANDLE(sh))
	return toError(e)
}

/* CloseAllSessions closes all sessions with a token. */
func (c *Ctx) CloseAllSessions(slotID uint) error {
	e := C.CloseAllSessions(c.ctx, C.CK_ULONG(slotID))
	return toError(e)
}

/* Login logs a user into a token. */
func (c *Ctx) Login(sh SessionHandle, userType uint, pin string) error {
	p := C.CString(pin)
	defer C.free(unsafe.Pointer(p))
	e := C.Login(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_USER_TYPE(userType), p, C.CK_ULONG(len(pin)))
	return toError(e)
}

/* Logout logs a user out from a token. */
func (c *Ctx) Logout(sh SessionHandle) error {
	e := C.Logout(c.ctx, C.CK_SESSION_HANDLE(sh))
	return toError(e)
}

/* GenerateKey generates a secret key, creating a new key object. */
func (c *Ctx) GenerateKey(sh SessionHandle, m []*Mechanism, temp []*Attribute) (ObjectHandle, error) {
	var key  C.CK_OBJECT_HANDLE
	t, tcount := cAttributeList(temp)
	mech, _ := cMechanismList(m)
	e := C.GenerateKey(c.ctx, C.CK_SESSION_HANDLE(sh), mech, t, tcount, C.CK_OBJECT_HANDLE_PTR(&key))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(key), nil
	}
	return 0, e1
}

/* GenerateKeyPair generates a public-key/private-key pair creating new key objects. */
func (c *Ctx) GenerateKeyPair(sh SessionHandle, m []*Mechanism, public, private []*Attribute) (ObjectHandle, ObjectHandle, error) {
	var (
		pubkey  C.CK_OBJECT_HANDLE
		privkey C.CK_OBJECT_HANDLE
	)
	pub, pubcount := cAttributeList(public)
	priv, privcount := cAttributeList(private)
	mech, _ := cMechanismList(m)
	e := C.GenerateKeyPair(c.ctx, C.CK_SESSION_HANDLE(sh), mech, pub, pubcount, priv, privcount, C.CK_OBJECT_HANDLE_PTR(&pubkey), C.CK_OBJECT_HANDLE_PTR(&privkey))
	e1 := toError(e)
	if e1 == nil {
		return ObjectHandle(pubkey), ObjectHandle(privkey), nil
	}
	return 0, 0, e1
}

func (c *Ctx) DigestInit(sh SessionHandle, m []*Mechanism) error {
	mech, _ := cMechanismList(m)
	e := C.DigestInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech)
	return toError(e)
}

func (c *Ctx) Digest(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		hash    C.CK_BYTE_PTR
		hashlen C.CK_ULONG
	)
	e := C.Digest(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &hash, &hashlen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	h := C.GoBytes(unsafe.Pointer(hash), C.int(hashlen))
	return h, nil
}

func (c *Ctx) SignInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
	mech, _ := cMechanismList(m) // Only the first is used, but still use a list.
	e := C.SignInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
	return toError(e)
}

// Sign signs (encrypts with private key) data in a single part, where the signature
// is (will be) an appendix to the data, and plaintext cannot be recovered from the signature.
func (c *Ctx) Sign(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		sig    C.CK_BYTE_PTR
		siglen C.CK_ULONG
	)
	e := C.Sign(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &sig, &siglen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	s := C.GoBytes(unsafe.Pointer(sig), C.int(siglen))
	return s, nil
}

/* EncryptInit initializes an encryption operation. */
func (c *Ctx) EncryptInit(sh SessionHandle, m []*Mechanism, o ObjectHandle) error {
	mech, _ := cMechanismList(m)
	e := C.EncryptInit(c.ctx, C.CK_SESSION_HANDLE(sh), mech, C.CK_OBJECT_HANDLE(o))
	return toError(e)
}

/* Encrypt encrypts single-part data. */
func (c *Ctx) Encrypt(sh SessionHandle, message []byte) ([]byte, error) {
	var (
		enc    C.CK_BYTE_PTR
		enclen C.CK_ULONG
	)
	e := C.Encrypt(c.ctx, C.CK_SESSION_HANDLE(sh), C.CK_BYTE_PTR(unsafe.Pointer(&message[0])), C.CK_ULONG(len(message)), &enc, &enclen)
	if toError(e) != nil {
		return nil, toError(e)
	}
	s := C.GoBytes(unsafe.Pointer(enc), C.int(enclen))
	return s, nil
}
<|endoftext|>