Skip to content

Commit ad304c6

Browse files
authored
Merge pull request #46 from dolmen-go/fix-EncodeHex
Fix EncodeHex/DecodeHex
2 parents d5e694a + b97bdbf commit ad304c6

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

hashids.go

+28-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"errors"
1111
"fmt"
1212
"math"
13-
"strconv"
1413
"strings"
1514
)
1615

@@ -224,16 +223,26 @@ func (h *HashID) EncodeInt64(numbers []int64) (string, error) {
224223
// EncodeHex hashes a hexadecimal string to a string containing at least MinLength characters taken from the Alphabet.
225224
// A hexadecimal string should not contain the 0x prefix.
226225
// Use DecodeHex using the same Alphabet and Salt to get back the hexadecimal string.
226+
//
227+
// Each hex nibble is encoded as an integer in range [16, 31].
227228
func (h *HashID) EncodeHex(hex string) (string, error) {
228-
chars := []rune(hex)
229-
nums := []int{}
230-
231-
for _, s := range chars {
232-
n, err := strconv.ParseInt(fmt.Sprintf("1%s", string(s)), 16, 64)
233-
if err != nil {
234-
return "", err
229+
nums := make([]int, len(hex))
230+
231+
for i := 0; i < len(hex); i++ {
232+
b := hex[i]
233+
switch {
234+
case (b >= '0') && (b <= '9'):
235+
b -= '0'
236+
case (b >= 'a') && (b <= 'f'):
237+
b -= 'a' - 'A'
238+
fallthrough
239+
case (b >= 'A') && (b <= 'F'):
240+
b -= ('A' - 0xA)
241+
default:
242+
return "", errors.New("invalid hex digit")
235243
}
236-
nums = append(nums, int(n))
244+
// Each int is in range [16, 31]
245+
nums[i] = 0x10 + int(b)
237246
}
238247

239248
return h.Encode(nums)
@@ -322,19 +331,23 @@ func (h *HashID) DecodeInt64WithError(hash string) ([]int64, error) {
322331

323332
// DecodeHex unhashes the string passed to a hexadecimal string.
324333
// It is symmetric with EncodeHex if the Alphabet and Salt are the same ones which were used to hash.
334+
//
335+
// Each hex nibble is decoded from an integer in range [16, 31].
325336
func (h *HashID) DecodeHex(hash string) (string, error) {
326337
numbers, err := h.DecodeInt64WithError(hash)
327338
if err != nil {
328339
return "", err
329340
}
330341

331-
ret := ""
332-
for _, n := range numbers {
333-
nHex := fmt.Sprintf("%X", n)
334-
ret = strings.ToLower(fmt.Sprintf("%s%s", ret, nHex[1:len(nHex)]))
342+
const hex = "0123456789abcdef"
343+
b := make([]byte, len(numbers))
344+
for i, n := range numbers {
345+
if n < 0x10 || n > 0x1f {
346+
return "", errors.New("invalid number")
347+
}
348+
b[i] = hex[n-0x10]
335349
}
336-
337-
return ret, nil
350+
return string(b), nil
338351
}
339352

340353
func splitRunes(input, seps []rune) [][]rune {

hashids_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ func TestEncodeDecodeHex(t *testing.T) {
6161
t.Fatal(err)
6262
}
6363

64+
const expected = "qmTqfesOIqHrsoCYf9UkFZixSKuBT4umuruXuMiDsVsbSrfV"
65+
if hash != expected {
66+
t.Fatalf("got %q, expected %q", hash, expected)
67+
}
68+
6469
dec, err := hid.DecodeHex(hash)
6570
if err != nil {
6671
t.Fatal(err)

0 commit comments

Comments
 (0)