Add redis support for distributed caching

This commit is contained in:
2025-10-19 01:05:41 +01:00
parent e45b06c86d
commit 92a058da83
238 changed files with 78024 additions and 124 deletions
+26
View File
@@ -0,0 +1,26 @@
This code is derived from the C code in redis-7.2.0/deps/fpconv/*, which has
this license:
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+6
View File
@@ -0,0 +1,6 @@
.PHONY: test fuzz
test:
go test
fuzz:
go test -fuzz=Fuzz
+3
View File
@@ -0,0 +1,3 @@
This is a translation of the actual C code in Redis (7.2) which does the float
-> string conversion.
Strconv does a close enough job, but we can use the exact same logic, so why not.
+286
View File
@@ -0,0 +1,286 @@
package fpconv
import (
"math"
)
var (
fracmask uint64 = 0x000FFFFFFFFFFFFF
expmask uint64 = 0x7FF0000000000000
hiddenbit uint64 = 0x0010000000000000
signmask uint64 = 0x8000000000000000
expbias int64 = 1023 + 52
zeros = []rune("0000000000000000000000")
tens = []uint64{
10000000000000000000,
1000000000000000000,
100000000000000000,
10000000000000000,
1000000000000000,
100000000000000,
10000000000000,
1000000000000,
100000000000,
10000000000,
1000000000,
100000000,
10000000,
1000000,
100000,
10000,
1000,
100,
10,
1}
)
func absv(n int) int {
if n < 0 {
return -n
}
return n
}
func minv(a, b int) int {
if a < b {
return a
}
return b
}
func Dtoa(d float64) string {
var (
dest [25]rune // Note C has 24, which is broken
digits [18]rune
str_len int = 0
neg = false
)
if get_dbits(d)&signmask != 0 {
dest[0] = '-'
str_len++
neg = true
}
if spec := filter_special(d, dest[str_len:]); spec != 0 {
return string(dest[:str_len+spec])
}
var (
k int = 0
ndigits int = grisu2(d, &digits, &k)
)
str_len += emit_digits(&digits, ndigits, dest[str_len:], k, neg)
return string(dest[:str_len])
}
func filter_special(fp float64, dest []rune) int {
if fp == 0.0 {
dest[0] = '0'
return 1
}
if math.IsNaN(fp) {
dest[0] = 'n'
dest[1] = 'a'
dest[2] = 'n'
return 3
}
if math.IsInf(fp, 0) {
dest[0] = 'i'
dest[1] = 'n'
dest[2] = 'f'
return 3
}
return 0
}
func grisu2(d float64, digits *[18]rune, K *int) int {
w := build_fp(d)
lower, upper := get_normalized_boundaries(w)
w = normalize(w)
var k int64
cp := find_cachedpow10(upper.exp, &k)
w = multiply(w, cp)
upper = multiply(upper, cp)
lower = multiply(lower, cp)
lower.frac++
upper.frac--
*K = int(-k)
return generate_digits(w, upper, lower, digits[:], K)
}
func emit_digits(digits *[18]rune, ndigits int, dest []rune, K int, neg bool) int {
exp := int(absv(K + ndigits - 1))
/* write plain integer */
if K >= 0 && (exp < (ndigits + 7)) {
copy(dest, digits[:ndigits])
copy(dest[ndigits:], zeros[:K])
return ndigits + K
}
/* write decimal w/o scientific notation */
if K < 0 && (K > -7 || exp < 4) {
offset := int(ndigits - absv(K))
/* fp < 1.0 -> write leading zero */
if offset <= 0 {
offset = -offset
dest[0] = '0'
dest[1] = '.'
copy(dest[2:], zeros[:offset])
copy(dest[offset+2:], digits[:ndigits])
return ndigits + 2 + offset
/* fp > 1.0 */
} else {
copy(dest, digits[:offset])
dest[offset] = '.'
copy(dest[offset+1:], digits[offset:offset+ndigits-offset])
return ndigits + 1
}
}
/* write decimal w/ scientific notation */
l := 18 // was: 18-neg
if neg {
l--
}
ndigits = minv(ndigits, l)
var idx int = 0
dest[idx] = digits[0]
idx++
if ndigits > 1 {
dest[idx] = '.'
idx++
copy(dest[idx:], digits[+1:ndigits-1+1])
idx += ndigits - 1
}
dest[idx] = 'e'
idx++
sign := '+'
if K+ndigits-1 < 0 {
sign = '-'
}
dest[idx] = sign
idx++
var cent rune = 0
if exp > 99 {
cent = rune(exp / 100)
dest[idx] = cent + '0'
idx++
exp -= int(cent) * 100
}
if exp > 9 {
dec := rune(exp / 10)
dest[idx] = dec + '0'
idx++
exp -= int(dec) * 10
} else if cent != 0 {
dest[idx] = '0'
idx++
}
dest[idx] = rune(exp%10) + '0'
idx++
return idx
}
func generate_digits(fp, upper, lower Fp, digits []rune, K *int) int {
var (
wfrac = uint64(upper.frac - fp.frac)
delta = uint64(upper.frac - lower.frac)
)
one := Fp{
frac: 1 << -upper.exp,
exp: upper.exp,
}
part1 := uint64(upper.frac >> -one.exp)
part2 := uint64(upper.frac & (one.frac - 1))
var (
idx = 0
kappa = 10
index = 10
)
/* 1000000000 */
for ; kappa > 0; index++ {
div := tens[index]
digit := part1 / div
if digit != 0 || idx != 0 {
digits[idx] = rune(digit) + '0'
idx++
}
part1 -= digit * div
kappa--
tmp := (part1 << -one.exp) + part2
if tmp <= delta {
*K += kappa
round_digit(digits, idx, delta, tmp, div<<-one.exp, wfrac)
return idx
}
}
/* 10 */
index = 18
for {
var unit uint64 = tens[index]
part2 *= 10
delta *= 10
kappa--
digit := part2 >> -one.exp
if digit != 0 || idx != 0 {
digits[idx] = rune(digit) + '0'
idx++
}
part2 &= uint64(one.frac) - 1
if part2 < delta {
*K += kappa
round_digit(digits, idx, delta, part2, uint64(one.frac), wfrac*unit)
return idx
}
index--
}
}
func round_digit(digits []rune,
ndigits int,
delta uint64,
rem uint64,
kappa uint64,
frac uint64) {
for rem < frac && delta-rem >= kappa &&
(rem+kappa < frac || frac-rem > rem+kappa-frac) {
digits[ndigits-1]--
rem += kappa
}
}
+96
View File
@@ -0,0 +1,96 @@
package fpconv
import (
"math"
)
type (
Fp struct {
frac uint64
exp int64
}
)
func build_fp(d float64) Fp {
bits := get_dbits(d)
fp := Fp{
frac: bits & fracmask,
exp: int64((bits & expmask) >> 52),
}
if fp.exp != 0 {
fp.frac += hiddenbit
fp.exp -= expbias
} else {
fp.exp = -expbias + 1
}
return fp
}
func normalize(fp Fp) Fp {
for (fp.frac & hiddenbit) == 0 {
fp.frac <<= 1
fp.exp--
}
var shift int64 = 64 - 52 - 1
fp.frac <<= shift
fp.exp -= shift
return fp
}
func multiply(a, b Fp) Fp {
lomask := uint64(0x00000000FFFFFFFF)
var (
ah_bl = uint64((a.frac >> 32) * (b.frac & lomask))
al_bh = uint64((a.frac & lomask) * (b.frac >> 32))
al_bl = uint64((a.frac & lomask) * (b.frac & lomask))
ah_bh = uint64((a.frac >> 32) * (b.frac >> 32))
)
tmp := uint64((ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32))
/* round up */
tmp += uint64(1) << 31
return Fp{
ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32),
a.exp + b.exp + 64,
}
}
func get_dbits(d float64) uint64 {
return math.Float64bits(d)
}
func get_normalized_boundaries(fp Fp) (Fp, Fp) {
upper := Fp{
frac: (fp.frac << 1) + 1,
exp: fp.exp - 1,
}
for (upper.frac & (hiddenbit << 1)) == 0 {
upper.frac <<= 1
upper.exp--
}
var u_shift int64 = 64 - 52 - 2
upper.frac <<= u_shift
upper.exp = upper.exp - u_shift
l_shift := int64(1)
if fp.frac == hiddenbit {
l_shift = 2
}
lower := Fp{
frac: (fp.frac << l_shift) - 1,
exp: fp.exp - l_shift,
}
lower.frac <<= lower.exp - upper.exp
lower.exp = upper.exp
return lower, upper
}
+82
View File
@@ -0,0 +1,82 @@
package fpconv
var (
npowers int64 = 87
steppowers int64 = 8
firstpower int64 = -348 /* 10 ^ -348 */
expmax = -32
expmin = -60
powers_ten = []Fp{
{18054884314459144840, -1220}, {13451937075301367670, -1193},
{10022474136428063862, -1166}, {14934650266808366570, -1140},
{11127181549972568877, -1113}, {16580792590934885855, -1087},
{12353653155963782858, -1060}, {18408377700990114895, -1034},
{13715310171984221708, -1007}, {10218702384817765436, -980},
{15227053142812498563, -954}, {11345038669416679861, -927},
{16905424996341287883, -901}, {12595523146049147757, -874},
{9384396036005875287, -847}, {13983839803942852151, -821},
{10418772551374772303, -794}, {15525180923007089351, -768},
{11567161174868858868, -741}, {17236413322193710309, -715},
{12842128665889583758, -688}, {9568131466127621947, -661},
{14257626930069360058, -635}, {10622759856335341974, -608},
{15829145694278690180, -582}, {11793632577567316726, -555},
{17573882009934360870, -529}, {13093562431584567480, -502},
{9755464219737475723, -475}, {14536774485912137811, -449},
{10830740992659433045, -422}, {16139061738043178685, -396},
{12024538023802026127, -369}, {17917957937422433684, -343},
{13349918974505688015, -316}, {9946464728195732843, -289},
{14821387422376473014, -263}, {11042794154864902060, -236},
{16455045573212060422, -210}, {12259964326927110867, -183},
{18268770466636286478, -157}, {13611294676837538539, -130},
{10141204801825835212, -103}, {15111572745182864684, -77},
{11258999068426240000, -50}, {16777216000000000000, -24},
{12500000000000000000, 3}, {9313225746154785156, 30},
{13877787807814456755, 56}, {10339757656912845936, 83},
{15407439555097886824, 109}, {11479437019748901445, 136},
{17105694144590052135, 162}, {12744735289059618216, 189},
{9495567745759798747, 216}, {14149498560666738074, 242},
{10542197943230523224, 269}, {15709099088952724970, 295},
{11704190886730495818, 322}, {17440603504673385349, 348},
{12994262207056124023, 375}, {9681479787123295682, 402},
{14426529090290212157, 428}, {10748601772107342003, 455},
{16016664761464807395, 481}, {11933345169920330789, 508},
{17782069995880619868, 534}, {13248674568444952270, 561},
{9871031767461413346, 588}, {14708983551653345445, 614},
{10959046745042015199, 641}, {16330252207878254650, 667},
{12166986024289022870, 694}, {18130221999122236476, 720},
{13508068024458167312, 747}, {10064294952495520794, 774},
{14996968138956309548, 800}, {11173611982879273257, 827},
{16649979327439178909, 853}, {12405201291620119593, 880},
{9242595204427927429, 907}, {13772540099066387757, 933},
{10261342003245940623, 960}, {15290591125556738113, 986},
{11392378155556871081, 1013}, {16975966327722178521, 1039},
{12648080533535911531, 1066},
}
)
func find_cachedpow10(exp int64, k *int64) Fp {
one_log_ten := 0.30102999566398114
approx := int64(float64(-(exp + npowers)) * one_log_ten)
idx := int((approx - firstpower) / steppowers)
for {
current := int(exp + powers_ten[idx].exp + 64)
if current < expmin {
idx++
continue
}
if current > expmax {
idx--
continue
}
*k = (firstpower + int64(idx)*steppowers)
return powers_ten[idx]
}
}