Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.103",
"version": "10.0.102",
"rollForward": "latestMajor",
"allowPrerelease": false
}
Expand Down
9 changes: 7 additions & 2 deletions libs/server/Objects/SortedSetGeo/GeoHash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,13 @@ public static string GetGeoHashCode(long hash)

for (var i = 0; i < chars.Length; i++)
{
// Shift and mask the five most significant bits for index to the base-32 table.
chars[i] = (char)base32Chars[(int)(hash >> (BitsOfPrecision - 5)) & 0x1F];
// We have just 52 bits, but the API outputs an 11-character geohash (55 bits).
// For compatibility with Redis, the last character is always '0' since the
// remaining bits are not stored with sufficient precision to determine it.
var idx = i < chars.Length - 1
? (int)(hash >> (BitsOfPrecision - 5)) & 0x1F
Copy link
Copy Markdown
Contributor

@PaulusParssinen PaulusParssinen Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider instead something along the lines of

var base32Chars = "0123456789bcdefghjkmnpqrstuvwxyz"u8;

// We have just 52 bits, but the API outputs an 11-character geohash (55 bits).
// For compatibility with Redis, the last character is always '0'.
chars[^1] = '0';

for (var i = 0; i < chars.Length - 1; i++)
{
    // Shift and mask the five most significant bits for index to the base-32 table.
    chars[i] = (char)base32Chars[(int)(hash >> (BitsOfPrecision - 5)) & 0x1F];

    // Shift the encoded bits out.
    hash <<= 5;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot - incorporate this feedback

: 0;
chars[i] = (char)base32Chars[idx];

// Shift the encoded bits out.
hash <<= 5;
Expand Down
17 changes: 9 additions & 8 deletions test/Garnet.test/GeoHashTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public void CanEncodeAndDecodeCoordinates(double latitude, double longitude)
}

[Test]
[TestCase(30.5388942218, 104.0555758833, 4024744861876082, "wm3vxz6vywh")]
[TestCase(27.988056, 86.925278, 3636631039000829, "tuvz4p141z8")]
[TestCase(30.5388942218, 104.0555758833, 4024744861876082, "wm3vxz6vyw0")]
[TestCase(27.988056, 86.925278, 3636631039000829, "tuvz4p141z0")]
[TestCase(37.502669, 15.087269, 3476216502357864, "sqdtr74hyu0")]
[TestCase(38.115556, 13.361389, 3476004292229755, "sqc8b49rnys")]
[TestCase(38.918250, -77.427944, 1787100258949719, "dqbvqhfenps")]
[TestCase(38.115556, 13.361389, 3476004292229755, "sqc8b49rny0")]
[TestCase(38.918250, -77.427944, 1787100258949719, "dqbvqhfenp0")]
[TestCase(0.0, 0.0, 0xC000000000000, "s0000000000")]
[TestCase(-90.0, -180.0, 0, "00000000000")]
[TestCase(90.0, 180.0, 0xFFFFFFFFFFFFF, "zzzzzzzzzzs")]
[TestCase(89.99999999999999, 179.99999999999997, 0xFFFFFFFFFFFFF, "zzzzzzzzzzs")]
[TestCase(90.0, 180.0, 0xFFFFFFFFFFFFF, "zzzzzzzzzz0")]
[TestCase(89.99999999999999, 179.99999999999997, 0xFFFFFFFFFFFFF, "zzzzzzzzzz0")]
public void CanEncodeAndDecodeCoordinatesWithGeoHashCode(
double latitude,
double longitude,
Expand All @@ -62,8 +62,9 @@ public void CanEncodeAndDecodeCoordinatesWithGeoHashCode(

ClassicAssert.AreEqual(expectedHashInteger, hashInteger);

// Note: while we are comparing the entire textual representation of geohash (11 characters)
// we are comparing in 52-bit precision, not 55-bit that is expected from GeoHash standard.
// Note: while we are comparing the entire textual representation of geohash (11 characters),
// the data is stored in 52-bit precision (not 55-bit as required by the GeoHash standard).
// For compatibility with Redis, the last character is always '0'.
ClassicAssert.AreEqual(expectedHash, hash);
}
}
Expand Down
8 changes: 4 additions & 4 deletions test/Garnet.test/RespSortedSetGeoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void CanUseGeoPos()

var responseHash = db.GeoHash(new RedisKey("SecondKey"), ["Palermo"]);
ClassicAssert.AreEqual(1, responseHash.Length);
ClassicAssert.AreEqual("sqc8b49rnys", responseHash[0]);
ClassicAssert.AreEqual("sqc8b49rny0", responseHash[0]);

memresponse = db.Execute("MEMORY", "USAGE", "SecondKey");
actualValue = ResultType.Integer == memresponse.Resp2Type ? Int32.Parse(memresponse.ToString()) : -1;
Expand Down Expand Up @@ -590,16 +590,16 @@ public void CanUseGeoHash(int bytesSent)
var response = lightClientRequest.Execute("GEOADD Sicily 13.361389 38.115556 Palermo 15.087269 37.502669 Catania", "PING", expectedResponse.Length, bytesSent);
ClassicAssert.AreEqual(expectedResponse, response);

expectedResponse = "*3\r\n$11\r\nsqc8b49rnys\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n+PONG\r\n";
expectedResponse = "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n+PONG\r\n";
response = lightClientRequest.Execute("GEOHASH Sicily Palermo Catania Unknown", "PING", expectedResponse.Length, bytesSent);
ClassicAssert.AreEqual(expectedResponse, response);

expectedResponse = "*3\r\n$11\r\nsqc8b49rnys\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n";
expectedResponse = "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n";
response = lightClientRequest.Execute("GEOHASH Sicily Palermo Catania Unknown", expectedResponse.Length, bytesSent);
ClassicAssert.AreEqual(expectedResponse, response);

// Execute command in chunks
expectedResponse = "*1\r\n$11\r\nsqc8b49rnys\r\n";
expectedResponse = "*1\r\n$11\r\nsqc8b49rny0\r\n";
response = lightClientRequest.Execute("GEOHASH Sicily Palermo", expectedResponse.Length, bytesSent);
ClassicAssert.AreEqual(expectedResponse, response);
}
Expand Down
Loading