Hashids algorithm v1.0.0 implementation in Java

Overview

Hashids.java Version Stories in Ready CircleCI

A small Java class to generate YouTube-like hashes from one or many numbers.

Ported from javascript hashids.js by Ivan Akimov

What is it?

hashids (Hash ID's) creates short, unique, decodable hashes from unsigned (long) integers.

It was designed for websites to use in URL shortening, tracking stuff, or making pages private (or at least unguessable).

This algorithm tries to satisfy the following requirements:

  1. Hashes must be unique and decodable.
  2. They should be able to contain more than one integer (so you can use them in complex or clustered systems).
  3. You should be able to specify minimum hash length.
  4. Hashes should not contain basic English curse words (since they are meant to appear in public places - like the URL).

Instead of showing items as 1, 2, or 3, you could show them as U6dc, u87U, and HMou. You don't have to store these hashes in the database, but can encode + decode on the fly.

All (long) integers need to be greater than or equal to zero.

Usage

Add the dependency

hashids is available in Maven Central. If you are using Maven, add the following dependency to your pom.xml's dependencies:

<dependency>
  <groupId>org.hashids</groupId>
  <artifactId>hashids</artifactId>
  <version>1.0.3</version>
</dependency>

Alternatively, if you use gradle or are on android, add the following to your app's build.gradle file under dependencies:

compile 'org.hashids:hashids:1.0.3'

Import the package

import org.hashids;

Encoding one number

You can pass a unique salt value so your hashes differ from everyone else's. I use "this is my salt" as an example.

Hashids hashids = new Hashids("this is my salt");
String hash = hashids.encode(12345L);

hash is now going to be:

NkK9

Decoding

Notice during decoding, same salt value is used:

Hashids hashids = new Hashids("this is my salt");
long[] numbers = hashids.decode("NkK9");

numbers is now going to be:

[ 12345 ]

Decoding with different salt

Decoding will not work if salt is changed:

Hashids hashids = new Hashids("this is my pepper");
long[] numbers = hashids.decode("NkK9");

numbers is now going to be:

[]

Encoding several numbers

Hashids hashids = new Hashids("this is my salt");
String hash = hashids.encode(683L, 94108L, 123L, 5L);

hash is now going to be:

aBMswoO2UB3Sj

Decoding is done the same way

Hashids hashids = new Hashids("this is my salt");
long[] numbers = hashids.decode("aBMswoO2UB3Sj");

numbers is now going to be:

[ 683, 94108, 123, 5 ]

Encoding and specifying minimum hash length

Here we encode integer 1, and set the minimum hash length to 8 (by default it's 0 -- meaning hashes will be the shortest possible length).

Hashids hashids = new Hashids("this is my salt", 8);
String hash = hashids.encode(1L);

hash is now going to be:

gB0NV05e

Decoding

Hashids hashids = new Hashids("this is my salt", 8);
long[] numbers = hashids.decode("gB0NV05e");

numbers is now going to be:

[ 1 ]

Specifying custom hash alphabet

Here we set the alphabet to consist of only six letters: "0123456789abcdef"

Hashids hashids = new Hashids("this is my salt", 0, "0123456789abcdef");
String hash = hashids.encode(1234567L);

hash is now going to be:

b332db5

Encoding and decoding "MongoDB" ids

In addition to encoding and decoding long values Hashids provides functionality for encoding and decoding ids in a hex notation such as object ids generated by MongoDB.

Hashids hashids = new Hashids("This is my salt");
String hash = hashids.encodeHex("507f1f77bcf86cd799439011"); // goMYDnAezwurPKWKKxL2
String objectId = hashids.decodeHex(hash); // 507f1f77bcf86cd799439011

Note that the algorithm used for encoding and decoding hex values is not compatible with the algorthm for encoding and decoding long values. That means that you cannot use decodeHex to extract a hex representation of a long id that was encoded with encode.

Randomness

The primary purpose of hashids is to obfuscate ids. It's not meant or tested to be used for security purposes or compression. Having said that, this algorithm does try to make these hashes unguessable and unpredictable:

Repeating numbers

Hashids hashids = new Hashids("this is my salt");
String hash = hashids.encode(5L, 5L, 5L, 5L);

You don't see any repeating patterns that might show there's 4 identical numbers in the hash:

1Wc8cwcE

Same with incremented numbers:

Hashids hashids = new Hashids("this is my salt");
String hash = hashids.encode(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L);

hash will be :

kRHnurhptKcjIDTWC3sx

Incrementing number hashes:

Hashids hashids = new Hashids("this is my salt");
String hash1 = hashids.encode(1L); /* NV */
String hash2 = hashids.encode(2L); /* 6m */
String hash3 = hashids.encode(3L); /* yD */
String hash4 = hashids.encode(4L); /* 2l */
String hash5 = hashids.encode(5L); /* rD */

Bad hashes

I wrote this class with the intent of placing these hashes in visible places - like the URL. If I create a unique hash for each user, it would be unfortunate if the hash ended up accidentally being a bad word. Imagine auto-creating a URL with hash for your user that looks like this - http://example.com/user/a**hole

Therefore, this algorithm tries to avoid generating most common English curse words with the default alphabet. This is done by never placing the following letters next to each other:

c, C, s, S, f, F, h, H, u, U, i, I, t, T

Limitations

The original and reference implementation is the JS (Hashids Website) version. JS number limitation is (2^53 - 1). Our java implementation uses Long, but is limited to the same limits JS is, for the sake of compatibility. If a bigger number is provided, an IllegalArgumentException will be thrown.

Contributors

Contact

Follow me C.C.(@fanweixiao), @IvanAkimov, @0x3333.

License

MIT License. See the LICENSE file.

Comments
  • Should we throw an exception for negative numbers as well?

    Should we throw an exception for negative numbers as well?

    When encoding a number the library throw an exception if the number is loo long (greater than MAX_NUMBER) but return an empty string for negative numbers. IMHO this is a little unexpected, using two different behaviours for the same thing, input validation.

    Since negative numbers are also illegal arguments, shouldn't it throw a IllegalArgumentException?

    bug 
    opened by gabrielhora 10
  • ArrayIndexOutOfBoundsException issues

    ArrayIndexOutOfBoundsException issues

    Both:

    Hashids hashids = new Hashids("this is my salt");
    long[] numbers = hashids.decode("0");
    

    and:

    Hashids hashids = new Hashids("this is my salt");
    long[] numbers = hashids.decode("test.txt");
    

    are causing exceptions in various places.

    I realize that they are not valid hashes, but the exception should either be declared or handled.

    Hope this helps.

    bug 
    opened by ethauvin 7
  • Relax MAX_NUMBER restriction

    Relax MAX_NUMBER restriction

    I understand the desire to be completely compatible with the Javascript implementation/limitiations, as explained in #47 and in the docs, but is it possible to provide a toggle that disables this functionality?

    This would ensure backward compatibility with Javascript's limitations, but would allow this library to make use of the full potential that the Java platform provides.

    Other implementations (such as Scala and .Net) don't impose these restrictions and allow the library to be used with the native implementation of the number.

    opened by iggymoran 6
  • Why hashids has MAX_NUMBER limitation

    Why hashids has MAX_NUMBER limitation

    Hello,

    I was looking into this great library for my project, but I quickly found undocumented limitation that this library doesn't support any number > 9007199254740992L. Could you please explain the reasoning behind this limitation?

    ids.encode(new Random().nextLong()

    Exception in thread "main" java.lang.IllegalArgumentException: number can not be greater than 9007199254740992L at org.hashids.Hashids.encode(Hashids.java:178) at io.remotable.experiments.ids.IdsExperiment.main(IdsExperiment.java:21)

    opened by opensso 6
  • ArrayIndexOutOfBoundsException

    ArrayIndexOutOfBoundsException

    From issue Issue 43 - kmandeville:

    I have found two other occurrences where I get ArrayIndexOutOfBoundsException from deep in the HashIds code. Hashids hashids = new Hashids("this is my salt"); long[] numbers = hashids.decode("[]");

    and

    Hashids hashids = new Hashids("this is my salt"); long[] numbers = hashids.decode("()");

    Like the original submitter above, these aren't valid hashes, but I'm running into these errors in my web app because someone COULD put these characters in as an ID in a URL accidentally or on purpose. My app is just passing in these characters directly into HashIds. I would expect something other than ArrayIndexOutOfBoundsExceptions.

    bug 
    opened by 0x3333 6
  • Add additonal prefix/suffix for salt version

    Add additonal prefix/suffix for salt version

    Since we are using a salt, it might be a good idea to have a version as part of the actual id. That will allow us to change the salt later if we need to. I'm not sure if this is out of scope for this library or maybe should be added as a best practice. What do you guys think?

    Cheers, Itay

    opened by iyahimovitz 6
  • array index out of bound bug

    array index out of bound bug

    if you call the encode(number) with a random large number. then numberHashInt += (numbers[i] % (i+100)) has a chance to return a negative number, in my case -35. the next calculation use alphabet.toCharArray()[numberHashInt % alphabet.length()] could thow an ArrayIndexOutOfBoundsException.

    bug 
    opened by maxiwu 5
  • Performance Issues

    Performance Issues

    I ran a quick stress test on this library and I'm seeing very poor and unpredictable performance. Generating 1,000,000 ids from the same Hashids instance (same salt) takes anywhere between 15 and 90 seconds on a 2.9GHz i7 CPU using 1.8 GB of RAM. There's also a strange stall somewhere between 30k and 60k in the test loop, after which the rest of the test runs quickly.

    Here's my test case:

        // generates a random 32 character string
        Hashids h = new Hashids(RandomStringUtils.random(32));
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
          h.encode(i);
        }
        long end = System.currentTimeMillis();
        long duration = end - start;
        System.out.println("time taken: " + duration/1000 + " seconds");
        System.out.println("hashes per second: " + 1000000/(duration/1000.0));
    
    invalid 
    opened by nmickuli 5
  • StringIndexOutOfBoundsException when decoding a guard and minHashLength >= 3

    StringIndexOutOfBoundsException when decoding a guard and minHashLength >= 3

    I got this exception when trying to decode some bad data. This test case illustrates the exception.

    public class TestClass
    {
        @Test
        public void test()
        {
            Hashids hasher = new Hashids("this is my salt", 3);
            long[] numbers = hasher.decode("A");
        }
    }
    

    And it throws this exception.

    java.lang.StringIndexOutOfBoundsException: String index out of range: 2
    	at java.lang.String.charAt(String.java:658)
    	at org.hashids.Hashids._encode(Hashids.java:292)
    	at org.hashids.Hashids._decode(Hashids.java:350)
    	at org.hashids.Hashids.decode(Hashids.java:194)
    	at TestClass.test(TestClass.java:15)
    

    Looks like any character that is in HashIds.guards will throw this exception. Other input like "asdf" or any other character not in HashIds.guards seem to work ok.

    Debugging, I noticed that on the call to _encode, usually an array with 0L is passed, but when one of those chars is used, the call is made with an empty array.

    duplicate 
    opened by meiao 4
  • different salt can give random number or crash

    different salt can give random number or crash

    Hi, When using different salt it should return empty array. But I recognized the behavior is quite unpredictable, there is a probability that it decodes to a wrong number instead or crashes with an ArrayIndexOutOfBoundsException.

    Here and example that will decode to a wrong number:

        @Test
        public void encodeAndDecodeTokenWithWrongKey() {
            Hashids a = new Hashids("supersecret",4);
            Hashids b = new Hashids("differentsupersecret",4);
    
            long sampleId = 123l;
            String token = a.encode(sampleId);
    
            long[] ids = b.decode(token); // []long id = { 81143 }
            assertEquals(0, ids.length);
        }
    

    assertion will fail, id[0] will be 81143

    Same test with sampleId = 37l will cause "ArrayIndexOutOfBoundsException"

    opened by chrisport 4
  • Confusion about supported integer range

    Confusion about supported integer range

    The doc says:

    decodable hashes from unsigned (long) integers

    but then it says

    All (long) integers need to be greater than or equal to zero

    And the implementation caps the max number to 2^53 (0-9,007,199,254,740,992)

    https://github.com/10cella/hashids-java/blob/162a263c8007652472bf1821a26d33190730f8a5/src/main/java/org/hashids/Hashids.java#L24


    • IF unsigned 64-bit integer was support, it would accept: 2^64 or 0 - 18,446,744,073,709,551,616 (1.8x10^19)
    • IF signed 64-bit integer was support from zero up, it would accept: 2^63 or 0 - 9,223,372,036,854,775,807 (9.2x10^18)

    You currently support 53 bit long integers - I can only assume that is a bug? Java's primitives are always signed, so you have about 2^63 values in the plus and minus range. The most logical range would be 2^63 if you only want to support from 0 up and not make the caller convert to unsigned longs?

    opened by patrickfav 3
  • Add configurable hash mod base

    Add configurable hash mod base

    When generating hashes that are separated by 100 the hash strings are very similar. This is unavoidable with this implementation and perhaps not a huge deal, but maybe it would be best if the periodicity were a number that is not so "tidy". This PR adds the ability to construct the class with a user-specified hash mod base. I personally would use a similarly large prime number, like 97. The strings are very similar for every 97 ids, but this is probably less likely to be noticed.

    Also increase JUnit 4 version to avoid CVE flagging

    opened by jchennales 0
  • How to solve IllegalArgumentException in encode.

    How to solve IllegalArgumentException in encode.

    In encode, we passed a greater number(like 44913000000003047) than fixed MAX_LIMIT(9007199254740992L). How to solve this case and handle any number to encode.

    opened by SivaKarthik2120 0
  • Added

    Added "separators" customization, NULL pre-checks, and Javadoc comment blocks to all constructors

    Added a new Hashids constructor to support customized "separators" plus a pre-check for NULL that prevents a NullException by defaulting to DEFAULT_SEPS (this behaviour is modeled after the "salt" pre-check for NULL).

    Added a pre-check for NULL for the custom "alphabet" that prevents a NullException by defaulting to DEFAULT_ALPHABET (this behaviour is also modeled after the "salt" pre-check for NULL).

    Added rudimentary comments for existing constructors for better Javadoc output. Added relevant details about the new constructor that supports custom "separators" (should this also be mentioned in the documentation on the web site?).

    opened by randolf 1
  • V2 Proposal

    V2 Proposal

    Version 2.0 Proposal

    This issue will be a discussion point to exchange ideas about version 2.0.

    Big picture

    The community is urging for a more java idiom version, and the current version can't handle these changes, that's why I'm proposing a new version, incorporating all the needs from the community in a broad discussion.

    What about v2?

    The new version must be compatible with original implementation as much as it can, without sacrificing java idiom. We will discuss how it will be implemented and the design considerations will be made here.

    @cazacugmihai Proposal

    • [ ] Use of char arrays and StringBuilder instead of Strings
    • [ ] Precompute and cache recurrent operations
    • [ ] Helper class (CharUtils) for working with char arrays

    @iggymoran Proposal

    • [] Relax MAX_NUMBER restriction

    @KangoV Proposal

    • [] BigInteger as input/output

    Reference implementation PR #53.

    question 
    opened by 0x3333 13
Releases(v1.0.2)
  • v1.0.2(May 29, 2017)

    Version 1.0.2 - May 29, 2017

    Issues fixed:

    • Issue #18 - Thread Safety information
    • Issue #20 - ArrayIndexOutOfBoundsException when decoding invalid token
    • Issue #23 - Fixed separators initialization
    • Issue #30 - Fixed negative numbers encoding
    • Issue #31 - Fixed large number encoding resulting in an ArrayIndexOutOfBoundsException
    • Issue #33 - Create a new release 1.0.2
    • Issue #37 - Performance improvements
    • Issue #43 - Fixed decode of invalid data resulting in an ArrayIndexOutOfBoundsException

    PR merged:

    • PR #25 - Fixed SonarJava squid:S2131 and squid:S2325
    • PR #26 - Started to use StringBuilder instead of adding Strings
    • PR #27 - Fixed identation and Issue #20
    • PR #28 - Performance improvements and code organization
    • PR #34 - Reimplemented unhash using Horner's method(Internal commit, PR closed)
    • PR #39 - Added Gradle specification
    • PR #40 - Fix typo in test names
    Source code(tar.gz)
    Source code(zip)
    hashids-1.0.2-javadoc.jar(25.00 KB)
    hashids-1.0.2-sources.jar(3.75 KB)
    hashids-1.0.2.jar(7.65 KB)
Owner
YoMo
Geo-distributed system Framework
YoMo
Search API with spelling correction using ngram-index algorithm: implementation using Java Spring-boot and MySQL ngram full text search index

Search API to handle Spelling-Corrections Based on N-gram index algorithm: using MySQL Ngram Full-Text Parser Sample Screen-Recording Screen.Recording

Hardik Singh Behl 5 Dec 4, 2021
A near-real-time Mesh Join Algorithm Implementation provided with a Complete Data warehouse for METRO

Mesh Join Algorithm and Data Warehouse A complete Mesh-Join Algorithm Implementation as provided in the paper R-MESHJOIN . This is demonstrated by the

null 3 Aug 11, 2022
A fast and reliable Java micro-library which chooses the sorting algorithm that best fits your needs and sorts the parameter.

A fast and reliable Java micro-library which chooses the sorting algorithm that best fits your needs and sorts the parameter.

Simone Nicol 2 Feb 19, 2022
Ship React Native .jsbundles compressed by Brotli algorithm.

Ship compressed JS bundles Warning: not yet available for Android. Sometimes you need a very small .app, for example if you are building an App Clip.

Ivan Moskalev 37 Nov 15, 2022
Sequence Alignment - Aligns two strings optimally as to minimize the cost of alignment. This algorithm has applications in aligning DNA, RNA, or protein.

Sequence_Alignment Aligns two strings optimally as to minimize the cost of alignment. This algorithm has applications in aligning DNA, RNA, or protein

Suki 1 Jan 8, 2022
A near real time Data Warehouse using the MeshJoin Algorithm

MeshJoin-Data-Warehouse A near real time Data Warehouse using the MeshJoin Algorithm Steps to run the project: Step 1: Run the createDW.sql file -This

M. Adil Fayyaz 2 Dec 1, 2022
We are proceeding Algorithm Study from January, 2022.

AlgorithmStudy2022 We are proceeding Algorithm Study from January, 2022. 2022년 1월 부터 스터디를 진행하고 있습니다. The 1st quarter: 우리는 첫 시즌을 마무리 하였습니다. 첫 시즌은 알고리즘

Jung Hojin 2 Jul 5, 2022
We are proceeding Algorithm Study from July, 2022.

Coding Test in Java 백준 고난이도 문제 ????‍?? 7월 4주차 (07.25 - 07.31) 문제 레벨 URL 승희 코드 호진 코드 수진 코드 연의 코드 문제 가져온 사람 문제 승희 문제 호진 문제 수진 문제 연의 ????‍?? 7월 3주차 (07.1

Jung Hojin (Edlin) 2 Jul 13, 2022
Daily mail subscription implementation using Java Spring-boot and Quartz Scheduler

Daily Mail Subscription Service POC Implemented using Java Spring-boot and Quartz Scheduler Working Application Exposing 3 endpoints /subscription/cre

null 16 Jun 3, 2022
Pure Java implementation of ONCRPC/SUNRPC

ONCRPC4J This is a part of dCache.ORG's NFSv4.1 work. Technically, this is not a fork of Remote Tea RPC library, but formally it is as we was inspired

dCache Project 26 Oct 27, 2022
Pure Java NFSv3 and NFSv4.1 implementation

NFS4J The pure java implementation of NFS server version 3, 4.0 and 4.1 including pNFS extension with nfs4.1-files and flex-files layout types. Buildi

dCache Project 189 Dec 13, 2022
Implementation of mustache.js for Java

Mustache.java Mustache.java is not designed to allow untrusted parties to provide templates. It may be possible to lock it down to provide that safely

Sam Pullara 1.8k Jan 1, 2023
This repository contains CQRS implementation in Java

CQRS Design Pattern Java This repository contains CQRS implementation in Java. I've written this code-base step by step on Medium that is my Turkish c

Yusuf Yılmaz 14 Oct 25, 2022
SimpleIcons4J is a Java implementation of the simple-icons JavaScript library

SimpleIcons4J SimpleIcons4J is a Java implementation of the simple-icons JavaScript library and is inspired by simpleicons.org. This library currently

Hyesung Lee 3 Apr 9, 2022
This program is a simple machine learning implementation in Java for detecting skin pixels.

Skin Detector ?? ?? Detects human skin from images This program is a simple machine learning implementation in Java for detecting skin pixels. How to

Tasmia Zerin 1 Jan 21, 2022
Java implementation of Beacon Chain for Ethereum 2.0, and its Backend API and full Infrastructure.

hailong Implementation of the Ethereum 2.0 Beacon Chain. Based on the (evolving) specification. Build Instructions Install Prerequisites 1) Java 11 Ub

我是高天才! 14 Feb 6, 2022
This project illustrates TDD & Clean Architecture implementation in Java

Banking Kata - Java Overview This project illustrates TDD & Clean Architecture implementation in Java, showing the Use Case Driven Development Approac

Valentina Cupać 191 Dec 28, 2022
A implementation of shadowsocks that base on java's netty framework

Shadowsocks shadowsocks is a fast tunnel proxy that helps you bypass firewalls. shadowsocks-java is a implementation of shadowsocks protocol that base

bigbyto 36 Oct 17, 2022
Budget Proof Key for Code Exchange (PKCE) implementation using Java Spring-boot

Low Budget Proof Key for Code Exchange (PKCE) Implementation using Java Spring-boot Just for fun, low budget implementation of PKCE Auth Flow using a

Hardik Singh Behl 10 Dec 11, 2022