about summary refs log tree commit diff
path: root/compiler/rustc_serialize/src/leb128.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_serialize/src/leb128.rs')
-rw-r--r--compiler/rustc_serialize/src/leb128.rs110
1 files changed, 110 insertions, 0 deletions
diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs
new file mode 100644
index 00000000000..1fe6a309e96
--- /dev/null
+++ b/compiler/rustc_serialize/src/leb128.rs
@@ -0,0 +1,110 @@
+macro_rules! impl_write_unsigned_leb128 {
+    ($fn_name:ident, $int_ty:ident) => {
+        #[inline]
+        pub fn $fn_name(out: &mut Vec<u8>, mut value: $int_ty) {
+            loop {
+                if value < 0x80 {
+                    out.push(value as u8);
+                    break;
+                } else {
+                    out.push(((value & 0x7f) | 0x80) as u8);
+                    value >>= 7;
+                }
+            }
+        }
+    };
+}
+
+impl_write_unsigned_leb128!(write_u16_leb128, u16);
+impl_write_unsigned_leb128!(write_u32_leb128, u32);
+impl_write_unsigned_leb128!(write_u64_leb128, u64);
+impl_write_unsigned_leb128!(write_u128_leb128, u128);
+impl_write_unsigned_leb128!(write_usize_leb128, usize);
+
+macro_rules! impl_read_unsigned_leb128 {
+    ($fn_name:ident, $int_ty:ident) => {
+        #[inline]
+        pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
+            let mut result = 0;
+            let mut shift = 0;
+            let mut position = 0;
+            loop {
+                let byte = slice[position];
+                position += 1;
+                if (byte & 0x80) == 0 {
+                    result |= (byte as $int_ty) << shift;
+                    return (result, position);
+                } else {
+                    result |= ((byte & 0x7F) as $int_ty) << shift;
+                }
+                shift += 7;
+            }
+        }
+    };
+}
+
+impl_read_unsigned_leb128!(read_u16_leb128, u16);
+impl_read_unsigned_leb128!(read_u32_leb128, u32);
+impl_read_unsigned_leb128!(read_u64_leb128, u64);
+impl_read_unsigned_leb128!(read_u128_leb128, u128);
+impl_read_unsigned_leb128!(read_usize_leb128, usize);
+
+#[inline]
+/// encodes an integer using signed leb128 encoding and stores
+/// the result using a callback function.
+///
+/// The callback `write` is called once for each position
+/// that is to be written to with the byte to be encoded
+/// at that position.
+pub fn write_signed_leb128_to<W>(mut value: i128, mut write: W)
+where
+    W: FnMut(u8),
+{
+    loop {
+        let mut byte = (value as u8) & 0x7f;
+        value >>= 7;
+        let more =
+            !(((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && ((byte & 0x40) != 0)));
+
+        if more {
+            byte |= 0x80; // Mark this byte to show that more bytes will follow.
+        }
+
+        write(byte);
+
+        if !more {
+            break;
+        }
+    }
+}
+
+#[inline]
+pub fn write_signed_leb128(out: &mut Vec<u8>, value: i128) {
+    write_signed_leb128_to(value, |v| out.push(v))
+}
+
+#[inline]
+pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
+    let mut result = 0;
+    let mut shift = 0;
+    let mut position = start_position;
+    let mut byte;
+
+    loop {
+        byte = data[position];
+        position += 1;
+        result |= i128::from(byte & 0x7F) << shift;
+        shift += 7;
+
+        if (byte & 0x80) == 0 {
+            break;
+        }
+    }
+
+    if (shift < 64) && ((byte & 0x40) != 0) {
+        // sign extend
+        result |= -(1 << shift);
+    }
+
+    (result, position - start_position)
+}