File size: 4,315 Bytes
b110593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//                           _       _
// __      _____  __ ___   ___  __ _| |_ ___
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
//  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
//   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
//
//  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
//
//  CONTACT: [email protected]
//

package inverted

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math"

	"github.com/pkg/errors"
)

// LexicographicallySortableFloat64 transforms a conversion to a
// lexicographically sortable byte slice. In general, for lexicographical
// sorting big endian notatino is required. Additionally  the sign needs to be
// flipped in any case, but additionally each remaining byte also needs to be
// flipped if the number is negative
func LexicographicallySortableFloat64(in float64) ([]byte, error) {
	buf := bytes.NewBuffer(nil)

	err := binary.Write(buf, binary.BigEndian, in)
	if err != nil {
		return nil, errors.Wrap(err, "serialize float64 value as big endian")
	}

	var out []byte
	if in >= 0 {
		// on positive numbers only flip the sign
		out = buf.Bytes()
		firstByte := out[0] ^ 0x80
		out = append([]byte{firstByte}, out[1:]...)
	} else {
		// on negative numbers flip every bit
		out = make([]byte, 8)
		for i, b := range buf.Bytes() {
			out[i] = b ^ 0xFF
		}
	}

	return out, nil
}

// ParseLexicographicallySortableFloat64 reverses the changes in
// LexicographicallySortableFloat64
func ParseLexicographicallySortableFloat64(in []byte) (float64, error) {
	if len(in) != 8 {
		return 0, fmt.Errorf("float64 must be 8 bytes long, got: %d", len(in))
	}

	flipped := make([]byte, 8)
	if in[0]&0x80 == 0x80 {
		// encoded as negative means it was originally positive, so we only need to
		// flip the sign
		flipped[0] = in[0] ^ 0x80

		// the remainder can be copied
		for i := 1; i < 8; i++ {
			flipped[i] = in[i]
		}
	} else {
		// encoded as positive means it was originally negative, so we need to flip
		// everything
		for i := 0; i < 8; i++ {
			flipped[i] = in[i] ^ 0xFF
		}
	}

	r := bytes.NewReader(flipped)
	var value float64

	err := binary.Read(r, binary.BigEndian, &value)
	if err != nil {
		return 0, errors.Wrap(err, "deserialize float64 value as big endian")
	}

	return value, nil
}

// LexicographicallySortableInt64 performs a conversion to a lexicographically
// sortable byte slice. For this, big endian notation is required and the sign
// must be flipped
func LexicographicallySortableInt64(in int64) ([]byte, error) {
	buf := bytes.NewBuffer(nil)
	asInt64 := int64(in)

	// flip the sign
	asInt64 = asInt64 ^ math.MinInt64

	err := binary.Write(buf, binary.BigEndian, asInt64)
	if err != nil {
		return nil, errors.Wrap(err, "serialize int value as big endian")
	}

	return buf.Bytes(), nil
}

// ParseLexicographicallySortableInt64 reverses the changes in
// LexicographicallySortableInt64
func ParseLexicographicallySortableInt64(in []byte) (int64, error) {
	if len(in) != 8 {
		return 0, fmt.Errorf("int64 must be 8 bytes long, got: %d", len(in))
	}

	r := bytes.NewReader(in)
	var value int64

	err := binary.Read(r, binary.BigEndian, &value)
	if err != nil {
		return 0, errors.Wrap(err, "deserialize int64 value as big endian")
	}

	return value ^ math.MinInt64, nil
}

// LexicographicallySortableUint64 performs a conversion to a lexicographically
// sortable byte slice. For this, big endian notation is required.
func LexicographicallySortableUint64(in uint64) ([]byte, error) {
	buf := bytes.NewBuffer(nil)

	// no signs to flip as this is a uint
	err := binary.Write(buf, binary.BigEndian, in)
	if err != nil {
		return nil, errors.Wrap(err, "serialize int value as big endian")
	}

	return buf.Bytes(), nil
}

// ParseLexicographicallySortableUint64 reverses the changes in
// LexicographicallySortableUint64
func ParseLexicographicallySortableUint64(in []byte) (uint64, error) {
	if len(in) != 8 {
		return 0, fmt.Errorf("uint64 must be 8 bytes long, got: %d", len(in))
	}

	r := bytes.NewReader(in)
	var value uint64

	err := binary.Read(r, binary.BigEndian, &value)
	if err != nil {
		return 0, errors.Wrap(err, "deserialize uint64 value as big endian")
	}

	return value, nil
}