Skip to content

Instantly share code, notes, and snippets.

@ArtemGr
Created May 12, 2009 11:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ArtemGr/110426 to your computer and use it in GitHub Desktop.
Save ArtemGr/110426 to your computer and use it in GitHub Desktop.
VariableLengthInteger
/** Format should be compatible with the one described in http://www.dlugosz.com/ZIP2/VLI.html,
* although we currently use and support only the first "0", "10" and "11" selectors. */
object VariableLengthInteger {
/** Encodes an unsigned integer with a value up to 134217727.
* Returns the position in the "out" array directly after the encoded integer. */
def encodeInt (num: Int, out: Array[Byte], pos: Int): Int = {
if (num < 0) throw new Exception ("Signed integers are not supported.")
else if (num <= 127) {out(pos) = num.toByte; pos + 1}
// Integer.toString (Integer.parseInt ("10000000", 2), 16) == "80"
else if (num <= 16383) {out(pos) = (0x80 + (num >>> 8)).toByte; out(pos+1) = (num & 0xFF).toByte; pos + 2}
// Integer.toString (Integer.parseInt ("11000000", 2), 16) == "c0"
else if (num <= 2097151) {out(pos) = (0xC0 + (num >>> 16)).toByte;
out(pos+1) = ((num >>> 8) & 0xFF).toByte; out(pos+2) = (num & 0xFF).toByte; pos + 3}
// Integer.toString (Integer.parseInt ("11100" + "000", 2), 16) == "e0"
else if (num <= 134217727) {out(pos) = (0xE0 + (num >>> 24)).toByte;
out(pos+1) = ((num >>> 16) & 0xFF).toByte; out(pos+2) = ((num >>> 8) & 0xFF).toByte;
out(pos+3) = (num & 0xFF).toByte; pos + 4}
else throw new Exception ("Encoding integers larger than 134,217,727 is not supported: " + num)
}
/** Decodes an unsigned variable-length integer with a value up to 134217727.
* Returns the position in the "in" array directly after the decoded integer and the integer itself. */
def decodeInt (in: Array[Byte], pos: Int): (Int, Int) = {
val first = in(pos)
if ((first & 0x80) == 0) (pos + 1, first & 0x7F) // "0"
else if ((first & 0xC0) == 0x80) (pos + 2, ((first & 0x3F) << 8) + (in(pos+1) & 0xFF)) // "10"
else if ((first & 0xE0) == 0xC0) (pos + 3, ((first & 0x1F) << 16) + ((in(pos+1) & 0xFF) << 8) + (in(pos+2) & 0xFF)) // "11"
else if ((first & 0xF8) == 0xE0) (pos + 4, ((first & 0x7) << 24) + ((in(pos+1) & 0xFF) << 16) + // "111 00"
((in(pos+2) & 0xFF) << 8) + (in(pos+3) & 0xFF))
else throw new Exception ("Unsupported variable-length integer selector - 111 01")
}
def test: Unit = {
val buf = new Array[Byte] (10)
assert (encodeInt (0, buf, 0) == 1); assert (decodeInt (buf, 0) == (1, 0))
assert (encodeInt (9, buf, 0) == 1); assert (decodeInt (buf, 0) == (1, 9))
assert (encodeInt (127, buf, 0) == 1); assert (decodeInt (buf, 0) == (1, 127))
assert (encodeInt (128, buf, 0) == 2); assert (decodeInt (buf, 0) == (2, 128), decodeInt (buf, 0))
assert (encodeInt (16383, buf, 0) == 2); assert (decodeInt (buf, 0) == (2, 16383), decodeInt (buf, 0))
assert (encodeInt (16384, buf, 0) == 3); assert (decodeInt (buf, 0) == (3, 16384))
assert (encodeInt (2097151, buf, 0) == 3); assert (decodeInt (buf, 0) == (3, 2097151), decodeInt (buf, 0))
assert (encodeInt (12345678, buf, 0) == 4); assert (decodeInt (buf, 0) == (4, 12345678), decodeInt (buf, 0))
assert (encodeInt (123456789, buf, 0) == 4); assert (decodeInt (buf, 0) == (4, 123456789), decodeInt (buf, 0))
assert (encodeInt (134217727, buf, 0) == 4); assert (decodeInt (buf, 0) == (4, 134217727), decodeInt (buf, 0))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment