mirror of
https://bitbucket.org/s_l_teichmann/mtsatellite
synced 2024-12-24 17:20:18 +01:00
179 lines
3.8 KiB
Go
179 lines
3.8 KiB
Go
// 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)
|
|
KeyTranscoder func([]byte) ([]byte, 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<<numBitsPerComponent {
|
|
x -= end
|
|
}
|
|
if y >= 1<<numBitsPerComponent {
|
|
y -= end
|
|
}
|
|
if z >= 1<<numBitsPerComponent {
|
|
z -= end
|
|
}
|
|
return Coord{X: x, Y: y, Z: z}
|
|
}
|
|
|
|
func CoordToPlain(c Coord) int64 {
|
|
return int64(c.Z)<<(2*numBitsPerComponent) +
|
|
int64(c.Y)<<numBitsPerComponent +
|
|
int64(c.X)
|
|
}
|
|
|
|
func unsignedToSigned(i int16) int16 {
|
|
if i < maxPositive {
|
|
return i
|
|
}
|
|
return i - maxPositive*2
|
|
}
|
|
|
|
// To match C++ code.
|
|
func pythonModulo(i int16) int16 {
|
|
const mask = modulo - 1
|
|
if i >= 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))
|
|
}
|
|
}
|