// 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 } KeyTransformer func(int64) int64 KeyEncoder func(int64) ([]byte, error) KeyDecoder func([]byte) (int64, error) KeySplitter func(int64) Coord 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. func DecodeStringFromBytes(key []byte) (pos int64, err error) { return strconv.ParseInt(string(key), 10, 64) } // Encode a block pos to byte slice. func EncodeStringToBytes(key int64) ([]byte, error) { return []byte(strconv.FormatInt(key, 10)), nil } func EncodeToBigEndian(key int64) (enc []byte, err error) { enc = make([]byte, 8) binary.BigEndian.PutUint64(enc, uint64(key)) return } func DecodeFromBigEndian(key []byte) (int64, error) { return int64(binary.BigEndian.Uint64(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 TransformInterleavedPlain(pos int64) int64 { return CoordToPlain(InterleavedToCoord(pos)) } func SelectKeySplitter(interleaved bool) KeySplitter { if interleaved { return InterleavedToCoord } return PlainToCoord } func SelectKeyJoiner(interleaved bool) KeyJoiner { if interleaved { return CoordToInterleaved } return CoordToPlain } func SelectKeyDecoder(interleaved bool) KeyDecoder { if interleaved { return DecodeFromBigEndian } return DecodeStringFromBytes } func SelectKeyEncoder(interleaved bool) KeyEncoder { if interleaved { return EncodeToBigEndian } return EncodeStringToBytes }