{ "info": { "author": "Alexander Ljungberg", "author_email": "aljungberg@slevenbits.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: POSIX", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries" ], "description": "HHC\n===\n\n**The most compact way to encode a non-negative number in a URL.**\n\nHHC is a compact format to express a number or binary data in a URL. It uses all characters allowed in\na URL -- the [unreserved characters](http://tools.ietf.org/html/rfc3986#section-2.3) -- making it the most concise way to express a positive integer in a URL.\n\n\n| Format | Value | Length |\n| ------ | --- | --- |\n| Decimal | `302231454903657293676544` | 24 |\n| Hex | `40000000000000000000` | 20 |\n| Naive Base64[1] | `QAAAAAAAAAAAAA%3D%3D` | 20 |\n| Custom Base64[2] | `BAAAAAAAAAAAAA` | 14 |\n| **HHC** | **`fDpEShMz-qput`** | **13** |\n\n\nBeyond being the pinnacle of numeral systems for URLs, HHC is also a ready to go, batteries included solution. This is unlike the most obvious alternative, base64.\n\nHHC encoded values sort alphabetically like the numbers they represent, so you can sort a long list of HHC values from smallest to largest without first decoding them (with one caveat, see below). \n\nHHC works for negative numbers. HHC uses a non-URL safe prefix in this case, so you may need to URL quote the result. Note that `urllib.quote` [escapes the tilde character (~)](http://bugs.python.org/issue16285), which is unnecessary as of RFC3986. If you use this on HHC data, you'll waste bytes. Use the provided `hhc_url_quote` function instead. Non-negative HHC values don't need any URL quoting.\n\n**[1]**: `hhc_url_quote(base64.urlsafe_b64encode(long_to_binary(X)))` \n**[2]**: Using the base64-like encoding scheme found [here](http://stackoverflow.com/a/561704/76900).\n\n## Usage\n\n >>> print(hhc(302231454903657293676544))\n iFsGUkO.0tsxw\n >>> print(hhc_to_int('iFsGUkO.0tsxw'))\n 302231454903657293676544\n\n\n### Sorting\n\nYou can sort HHC encoded values in numerical order using a standard alphabetical sort. The HHC value strings need to be of equal length for this property to hold. You can left-pad them with `-` just before sorting to make them so.\n\nThe `width` parameter of the `hhc` function can do this padding at encoding time. It's better to pad at sort time to save bytes and so you need not predict your maximum width ahead of time.\n\nThis ordering property holds for negative numbers too (-2, -1, 0, 1, 2...).\n\n### Negative Numbers\n\nHHC expresses negative numbers by prefixing the number with `,` (since minus is taken). This is not a URL-safe character so if you URL escape a negative number with HHC you end up with `%2C` which takes up two extra characters. For this reason, HHC is not necessarily the shortest representation of a negative number.\n\n## Installation\n\n pip install hhc\n\n## FAQ\n\n### Is HHC really better than base64 in URLs?\n\nThere are two parts to this.\n\nFirst, a na\u00efve base64 approach would be simple, but very inefficient. Take the 64 bit binary representation of your number and base64 encode it in an URL-safe manner: \n\n >>> base64.urlsafe_b64encode(struct.pack(\">> sum(len(hhc_url_quote(num_encode_base64(n))) for n in range(10 ** 6))\n 3733696\n >>> sum(len(hhc_url_quote(hhc(n))) for n in range(10 ** 8))\n 3708084\n\nLook at those massive savings!\n\n\n### What does HHC stand for?\n\nHexahexaconta. In [IUPAC nomenclature](https://en.wikipedia.org/wiki/IUPAC_numerical_multiplier) (what you use to describe atoms in a molecule), 66 is hexahexaconta. \n\nI originally called this numeral system \"hexahexacontadecimal\" to make it sound like \"hexadecimal\" but as amusing as I found that, it was annoying to type. Also, with the decimal suffix it was akin to saying \"sixty-six-tenth\" which made little sense.\n\n### With compression, couldn't I make shorter representations?\n\nSome data is compressible, some is not. For random data, there is no lossless compression algorithm that can compress the data, and in fact on average any such algorithm would make the data longer. HHC will still be the most compact.\n\n## Tests\n\n[![Build Status](https://travis-ci.org/aljungberg/hhc.svg?branch=master)](https://travis-ci.org/aljungberg/hhc)\n\nTo run the unit tests:\n\n nosetests --with-doctest\n\n## Changelog\n\n### 3.0.2\n\n* Fixed Python 2 compatibility.\n\n(This was already fixed in 3.0.1 but didn't make it into the Pypi release version.)\n\n### 3.0.1\n\n* Backported `.` and `..` special case handling to HHC 2.\n\nThis does not affect backwards compatibility because:\n\n1. It's disabled by default when encoding.\n2. Even when enabled, the output decodes to the same value as before by older versions of HHC.\n\n### 3.0\n\n* Renamed to HHC.\n* Sortable by default.\n* Special case `.` and `..` values.\n\nThis version is **not backwards compatible**. Previously, there were two versions of HHC: normal and sortable. With HHC 3.0 the sortable version is now the \"normal\" and the old normal version is deprecated. The old normal version didn't offer any strong benefits, and added needless complexity.\n\nTo encode or decode the normal HHC 2.0, use the provided legacy methods, `hhc2` and `hhc2_to_int`.\n\n### 2.2.1\n\n* Fixed: pandoc accidentally required.\n\n### 2.2\n\n* Python 3 support (backwards compatible with Python 2.7).\n\n### 2.1\n\n* Fixed: `hhc(-1)` would cause an infinite loop.\n* New: support for negative values.\n\n### 2.0\n\n* New: sortable HHC. This variant of HHC sorts the same alphabetically as numerically for equal length strings.\n* Shorter, more Pythonic method names. The main function is now simply called `hhc`, styled after Python's built in `hex` function. To decode the same, `hhc_to_int` is now used.\n* `import * from hexahexacontadecimal` now only imports the main functions.\n* `urlquote` was renamed to `hhc_url_quote` to make it easier to differentiate from the standard library method.\n\n### 1.0\n\nInitial release.\n\n## License\n\nFree to use and modify under the terms of the BSD open source license.\n\n## Author\n\nAlexander Ljungberg\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/aljungberg/hhc", "keywords": "base64,hexahexacontadecimal,hhc,base66,url", "license": "", "maintainer": "", "maintainer_email": "", "name": "hhc", "package_url": "https://pypi.org/project/hhc/", "platform": "", "project_url": "https://pypi.org/project/hhc/", "project_urls": { "Homepage": "https://github.com/aljungberg/hhc" }, "release_url": "https://pypi.org/project/hhc/3.0.2/", "requires_dist": null, "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "summary": "The best way to express a number in a URL.", "version": "3.0.2" }, "last_serial": 5522297, "releases": { "3.0.0": [ { "comment_text": "", "digests": { "md5": "453d942d7a4b576bf9f56629661ba72f", "sha256": "b472288c2f8340e8ca0afc054b6fcdf87ab8f729dba4d8b3ff2aacae98b82de2" }, "downloads": -1, "filename": "hhc-3.0.0-py2-none-any.whl", "has_sig": false, "md5_digest": "453d942d7a4b576bf9f56629661ba72f", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 8742, "upload_time": "2019-05-27T16:19:28", "url": "https://files.pythonhosted.org/packages/02/b9/8b8fce3306745557a2d2dd8db8d6d51be2b5833aacd6882cd9100aeb7782/hhc-3.0.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8f3ec47eade6916172058b48940426d5", "sha256": "138e8fda030de1a525f7dba9dd91c4ab39355c0223ec912bb01492045207d868" }, "downloads": -1, "filename": "hhc-3.0.0.tar.gz", "has_sig": false, "md5_digest": "8f3ec47eade6916172058b48940426d5", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 7090, "upload_time": "2019-05-27T16:19:31", "url": "https://files.pythonhosted.org/packages/d3/da/b034e43497401a433f19465c99e5f5a238a9cef8942c609eb66ae9979d03/hhc-3.0.0.tar.gz" } ], "3.0.1": [ { "comment_text": "", "digests": { "md5": "5df67316e4efaac44274344fa9b2d6ca", "sha256": "d2c525c3983981e49afa46e5d652a89018bc6d3a1ab17d39233d863c6d574b96" }, "downloads": -1, "filename": "hhc-3.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "5df67316e4efaac44274344fa9b2d6ca", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 8927, "upload_time": "2019-05-27T19:24:58", "url": "https://files.pythonhosted.org/packages/66/3f/72f0bf49882a71f6cf1787eb72100ceca61292151dc11559aeb6e50872de/hhc-3.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a0fbb7996fcb6bb569e920df7181cc7c", "sha256": "0909facb941dcf64f3178219fce4ef3de3411b7cfdb24a8e4757cf9a24ac9039" }, "downloads": -1, "filename": "hhc-3.0.1.tar.gz", "has_sig": false, "md5_digest": "a0fbb7996fcb6bb569e920df7181cc7c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 7253, "upload_time": "2019-05-27T19:25:00", "url": "https://files.pythonhosted.org/packages/85/f1/817408836ac7ee0d34a5e5ad5bdb40a8084d990073dbec6bac8a0135936c/hhc-3.0.1.tar.gz" } ], "3.0.1b0": [ { "comment_text": "", "digests": { "md5": "df2c45b4f9a717e7d4de766c1117db51", "sha256": "6c28cdef7710efc1037c17b073637e739ceb215da1d78140562e791b1b47790a" }, "downloads": -1, "filename": "hhc-3.0.1b0-py3-none-any.whl", "has_sig": false, "md5_digest": "df2c45b4f9a717e7d4de766c1117db51", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 9085, "upload_time": "2019-07-12T08:56:17", "url": "https://files.pythonhosted.org/packages/c5/09/fb29e06321ea4c03d31fd0df94f4b1247ad74149986bf178427711f6650e/hhc-3.0.1b0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "212be757cf478458ff3198ed87bcd583", "sha256": "7f5801a8953c7d8dac6108a1c976e7ad27a11b3e858dd1b8e3cdfe5d0956bdfd" }, "downloads": -1, "filename": "hhc-3.0.1b0.tar.gz", "has_sig": false, "md5_digest": "212be757cf478458ff3198ed87bcd583", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 7256, "upload_time": "2019-07-12T08:56:20", "url": "https://files.pythonhosted.org/packages/ed/bf/7a188860d175fc883a92bc1ce938a4e44d8c7f2bd55625d8ac492f62577e/hhc-3.0.1b0.tar.gz" } ], "3.0.2": [ { "comment_text": "", "digests": { "md5": "73a5b04a9ea6f585fcb28e4dbed89d12", "sha256": "e92d3ce94c9549ac2df8eac745e5c963fbdf76ce30b84ef8e5a84f8d8b936f05" }, "downloads": -1, "filename": "hhc-3.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "73a5b04a9ea6f585fcb28e4dbed89d12", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 9262, "upload_time": "2019-07-12T08:56:18", "url": "https://files.pythonhosted.org/packages/a2/be/ab3a2b1aa6d5c0f2d67f0ad8697cbf1c1704a87ce141010035201436ae63/hhc-3.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c430b8e72f6efcf67377362a796e6f7b", "sha256": "3b77a9d6ab2e92205816c826dd1a25db891bef499db2acd65189aaa0e45303b0" }, "downloads": -1, "filename": "hhc-3.0.2.tar.gz", "has_sig": false, "md5_digest": "c430b8e72f6efcf67377362a796e6f7b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 7454, "upload_time": "2019-07-12T08:56:21", "url": "https://files.pythonhosted.org/packages/58/ba/4e4421b31c582b48fe729d3af99e60c61979f338bd5be33e4d79a9bed061/hhc-3.0.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "73a5b04a9ea6f585fcb28e4dbed89d12", "sha256": "e92d3ce94c9549ac2df8eac745e5c963fbdf76ce30b84ef8e5a84f8d8b936f05" }, "downloads": -1, "filename": "hhc-3.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "73a5b04a9ea6f585fcb28e4dbed89d12", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 9262, "upload_time": "2019-07-12T08:56:18", "url": "https://files.pythonhosted.org/packages/a2/be/ab3a2b1aa6d5c0f2d67f0ad8697cbf1c1704a87ce141010035201436ae63/hhc-3.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c430b8e72f6efcf67377362a796e6f7b", "sha256": "3b77a9d6ab2e92205816c826dd1a25db891bef499db2acd65189aaa0e45303b0" }, "downloads": -1, "filename": "hhc-3.0.2.tar.gz", "has_sig": false, "md5_digest": "c430b8e72f6efcf67377362a796e6f7b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", "size": 7454, "upload_time": "2019-07-12T08:56:21", "url": "https://files.pythonhosted.org/packages/58/ba/4e4421b31c582b48fe729d3af99e60c61979f338bd5be33e4d79a9bed061/hhc-3.0.2.tar.gz" } ] }