Introduce struct type Coord to reduce tuples passing around.

This commit is contained in:
Sascha L. Teichmann 2014-08-18 21:33:58 +02:00
parent 066675896d
commit efe6c6abb8
2 changed files with 78 additions and 72 deletions

View File

@ -6,6 +6,7 @@ package common
import ( import (
"encoding/binary" "encoding/binary"
"fmt"
"strconv" "strconv"
) )
@ -16,13 +17,25 @@ const (
) )
type ( type (
Coord struct {
X, Y, Z int16
}
KeyTransformer func(int64) int64 KeyTransformer func(int64) int64
KeyEncoder func(int64) ([]byte, error) KeyEncoder func(int64) ([]byte, error)
KeyDecoder func([]byte) (int64, error) KeyDecoder func([]byte) (int64, error)
KeySplitter func(int64) (int16, int16, int16) KeySplitter func(int64) Coord
KeyJoiner func(int16, int16, int16) int64 KeyJoiner func(Coord) int64
) )
func (c Coord) String() string {
return fmt.Sprintf("(%d, %d, %d)", c.X, c.Y, c.Z)
}
func (c Coord) Equals(o Coord) bool {
return c.X == o.X && c.Y == o.Y && c.Z == o.Z
}
// Constructs a database key out of byte slice. // Constructs a database key out of byte slice.
func DecodeStringFromBytes(key []byte) (pos int64, err error) { func DecodeStringFromBytes(key []byte) (pos int64, err error) {
return strconv.ParseInt(string(key), 10, 64) return strconv.ParseInt(string(key), 10, 64)
@ -43,19 +56,19 @@ func DecodeFromBigEndian(key []byte) (int64, error) {
return int64(binary.BigEndian.Uint64(key)), nil return int64(binary.BigEndian.Uint64(key)), nil
} }
func XYZToInterleaved(x, y, z int16) (result int64) { func CoordToInterleaved(c Coord) (result int64) {
const end = 1 << (numBitsPerComponent + 1) const end = 1 << (numBitsPerComponent + 1)
setmask := int64(1) setmask := int64(1)
for mask := int16(1); mask != end; mask <<= 1 { for mask := int16(1); mask != end; mask <<= 1 {
if x&mask != 0 { if c.X&mask != 0 {
result |= setmask result |= setmask
} }
setmask <<= 1 setmask <<= 1
if y&mask != 0 { if c.Y&mask != 0 {
result |= setmask result |= setmask
} }
setmask <<= 1 setmask <<= 1
if z&mask != 0 { if c.Z&mask != 0 {
result |= setmask result |= setmask
} }
setmask <<= 1 setmask <<= 1
@ -63,21 +76,22 @@ func XYZToInterleaved(x, y, z int16) (result int64) {
return return
} }
func InterleavedToXYZ(c int64) (x, y, z int16) { func InterleavedToCoord(pos int64) Coord {
const end = 1 << (numBitsPerComponent + 1) const end = 1 << (numBitsPerComponent + 1)
var x, y, z int16
for mask := int16(1); mask != end; mask <<= 1 { for mask := int16(1); mask != end; mask <<= 1 {
if c&1 == 1 { if pos&1 == 1 {
x |= mask x |= mask
} }
c >>= 1 pos >>= 1
if c&1 == 1 { if pos&1 == 1 {
y |= mask y |= mask
} }
c >>= 1 pos >>= 1
if c&1 == 1 { if pos&1 == 1 {
z |= mask z |= mask
} }
c >>= 1 pos >>= 1
} }
if x >= 1<<numBitsPerComponent { if x >= 1<<numBitsPerComponent {
x -= end x -= end
@ -88,13 +102,13 @@ func InterleavedToXYZ(c int64) (x, y, z int16) {
if z >= 1<<numBitsPerComponent { if z >= 1<<numBitsPerComponent {
z -= end z -= end
} }
return return Coord{X: x, Y: y, Z: z}
} }
func XYZToPlain(x, y, z int16) int64 { func CoordToPlain(c Coord) int64 {
return int64(z)<<(2*numBitsPerComponent) + return int64(c.Z)<<(2*numBitsPerComponent) +
int64(y)<<numBitsPerComponent + int64(c.Y)<<numBitsPerComponent +
int64(x) int64(c.X)
} }
func unsignedToSigned(i int16) int16 { func unsignedToSigned(i int16) int16 {
@ -113,22 +127,19 @@ func pythonModulo(i int16) int16 {
return modulo - -i&mask return modulo - -i&mask
} }
// Only to match the C++ code. func PlainToCoord(i int64) (c Coord) {
func PlainToXYZ(i int64) (x, y, z int16) { c.X = unsignedToSigned(pythonModulo(int16(i)))
x = unsignedToSigned(pythonModulo(int16(i))) i = (i - int64(c.X)) >> numBitsPerComponent
i = (i - int64(x)) >> numBitsPerComponent c.Y = unsignedToSigned(pythonModulo(int16(i)))
y = unsignedToSigned(pythonModulo(int16(i))) i = (i - int64(c.Y)) >> numBitsPerComponent
i = (i - int64(y)) >> numBitsPerComponent c.Z = unsignedToSigned(pythonModulo(int16(i)))
z = unsignedToSigned(pythonModulo(int16(i)))
return return
} }
func TransformPlainToInterleaved(pos int64) int64 { func TransformPlainToInterleaved(pos int64) int64 {
x, y, z := PlainToXYZ(pos) return CoordToInterleaved(PlainToCoord(pos))
return XYZToInterleaved(x, y, z)
} }
func TransformInterleavedPlain(pos int64) int64 { func TransformInterleavedPlain(pos int64) int64 {
x, y, z := InterleavedToXYZ(pos) return CoordToPlain(InterleavedToCoord(pos))
return XYZToPlain(x, y, z)
} }

View File

@ -8,11 +8,11 @@ var data = []int16{
-150, -1, 0, 1, 88, 524, 527, 549, -150, -1, 0, 1, 88, 524, 527, 549,
1783, 1817, 1826, 2028, 2032} 1783, 1817, 1826, 2028, 2032}
func allData(f func(int16, int16, int16)) { func allData(f func(Coord)) {
for _, z := range data { for _, z := range data {
for _, y := range data { for _, y := range data {
for _, x := range data { for _, x := range data {
f(x, y, z) f(Coord{X: x, Y: y, Z: z})
} }
} }
} }
@ -22,84 +22,79 @@ func checkEncodeDecode(
desc string, desc string,
join KeyJoiner, join KeyJoiner,
encode KeyEncoder, decode KeyDecoder, encode KeyEncoder, decode KeyDecoder,
x, y, z int16, t *testing.T) { c Coord, t *testing.T) {
k1 := join(x, y, z) k1 := join(c)
var err error var err error
var b []byte var b []byte
if b, err = encode(k1); err != nil { if b, err = encode(k1); err != nil {
t.Errorf("%s: Failed to encode (%d, %d, %d) %s\n", t.Errorf("%s: Failed to encode %s %s\n", desc, c, err)
desc, x, y, z, err)
return return
} }
var k2 int64 var k2 int64
if k2, err = decode(b); err != nil { if k2, err = decode(b); err != nil {
t.Errorf("%s: Failed to decode (%d, %d, %d) %s\n", t.Errorf("%s: Failed to decode %s %s\n", desc, c, err)
desc, x, y, z, err)
return return
} }
if k1 != k2 { if k1 != k2 {
t.Errorf("%s: Expected %d got %d for (%d, %d, %d) %b\n", t.Errorf("%s: Expected %d got %d for %s\n", desc, k1, k2, c)
desc, k1, k2, x, y, z)
} }
} }
func TestEncodeDecode(t *testing.T) { func TestEncodeDecode(t *testing.T) {
allData(func(x, y, z int16) { allData(func(c Coord) {
checkEncodeDecode( checkEncodeDecode(
"Big endian", "Big endian",
XYZToInterleaved, CoordToInterleaved,
EncodeToBigEndian, DecodeFromBigEndian, EncodeToBigEndian, DecodeFromBigEndian,
x, y, z, t) c, t)
}) })
allData(func(x, y, z int16) { allData(func(c Coord) {
checkEncodeDecode( checkEncodeDecode(
"String", "String",
XYZToInterleaved, CoordToInterleaved,
EncodeStringToBytes, DecodeStringFromBytes, EncodeStringToBytes, DecodeStringFromBytes,
x, y, z, t) c, t)
}) })
} }
func checkJoinSplit( func checkJoinSplit(
desc string, desc string,
join KeyJoiner, split KeySplitter, join KeyJoiner, split KeySplitter,
x1, y1, z1 int16, t *testing.T) { c Coord, t *testing.T) {
k1 := join(x1, y1, z1) k := join(c)
x2, y2, z2 := split(k1) s := split(k)
if x1 != x2 || y1 != y2 || z1 != z2 { if !s.Equals(c) {
t.Errorf("%s: Expected (%d, %d, %d) got (%d, %d, %d) %b\n", t.Errorf("%s: Expected %s got %s %b\n", desc, c, s, k)
desc, x1, y1, z1, x2, y2, z2, k1)
} }
} }
func TestJoinSplit(t *testing.T) { func TestJoinSplit(t *testing.T) {
allData(func(x, y, z int16) { allData(func(c Coord) {
checkJoinSplit( checkJoinSplit(
"P2XYZ(XYZ2P(xyz))", "P2C(C2P(xyz))",
XYZToPlain, PlainToXYZ, CoordToPlain, PlainToCoord,
x, y, z, t) c, t)
}) })
allData(func(x, y, z int16) { allData(func(c Coord) {
checkJoinSplit( checkJoinSplit(
"I2XYZ(XYZ2I(xyz))", "I2C(C2I(xyz))",
XYZToInterleaved, InterleavedToXYZ, CoordToInterleaved, InterleavedToCoord,
x, y, z, t) c, t)
}) })
} }
func checkTransformer( func checkTransformer(
desc string, joiner KeyJoiner, desc string, joiner KeyJoiner,
transform KeyTransformer, transform KeyTransformer,
x, y, z int16, t *testing.T) { c Coord, t *testing.T) {
k1 := joiner(x, y, z) k1 := joiner(c)
k2 := transform(k1) k2 := transform(k1)
if k2 != k1 { if k2 != k1 {
t.Errorf("%s: Expected %v got %v for (%d, %d, %d)\n", t.Errorf("%s: Expected %v got %v for %s\n", desc, k1, k2, c)
desc, k1, k2, x, y, z)
} }
} }
@ -114,25 +109,25 @@ func compose(transforms ...KeyTransformer) KeyTransformer {
func TestTransforms(t *testing.T) { func TestTransforms(t *testing.T) {
// Mainly to check the test itself. // Mainly to check the test itself.
allData(func(x, y, z int16) { allData(func(c Coord) {
checkTransformer( checkTransformer(
"plain", "plain",
XYZToPlain, CoordToPlain,
compose(), compose(),
x, y, z, t) c, t)
}) })
allData(func(x, y, z int16) { allData(func(c Coord) {
checkTransformer( checkTransformer(
"I2P(P2I(plain))", "I2P(P2I(plain))",
XYZToPlain, CoordToPlain,
compose(TransformPlainToInterleaved, TransformInterleavedPlain), compose(TransformPlainToInterleaved, TransformInterleavedPlain),
x, y, z, t) c, t)
}) })
allData(func(x, y, z int16) { allData(func(c Coord) {
checkTransformer( checkTransformer(
"P2I(I2P(interleaved))", "P2I(I2P(interleaved))",
XYZToInterleaved, CoordToInterleaved,
compose(TransformInterleavedPlain, TransformPlainToInterleaved), compose(TransformInterleavedPlain, TransformPlainToInterleaved),
x, y, z, t) c, t)
}) })
} }