<commit_before>package fields

import (
	"github.com/cjtoolkit/form"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func TestFloat(t *testing.T) {
	Convey("PreCheck", t, func() {

		Convey("Should panic because Name is empty string", func() {
			go panicTrap(func() {
				(Float{}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: Name cannot be empty string"))
		})

		Convey("Should panic because Label is empty string", func() {
			go panicTrap(func() {
				(Float{
					Name: "hello",
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Label cannot be empty string"))
		})

		Convey("Should panic because Norm is nil value", func() {
			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Norm cannot be nil value"))
		})

		Convey("Should panic because Model is nil value", func() {
			var norm string

			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
					Norm:  &norm,
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Model cannot be nil value"))
		})

		Convey("Should panic because Err is nil value", func() {
			var norm string
			var model float64

			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
					Norm:  &norm,
					Model: &model,
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Err cannot be nil value"))
		})

		Convey("Every mandatory field is in order, so therefore should not panic", func() {
			var norm string
			var model float64
			var err error

			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
					Norm:  &norm,
					Model: &model,
					Err:   &err,
				}).PreCheck()
			})

			So(<-panicChannel, ShouldBeNil)
		})

	})

	Convey("ValidateModel", t, func() {

		Convey("validateRequired", func() {

			Convey("Should not panic because field is not required", func() {
				go panicTrap(func() {
					(Float{}).validateRequired()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should panic because required field is an empty string", func() {
				model := float64(0)

				go panicTrap(func() {
					(Float{
						Label:    "required",
						Model:    &model,
						Required: true,
					}).validateRequired()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_FIELD_REQUIRED,
					Value: map[string]interface{}{
						"Label": "required",
					},
				})
			})

			Convey("Should not panic because required field is not an empty string", func() {
				model := float64(5)

				go panicTrap(func() {
					(Float{
						Label:    "required",
						Model:    &model,
						Required: true,
					}).validateRequired()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateMin", func() {

			Convey("Don't validate 0 if MinZero is false", func() {
				label := "min"
				model := float64(-5)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
					}).validateMin()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 0 if MinZero is true, should panic because it's less than 0", func() {
				label := "min"
				model := float64(-0.1)

				go panicTrap(func() {
					(Float{
						Label:   label,
						Model:   &model,
						MinZero: true,
					}).validateMin()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MIN,
					Value: map[string]interface{}{
						"Label": "min",
						"Min":   float64(0),
					},
				})
			})

			Convey("Validate 0 if MinZero is true, shoud not panic because it's more than 0", func() {
				label := "min"
				model := float64(0.1)

				go panicTrap(func() {
					(Float{
						Label:   label,
						Model:   &model,
						MinZero: true,
					}).validateMin()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 5, should panic because it's less than 5", func() {
				label := "min"
				model := float64(4.9)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
						Min:   5,
					}).validateMin()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MIN,
					Value: map[string]interface{}{
						"Label": "min",
						"Min":   float64(5),
					},
				})
			})

			Convey("Validate 5, should not panic because it's more than 5", func() {
				label := "min"
				model := float64(5.1)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
						Min:   5,
					}).validateMin()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateMax", func() {

			Convey("Don't validate 0 if MaxZero is false", func() {
				label := "max"
				model := float64(-5)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
					}).validateMax()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 0 if MaxZero is true, should panic because it's more than 0", func() {
				label := "max"
				model := float64(0.1)

				go panicTrap(func() {
					(Float{
						Label:   label,
						Model:   &model,
						MaxZero: true,
					}).validateMax()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MAX,
					Value: map[string]interface{}{
						"Label": "max",
						"Max":   float64(0),
					},
				})
			})

			Convey("Validate 0 if MaxZero is true, shoud not panic because it's less than 0", func() {
				label := "max"
				model := float64(-0.1)

				go panicTrap(func() {
					(Float{
						Label:   label,
						Model:   &model,
						MaxZero: true,
					}).validateMax()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 5, should panic because it's more than 5", func() {
				label := "max"
				model := float64(5.1)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
						Max:   5,
					}).validateMax()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MAX,
					Value: map[string]interface{}{
						"Label": "max",
						"Max":   float64(5),
					},
				})
			})

			Convey("Validate 5, should not panic because it's less than 5", func() {
				label := "min"
				model := float64(4.9)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
						Max:   5,
					}).validateMax()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateStep", func() {

			Convey("Do nothing because step is set to Zero", func() {

				model := float64(3)

				go panicTrap(func() {
					(Float{Model: &model}).validateStep()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should because panic because model is not in step", func() {

				label := "step"
				model := float64(3)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
						Step:  2,
					}).validateStep()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_STEP,
					Value: map[string]interface{}{
						"Label": "step",
						"Step":  float64(2),
					},
				})
			})

			Convey("Should not panic because model is in step", func() {

				label := "step"
				model := float64(4)

				go panicTrap(func() {
					(Float{
						Label: label,
						Model: &model,
						Step:  2,
					}).validateStep()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateInList", func() {

			Convey("Should not panic, because InList is 'nil'", func() {

				go panicTrap(func() {
					(Float{}).validateInList()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should not panic, because Model is in the List", func() {

				label := "List"
				model := float64(1.5)
				list := []float64{1.4, 1.5, 1.6}

				go panicTrap(func() {
					(Float{
						Label:  label,
						Model:  &model,
						InList: list,
					}).validateInList()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should panic, because Model is not in the List", func() {

				label := "List"
				model := float64(1.3)
				list := []float64{1.4, 1.5, 1.6}

				go panicTrap(func() {
					(Float{
						Label:  label,
						Model:  &model,
						InList: list,
					}).validateInList()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_IN_LIST,
					Value: map[string]interface{}{
						"Label": "List",
						"List":  []float64{1.4, 1.5, 1.6},
					},
				})
			})

		})

	})
}
<commit_msg>Clean up float test<commit_after>package fields

import (
	"github.com/cjtoolkit/form"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func TestFloat(t *testing.T) {
	Convey("PreCheck", t, func() {

		Convey("Should panic because Name is empty string", func() {
			go panicTrap(func() {
				(Float{}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: Name cannot be empty string"))
		})

		Convey("Should panic because Label is empty string", func() {
			go panicTrap(func() {
				(Float{
					Name: "hello",
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Label cannot be empty string"))
		})

		Convey("Should panic because Norm is nil value", func() {
			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Norm cannot be nil value"))
		})

		Convey("Should panic because Model is nil value", func() {
			var norm string

			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
					Norm:  &norm,
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Model cannot be nil value"))
		})

		Convey("Should panic because Err is nil value", func() {
			var norm string
			var model float64

			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
					Norm:  &norm,
					Model: &model,
				}).PreCheck()
			})

			So(<-panicChannel, ShouldEqual, form.ErrorPreCheck("Float Field: hello: Err cannot be nil value"))
		})

		Convey("Every mandatory field is in order, so therefore should not panic", func() {
			var norm string
			var model float64
			var err error

			go panicTrap(func() {
				(Float{
					Name:  "hello",
					Label: "Hello",
					Norm:  &norm,
					Model: &model,
					Err:   &err,
				}).PreCheck()
			})

			So(<-panicChannel, ShouldBeNil)
		})

	})

	Convey("ValidateModel", t, func() {

		Convey("validateRequired", func() {

			Convey("Should not panic because field is not required", func() {
				go panicTrap(func() {
					(Float{}).validateRequired()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should panic because required field is an empty string", func() {
				model := float64(0)

				go panicTrap(func() {
					(Float{
						Model:    &model,
						Required: true,
					}).validateRequired()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_FIELD_REQUIRED,
					Value: map[string]interface{}{
						"Label": "",
					},
				})
			})

			Convey("Should not panic because required field is not an empty string", func() {
				model := float64(5)

				go panicTrap(func() {
					(Float{
						Model:    &model,
						Required: true,
					}).validateRequired()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateMin", func() {

			Convey("Don't validate 0 if MinZero is false", func() {
				model := float64(-5)

				go panicTrap(func() {
					(Float{
						Model: &model,
					}).validateMin()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 0 if MinZero is true, should panic because it's less than 0", func() {
				model := float64(-0.1)

				go panicTrap(func() {
					(Float{
						Model:   &model,
						MinZero: true,
					}).validateMin()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MIN,
					Value: map[string]interface{}{
						"Label": "",
						"Min":   float64(0),
					},
				})
			})

			Convey("Validate 0 if MinZero is true, shoud not panic because it's more than 0", func() {
				model := float64(0.1)

				go panicTrap(func() {
					(Float{
						Model:   &model,
						MinZero: true,
					}).validateMin()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 5, should panic because it's less than 5", func() {
				model := float64(4.9)

				go panicTrap(func() {
					(Float{
						Model: &model,
						Min:   5,
					}).validateMin()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MIN,
					Value: map[string]interface{}{
						"Label": "",
						"Min":   float64(5),
					},
				})
			})

			Convey("Validate 5, should not panic because it's more than 5", func() {
				model := float64(5.1)

				go panicTrap(func() {
					(Float{
						Model: &model,
						Min:   5,
					}).validateMin()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateMax", func() {

			Convey("Don't validate 0 if MaxZero is false", func() {
				model := float64(-5)

				go panicTrap(func() {
					(Float{
						Model: &model,
					}).validateMax()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 0 if MaxZero is true, should panic because it's more than 0", func() {
				model := float64(0.1)

				go panicTrap(func() {
					(Float{
						Model:   &model,
						MaxZero: true,
					}).validateMax()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MAX,
					Value: map[string]interface{}{
						"Label": "",
						"Max":   float64(0),
					},
				})
			})

			Convey("Validate 0 if MaxZero is true, shoud not panic because it's less than 0", func() {
				model := float64(-0.1)

				go panicTrap(func() {
					(Float{
						Model:   &model,
						MaxZero: true,
					}).validateMax()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Validate 5, should panic because it's more than 5", func() {
				model := float64(5.1)

				go panicTrap(func() {
					(Float{
						Model: &model,
						Max:   5,
					}).validateMax()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_MAX,
					Value: map[string]interface{}{
						"Label": "",
						"Max":   float64(5),
					},
				})
			})

			Convey("Validate 5, should not panic because it's less than 5", func() {
				model := float64(4.9)

				go panicTrap(func() {
					(Float{
						Model: &model,
						Max:   5,
					}).validateMax()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateStep", func() {

			Convey("Do nothing because step is set to Zero", func() {
				model := float64(3)

				go panicTrap(func() {
					(Float{Model: &model}).validateStep()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should because panic because model is not in step", func() {
				model := float64(3)

				go panicTrap(func() {
					(Float{
						Model: &model,
						Step:  2,
					}).validateStep()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_NUMBER_STEP,
					Value: map[string]interface{}{
						"Label": "",
						"Step":  float64(2),
					},
				})
			})

			Convey("Should not panic because model is in step", func() {
				model := float64(4)

				go panicTrap(func() {
					(Float{
						Model: &model,
						Step:  2,
					}).validateStep()
				})

				So(<-panicChannel, ShouldBeNil)
			})

		})

		Convey("validateInList", func() {

			Convey("Should not panic, because InList is 'nil'", func() {
				go panicTrap(func() {
					(Float{}).validateInList()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should not panic, because Model is in the List", func() {
				model := float64(1.5)
				list := []float64{1.4, 1.5, 1.6}

				go panicTrap(func() {
					(Float{
						Model:  &model,
						InList: list,
					}).validateInList()
				})

				So(<-panicChannel, ShouldBeNil)
			})

			Convey("Should panic, because Model is not in the List", func() {
				model := float64(1.3)
				list := []float64{1.4, 1.5, 1.6}

				go panicTrap(func() {
					(Float{
						Model:  &model,
						InList: list,
					}).validateInList()
				})

				So(<-panicChannel, ShouldResemble, &form.ErrorValidateModel{
					Key: form.LANG_IN_LIST,
					Value: map[string]interface{}{
						"Label": "",
						"List":  []float64{1.4, 1.5, 1.6},
					},
				})
			})

		})

	})
}
<|endoftext|>


<commit_before>package tiff

import (
	"encoding/binary"
	"fmt"
	"math"
	"math/big"
	"reflect"
)

/* FieldTypeRepr */
// These functions provide string representations of values based on field types.
func reprByte(in []byte, bo binary.ByteOrder) string   { return fmt.Sprintf("%d", in[0]) }
func reprSByte(in []byte, bo binary.ByteOrder) string  { return fmt.Sprintf("%d", int8(in[0])) }
func reprASCII(in []byte, bo binary.ByteOrder) string  { return string(in) }
func reprShort(in []byte, bo binary.ByteOrder) string  { return fmt.Sprintf("%d", bo.Uint16(in)) }
func reprSShort(in []byte, bo binary.ByteOrder) string { return fmt.Sprintf("%d", int16(bo.Uint16(in))) }
func reprLong(in []byte, bo binary.ByteOrder) string   { return fmt.Sprintf("%d", bo.Uint32(in)) }
func reprSLong(in []byte, bo binary.ByteOrder) string  { return fmt.Sprintf("%d", int32(bo.Uint32(in))) }
func reprRational(in []byte, bo binary.ByteOrder) string {
	// Print the representation directly to prevent panics from divide by
	// zero errors when using big.NewRat().
	return fmt.Sprintf("%d/%d", int64(bo.Uint32(in)), int64(bo.Uint32(in[4:])))
}
func reprSRational(in []byte, bo binary.ByteOrder) string {
	// Print the representation directly to prevent panics from divide by
	// zero errors when using big.NewRat()
	return fmt.Sprintf("%d/%d", int64(int32(bo.Uint32(in))), int64(int32(bo.Uint32(in[4:]))))
}
func reprFloat(in []byte, bo binary.ByteOrder) string {
	return fmt.Sprintf("%f", math.Float32frombits(bo.Uint32(in)))
}
func reprDouble(in []byte, bo binary.ByteOrder) string {
	return fmt.Sprintf("%f", math.Float64frombits(bo.Uint64(in)))
}

/* FieldTypeValuer */
func rvalByte(in []byte, bo binary.ByteOrder) reflect.Value  { return reflect.ValueOf(in[0]) }
func rvalSByte(in []byte, bo binary.ByteOrder) reflect.Value { return reflect.ValueOf(int8(in[0])) }
func rvalASCII(in []byte, bo binary.ByteOrder) reflect.Value { return reflect.ValueOf(string(in)) }
func rvalShort(in []byte, bo binary.ByteOrder) reflect.Value { return reflect.ValueOf(bo.Uint16(in)) }
func rvalSShort(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(int16(bo.Uint16(in)))
}
func rvalLong(in []byte, bo binary.ByteOrder) reflect.Value { return reflect.ValueOf(bo.Uint32(in)) }
func rvalSLong(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(int32(bo.Uint32(in)))
}
func rvalRational(in []byte, bo binary.ByteOrder) reflect.Value {
	denom := int64(bo.Uint32(in[4:]))
	if denom == 0 {
		// Prevent panics due to poorly written Rational fields with a
		// denominator of 0.
		return reflect.New(reflect.TypeOf(big.Rat{}))
	}
	numer := int64(bo.Uint32(in))
	return reflect.ValueOf(big.NewRat(numer, denom))
}
func rvalSRational(in []byte, bo binary.ByteOrder) reflect.Value {
	denom := int64(int32(bo.Uint32(in[4:])))
	if denom == 0 {
		// Prevent panics due to poorly written Rational fields with a
		// denominator of 0.  Their usable value would likely be 0.
		return reflect.New(reflect.TypeOf(big.Rat{}))
	}
	numer := int64(int32(bo.Uint32(in)))
	return reflect.ValueOf(big.NewRat(numer, denom))
}
func rvalFloat(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(math.Float32frombits(bo.Uint32(in)))
}
func rvalDouble(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(math.Float64frombits(bo.Uint64(in)))
}

/* reflect.Type */
var (
	typByte   = reflect.TypeOf(byte(0))         // BYTE, UNDEFINED
	typString = reflect.TypeOf(string(""))      // ASCII
	typU16    = reflect.TypeOf(uint16(0))       // SHORT
	typU32    = reflect.TypeOf(uint32(0))       // LONG, IFD
	typBigRat = reflect.TypeOf((*big.Rat)(nil)) // RATIONAL, SRATIONAL
	typI8     = reflect.TypeOf(int8(0))         // SBYTE
	typI16    = reflect.TypeOf(int16(0))        // SSHORT
	typI32    = reflect.TypeOf(int32(0))        // SLONG
	typF32    = reflect.TypeOf(float32(0))      // FLOAT
	typF64    = reflect.TypeOf(float64(0))      // DOUBLE
)

/* Field type definitions
From [TIFF6]:
	1-12: Field types 1 - 12 are described in [TIFF6].

	1  = BYTE	8-bit unsigned integer.
	2  = ASCII	8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero).
	3  = SHORT	16-bit (2-byte) unsigned integer.
	4  = LONG	32-bit (4-byte) unsigned integer.
	5  = RATIONAL	Two LONGs: the first represents the numerator of a fraction; the second, the denominator.
	6  = SBYTE	An 8-bit signed (twos-complement) integer.
	7  = UNDEFINED	An 8-bit byte that may contain anything, depending on the definition of the field.
	8  = SSHORT	A 16-bit (2-byte) signed (twos-complement) integer.
	9  = SLONG	A 32-bit (4-byte) signed (twos-complement) integer.
	10 = SRATIONAL	Two SLONG’s: the first represents the numerator of a fraction, the second the denominator.
	11 = FLOAT	Single precision (4-byte) IEEE format.
	12 = DOUBLE	Double precision (8-byte) IEEE format.
From [BIGTIFFDESIGN]:
	13-15: Field Type IDs 13 - 15 are mentioned in [BIGTIFFDESIGN], but only
		to explain why values 13 - 15 were skipped when identifying new
		Field Types for BigTIFF. These are meant to be used with regular
		TIFF, but were apparently not properly documented prior to the
		BigTIFF design discussion.

	13 = IFD	?? 32-bit unsigned integer offset value ??
	14 = UNICODE	??
	15 = COMPLEX	??
*/

// Default set of Field types.  These are exported for others to use in
// registering custom tags.
var (
	FTByte      = NewFieldType(1, "BYTE", 1, false, reprByte, rvalByte, typByte)
	FTAscii     = NewFieldType(2, "ASCII", 1, false, reprASCII, rvalASCII, typString)
	FTShort     = NewFieldType(3, "SHORT", 2, false, reprShort, rvalShort, typU16)
	FTLong      = NewFieldType(4, "LONG", 4, false, reprLong, rvalLong, typU32)
	FTRational  = NewFieldType(5, "RATIONAL", 8, false, reprRational, rvalRational, typBigRat)
	FTSByte     = NewFieldType(6, "SBYTE", 1, true, reprSByte, rvalSByte, typI8)
	FTUndefined = NewFieldType(7, "UNDEFINED", 1, false, reprByte, rvalByte, typByte)
	FTSShort    = NewFieldType(8, "SSHORT", 2, true, reprSShort, rvalSShort, typI16)
	FTSLong     = NewFieldType(9, "SLONG", 4, true, reprSLong, rvalSLong, typI32)
	FTSRational = NewFieldType(10, "SRATIONAL", 8, true, reprSRational, rvalSRational, typBigRat)
	FTFloat     = NewFieldType(11, "FLOAT", 4, true, reprFloat, rvalFloat, typF32)
	FTDouble    = NewFieldType(12, "DOUBLE", 8, true, reprDouble, rvalDouble, typF64)
	FTIFD       = NewFieldType(13, "IFD", 4, false, reprLong, rvalLong, typU32)

	// TODO: These two are not complete.  Get the details and finish them.
	// For now, represent them as bytes until a proper representation is known.
	FTUnicode = NewFieldType(14, "UNICODE", 1, false, reprByte, rvalByte, typByte)
	FTComplex = NewFieldType(15, "COMPLEX", 1, true, reprByte, rvalByte, typByte)
)

/*
Regarding UNICODE and COMPLEX field types:
  UNICODE: It is thought that it is intended for UNICODE to be represented by a
  32bit integer value.  It is NOT KNOWN whether this should be signed or
  unsigned.  In go terms, this would either be an int32 or uint32.  The size
  would then be 4 instead of 1.  In tiff terms this would mirror either a LONG
  or an SLONG.

  COMPLEX: It is thought that it is intended for COMPLEX to be represented by
  two float32 values with the real part followed by the imaginary part in the
  byte stream.  In go terms this would mirror a complex64.  The size would then
  be 8 instead of 1.  In tiff terms this would be two FLOAT types in a similar
  way that a RATIONAL is two LONGs.
*/

// DefaultFieldTypeSet is the default set of field types supported by this
// package.  A user is free to create their own FieldTypeSet from which to
// support extended functionality or to provide a substitute representation for
// known types.  Most users will be fine with the default set defined here.
var DefaultFieldTypeSet = NewFieldTypeSet("Default")

func init() {
	DefaultFieldTypeSet.Register(FTByte)
	DefaultFieldTypeSet.Register(FTAscii)
	DefaultFieldTypeSet.Register(FTShort)
	DefaultFieldTypeSet.Register(FTLong)
	DefaultFieldTypeSet.Register(FTRational)
	DefaultFieldTypeSet.Register(FTSByte)
	DefaultFieldTypeSet.Register(FTUndefined)
	DefaultFieldTypeSet.Register(FTSShort)
	DefaultFieldTypeSet.Register(FTSLong)
	DefaultFieldTypeSet.Register(FTSRational)
	DefaultFieldTypeSet.Register(FTFloat)
	DefaultFieldTypeSet.Register(FTDouble)
	DefaultFieldTypeSet.Register(FTIFD)
	DefaultFieldTypeSet.Register(FTUnicode)
	DefaultFieldTypeSet.Register(FTComplex)

	// Prevent further registration in the DefaultFieldTypeSet.  Others should
	// add to the DefaultFieldTypeSpace instead of the core set.
	DefaultFieldTypeSet.Lock()

	DefaultFieldTypeSpace.RegisterFieldTypeSet(DefaultFieldTypeSet)
}
<commit_msg>Improve the string value interpretation for the ASCII type, by stripping NULL chars from the end.<commit_after>package tiff

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math"
	"math/big"
	"reflect"
)

/* FieldTypeRepr */
// These functions provide string representations of values based on field types.
func reprByte(in []byte, bo binary.ByteOrder) string   { return fmt.Sprintf("%d", in[0]) }
func reprSByte(in []byte, bo binary.ByteOrder) string  { return fmt.Sprintf("%d", int8(in[0])) }
func reprASCII(in []byte, bo binary.ByteOrder) string  { return string(in) }
func reprShort(in []byte, bo binary.ByteOrder) string  { return fmt.Sprintf("%d", bo.Uint16(in)) }
func reprSShort(in []byte, bo binary.ByteOrder) string { return fmt.Sprintf("%d", int16(bo.Uint16(in))) }
func reprLong(in []byte, bo binary.ByteOrder) string   { return fmt.Sprintf("%d", bo.Uint32(in)) }
func reprSLong(in []byte, bo binary.ByteOrder) string  { return fmt.Sprintf("%d", int32(bo.Uint32(in))) }
func reprRational(in []byte, bo binary.ByteOrder) string {
	// Print the representation directly to prevent panics from divide by
	// zero errors when using big.NewRat().
	return fmt.Sprintf("%d/%d", int64(bo.Uint32(in)), int64(bo.Uint32(in[4:])))
}
func reprSRational(in []byte, bo binary.ByteOrder) string {
	// Print the representation directly to prevent panics from divide by
	// zero errors when using big.NewRat()
	return fmt.Sprintf("%d/%d", int64(int32(bo.Uint32(in))), int64(int32(bo.Uint32(in[4:]))))
}
func reprFloat(in []byte, bo binary.ByteOrder) string {
	return fmt.Sprintf("%f", math.Float32frombits(bo.Uint32(in)))
}
func reprDouble(in []byte, bo binary.ByteOrder) string {
	return fmt.Sprintf("%f", math.Float64frombits(bo.Uint64(in)))
}

/* FieldTypeValuer */
func rvalByte(in []byte, bo binary.ByteOrder) reflect.Value  { return reflect.ValueOf(in[0]) }
func rvalSByte(in []byte, bo binary.ByteOrder) reflect.Value { return reflect.ValueOf(int8(in[0])) }
func rvalASCII(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(string(bytes.TrimRight(in, "\x00")))
}
func rvalShort(in []byte, bo binary.ByteOrder) reflect.Value { return reflect.ValueOf(bo.Uint16(in)) }
func rvalSShort(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(int16(bo.Uint16(in)))
}
func rvalLong(in []byte, bo binary.ByteOrder) reflect.Value { return reflect.ValueOf(bo.Uint32(in)) }
func rvalSLong(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(int32(bo.Uint32(in)))
}
func rvalRational(in []byte, bo binary.ByteOrder) reflect.Value {
	denom := int64(bo.Uint32(in[4:]))
	if denom == 0 {
		// Prevent panics due to poorly written Rational fields with a
		// denominator of 0.
		return reflect.New(reflect.TypeOf(big.Rat{}))
	}
	numer := int64(bo.Uint32(in))
	return reflect.ValueOf(big.NewRat(numer, denom))
}
func rvalSRational(in []byte, bo binary.ByteOrder) reflect.Value {
	denom := int64(int32(bo.Uint32(in[4:])))
	if denom == 0 {
		// Prevent panics due to poorly written Rational fields with a
		// denominator of 0.  Their usable value would likely be 0.
		return reflect.New(reflect.TypeOf(big.Rat{}))
	}
	numer := int64(int32(bo.Uint32(in)))
	return reflect.ValueOf(big.NewRat(numer, denom))
}
func rvalFloat(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(math.Float32frombits(bo.Uint32(in)))
}
func rvalDouble(in []byte, bo binary.ByteOrder) reflect.Value {
	return reflect.ValueOf(math.Float64frombits(bo.Uint64(in)))
}

/* reflect.Type */
var (
	typByte   = reflect.TypeOf(byte(0))         // BYTE, UNDEFINED
	typString = reflect.TypeOf(string(""))      // ASCII
	typU16    = reflect.TypeOf(uint16(0))       // SHORT
	typU32    = reflect.TypeOf(uint32(0))       // LONG, IFD
	typBigRat = reflect.TypeOf((*big.Rat)(nil)) // RATIONAL, SRATIONAL
	typI8     = reflect.TypeOf(int8(0))         // SBYTE
	typI16    = reflect.TypeOf(int16(0))        // SSHORT
	typI32    = reflect.TypeOf(int32(0))        // SLONG
	typF32    = reflect.TypeOf(float32(0))      // FLOAT
	typF64    = reflect.TypeOf(float64(0))      // DOUBLE
)

/* Field type definitions
From [TIFF6]:
	1-12: Field types 1 - 12 are described in [TIFF6].

	1  = BYTE	8-bit unsigned integer.
	2  = ASCII	8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero).
	3  = SHORT	16-bit (2-byte) unsigned integer.
	4  = LONG	32-bit (4-byte) unsigned integer.
	5  = RATIONAL	Two LONGs: the first represents the numerator of a fraction; the second, the denominator.
	6  = SBYTE	An 8-bit signed (twos-complement) integer.
	7  = UNDEFINED	An 8-bit byte that may contain anything, depending on the definition of the field.
	8  = SSHORT	A 16-bit (2-byte) signed (twos-complement) integer.
	9  = SLONG	A 32-bit (4-byte) signed (twos-complement) integer.
	10 = SRATIONAL	Two SLONG’s: the first represents the numerator of a fraction, the second the denominator.
	11 = FLOAT	Single precision (4-byte) IEEE format.
	12 = DOUBLE	Double precision (8-byte) IEEE format.
From [BIGTIFFDESIGN]:
	13-15: Field Type IDs 13 - 15 are mentioned in [BIGTIFFDESIGN], but only
		to explain why values 13 - 15 were skipped when identifying new
		Field Types for BigTIFF. These are meant to be used with regular
		TIFF, but were apparently not properly documented prior to the
		BigTIFF design discussion.

	13 = IFD	?? 32-bit unsigned integer offset value ??
	14 = UNICODE	??
	15 = COMPLEX	??
*/

// Default set of Field types.  These are exported for others to use in
// registering custom tags.
var (
	FTByte      = NewFieldType(1, "BYTE", 1, false, reprByte, rvalByte, typByte)
	FTAscii     = NewFieldType(2, "ASCII", 1, false, reprASCII, rvalASCII, typString)
	FTShort     = NewFieldType(3, "SHORT", 2, false, reprShort, rvalShort, typU16)
	FTLong      = NewFieldType(4, "LONG", 4, false, reprLong, rvalLong, typU32)
	FTRational  = NewFieldType(5, "RATIONAL", 8, false, reprRational, rvalRational, typBigRat)
	FTSByte     = NewFieldType(6, "SBYTE", 1, true, reprSByte, rvalSByte, typI8)
	FTUndefined = NewFieldType(7, "UNDEFINED", 1, false, reprByte, rvalByte, typByte)
	FTSShort    = NewFieldType(8, "SSHORT", 2, true, reprSShort, rvalSShort, typI16)
	FTSLong     = NewFieldType(9, "SLONG", 4, true, reprSLong, rvalSLong, typI32)
	FTSRational = NewFieldType(10, "SRATIONAL", 8, true, reprSRational, rvalSRational, typBigRat)
	FTFloat     = NewFieldType(11, "FLOAT", 4, true, reprFloat, rvalFloat, typF32)
	FTDouble    = NewFieldType(12, "DOUBLE", 8, true, reprDouble, rvalDouble, typF64)
	FTIFD       = NewFieldType(13, "IFD", 4, false, reprLong, rvalLong, typU32)

	// TODO: These two are not complete.  Get the details and finish them.
	// For now, represent them as bytes until a proper representation is known.
	FTUnicode = NewFieldType(14, "UNICODE", 1, false, reprByte, rvalByte, typByte)
	FTComplex = NewFieldType(15, "COMPLEX", 1, true, reprByte, rvalByte, typByte)
)

/*
Regarding UNICODE and COMPLEX field types:
  UNICODE: It is thought that it is intended for UNICODE to be represented by a
  32bit integer value.  It is NOT KNOWN whether this should be signed or
  unsigned.  In go terms, this would either be an int32 or uint32.  The size
  would then be 4 instead of 1.  In tiff terms this would mirror either a LONG
  or an SLONG.

  COMPLEX: It is thought that it is intended for COMPLEX to be represented by
  two float32 values with the real part followed by the imaginary part in the
  byte stream.  In go terms this would mirror a complex64.  The size would then
  be 8 instead of 1.  In tiff terms this would be two FLOAT types in a similar
  way that a RATIONAL is two LONGs.
*/

// DefaultFieldTypeSet is the default set of field types supported by this
// package.  A user is free to create their own FieldTypeSet from which to
// support extended functionality or to provide a substitute representation for
// known types.  Most users will be fine with the default set defined here.
var DefaultFieldTypeSet = NewFieldTypeSet("Default")

func init() {
	DefaultFieldTypeSet.Register(FTByte)
	DefaultFieldTypeSet.Register(FTAscii)
	DefaultFieldTypeSet.Register(FTShort)
	DefaultFieldTypeSet.Register(FTLong)
	DefaultFieldTypeSet.Register(FTRational)
	DefaultFieldTypeSet.Register(FTSByte)
	DefaultFieldTypeSet.Register(FTUndefined)
	DefaultFieldTypeSet.Register(FTSShort)
	DefaultFieldTypeSet.Register(FTSLong)
	DefaultFieldTypeSet.Register(FTSRational)
	DefaultFieldTypeSet.Register(FTFloat)
	DefaultFieldTypeSet.Register(FTDouble)
	DefaultFieldTypeSet.Register(FTIFD)
	DefaultFieldTypeSet.Register(FTUnicode)
	DefaultFieldTypeSet.Register(FTComplex)

	// Prevent further registration in the DefaultFieldTypeSet.  Others should
	// add to the DefaultFieldTypeSpace instead of the core set.
	DefaultFieldTypeSet.Lock()

	DefaultFieldTypeSpace.RegisterFieldTypeSet(DefaultFieldTypeSet)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/flynn/go-discoverd"
	"github.com/flynn/go-flynn/postgres"
)

var storageDir = flag.String("s", "", "Path to store files, instead of Postgres")

func errorResponse(w http.ResponseWriter, err error) {
	if err == ErrNotFound {
		http.Error(w, "NotFound", 404)
		return
	}
	log.Println("error:", err)
	http.Error(w, "Internal Server Error", 500)
}

type File interface {
	io.ReadSeeker
	io.Closer
	Size() int64
	ModTime() time.Time
	Type() string
	ETag() string
}

type Filesystem interface {
	Open(name string) (File, error)
	Put(name string, r io.Reader, typ string) error
	Delete(name string) error
}

var ErrNotFound = errors.New("file not found")

func handler(fs Filesystem) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		switch req.Method {
		case "HEAD", "GET":
			file, err := fs.Open(req.URL.Path)
			if err != nil {
				errorResponse(w, err)
				return
			}
			defer file.Close()
			log.Println("GET", req.RequestURI)
			w.Header().Set("Content-Length", strconv.FormatInt(file.Size(), 10))
			w.Header().Set("Content-Type", file.Type())
			w.Header().Set("Etag", file.ETag())
			http.ServeContent(w, req, req.URL.Path, file.ModTime(), file)
		case "PUT":
			err := fs.Put(req.URL.Path, req.Body, req.Header.Get("Content-Type"))
			if err != nil {
				errorResponse(w, err)
				return
			}
			log.Println("PUT", req.RequestURI)
		case "DELETE":
			err := fs.Delete(req.URL.Path)
			if err != nil {
				errorResponse(w, err)
				return
			}
			log.Println("DELETE", req.RequestURI)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
}

func main() {
	flag.Parse()

	addr := os.Getenv("PORT")
	if addr == "" {
		addr = "3001"
	}
	addr = ":" + addr

	var fs Filesystem
	var storageDesc string

	if *storageDir != "" {
		fs = NewOSFilesystem(*storageDir)
		storageDesc = *storageDir
	} else {
		db, err := postgres.Open("", "")
		if err != nil {
			log.Fatal(err)
		}
		fs, err = NewPostgresFilesystem(db.DB)
		if err != nil {
			log.Fatal(err)
		}
		storageDesc = "Postgres"

		if err := discoverd.Register("shelf", addr); err != nil {
			log.Fatal(err)
		}
	}

	log.Println("Shelf serving files on " + addr + " from " + storageDesc)
	log.Fatal(http.ListenAndServe(addr, handler(fs)))
}
<commit_msg>blobstore: Merge pull request #9 from sosedoff/port-flag<commit_after>package main

import (
	"errors"
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/flynn/go-discoverd"
	"github.com/flynn/go-flynn/postgres"
)

var storageDir = flag.String("s", "", "Path to store files, instead of Postgres")
var listenPort = flag.String("p", "3001", "Port to listen on")

func errorResponse(w http.ResponseWriter, err error) {
	if err == ErrNotFound {
		http.Error(w, "NotFound", 404)
		return
	}
	log.Println("error:", err)
	http.Error(w, "Internal Server Error", 500)
}

type File interface {
	io.ReadSeeker
	io.Closer
	Size() int64
	ModTime() time.Time
	Type() string
	ETag() string
}

type Filesystem interface {
	Open(name string) (File, error)
	Put(name string, r io.Reader, typ string) error
	Delete(name string) error
}

var ErrNotFound = errors.New("file not found")

func handler(fs Filesystem) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		switch req.Method {
		case "HEAD", "GET":
			file, err := fs.Open(req.URL.Path)
			if err != nil {
				errorResponse(w, err)
				return
			}
			defer file.Close()
			log.Println("GET", req.RequestURI)
			w.Header().Set("Content-Length", strconv.FormatInt(file.Size(), 10))
			w.Header().Set("Content-Type", file.Type())
			w.Header().Set("Etag", file.ETag())
			http.ServeContent(w, req, req.URL.Path, file.ModTime(), file)
		case "PUT":
			err := fs.Put(req.URL.Path, req.Body, req.Header.Get("Content-Type"))
			if err != nil {
				errorResponse(w, err)
				return
			}
			log.Println("PUT", req.RequestURI)
		case "DELETE":
			err := fs.Delete(req.URL.Path)
			if err != nil {
				errorResponse(w, err)
				return
			}
			log.Println("DELETE", req.RequestURI)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
}

func main() {
	flag.Parse()

	addr := os.Getenv("PORT")
	if addr == "" {
		addr = *listenPort
	}
	addr = ":" + addr

	var fs Filesystem
	var storageDesc string

	if *storageDir != "" {
		fs = NewOSFilesystem(*storageDir)
		storageDesc = *storageDir
	} else {
		db, err := postgres.Open("", "")
		if err != nil {
			log.Fatal(err)
		}
		fs, err = NewPostgresFilesystem(db.DB)
		if err != nil {
			log.Fatal(err)
		}
		storageDesc = "Postgres"

		if err := discoverd.Register("shelf", addr); err != nil {
			log.Fatal(err)
		}
	}

	log.Println("Shelf serving files on " + addr + " from " + storageDesc)
	log.Fatal(http.ListenAndServe(addr, handler(fs)))
}
<|endoftext|>


<commit_before>package pretty

import (
	"fmt"
	"html/template"
	"io"
	"os"

	"github.com/docker/go-units"
	"github.com/pachyderm/pachyderm/src/client/pfs"
	"github.com/pachyderm/pachyderm/src/server/pkg/pretty"
)

const (
	// RepoHeader is the header for repos.
	RepoHeader = "NAME\tCREATED\tSIZE (MASTER)\t\n"
	// RepoAuthHeader is the header for repos with auth information attached.
	RepoAuthHeader = "NAME\tCREATED\tSIZE (MASTER)\tACCESS LEVEL\t\n"
	// CommitHeader is the header for commits.
	CommitHeader = "REPO\tCOMMIT\tPARENT\tSTARTED\tDURATION\tSIZE\t\n"
	// BranchHeader is the header for branches.
	BranchHeader = "BRANCH\tHEAD\t\n"
	// FileHeader is the header for files.
	FileHeader = "COMMIT\tNAME\tTYPE\tCOMMITTED\tSIZE\t\n"
)

// PrintRepoHeader prints a repo header.
func PrintRepoHeader(w io.Writer, printAuth bool) {
	if printAuth {
		fmt.Fprint(w, RepoAuthHeader)
		return
	}
	fmt.Fprint(w, RepoHeader)
}

// PrintRepoInfo pretty-prints repo info.
func PrintRepoInfo(w io.Writer, repoInfo *pfs.RepoInfo, fullTimestamps bool) {
	fmt.Fprintf(w, "%s\t", repoInfo.Repo.Name)
	if fullTimestamps {
		fmt.Fprintf(w, "%s\t", repoInfo.Created.String())
	} else {
		fmt.Fprintf(w, "%s\t", pretty.Ago(repoInfo.Created))
	}
	fmt.Fprintf(w, "%s\t", units.BytesSize(float64(repoInfo.SizeBytes)))
	if repoInfo.AuthInfo != nil {
		fmt.Fprintf(w, "%s\t", repoInfo.AuthInfo.AccessLevel.String())
	}
	fmt.Fprintln(w)
}

// PrintableRepoInfo is a wrapper around RepoInfo containing any formatting options
// used within the template to conditionally print information.
type PrintableRepoInfo struct {
	*pfs.RepoInfo
	FullTimestamps bool
}

// NewPrintableRepoInfo constructs a PrintableRepoInfo from just a RepoInfo.
func NewPrintableRepoInfo(ri *pfs.RepoInfo) *PrintableRepoInfo {
	return &PrintableRepoInfo{
		RepoInfo: ri,
	}
}

// PrintDetailedRepoInfo pretty-prints detailed repo info.
func PrintDetailedRepoInfo(repoInfo *PrintableRepoInfo) error {
	template, err := template.New("RepoInfo").Funcs(funcMap).Parse(
		`Name: {{.Repo.Name}}{{if .Description}}
Description: {{.Description}}{{end}}{{if .FullTimestamps}}
Created: {{.Created}}{{else}}
Created: {{prettyAgo .Created}}{{end}}
Size of HEAD on master: {{prettySize .SizeBytes}}{{if .AuthInfo}}
Access level: {{ .AuthInfo.AccessLevel.String }}{{end}}
`)
	if err != nil {
		return err
	}
	err = template.Execute(os.Stdout, repoInfo)
	if err != nil {
		return err
	}
	return nil
}

// PrintBranchHeader prints a branch header.
func PrintBranchHeader(w io.Writer) {
	fmt.Fprint(w, BranchHeader)
}

// PrintBranch pretty-prints a Branch.
func PrintBranch(w io.Writer, branchInfo *pfs.BranchInfo) {
	fmt.Fprintf(w, "%s\t", branchInfo.Branch.Name)
	if branchInfo.Head != nil {
		fmt.Fprintf(w, "%s\t\n", branchInfo.Head.ID)
	} else {
		fmt.Fprintf(w, "-\t\n")
	}
}

// PrintCommitInfoHeader prints a commit info header.
func PrintCommitInfoHeader(w io.Writer) {
	fmt.Fprint(w, CommitHeader)
}

// PrintCommitInfo pretty-prints commit info.
func PrintCommitInfo(w io.Writer, commitInfo *pfs.CommitInfo, fullTimestamps bool) {
	fmt.Fprintf(w, "%s\t", commitInfo.Commit.Repo.Name)
	fmt.Fprintf(w, "%s\t", commitInfo.Commit.ID)
	if commitInfo.ParentCommit != nil {
		fmt.Fprintf(w, "%s\t", commitInfo.ParentCommit.ID)
	} else {
		fmt.Fprint(w, "<none>\t")
	}
	if fullTimestamps {
		fmt.Fprintf(w, "%s\t", commitInfo.Started.String())
	} else {
		fmt.Fprintf(w, "%s\t", pretty.Ago(commitInfo.Started))
	}
	if commitInfo.Finished != nil {
		fmt.Fprintf(w, fmt.Sprintf("%s\t", pretty.TimeDifference(commitInfo.Started, commitInfo.Finished)))
		fmt.Fprintf(w, "%s\t\n", units.BytesSize(float64(commitInfo.SizeBytes)))
	} else {
		fmt.Fprintf(w, "-\t")
		// Open commits don't have meaningful size information
		fmt.Fprintf(w, "-\t\n")
	}
}

// PrintableCommitInfo is a wrapper around CommitInfo containing any formatting options
// used within the template to conditionally print information.
type PrintableCommitInfo struct {
	*pfs.CommitInfo
	FullTimestamps bool
}

// NewPrintableCommitInfo constructs a PrintableCommitInfo from just a CommitInfo.
func NewPrintableCommitInfo(ci *pfs.CommitInfo) *PrintableCommitInfo {
	return &PrintableCommitInfo{
		CommitInfo: ci,
	}
}

// PrintDetailedCommitInfo pretty-prints detailed commit info.
func PrintDetailedCommitInfo(commitInfo *PrintableCommitInfo) error {
	template, err := template.New("CommitInfo").Funcs(funcMap).Parse(
		`Commit: {{.Commit.Repo.Name}}/{{.Commit.ID}}{{if .Description}}
Description: {{.Description}}{{end}}{{if .ParentCommit}}
Parent: {{.ParentCommit.ID}}{{end}}{{if .FullTimestamps}}
Started: {{.Started}}{{else}}
Started: {{prettyAgo .Started}}{{end}}{{if .Finished}}{{if .FullTimestamps}}
Finished: {{.Finished}}{{else}}
Finished: {{prettyAgo .Finished}}{{end}}{{end}}
Size: {{prettySize .SizeBytes}}{{if .Provenance}}
Provenance: {{range .Provenance}} {{.Repo.Name}}/{{.ID}} {{end}} {{end}}
`)
	if err != nil {
		return err
	}
	err = template.Execute(os.Stdout, commitInfo)
	if err != nil {
		return err
	}
	return nil
}

// PrintFileInfoHeader prints a file info header.
func PrintFileInfoHeader(w io.Writer) {
	fmt.Fprint(w, FileHeader)
}

// PrintFileInfo pretty-prints file info.
// If recurse is false and directory size is 0, display "-" instead
// If fast is true and file size is 0, display "-" instead
func PrintFileInfo(w io.Writer, fileInfo *pfs.FileInfo, fullTimestamps bool) {
	fmt.Fprintf(w, "%s\t", fileInfo.File.Commit.ID)
	fmt.Fprintf(w, "%s\t", fileInfo.File.Path)
	if fileInfo.FileType == pfs.FileType_FILE {
		fmt.Fprint(w, "file\t")
	} else {
		fmt.Fprint(w, "dir\t")
	}
	if fullTimestamps {
		fmt.Fprintf(w, "%s\t", fileInfo.Committed.String())
	} else {
		fmt.Fprintf(w, "%s\t", pretty.Ago(fileInfo.Committed))
	}
	fmt.Fprintf(w, "%s\t\n", units.BytesSize(float64(fileInfo.SizeBytes)))
}

// PrintDetailedFileInfo pretty-prints detailed file info.
func PrintDetailedFileInfo(fileInfo *pfs.FileInfo) error {
	template, err := template.New("FileInfo").Funcs(funcMap).Parse(
		`Path: {{.File.Path}}
Type: {{fileType .FileType}}
Size: {{prettySize .SizeBytes}}
Children: {{range .Children}} {{.}} {{end}}
`)
	if err != nil {
		return err
	}
	return template.Execute(os.Stdout, fileInfo)
}

type uint64Slice []uint64

func (s uint64Slice) Len() int           { return len(s) }
func (s uint64Slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s uint64Slice) Less(i, j int) bool { return s[i] < s[j] }

func fileType(fileType pfs.FileType) string {
	if fileType == pfs.FileType_FILE {
		return "file"
	}
	return "dir"
}

var funcMap = template.FuncMap{
	"prettyAgo":  pretty.Ago,
	"prettySize": pretty.Size,
	"fileType":   fileType,
}

// CompactPrintBranch renders 'b' as a compact string, e.g.
// "myrepo@master:/my/file"
func CompactPrintBranch(b *pfs.Branch) string {
	return fmt.Sprintf("%s@%s", b.Repo.Name, b.Name)
}

// CompactPrintCommit renders 'c' as a compact string, e.g.
// "myrepo@123abc:/my/file"
func CompactPrintCommit(c *pfs.Commit) string {
	return fmt.Sprintf("%s@%s", c.Repo.Name, c.ID)
}

// CompactPrintFile renders 'f' as a compact string, e.g.
// "myrepo@master:/my/file"
func CompactPrintFile(f *pfs.File) string {
	return fmt.Sprintf("%s@%s:%s", f.Commit.Repo.Name, f.Commit.ID, f.Path)
}
<commit_msg>Fix list-file output for open commits (#3601)<commit_after>package pretty

import (
	"fmt"
	"html/template"
	"io"
	"os"

	"github.com/docker/go-units"
	"github.com/pachyderm/pachyderm/src/client/pfs"
	"github.com/pachyderm/pachyderm/src/server/pkg/pretty"
)

const (
	// RepoHeader is the header for repos.
	RepoHeader = "NAME\tCREATED\tSIZE (MASTER)\t\n"
	// RepoAuthHeader is the header for repos with auth information attached.
	RepoAuthHeader = "NAME\tCREATED\tSIZE (MASTER)\tACCESS LEVEL\t\n"
	// CommitHeader is the header for commits.
	CommitHeader = "REPO\tCOMMIT\tPARENT\tSTARTED\tDURATION\tSIZE\t\n"
	// BranchHeader is the header for branches.
	BranchHeader = "BRANCH\tHEAD\t\n"
	// FileHeader is the header for files.
	FileHeader = "COMMIT\tNAME\tTYPE\tCOMMITTED\tSIZE\t\n"
)

// PrintRepoHeader prints a repo header.
func PrintRepoHeader(w io.Writer, printAuth bool) {
	if printAuth {
		fmt.Fprint(w, RepoAuthHeader)
		return
	}
	fmt.Fprint(w, RepoHeader)
}

// PrintRepoInfo pretty-prints repo info.
func PrintRepoInfo(w io.Writer, repoInfo *pfs.RepoInfo, fullTimestamps bool) {
	fmt.Fprintf(w, "%s\t", repoInfo.Repo.Name)
	if fullTimestamps {
		fmt.Fprintf(w, "%s\t", repoInfo.Created.String())
	} else {
		fmt.Fprintf(w, "%s\t", pretty.Ago(repoInfo.Created))
	}
	fmt.Fprintf(w, "%s\t", units.BytesSize(float64(repoInfo.SizeBytes)))
	if repoInfo.AuthInfo != nil {
		fmt.Fprintf(w, "%s\t", repoInfo.AuthInfo.AccessLevel.String())
	}
	fmt.Fprintln(w)
}

// PrintableRepoInfo is a wrapper around RepoInfo containing any formatting options
// used within the template to conditionally print information.
type PrintableRepoInfo struct {
	*pfs.RepoInfo
	FullTimestamps bool
}

// NewPrintableRepoInfo constructs a PrintableRepoInfo from just a RepoInfo.
func NewPrintableRepoInfo(ri *pfs.RepoInfo) *PrintableRepoInfo {
	return &PrintableRepoInfo{
		RepoInfo: ri,
	}
}

// PrintDetailedRepoInfo pretty-prints detailed repo info.
func PrintDetailedRepoInfo(repoInfo *PrintableRepoInfo) error {
	template, err := template.New("RepoInfo").Funcs(funcMap).Parse(
		`Name: {{.Repo.Name}}{{if .Description}}
Description: {{.Description}}{{end}}{{if .FullTimestamps}}
Created: {{.Created}}{{else}}
Created: {{prettyAgo .Created}}{{end}}
Size of HEAD on master: {{prettySize .SizeBytes}}{{if .AuthInfo}}
Access level: {{ .AuthInfo.AccessLevel.String }}{{end}}
`)
	if err != nil {
		return err
	}
	err = template.Execute(os.Stdout, repoInfo)
	if err != nil {
		return err
	}
	return nil
}

// PrintBranchHeader prints a branch header.
func PrintBranchHeader(w io.Writer) {
	fmt.Fprint(w, BranchHeader)
}

// PrintBranch pretty-prints a Branch.
func PrintBranch(w io.Writer, branchInfo *pfs.BranchInfo) {
	fmt.Fprintf(w, "%s\t", branchInfo.Branch.Name)
	if branchInfo.Head != nil {
		fmt.Fprintf(w, "%s\t\n", branchInfo.Head.ID)
	} else {
		fmt.Fprintf(w, "-\t\n")
	}
}

// PrintCommitInfoHeader prints a commit info header.
func PrintCommitInfoHeader(w io.Writer) {
	fmt.Fprint(w, CommitHeader)
}

// PrintCommitInfo pretty-prints commit info.
func PrintCommitInfo(w io.Writer, commitInfo *pfs.CommitInfo, fullTimestamps bool) {
	fmt.Fprintf(w, "%s\t", commitInfo.Commit.Repo.Name)
	fmt.Fprintf(w, "%s\t", commitInfo.Commit.ID)
	if commitInfo.ParentCommit != nil {
		fmt.Fprintf(w, "%s\t", commitInfo.ParentCommit.ID)
	} else {
		fmt.Fprint(w, "<none>\t")
	}
	if fullTimestamps {
		fmt.Fprintf(w, "%s\t", commitInfo.Started.String())
	} else {
		fmt.Fprintf(w, "%s\t", pretty.Ago(commitInfo.Started))
	}
	if commitInfo.Finished != nil {
		fmt.Fprintf(w, fmt.Sprintf("%s\t", pretty.TimeDifference(commitInfo.Started, commitInfo.Finished)))
		fmt.Fprintf(w, "%s\t\n", units.BytesSize(float64(commitInfo.SizeBytes)))
	} else {
		fmt.Fprintf(w, "-\t")
		// Open commits don't have meaningful size information
		fmt.Fprintf(w, "-\t\n")
	}
}

// PrintableCommitInfo is a wrapper around CommitInfo containing any formatting options
// used within the template to conditionally print information.
type PrintableCommitInfo struct {
	*pfs.CommitInfo
	FullTimestamps bool
}

// NewPrintableCommitInfo constructs a PrintableCommitInfo from just a CommitInfo.
func NewPrintableCommitInfo(ci *pfs.CommitInfo) *PrintableCommitInfo {
	return &PrintableCommitInfo{
		CommitInfo: ci,
	}
}

// PrintDetailedCommitInfo pretty-prints detailed commit info.
func PrintDetailedCommitInfo(commitInfo *PrintableCommitInfo) error {
	template, err := template.New("CommitInfo").Funcs(funcMap).Parse(
		`Commit: {{.Commit.Repo.Name}}/{{.Commit.ID}}{{if .Description}}
Description: {{.Description}}{{end}}{{if .ParentCommit}}
Parent: {{.ParentCommit.ID}}{{end}}{{if .FullTimestamps}}
Started: {{.Started}}{{else}}
Started: {{prettyAgo .Started}}{{end}}{{if .Finished}}{{if .FullTimestamps}}
Finished: {{.Finished}}{{else}}
Finished: {{prettyAgo .Finished}}{{end}}{{end}}
Size: {{prettySize .SizeBytes}}{{if .Provenance}}
Provenance: {{range .Provenance}} {{.Repo.Name}}/{{.ID}} {{end}} {{end}}
`)
	if err != nil {
		return err
	}
	err = template.Execute(os.Stdout, commitInfo)
	if err != nil {
		return err
	}
	return nil
}

// PrintFileInfoHeader prints a file info header.
func PrintFileInfoHeader(w io.Writer) {
	fmt.Fprint(w, FileHeader)
}

// PrintFileInfo pretty-prints file info.
// If recurse is false and directory size is 0, display "-" instead
// If fast is true and file size is 0, display "-" instead
func PrintFileInfo(w io.Writer, fileInfo *pfs.FileInfo, fullTimestamps bool) {
	fmt.Fprintf(w, "%s\t", fileInfo.File.Commit.ID)
	fmt.Fprintf(w, "%s\t", fileInfo.File.Path)
	if fileInfo.FileType == pfs.FileType_FILE {
		fmt.Fprint(w, "file\t")
	} else {
		fmt.Fprint(w, "dir\t")
	}
	if fileInfo.Committed == nil {
		fmt.Fprintf(w, "-\t")
	} else if fullTimestamps {
		fmt.Fprintf(w, "%s\t", fileInfo.Committed.String())
	} else {
		fmt.Fprintf(w, "%s\t", pretty.Ago(fileInfo.Committed))
	}
	fmt.Fprintf(w, "%s\t\n", units.BytesSize(float64(fileInfo.SizeBytes)))
}

// PrintDetailedFileInfo pretty-prints detailed file info.
func PrintDetailedFileInfo(fileInfo *pfs.FileInfo) error {
	template, err := template.New("FileInfo").Funcs(funcMap).Parse(
		`Path: {{.File.Path}}
Type: {{fileType .FileType}}
Size: {{prettySize .SizeBytes}}
Children: {{range .Children}} {{.}} {{end}}
`)
	if err != nil {
		return err
	}
	return template.Execute(os.Stdout, fileInfo)
}

type uint64Slice []uint64

func (s uint64Slice) Len() int           { return len(s) }
func (s uint64Slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s uint64Slice) Less(i, j int) bool { return s[i] < s[j] }

func fileType(fileType pfs.FileType) string {
	if fileType == pfs.FileType_FILE {
		return "file"
	}
	return "dir"
}

var funcMap = template.FuncMap{
	"prettyAgo":  pretty.Ago,
	"prettySize": pretty.Size,
	"fileType":   fileType,
}

// CompactPrintBranch renders 'b' as a compact string, e.g.
// "myrepo@master:/my/file"
func CompactPrintBranch(b *pfs.Branch) string {
	return fmt.Sprintf("%s@%s", b.Repo.Name, b.Name)
}

// CompactPrintCommit renders 'c' as a compact string, e.g.
// "myrepo@123abc:/my/file"
func CompactPrintCommit(c *pfs.Commit) string {
	return fmt.Sprintf("%s@%s", c.Repo.Name, c.ID)
}

// CompactPrintFile renders 'f' as a compact string, e.g.
// "myrepo@master:/my/file"
func CompactPrintFile(f *pfs.File) string {
	return fmt.Sprintf("%s@%s:%s", f.Commit.Repo.Name, f.Commit.ID, f.Path)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/PagerDuty/godspeed"
)

type RuntimeStats interface {
	LogStartup()

	LogClientCount(int64)

	LogCommand(cmdType string)
	LogPageMessage()
	LogUserMessage()
	LogBroadcastMessage()
	LogReadMessage()
	LogWriteMessage()

	LogWebsocketConnection()
	LogWebsocketDisconnection()

	LogLongpollConnect()
	LogLongpollDisconnect()

	LogAPNSPush()
	LogAPNSError()

	LogGCMPush()
	LogGCMError()
	LogGCMFailure()
}

type DiscardStats struct{}

func (d *DiscardStats) LogStartup()                {}
func (d *DiscardStats) LogClientCount(int64)       {}
func (d *DiscardStats) LogCommand(cmdType string)  {}
func (d *DiscardStats) LogPageMessage()            {}
func (d *DiscardStats) LogUserMessage()            {}
func (d *DiscardStats) LogBroadcastMessage()       {}
func (d *DiscardStats) LogWebsocketConnection()    {}
func (d *DiscardStats) LogWebsocketDisconnection() {}
func (d *DiscardStats) LogReadMessage()            {}
func (d *DiscardStats) LogWriteMessage()           {}
func (d *DiscardStats) LogLongpollConnect()        {}
func (d *DiscardStats) LogLongpollDisconnect()     {}
func (d *DiscardStats) LogAPNSPush()               {}
func (d *DiscardStats) LogGCMPush()                {}
func (d *DiscardStats) LogAPNSError()              {}
func (d *DiscardStats) LogGCMError()               {}
func (d *DiscardStats) LogGCMFailure()             {}

type DatadogStats struct {
	dog *godspeed.Godspeed
}

func NewDatadogStats(datadogHost string) (*DatadogStats, error) {
	gdsp, err := godspeed.New(datadogHost, godspeed.DefaultPort, false)
	if err == nil {
		return &DatadogStats{gdsp}, nil
	} else {
		return nil, err
	}
}

func (d *DatadogStats) LogStartup() {
	d.dog.Incr("incus.startup", nil)
}

func (d *DatadogStats) LogClientCount(clients int64) {
	d.dog.Gauge("incus.client_count", float64(clients), nil)
}

func (d *DatadogStats) LogCommand(cmdType string) {
	d.dog.Incr("incus.command", nil)
	d.dog.Incr("incus.command."+cmdType, nil)
}

func (d *DatadogStats) LogPageMessage() {
	d.dog.Incr("incus.message", nil)
	d.dog.Incr("incus.message.page", nil)
}

func (d *DatadogStats) LogUserMessage() {
	d.dog.Incr("incus.message", nil)
	d.dog.Incr("incus.message.user", nil)
}

func (d *DatadogStats) LogBroadcastMessage() {
	d.dog.Incr("incus.message", nil)
	d.dog.Incr("incus.message.all", nil)
}

func (d *DatadogStats) LogWebsocketConnection() {
	d.dog.Incr("incus.websocket.connect", nil)
}

func (d *DatadogStats) LogWebsocketDisconnection() {
	d.dog.Incr("incus.websocket.disconnect", nil)
}

func (d *DatadogStats) LogReadMessage() {
	d.dog.Incr("incus.read", nil)
}

func (d *DatadogStats) LogWriteMessage() {
	d.dog.Incr("incus.write", nil)
}

func (d *DatadogStats) LogLongpollConnect() {
	d.dog.Incr("incus.longpoll.connect", nil)
}

func (d *DatadogStats) LogLongpollDisconnect() {
	d.dog.Incr("incus.longpoll.disconnect", nil)
}

func (d *DatadogStats) LogAPNSPush() {
	d.dog.Incr("incus.apns.push", nil)
}

func (d *DatadogStats) LogGCMPush() {
	d.dog.Incr("incus.gcm.push", nil)
}

func (d *DatadogStats) LogAPNSError() {
	d.dog.Incr("incus.apns.error", nil)
}

func (d *DatadogStats) LogGCMError() {
	d.dog.Incr("incus.gcm.error", nil)
}

func (d *DatadogStats) LogGCMFailure() {
	d.dog.Incr("incus.gcm.fail", nil)
}
<commit_msg>Resolve hostnames to IPs<commit_after>package main

import (
	"github.com/PagerDuty/godspeed"
	"net"
)

type RuntimeStats interface {
	LogStartup()

	LogClientCount(int64)

	LogCommand(cmdType string)
	LogPageMessage()
	LogUserMessage()
	LogBroadcastMessage()
	LogReadMessage()
	LogWriteMessage()

	LogWebsocketConnection()
	LogWebsocketDisconnection()

	LogLongpollConnect()
	LogLongpollDisconnect()

	LogAPNSPush()
	LogAPNSError()

	LogGCMPush()
	LogGCMError()
	LogGCMFailure()
}

type DiscardStats struct{}

func (d *DiscardStats) LogStartup()                {}
func (d *DiscardStats) LogClientCount(int64)       {}
func (d *DiscardStats) LogCommand(cmdType string)  {}
func (d *DiscardStats) LogPageMessage()            {}
func (d *DiscardStats) LogUserMessage()            {}
func (d *DiscardStats) LogBroadcastMessage()       {}
func (d *DiscardStats) LogWebsocketConnection()    {}
func (d *DiscardStats) LogWebsocketDisconnection() {}
func (d *DiscardStats) LogReadMessage()            {}
func (d *DiscardStats) LogWriteMessage()           {}
func (d *DiscardStats) LogLongpollConnect()        {}
func (d *DiscardStats) LogLongpollDisconnect()     {}
func (d *DiscardStats) LogAPNSPush()               {}
func (d *DiscardStats) LogGCMPush()                {}
func (d *DiscardStats) LogAPNSError()              {}
func (d *DiscardStats) LogGCMError()               {}
func (d *DiscardStats) LogGCMFailure()             {}

type DatadogStats struct {
	dog *godspeed.Godspeed
}

func NewDatadogStats(datadogHost string) (*DatadogStats, error) {
	ips, err := net.LookupIP(datadogHost)
	if len(ips) > 0 {
		gdsp, err := godspeed.New(ips[0].String(), godspeed.DefaultPort, false)
		if err == nil {
			return &DatadogStats{gdsp}, nil
		}
	}

	return nil, err
}

func (d *DatadogStats) LogStartup() {
	d.dog.Incr("incus.startup", nil)
}

func (d *DatadogStats) LogClientCount(clients int64) {
	d.dog.Gauge("incus.client_count", float64(clients), nil)
}

func (d *DatadogStats) LogCommand(cmdType string) {
	d.dog.Incr("incus.command", nil)
	d.dog.Incr("incus.command."+cmdType, nil)
}

func (d *DatadogStats) LogPageMessage() {
	d.dog.Incr("incus.message", nil)
	d.dog.Incr("incus.message.page", nil)
}

func (d *DatadogStats) LogUserMessage() {
	d.dog.Incr("incus.message", nil)
	d.dog.Incr("incus.message.user", nil)
}

func (d *DatadogStats) LogBroadcastMessage() {
	d.dog.Incr("incus.message", nil)
	d.dog.Incr("incus.message.all", nil)
}

func (d *DatadogStats) LogWebsocketConnection() {
	d.dog.Incr("incus.websocket.connect", nil)
}

func (d *DatadogStats) LogWebsocketDisconnection() {
	d.dog.Incr("incus.websocket.disconnect", nil)
}

func (d *DatadogStats) LogReadMessage() {
	d.dog.Incr("incus.read", nil)
}

func (d *DatadogStats) LogWriteMessage() {
	d.dog.Incr("incus.write", nil)
}

func (d *DatadogStats) LogLongpollConnect() {
	d.dog.Incr("incus.longpoll.connect", nil)
}

func (d *DatadogStats) LogLongpollDisconnect() {
	d.dog.Incr("incus.longpoll.disconnect", nil)
}

func (d *DatadogStats) LogAPNSPush() {
	d.dog.Incr("incus.apns.push", nil)
}

func (d *DatadogStats) LogGCMPush() {
	d.dog.Incr("incus.gcm.push", nil)
}

func (d *DatadogStats) LogAPNSError() {
	d.dog.Incr("incus.apns.error", nil)
}

func (d *DatadogStats) LogGCMError() {
	d.dog.Incr("incus.gcm.error", nil)
}

func (d *DatadogStats) LogGCMFailure() {
	d.dog.Incr("incus.gcm.fail", nil)
}
<|endoftext|>


<commit_before>package contentstore

import (
	"encoding/csv"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strconv"
	"sync"

	"github.com/kjk/u"
)

// Files we use:
// - index where for each blob we store: sha1 of the content, number of segment
//   file in which the blob is stored, offset within the segment and size of
//   the blob
// - one or more segment files. User can control the max size of segment
//   file (10 MB by default) to pick the right file size/number of files
//   balance for his needs

// Ideas for the future:
// - add a mode where we store big blobs (e.g. over 1MB) in their own files
// - add a way to delete files and re-use deleted space via some sort of
//   best-fit allocator (if we're adding a new blob and have free space
//   due to deletion, pick the free space that is the closest in size to
//   new blob but only if it's e.g. withing 10% in size, to avoid excessive
//   fragmentation)
// - add a way to delete files by rewriting the files (expensive! we have to
//   rewrite the whole index and each segment that contains deleted files)

var (
	errNotFound           = errors.New("not found")
	errInvalidIndexHdr    = errors.New("invalid index file header")
	errInvalidIndexLine   = errors.New("invalid index line")
	errSegmentFileMissing = errors.New("segment file missing")
	errNotValidSha1       = errors.New("not a valid sha1")
	// first line in index file, for additional safety
	idxHdr = "github.com/kjk/contentstore header 1.0"
)

type blob struct {
	sha1     [20]byte
	nSegment int
	offset   int
	size     int
}

type Store struct {
	sync.Mutex
	basePath       string
	maxSegmentSize int
	blobs          []blob
	// sha1ToBlob is to quickly find a message based on sha1
	// string is really [20]byte cast to string and int is a position within blobs array
	sha1HexToBlobNo map[string]int
	idxFile         *os.File
	idxCsvWriter    *csv.Writer
	currSegmentFile *os.File
	currSegmentNo   int
	currSegmentSize int
	// we cache file descriptor for one segment file (in addition to current
	// segment file) to reduce file open/close for Get()
	cachedSegmentFile *os.File
	cachedSegmentNo   int
}

func idxFilePath(basePath string) string {
	return basePath + "_idx.txt"
}

func segmentFilePath(basePath string, nSegment int) string {
	return fmt.Sprintf("%s_%d.txt", basePath, nSegment)
}

func decodeIndexLine(rec []string) (blob blob, err error) {
	if len(rec) != 4 {
		return blob, errInvalidIndexLine
	}
	sha1, err := hex.DecodeString(rec[0])
	if err != nil {
		return blob, err
	}
	if len(sha1) != 20 {
		return blob, errNotValidSha1
	}
	copy(blob.sha1[:], sha1)
	if blob.nSegment, err = strconv.Atoi(rec[1]); err != nil {
		return blob, err
	}
	if blob.offset, err = strconv.Atoi(rec[2]); err != nil {
		return blob, err
	}
	if blob.size, err = strconv.Atoi(rec[3]); err != nil {
		return blob, err
	}
	return blob, nil
}

// appends x to array of ints
func appendIntIfNotExists(aPtr *[]int, x int) {
	a := *aPtr
	if sort.SearchInts(a, x) >= 0 {
		return
	}
	a = append(a, x)
	sort.Ints(a)
	*aPtr = a
}

// TODO: error out if already in sha1HexToBlobNo
func (store *Store) appendBlob(blob blob) {
	sha1 := fmt.Sprintf("%x", blob.sha1[:])
	blobNo := len(store.blobs)
	store.blobs = append(store.blobs, blob)
	store.sha1HexToBlobNo[sha1] = blobNo
}

func (store *Store) readIndex() error {
	// at this point idx file must exist
	fidx, err := os.Open(idxFilePath(store.basePath))
	if err != nil {
		return err
	}
	// TODO: would be faster (and easier?) to use a bitset since we know
	// segment numbers are consequitive integers
	segments := make([]int, 0)
	csvReader := csv.NewReader(fidx)
	csvReader.Comma = ','
	csvReader.FieldsPerRecord = -1
	rec, err := csvReader.Read()
	if err != nil || len(rec) != 1 || rec[0] != idxHdr {
		return errInvalidIndexHdr
	}
	for {
		rec, err = csvReader.Read()
		if err != nil {
			break
		}
		blob, err := decodeIndexLine(rec)
		if err != nil {
			break
		}
		appendIntIfNotExists(&segments, blob.nSegment)
		store.appendBlob(blob)
	}
	if err == io.EOF {
		err = nil
	}
	// verify segment files exist
	// TODO: also verify offset + size is <= size of segment file
	for _, nSegment := range segments {
		path := segmentFilePath(store.basePath, nSegment)
		if !u.PathExists(path) {
			return errSegmentFileMissing
		}
		store.currSegmentNo = nSegment
	}
	return nil
}

func NewWithLimit(basePath string, maxSegmentSize int) (store *Store, err error) {
	store = &Store{
		basePath:        basePath,
		blobs:           make([]blob, 0),
		sha1HexToBlobNo: make(map[string]int),
		maxSegmentSize:  maxSegmentSize,
		cachedSegmentNo: -1,
	}
	idxPath := idxFilePath(basePath)
	idxDidExist := u.PathExists(idxPath)
	if idxDidExist {
		if err = store.readIndex(); err != nil {
			return nil, err
		}
	}
	if store.idxFile, err = os.OpenFile(idxPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644); err != nil {
		return nil, err
	}
	store.idxCsvWriter = csv.NewWriter(store.idxFile)
	if !idxDidExist {
		rec := [][]string{{idxHdr}}
		err = store.idxCsvWriter.WriteAll(rec)
		if err != nil {
			return nil, err
		}
	}
	segmentPath := segmentFilePath(store.basePath, store.currSegmentNo)
	stat, err := os.Stat(segmentPath)
	if err != nil {
		// TODO: fail if error is different than "file doesn't exist"
		if store.currSegmentNo != 0 {
			store.Close()
			return nil, errSegmentFileMissing
		}
		store.currSegmentFile, err = os.Create(segmentPath)
		if err != nil {
			store.Close()
			return nil, err
		}
	} else {
		store.currSegmentSize = int(stat.Size())
		store.currSegmentFile, err = os.OpenFile(segmentPath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644)
		if err != nil {
			store.Close()
			return nil, err
		}
	}
	return store, nil
}

func New(basePath string) (*Store, error) {
	return NewWithLimit(basePath, 10*1024*1024)
}

func closeFilePtr(filePtr **os.File) (err error) {
	f := *filePtr
	if f != nil {
		err = f.Close()
		*filePtr = nil
	}
	return err
}

func (store *Store) Close() {
	closeFilePtr(&store.idxFile)
	closeFilePtr(&store.currSegmentFile)
	closeFilePtr(&store.cachedSegmentFile)
}

func writeBlobRec(csvWriter *csv.Writer, blob *blob) error {
	sha1Str := hex.EncodeToString(blob.sha1[:])
	rec := [][]string{
		{
			sha1Str,
			strconv.Itoa(blob.nSegment),
			strconv.Itoa(blob.offset),
			strconv.Itoa(blob.size),
		}}
	return csvWriter.WriteAll(rec)
}

func (store *Store) Put(d []byte) (id string, err error) {
	store.Lock()
	defer store.Unlock()

	idBytes := u.Sha1OfBytes(d)
	id = fmt.Sprintf("%x", idBytes)
	if _, ok := store.sha1HexToBlobNo[id]; ok {
		return id, nil
	}
	blob := blob{
		size:     len(d),
		offset:   store.currSegmentSize,
		nSegment: store.currSegmentNo,
	}
	copy(blob.sha1[:], idBytes)

	if _, err = store.currSegmentFile.Write(d); err != nil {
		return "", err
	}
	if err = store.currSegmentFile.Sync(); err != nil {
		return "", err
	}
	store.currSegmentSize += blob.size
	if store.currSegmentSize >= store.maxSegmentSize {
		// filled current segment => create a new one
		err = store.currSegmentFile.Close()
		if err != nil {
			return "", err
		}
		store.currSegmentNo += 1
		store.currSegmentSize = 0
		path := segmentFilePath(store.basePath, store.currSegmentNo)
		store.currSegmentFile, err = os.Create(path)
		if err != nil {
			return "", err
		}
	}
	if err = writeBlobRec(store.idxCsvWriter, &blob); err != nil {
		return "", err
	}
	store.appendBlob(blob)
	return id, nil
}

func readFromFile(file *os.File, offset, size int) ([]byte, error) {
	res := make([]byte, size, size)
	if _, err := file.ReadAt(res, int64(offset)); err != nil {
		return nil, err
	}
	return res, nil
}

func (store *Store) getSegmentFile(nSegment int) (*os.File, error) {
	if nSegment == store.currSegmentNo {
		return store.currSegmentFile, nil
	}
	if nSegment == store.cachedSegmentNo {
		return store.cachedSegmentFile, nil
	}
	closeFilePtr(&store.cachedSegmentFile)
	path := segmentFilePath(store.basePath, nSegment)
	var err error
	if store.cachedSegmentFile, err = os.Open(path); err != nil {
		return nil, err
	}
	store.cachedSegmentNo = nSegment
	return store.cachedSegmentFile, nil
}

func (store *Store) Get(id string) ([]byte, error) {
	store.Lock()
	defer store.Unlock()

	blobNo, ok := store.sha1HexToBlobNo[id]
	if !ok {
		return nil, errNotFound
	}
	blob := store.blobs[blobNo]
	segmentFile, err := store.getSegmentFile(blob.nSegment)
	if err != nil {
		return nil, err
	}
	return readFromFile(segmentFile, blob.offset, blob.size)
}
<commit_msg>forgot to close file when reading index<commit_after>package contentstore

import (
	"encoding/csv"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strconv"
	"sync"

	"github.com/kjk/u"
)

// Files we use:
// - index where for each blob we store: sha1 of the content, number of segment
//   file in which the blob is stored, offset within the segment and size of
//   the blob
// - one or more segment files. User can control the max size of segment
//   file (10 MB by default) to pick the right file size/number of files
//   balance for his needs

// Ideas for the future:
// - add a mode where we store big blobs (e.g. over 1MB) in their own files
// - add a way to delete files and re-use deleted space via some sort of
//   best-fit allocator (if we're adding a new blob and have free space
//   due to deletion, pick the free space that is the closest in size to
//   new blob but only if it's e.g. withing 10% in size, to avoid excessive
//   fragmentation)
// - add a way to delete files by rewriting the files (expensive! we have to
//   rewrite the whole index and each segment that contains deleted files)

var (
	errNotFound           = errors.New("not found")
	errInvalidIndexHdr    = errors.New("invalid index file header")
	errInvalidIndexLine   = errors.New("invalid index line")
	errSegmentFileMissing = errors.New("segment file missing")
	errNotValidSha1       = errors.New("not a valid sha1")
	// first line in index file, for additional safety
	idxHdr = "github.com/kjk/contentstore header 1.0"
)

type blob struct {
	sha1     [20]byte
	nSegment int
	offset   int
	size     int
}

type Store struct {
	sync.Mutex
	basePath       string
	maxSegmentSize int
	blobs          []blob
	// sha1ToBlob is to quickly find a message based on sha1
	// string is really [20]byte cast to string and int is a position within blobs array
	sha1HexToBlobNo map[string]int
	idxFile         *os.File
	idxCsvWriter    *csv.Writer
	currSegmentFile *os.File
	currSegmentNo   int
	currSegmentSize int
	// we cache file descriptor for one segment file (in addition to current
	// segment file) to reduce file open/close for Get()
	cachedSegmentFile *os.File
	cachedSegmentNo   int
}

func idxFilePath(basePath string) string {
	return basePath + "_idx.txt"
}

func segmentFilePath(basePath string, nSegment int) string {
	return fmt.Sprintf("%s_%d.txt", basePath, nSegment)
}

func decodeIndexLine(rec []string) (blob blob, err error) {
	if len(rec) != 4 {
		return blob, errInvalidIndexLine
	}
	sha1, err := hex.DecodeString(rec[0])
	if err != nil {
		return blob, err
	}
	if len(sha1) != 20 {
		return blob, errNotValidSha1
	}
	copy(blob.sha1[:], sha1)
	if blob.nSegment, err = strconv.Atoi(rec[1]); err != nil {
		return blob, err
	}
	if blob.offset, err = strconv.Atoi(rec[2]); err != nil {
		return blob, err
	}
	if blob.size, err = strconv.Atoi(rec[3]); err != nil {
		return blob, err
	}
	return blob, nil
}

// appends x to array of ints
func appendIntIfNotExists(aPtr *[]int, x int) {
	a := *aPtr
	if sort.SearchInts(a, x) >= 0 {
		return
	}
	a = append(a, x)
	sort.Ints(a)
	*aPtr = a
}

// TODO: error out if already in sha1HexToBlobNo
func (store *Store) appendBlob(blob blob) {
	sha1 := fmt.Sprintf("%x", blob.sha1[:])
	blobNo := len(store.blobs)
	store.blobs = append(store.blobs, blob)
	store.sha1HexToBlobNo[sha1] = blobNo
}

func (store *Store) readIndex() error {
	// at this point idx file must exist
	file, err := os.Open(idxFilePath(store.basePath))
	if err != nil {
		return err
	}
	defer file.Close()
	// TODO: would be faster (and easier?) to use a bitset since we know
	// segment numbers are consequitive integers
	segments := make([]int, 0)
	csvReader := csv.NewReader(file)
	csvReader.Comma = ','
	csvReader.FieldsPerRecord = -1
	rec, err := csvReader.Read()
	if err != nil || len(rec) != 1 || rec[0] != idxHdr {
		return errInvalidIndexHdr
	}
	var blob blob
	for {
		if rec, err = csvReader.Read(); err != nil {
			break
		}
		if blob, err = decodeIndexLine(rec); err != nil {
			break
		}
		appendIntIfNotExists(&segments, blob.nSegment)
		store.appendBlob(blob)
	}
	if err == io.EOF {
		err = nil
	}
	// verify segment files exist
	// TODO: also verify offset + size is <= size of segment file
	for _, nSegment := range segments {
		path := segmentFilePath(store.basePath, nSegment)
		if !u.PathExists(path) {
			return errSegmentFileMissing
		}
		store.currSegmentNo = nSegment
	}
	return nil
}

func NewWithLimit(basePath string, maxSegmentSize int) (store *Store, err error) {
	store = &Store{
		basePath:        basePath,
		blobs:           make([]blob, 0),
		sha1HexToBlobNo: make(map[string]int),
		maxSegmentSize:  maxSegmentSize,
		cachedSegmentNo: -1,
	}
	idxPath := idxFilePath(basePath)
	idxDidExist := u.PathExists(idxPath)
	if idxDidExist {
		if err = store.readIndex(); err != nil {
			return nil, err
		}
	}
	if store.idxFile, err = os.OpenFile(idxPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644); err != nil {
		return nil, err
	}
	store.idxCsvWriter = csv.NewWriter(store.idxFile)
	if !idxDidExist {
		rec := [][]string{{idxHdr}}
		err = store.idxCsvWriter.WriteAll(rec)
		if err != nil {
			return nil, err
		}
	}
	segmentPath := segmentFilePath(store.basePath, store.currSegmentNo)
	stat, err := os.Stat(segmentPath)
	if err != nil {
		// TODO: fail if error is different than "file doesn't exist"
		if store.currSegmentNo != 0 {
			store.Close()
			return nil, errSegmentFileMissing
		}
		store.currSegmentFile, err = os.Create(segmentPath)
		if err != nil {
			store.Close()
			return nil, err
		}
	} else {
		store.currSegmentSize = int(stat.Size())
		store.currSegmentFile, err = os.OpenFile(segmentPath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644)
		if err != nil {
			store.Close()
			return nil, err
		}
	}
	return store, nil
}

func New(basePath string) (*Store, error) {
	return NewWithLimit(basePath, 10*1024*1024)
}

func closeFilePtr(filePtr **os.File) (err error) {
	f := *filePtr
	if f != nil {
		err = f.Close()
		*filePtr = nil
	}
	return err
}

func (store *Store) Close() {
	closeFilePtr(&store.idxFile)
	closeFilePtr(&store.currSegmentFile)
	closeFilePtr(&store.cachedSegmentFile)
}

func writeBlobRec(csvWriter *csv.Writer, blob *blob) error {
	sha1Str := hex.EncodeToString(blob.sha1[:])
	rec := [][]string{
		{
			sha1Str,
			strconv.Itoa(blob.nSegment),
			strconv.Itoa(blob.offset),
			strconv.Itoa(blob.size),
		}}
	return csvWriter.WriteAll(rec)
}

func (store *Store) Put(d []byte) (id string, err error) {
	store.Lock()
	defer store.Unlock()

	idBytes := u.Sha1OfBytes(d)
	id = fmt.Sprintf("%x", idBytes)
	if _, ok := store.sha1HexToBlobNo[id]; ok {
		return id, nil
	}
	blob := blob{
		size:     len(d),
		offset:   store.currSegmentSize,
		nSegment: store.currSegmentNo,
	}
	copy(blob.sha1[:], idBytes)

	if _, err = store.currSegmentFile.Write(d); err != nil {
		return "", err
	}
	if err = store.currSegmentFile.Sync(); err != nil {
		return "", err
	}
	store.currSegmentSize += blob.size
	if store.currSegmentSize >= store.maxSegmentSize {
		// filled current segment => create a new one
		err = store.currSegmentFile.Close()
		if err != nil {
			return "", err
		}
		store.currSegmentNo += 1
		store.currSegmentSize = 0
		path := segmentFilePath(store.basePath, store.currSegmentNo)
		store.currSegmentFile, err = os.Create(path)
		if err != nil {
			return "", err
		}
	}
	if err = writeBlobRec(store.idxCsvWriter, &blob); err != nil {
		return "", err
	}
	store.appendBlob(blob)
	return id, nil
}

func readFromFile(file *os.File, offset, size int) ([]byte, error) {
	res := make([]byte, size, size)
	if _, err := file.ReadAt(res, int64(offset)); err != nil {
		return nil, err
	}
	return res, nil
}

func (store *Store) getSegmentFile(nSegment int) (*os.File, error) {
	if nSegment == store.currSegmentNo {
		return store.currSegmentFile, nil
	}
	if nSegment == store.cachedSegmentNo {
		return store.cachedSegmentFile, nil
	}
	closeFilePtr(&store.cachedSegmentFile)
	path := segmentFilePath(store.basePath, nSegment)
	var err error
	if store.cachedSegmentFile, err = os.Open(path); err != nil {
		return nil, err
	}
	store.cachedSegmentNo = nSegment
	return store.cachedSegmentFile, nil
}

func (store *Store) Get(id string) ([]byte, error) {
	store.Lock()
	defer store.Unlock()

	blobNo, ok := store.sha1HexToBlobNo[id]
	if !ok {
		return nil, errNotFound
	}
	blob := store.blobs[blobNo]
	segmentFile, err := store.getSegmentFile(blob.nSegment)
	if err != nil {
		return nil, err
	}
	return readFromFile(segmentFile, blob.offset, blob.size)
}
<|endoftext|>


<commit_before>package main

import (
	"gob"
	"io"
	"log"
	"os"
	"rpc"
	"sync"
	"time"
)

const saveTimeout = 10e9

type Store interface {
	Put(url, key *string) os.Error
	Get(key, url *string) os.Error
}

type URLStore struct {
	mu       sync.Mutex
	urls     *URLMap
	count    int64
	filename string
	dirtied  chan bool
}

func NewURLStore(filename string) *URLStore {
	s := &URLStore{
		urls:        NewURLMap(),
		filename:    filename,
		saveTrigger: make(chan bool, 1000), // some headroom
	}
	if err := s.load(); err != nil {
		log.Println("URLStore:", err)
	}
	go s.saveLoop()
	return s
}

func (s *URLStore) Get(key, url *string) os.Error {
	if u, ok := s.urls.Get(*key); ok {
		*url = u
		return nil
	}
	return os.NewError("key not found")
}

func (s *URLStore) Put(url, key *string) os.Error {
	s.mu.Lock()
	for {
		*key = genKey(s.count)
		s.count++
		if _, ok := s.urls.Get(*key); !ok {
			break
		}
	}
	s.mu.Unlock()
	s.urls.Set(*key, *url)
	s.dirtied <- true
	return nil
}

func (s *URLStore) load() os.Error {
	f, err := os.Open(s.filename, os.O_RDONLY, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	return s.urls.ReadFrom(f)
}

func (s *URLStore) save() os.Error {
	s.mu.Lock()
	defer s.mu.Unlock()
	f, err := os.Open(s.filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	return s.urls.WriteTo(f)
}

func (s *URLStore) saveLoop() {
	saving := false
	timeout := make(chan bool)
	for {
		select {
		case <-s.dirtied:
			if !saving {
				go func() {
					time.Sleep(saveTimeout)
					timeout <- true
				}()
				saving = true
			}
		case <-timeout:
			if err := s.save(); err != nil {
				log.Println("URLStore:", err)
			}
			saving = false
		}
	}
}

type ProxyStore struct {
	urls   *URLMap
	client *rpc.Client
}

func NewProxyStore(addr string) *ProxyStore {
	client, err := rpc.DialHTTP("tcp", addr)
	if err != nil {
		log.Println("ProxyStore:", err)
	}
	return &ProxyStore{urls: NewURLMap(), client: client}
}

func (s *ProxyStore) Get(key, url *string) os.Error {
	if u, ok := s.urls.Get(*key); ok {
		*url = u
		return nil
	}
	err := s.client.Call("Store.Get", key, url)
	if err == nil {
		s.urls.Set(*key, *url)
	}
	return err
}

func (s *ProxyStore) Put(url, key *string) os.Error {
	err := s.client.Call("Store.Put", url, key)
	if err == nil {
		s.urls.Set(*key, *url)
	}
	return err
}

type URLMap struct {
	mu   sync.RWMutex
	urls map[string]string
}

func NewURLMap() *URLMap {
	return &URLMap{urls: make(map[string]string)}
}

func (m *URLMap) Set(key, url string) {
	m.mu.Lock()
	m.urls[key] = url
	m.mu.Unlock()
}

func (m *URLMap) Get(key string) (string, bool) {
	m.mu.RLock()
	url, ok := m.urls[key]
	m.mu.RUnlock()
	return url, ok
}

func (m *URLMap) WriteTo(w io.Writer) os.Error {
	e := gob.NewEncoder(w)
	m.mu.RLock()
	defer m.mu.RUnlock()
	return e.Encode(m.urls)
}

func (m *URLMap) ReadFrom(r io.Reader) os.Error {
	d := gob.NewDecoder(r)
	m.mu.Lock()
	defer m.mu.Unlock()
	return d.Decode(&m.urls)
}
<commit_msg>simplified save loop<commit_after>package main

import (
	"gob"
	"io"
	"log"
	"os"
	"rpc"
	"sync"
	"time"
)

const saveTimeout = 10e9

type Store interface {
	Put(url, key *string) os.Error
	Get(key, url *string) os.Error
}

type URLStore struct {
	mu       sync.Mutex
	urls     *URLMap
	count    int64
	filename string
	dirty    chan bool
}

func NewURLStore(filename string) *URLStore {
	s := &URLStore{
		urls:     NewURLMap(),
		filename: filename,
		dirty:    make(chan bool, 1),
	}
	if err := s.load(); err != nil {
		log.Println("URLStore:", err)
	}
	go s.saveLoop()
	return s
}

func (s *URLStore) Get(key, url *string) os.Error {
	if u, ok := s.urls.Get(*key); ok {
		*url = u
		return nil
	}
	return os.NewError("key not found")
}

func (s *URLStore) Put(url, key *string) os.Error {
	s.mu.Lock()
	for {
		*key = genKey(s.count)
		s.count++
		if _, ok := s.urls.Get(*key); !ok {
			break
		}
	}
	s.mu.Unlock()
	s.urls.Set(*key, *url)
	_ = s.dirty <- true
	return nil
}

func (s *URLStore) load() os.Error {
	f, err := os.Open(s.filename, os.O_RDONLY, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	return s.urls.ReadFrom(f)
}

func (s *URLStore) save() os.Error {
	s.mu.Lock()
	defer s.mu.Unlock()
	f, err := os.Open(s.filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	return s.urls.WriteTo(f)
}

func (s *URLStore) saveLoop() {
	for {
		time.Sleep(saveTimeout)
		if _, ok := <-s.dirty; !ok {
			continue
		}
		if err := s.save(); err != nil {
			log.Println("URLStore:", err)
		}
	}
}

type ProxyStore struct {
	urls   *URLMap
	client *rpc.Client
}

func NewProxyStore(addr string) *ProxyStore {
	client, err := rpc.DialHTTP("tcp", addr)
	if err != nil {
		log.Println("ProxyStore:", err)
	}
	return &ProxyStore{urls: NewURLMap(), client: client}
}

func (s *ProxyStore) Get(key, url *string) os.Error {
	if u, ok := s.urls.Get(*key); ok {
		*url = u
		return nil
	}
	err := s.client.Call("Store.Get", key, url)
	if err == nil {
		s.urls.Set(*key, *url)
	}
	return err
}

func (s *ProxyStore) Put(url, key *string) os.Error {
	err := s.client.Call("Store.Put", url, key)
	if err == nil {
		s.urls.Set(*key, *url)
	}
	return err
}

type URLMap struct {
	mu   sync.RWMutex
	urls map[string]string
}

func NewURLMap() *URLMap {
	return &URLMap{urls: make(map[string]string)}
}

func (m *URLMap) Set(key, url string) {
	m.mu.Lock()
	m.urls[key] = url
	m.mu.Unlock()
}

func (m *URLMap) Get(key string) (string, bool) {
	m.mu.RLock()
	url, ok := m.urls[key]
	m.mu.RUnlock()
	return url, ok
}

func (m *URLMap) WriteTo(w io.Writer) os.Error {
	e := gob.NewEncoder(w)
	m.mu.RLock()
	defer m.mu.RUnlock()
	return e.Encode(m.urls)
}

func (m *URLMap) ReadFrom(r io.Reader) os.Error {
	d := gob.NewDecoder(r)
	m.mu.Lock()
	defer m.mu.Unlock()
	return d.Decode(&m.urls)
}
<|endoftext|>


<commit_before>// Copyright 2017 The go-hep 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 fmom

import (
	"math"
)

type IPtCotThPhiM [4]float64

func NewIPtCotThPhiM(pt, eta, phi, m float64) IPtCotThPhiM {
	return IPtCotThPhiM([4]float64{pt, eta, phi, m})
}

func (p4 *IPtCotThPhiM) Clone() P4 {
	pp := *p4
	return &pp
}

func (p4 *IPtCotThPhiM) IPt() float64 {
	return p4[0]
}

func (p4 *IPtCotThPhiM) CotTh() float64 {
	return p4[1]
}

func (p4 *IPtCotThPhiM) Phi() float64 {
	return p4[2]
}

func (p4 *IPtCotThPhiM) M() float64 {
	return p4[3]
}

func (p4 *IPtCotThPhiM) Pt() float64 {
	ipt := p4.IPt()
	return 1 / ipt
}

func (p4 *IPtCotThPhiM) P() float64 {
	cotth := p4.CotTh()
	ipt := p4.IPt()
	return math.Sqrt(1+cotth*cotth) / ipt
}

func (p4 *IPtCotThPhiM) P2() float64 {
	cotth := p4.CotTh()
	ipt := p4.IPt()
	return (1 + cotth*cotth) / (ipt * ipt)
}

func (p4 *IPtCotThPhiM) M2() float64 {
	m := p4.M()
	return m * m
}

func (p4 *IPtCotThPhiM) TanTh() float64 {
	cotth := p4.CotTh()
	return 1 / cotth
}

func (p4 *IPtCotThPhiM) SinTh() float64 {
	cotth := p4.CotTh()
	return 1 / math.Sqrt(1+cotth*cotth)
}

func (p4 *IPtCotThPhiM) CosTh() float64 {
	cotth := p4.CotTh()
	cotth2 := cotth * cotth
	costh := math.Sqrt(cotth2 / (1 + cotth2))
	sign := 1.0
	if cotth < 0 {
		sign = -1.0
	}
	return sign * costh
}

func (p4 *IPtCotThPhiM) E() float64 {
	m := p4.M()
	p := p4.P()
	if m == 0 {
		return p
	}
	return math.Sqrt(p*p + m*m)
}

func (p4 *IPtCotThPhiM) Et() float64 {
	cotth := p4.CotTh()
	e := p4.E()
	return e / math.Sqrt(1+cotth*cotth)
}

func (p4 *IPtCotThPhiM) Eta() float64 {
	cotth := p4.CotTh()
	aux := math.Sqrt(1 + cotth*cotth)
	return -0.5 * math.Log((aux-cotth)/(aux+cotth))
}

func (p4 *IPtCotThPhiM) Rapidity() float64 {
	e := p4.E()
	pz := p4.Pz()
	return 0.5 * math.Log((e+pz)/(e-pz))
}

func (p4 *IPtCotThPhiM) Px() float64 {
	cosphi := p4.CosPhi()
	ipt := p4.IPt()
	pt := 1 / ipt
	return pt * cosphi
}

func (p4 *IPtCotThPhiM) Py() float64 {
	sinphi := p4.SinPhi()
	ipt := p4.IPt()
	pt := 1 / ipt
	return pt * sinphi
}

func (p4 *IPtCotThPhiM) Pz() float64 {
	cotth := p4.CotTh()
	ipt := p4.IPt()
	pt := 1 / ipt
	return pt * cotth
}

func (p4 *IPtCotThPhiM) CosPhi() float64 {
	phi := p4.Phi()
	return math.Cos(phi)
}

func (p4 *IPtCotThPhiM) SinPhi() float64 {
	phi := p4.Phi()
	return math.Sin(phi)
}

func (p4 *IPtCotThPhiM) Set(p P4) {
	p4[0] = p.IPt()
	p4[1] = p.CotTh()
	p4[2] = p.Phi()
	p4[3] = p.M()
}
<commit_msg>fmom: make IPtCotThPhiM a struct instead of [4]float64<commit_after>// Copyright 2017 The go-hep 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 fmom

import (
	"math"
)

type IPtCotThPhiM struct {
	P4 Vec4
}

func NewIPtCotThPhiM(pt, eta, phi, m float64) IPtCotThPhiM {
	return IPtCotThPhiM{P4: Vec4{X: pt, Y: eta, Z: phi, T: m}}
}

func (p4 *IPtCotThPhiM) Clone() P4 {
	pp := *p4
	return &pp
}

func (p4 *IPtCotThPhiM) IPt() float64 {
	return p4.P4.X
}

func (p4 *IPtCotThPhiM) CotTh() float64 {
	return p4.P4.Y
}

func (p4 *IPtCotThPhiM) Phi() float64 {
	return p4.P4.Z
}

func (p4 *IPtCotThPhiM) M() float64 {
	return p4.P4.T
}

func (p4 *IPtCotThPhiM) Pt() float64 {
	ipt := p4.IPt()
	return 1 / ipt
}

func (p4 *IPtCotThPhiM) P() float64 {
	cotth := p4.CotTh()
	ipt := p4.IPt()
	return math.Sqrt(1+cotth*cotth) / ipt
}

func (p4 *IPtCotThPhiM) P2() float64 {
	cotth := p4.CotTh()
	ipt := p4.IPt()
	return (1 + cotth*cotth) / (ipt * ipt)
}

func (p4 *IPtCotThPhiM) M2() float64 {
	m := p4.M()
	return m * m
}

func (p4 *IPtCotThPhiM) TanTh() float64 {
	cotth := p4.CotTh()
	return 1 / cotth
}

func (p4 *IPtCotThPhiM) SinTh() float64 {
	cotth := p4.CotTh()
	return 1 / math.Sqrt(1+cotth*cotth)
}

func (p4 *IPtCotThPhiM) CosTh() float64 {
	cotth := p4.CotTh()
	cotth2 := cotth * cotth
	costh := math.Sqrt(cotth2 / (1 + cotth2))
	sign := 1.0
	if cotth < 0 {
		sign = -1.0
	}
	return sign * costh
}

func (p4 *IPtCotThPhiM) E() float64 {
	m := p4.M()
	p := p4.P()
	if m == 0 {
		return p
	}
	return math.Sqrt(p*p + m*m)
}

func (p4 *IPtCotThPhiM) Et() float64 {
	cotth := p4.CotTh()
	e := p4.E()
	return e / math.Sqrt(1+cotth*cotth)
}

func (p4 *IPtCotThPhiM) Eta() float64 {
	cotth := p4.CotTh()
	aux := math.Sqrt(1 + cotth*cotth)
	return -0.5 * math.Log((aux-cotth)/(aux+cotth))
}

func (p4 *IPtCotThPhiM) Rapidity() float64 {
	e := p4.E()
	pz := p4.Pz()
	return 0.5 * math.Log((e+pz)/(e-pz))
}

func (p4 *IPtCotThPhiM) Px() float64 {
	cosphi := p4.CosPhi()
	ipt := p4.IPt()
	pt := 1 / ipt
	return pt * cosphi
}

func (p4 *IPtCotThPhiM) Py() float64 {
	sinphi := p4.SinPhi()
	ipt := p4.IPt()
	pt := 1 / ipt
	return pt * sinphi
}

func (p4 *IPtCotThPhiM) Pz() float64 {
	cotth := p4.CotTh()
	ipt := p4.IPt()
	pt := 1 / ipt
	return pt * cotth
}

func (p4 *IPtCotThPhiM) CosPhi() float64 {
	phi := p4.Phi()
	return math.Cos(phi)
}

func (p4 *IPtCotThPhiM) SinPhi() float64 {
	phi := p4.Phi()
	return math.Sin(phi)
}

func (p4 *IPtCotThPhiM) Set(p P4) {
	p4.P4.X = p.IPt()
	p4.P4.Y = p.CotTh()
	p4.P4.Z = p.Phi()
	p4.P4.T = p.M()
}
<|endoftext|>