// Copyright 2014 by Sascha L. Teichmann // Use of this source code is governed by the MIT license // that can be found in the LICENSE file. package common import ( "encoding/binary" "fmt" "strconv" ) const ( numBitsPerComponent = 12 modulo = 1 << numBitsPerComponent maxPositive = modulo / 2 ) type ( Coord struct { X, Y, Z int16 } Cuboid struct { P1, P2 Coord } KeyTransformer func(int64) int64 KeyEncoder func(int64) ([]byte, error) KeyDecoder func([]byte) (int64, error) KeyTranscoder func([]byte) ([]byte, error) KeySplitter func(int64) Coord KeyJoiner func(Coord) int64 ) func (cub Cuboid) Contains(c Coord) bool { return c.X >= cub.P1.X && c.X <= cub.P2.X && c.Y >= cub.P1.Y && c.Y <= cub.P2.Y && c.Z >= cub.P1.Z && c.Z <= cub.P2.Z } 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 } func minComponent(a, b int16) int16 { if a < b { return a } return b } func maxComponent(a, b int16) int16 { if a > b { return a } return b } func MinCoord(a, b Coord) Coord { return Coord{ X: minComponent(a.X, b.X), Y: minComponent(a.Y, b.Y), Z: minComponent(a.Z, b.Z)} } func MaxCoord(a, b Coord) Coord { return Coord{ X: maxComponent(a.X, b.X), Y: maxComponent(a.Y, b.Y), Z: maxComponent(a.Z, b.Z)} } // Constructs a database key out of byte slice. func DecodeStringFromBytes(key []byte) (pos int64, err error) { return strconv.ParseInt(string(key), 10, 64) } func StringToBytes(key int64) []byte { return []byte(strconv.FormatInt(key, 10)) } // Encode a block pos to byte slice. func EncodeStringToBytes(key int64) ([]byte, error) { return StringToBytes(key), nil } func ToBigEndian(key int64) []byte { enc := make([]byte, 8) binary.BigEndian.PutUint64(enc, uint64(key)) return enc } func EncodeToBigEndian(key int64) ([]byte, error) { return ToBigEndian(key), nil } func FromBigEndian(key []byte) int64 { return int64(binary.BigEndian.Uint64(key)) } func DecodeFromBigEndian(key []byte) (int64, error) { return FromBigEndian(key), nil } func CoordToInterleaved(c Coord) (result int64) { const end = 1 << (numBitsPerComponent + 1) setmask := int64(1) for mask := int16(1); mask != end; mask <<= 1 { if c.X&mask != 0 { result |= setmask } setmask <<= 1 if c.Y&mask != 0 { result |= setmask } setmask <<= 1 if c.Z&mask != 0 { result |= setmask } setmask <<= 1 } return } func InterleavedToCoord(pos int64) Coord { const end = 1 << (numBitsPerComponent + 1) var x, y, z int16 for mask := int16(1); mask != end; mask <<= 1 { if pos&1 == 1 { x |= mask } pos >>= 1 if pos&1 == 1 { y |= mask } pos >>= 1 if pos&1 == 1 { z |= mask } pos >>= 1 } if x >= 1<= 1<= 1<= 0 { return i & mask } return modulo - -i&mask } func PlainToCoord(i int64) (c Coord) { c.X = unsignedToSigned(pythonModulo(int16(i))) i = (i - int64(c.X)) >> numBitsPerComponent c.Y = unsignedToSigned(pythonModulo(int16(i))) i = (i - int64(c.Y)) >> numBitsPerComponent c.Z = unsignedToSigned(pythonModulo(int16(i))) return } func TransformPlainToInterleaved(pos int64) int64 { return CoordToInterleaved(PlainToCoord(pos)) } func TransformInterleavedToPlain(pos int64) int64 { return CoordToPlain(InterleavedToCoord(pos)) } func DecodeStringFromBytesToInterleaved(key []byte) (v int64, err error) { if v, err = DecodeStringFromBytes(key); err != nil { return } v = TransformPlainToInterleaved(v) return } func EncodeStringToBytesFromInterleaved(key int64) ([]byte, error) { return EncodeStringToBytes(TransformInterleavedToPlain(key)) } func IdentityTranscoder(key []byte) ([]byte, error) { return key, nil } func TranscodePlainToInterleaved(key []byte) ([]byte, error) { if pos, err := DecodeStringFromBytesToInterleaved(key); err != nil { return nil, err } else { return EncodeToBigEndian(pos) } } func TranscodeInterleavedToPlain(key []byte) ([]byte, error) { if pos, err := DecodeFromBigEndian(key); err != nil { return nil, err } else { return EncodeStringToBytes(TransformInterleavedToPlain(pos)) } } // For correctness checks only. func NaiveBigMin(minz, maxz, zcode int64) int64 { var ( c1 = InterleavedToCoord(minz) c2 = InterleavedToCoord(maxz) cand = minz c Coord ) for c.X = c1.X; c.X <= c2.X; c.X++ { for c.Y = c1.Y; c.Y <= c2.Y; c.Y++ { for c.Z = c1.Z; c.Z <= c2.Z; c.Z++ { if z := CoordToInterleaved(c); z > zcode && z < cand { cand = z } } } } return cand }