2014-08-04 17:10:19 +02:00
|
|
|
// 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.
|
|
|
|
|
2014-08-16 16:06:42 +02:00
|
|
|
package common
|
2014-08-04 17:10:19 +02:00
|
|
|
|
2014-08-16 17:41:54 +02:00
|
|
|
import (
|
2014-08-18 16:29:17 +02:00
|
|
|
"encoding/binary"
|
2014-08-18 21:33:58 +02:00
|
|
|
"fmt"
|
2014-08-16 17:41:54 +02:00
|
|
|
"strconv"
|
|
|
|
)
|
2014-08-04 17:10:19 +02:00
|
|
|
|
2014-08-18 18:01:34 +02:00
|
|
|
const (
|
|
|
|
numBitsPerComponent = 12
|
2014-08-18 19:58:10 +02:00
|
|
|
modulo = 1 << numBitsPerComponent
|
|
|
|
maxPositive = modulo / 2
|
2014-08-18 18:01:34 +02:00
|
|
|
)
|
|
|
|
|
2014-08-18 16:29:17 +02:00
|
|
|
type (
|
2014-08-18 21:33:58 +02:00
|
|
|
Coord struct {
|
|
|
|
X, Y, Z int16
|
|
|
|
}
|
|
|
|
|
2014-08-18 16:29:17 +02:00
|
|
|
KeyTransformer func(int64) int64
|
2014-08-18 18:01:34 +02:00
|
|
|
KeyEncoder func(int64) ([]byte, error)
|
|
|
|
KeyDecoder func([]byte) (int64, error)
|
2014-08-22 22:26:03 +02:00
|
|
|
KeyTranscoder func([]byte) ([]byte, error)
|
2014-08-18 21:33:58 +02:00
|
|
|
KeySplitter func(int64) Coord
|
|
|
|
KeyJoiner func(Coord) int64
|
2014-08-18 16:29:17 +02:00
|
|
|
)
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-08-04 17:10:19 +02:00
|
|
|
// Constructs a database key out of byte slice.
|
2014-08-18 18:01:34 +02:00
|
|
|
func DecodeStringFromBytes(key []byte) (pos int64, err error) {
|
2014-08-04 17:10:19 +02:00
|
|
|
return strconv.ParseInt(string(key), 10, 64)
|
|
|
|
}
|
2014-08-16 17:41:54 +02:00
|
|
|
|
|
|
|
// Encode a block pos to byte slice.
|
2014-08-18 18:01:34 +02:00
|
|
|
func EncodeStringToBytes(key int64) ([]byte, error) {
|
|
|
|
return []byte(strconv.FormatInt(key, 10)), nil
|
2014-08-16 17:41:54 +02:00
|
|
|
}
|
2014-08-18 16:29:17 +02:00
|
|
|
|
2014-08-18 18:01:34 +02:00
|
|
|
func EncodeToBigEndian(key int64) (enc []byte, err error) {
|
2014-08-18 16:29:17 +02:00
|
|
|
enc = make([]byte, 8)
|
|
|
|
binary.BigEndian.PutUint64(enc, uint64(key))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-18 18:01:34 +02:00
|
|
|
func DecodeFromBigEndian(key []byte) (int64, error) {
|
|
|
|
return int64(binary.BigEndian.Uint64(key)), nil
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
func CoordToInterleaved(c Coord) (result int64) {
|
2014-08-18 16:29:17 +02:00
|
|
|
const end = 1 << (numBitsPerComponent + 1)
|
|
|
|
setmask := int64(1)
|
|
|
|
for mask := int16(1); mask != end; mask <<= 1 {
|
2014-08-18 21:33:58 +02:00
|
|
|
if c.X&mask != 0 {
|
2014-08-18 16:29:17 +02:00
|
|
|
result |= setmask
|
|
|
|
}
|
|
|
|
setmask <<= 1
|
2014-08-18 21:33:58 +02:00
|
|
|
if c.Y&mask != 0 {
|
2014-08-18 16:29:17 +02:00
|
|
|
result |= setmask
|
|
|
|
}
|
|
|
|
setmask <<= 1
|
2014-08-18 21:33:58 +02:00
|
|
|
if c.Z&mask != 0 {
|
2014-08-18 16:29:17 +02:00
|
|
|
result |= setmask
|
|
|
|
}
|
|
|
|
setmask <<= 1
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
func InterleavedToCoord(pos int64) Coord {
|
2014-08-18 16:29:17 +02:00
|
|
|
const end = 1 << (numBitsPerComponent + 1)
|
2014-08-18 21:33:58 +02:00
|
|
|
var x, y, z int16
|
2014-08-18 16:29:17 +02:00
|
|
|
for mask := int16(1); mask != end; mask <<= 1 {
|
2014-08-18 21:33:58 +02:00
|
|
|
if pos&1 == 1 {
|
2014-08-18 16:29:17 +02:00
|
|
|
x |= mask
|
|
|
|
}
|
2014-08-18 21:33:58 +02:00
|
|
|
pos >>= 1
|
|
|
|
if pos&1 == 1 {
|
2014-08-18 16:29:17 +02:00
|
|
|
y |= mask
|
|
|
|
}
|
2014-08-18 21:33:58 +02:00
|
|
|
pos >>= 1
|
|
|
|
if pos&1 == 1 {
|
2014-08-18 16:29:17 +02:00
|
|
|
z |= mask
|
|
|
|
}
|
2014-08-18 21:33:58 +02:00
|
|
|
pos >>= 1
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
if x >= 1<<numBitsPerComponent {
|
|
|
|
x -= end
|
|
|
|
}
|
|
|
|
if y >= 1<<numBitsPerComponent {
|
|
|
|
y -= end
|
|
|
|
}
|
|
|
|
if z >= 1<<numBitsPerComponent {
|
|
|
|
z -= end
|
|
|
|
}
|
2014-08-18 21:33:58 +02:00
|
|
|
return Coord{X: x, Y: y, Z: z}
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
func CoordToPlain(c Coord) int64 {
|
|
|
|
return int64(c.Z)<<(2*numBitsPerComponent) +
|
|
|
|
int64(c.Y)<<numBitsPerComponent +
|
|
|
|
int64(c.X)
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func unsignedToSigned(i int16) int16 {
|
|
|
|
if i < maxPositive {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
return i - maxPositive*2
|
|
|
|
}
|
|
|
|
|
|
|
|
// To match C++ code.
|
|
|
|
func pythonModulo(i int16) int16 {
|
2014-08-18 19:58:10 +02:00
|
|
|
const mask = modulo - 1
|
2014-08-18 16:29:17 +02:00
|
|
|
if i >= 0 {
|
2014-08-18 19:58:10 +02:00
|
|
|
return i & mask
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
2014-08-18 19:58:10 +02:00
|
|
|
return modulo - -i&mask
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:33:58 +02:00
|
|
|
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)))
|
2014-08-18 16:29:17 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func TransformPlainToInterleaved(pos int64) int64 {
|
2014-08-18 21:33:58 +02:00
|
|
|
return CoordToInterleaved(PlainToCoord(pos))
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 22:19:42 +02:00
|
|
|
func TransformInterleavedToPlain(pos int64) int64 {
|
2014-08-18 21:33:58 +02:00
|
|
|
return CoordToPlain(InterleavedToCoord(pos))
|
2014-08-18 16:29:17 +02:00
|
|
|
}
|
2014-08-21 14:46:34 +02:00
|
|
|
|
2014-08-21 22:19:42 +02:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2014-08-22 22:26:03 +02:00
|
|
|
func IdentityTranscoder(key []byte) ([]byte, error) {
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
2014-08-21 14:46:34 +02:00
|
|
|
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
|
|
|
|
}
|