Skip to content

Commit

Permalink
Validate function (#20)
Browse files Browse the repository at this point in the history
Adds a function to validate string geohashes.

Fixes #19
  • Loading branch information
mmcloughlin committed Aug 3, 2019
1 parent a84196f commit e0493a2
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -133,6 +133,13 @@ func NeighborsIntWithPrecision(hash uint64, bits uint) []uint64
NeighborsIntWithPrecision returns a slice of uint64s that correspond to the
provided hash's neighbors at the given precision.

#### func Validate

```go
func Validate(hash string) error
```
Validate the string geohash.

#### type Box

```go
Expand Down
10 changes: 9 additions & 1 deletion base32.go
@@ -1,5 +1,8 @@
package geohash

// invalid is a placeholder for invalid character decodings.
const invalid = 0xff

// encoding encapsulates an encoding defined by a given base32 alphabet.
type encoding struct {
encode string
Expand All @@ -12,14 +15,19 @@ func newEncoding(encoder string) *encoding {
e := new(encoding)
e.encode = encoder
for i := 0; i < len(e.decode); i++ {
e.decode[i] = 0xff
e.decode[i] = invalid
}
for i := 0; i < len(encoder); i++ {
e.decode[encoder[i]] = byte(i)
}
return e
}

// ValidByte reports whether b is part of the encoding.
func (e *encoding) ValidByte(b byte) bool {
return e.decode[b] != invalid
}

// Decode string into bits of a 64-bit word. The string s may be at most 12
// characters.
func (e *encoding) Decode(s string) uint64 {
Expand Down
9 changes: 9 additions & 0 deletions extensive_test.go
Expand Up @@ -49,6 +49,15 @@ func TestPrefixProperty(t *testing.T) {
}
}

// Verify all string hashes pass validation.
func TestValidate(t *testing.T) {
for _, c := range testcases {
if err := Validate(c.hash); err != nil {
t.Fatalf("unexpected error for %q: %s", c.hash, err)
}
}
}

// Test bounding boxes for string geohashes.
func TestBoundingBox(t *testing.T) {
for _, c := range testcases {
Expand Down
19 changes: 19 additions & 0 deletions geohash.go
Expand Up @@ -3,6 +3,8 @@
package geohash

import (
"errors"
"fmt"
"math"
)

Expand Down Expand Up @@ -136,6 +138,23 @@ func BoundingBoxInt(hash uint64) Box {
return BoundingBoxIntWithPrecision(hash, 64)
}

// Validate the string geohash.
func Validate(hash string) error {
// Check length.
if 5*len(hash) > 64 {
return errors.New("too long")
}

// Check characters.
for i := 0; i < len(hash); i++ {
if !base32encoding.ValidByte(hash[i]) {
return fmt.Errorf("invalid character %q", hash[i])
}
}

return nil
}

// Decode the string geohash to a (lat, lng) point.
func Decode(hash string) (lat, lng float64) {
box := BoundingBox(hash)
Expand Down
25 changes: 25 additions & 0 deletions geohash_test.go
Expand Up @@ -117,6 +117,31 @@ func TestLeadingZero(t *testing.T) {
}
}

func TestValidateErrors(t *testing.T) {
cases := []struct {
Hash string
ExpectError string
}{
{
Hash: "0123456789bcdefghjkmnpqrstuvwxyz",
ExpectError: "too long",
},
{
Hash: "bcopqr",
ExpectError: "invalid character 'o'",
},
}
for _, c := range cases {
err := Validate(c.Hash)
if err == nil {
t.Fatalf("%s: got nil, expected %q", c.Hash, c.ExpectError)
}
if err.Error() != c.ExpectError {
t.Errorf("%s: got %q, expected %q", c.Hash, err, c.ExpectError)
}
}
}

func TestNeighbors(t *testing.T) {
for _, c := range neighborsTestCases {
neighbors := Neighbors(c.hashStr)
Expand Down

0 comments on commit e0493a2

Please sign in to comment.