Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 158 additions & 84 deletions src/bitvec/bitvec.mbt

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions src/bitvec/pkg.generated.mbti
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Generated using `moon info`, DON'T EDIT IT
package "AdUhTkJm/bitviewer/bitvec"

// Values

// Errors

// Types and methods
pub struct Bitvector {
v : Array[Byte]
}
pub fn[T : BitvectorConvertible] Bitvector::from(T) -> Self
pub fn Bitvector::op_as_view(Self, start~ : Int, end? : Int) -> Self
pub fn Bitvector::op_get(Self, Int) -> Int
pub fn Bitvector::op_set(Self, Int, Bool) -> Unit
pub fn[T : BitvectorConvertible] Bitvector::out(Self) -> T
pub fn Bitvector::raw(Self) -> Bytes
pub fn Bitvector::rev(Self) -> Self
pub fn[T : BitvectorConvertible] Bitvector::set(Self, Int, Int, T) -> Unit
pub fn[T : BitvectorConvertible] Bitvector::to(Self, T) -> T
pub fn Bitvector::view(Self) -> Array[Byte]
pub impl BitvectorConvertible for Bitvector
pub impl Show for Bitvector

// Type aliases

// Traits
pub trait BitvectorConvertible {
from(Self) -> Bitvector
to(Bitvector) -> Self
}
pub impl BitvectorConvertible for Bool
pub impl BitvectorConvertible for Byte
pub impl BitvectorConvertible for Char
pub impl BitvectorConvertible for Int
pub impl BitvectorConvertible for Int64
pub impl BitvectorConvertible for UInt
pub impl BitvectorConvertible for UInt64
pub impl BitvectorConvertible for Float
pub impl BitvectorConvertible for Double
pub impl BitvectorConvertible for Bytes
pub impl BitvectorConvertible for Array[Byte]
pub impl BitvectorConvertible for ArrayView[Byte]
pub impl BitvectorConvertible for BytesView

73 changes: 44 additions & 29 deletions src/bitview/bitbuild.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,68 @@
///
/// See `extract()` for the details of the specification language.
///
pub fn[T: BitvectorConvertible] Bitviewer::build(spec: String, x: Map[String, T]) -> Bitviewer {
pub fn[T : BitvectorConvertible] Bitviewer::build(
spec : String,
x : Map[String, T],
) -> Bitviewer {
(try? Bitviewer::build_malformable(spec, x)).unwrap()
}

///|
/// Same as `extract()`, but raises an error on malformed specification, rather than panicking.
///
pub fn[T: BitvectorConvertible] Bitviewer::build_malformable(spec: String, x_: Map[String, T]) -> Bitviewer raise Malformed {
let spec = parse_spec(spec);
if (spec.size() == 0) {
return Bitviewer::new(b"");
pub fn[T : BitvectorConvertible] Bitviewer::build_malformable(
spec : String,
x_ : Map[String, T],
) -> Bitviewer raise Malformed {
let spec = parse_spec(spec)
if spec.length() == 0 {
return Bitviewer::new(b"")
}

let x = {};
let x = {}
for k, v in x_ {
x[k] = v.from();
x[k] = v.from()
}

let places = {};
let mut from = 0;
let places = {}
let mut from = 0
for k, v in spec {
let len = eval(v.len, x);
places[k] = if (v.from is Some(f)) { let f = eval(f, x); from = f; (f, f + len, v.endian) } else { (from, from + len, v.endian) };
from += len;
let len = eval(v.len, x)
places[k] = if v.from is Some(f) {
let f = eval(f, x)
from = f
(f, f + len, v.endian)
} else {
(from, from + len, v.endian)
}
from += len
}
let max = places.values().map((x) => x.1).maximum().unwrap();
let bv = Bitvector::from(Array::make((max + 7) / 8, (0: Byte)));
let max = places.values().map(x => x.1).maximum().unwrap()
let bv = Bitvector::from(Array::make((max + 7) / 8, (0 : Byte)))
for k, v in places {
let (from, to, endian) = v;
let mut w = if (k.char_at(0) == '=') {
k.to_array()[1:].map((x) => match x {
'0' => false; '1' => true; _ => raise Malformed("'=' expects binary string")
}) |> bit2byte |> Bitvector::from
let (from, to, endian) = v
let mut w = if k.code_unit_at(0) == '='.to_int().to_uint16() {
k.to_array()[1:].map(x => match x {
'0' => false
'1' => true
_ => raise Malformed("'=' expects binary string")
})
|> bit2byte
|> Bitvector::from
} else {
x.get(k).unwrap();
x.get(k).unwrap()
}
if (endian is Big) {
if ((to - from) % 8 != 0) {
raise Malformed("endianness only applies to whole-bytes");
if endian {
if (to - from) % 8 != 0 {
raise Malformed("endianness only applies to whole-bytes")
}
w = w.rev();
w = w.rev()
}
bv.set(from, to, w);
bv.set(from, to, w)
}
{ bytes: bv.raw() }
}

pub fn Bitviewer::raw(self: Bitviewer) -> Bitvector {
Bitvector::from(self.bytes);
///|
pub fn Bitviewer::raw(self : Bitviewer) -> Bitvector {
Bitvector::from(self.bytes)
}
23 changes: 11 additions & 12 deletions src/bitview/bitbuild_test.mbt
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
///|
test "arith" {
let spec =
#|h1 : 3*5-4+1;
#|h2 : 4;

let viewer = Bitviewer::build(spec, {
"h1": 0x234,
"h2": 1
});
inspect(viewer.raw(), content=b"\x34\x12".to_string());
inspect(viewer.raw().to(0), content="\{0x1234}");
#|h1 : 3*5-4+1;
#|h2 : 4;
let viewer = Bitviewer::build(spec, { "h1": 0x234, "h2": 1 })
inspect(viewer.raw(), content=b"\x34\x12".to_string())
inspect(viewer.raw().to(0), content="\{0x1234}")
}

///|
test "endian" {
let viewer = Bitviewer::build("rev: >32", { "rev": 0x12345678 });
inspect(viewer.raw(), content=b"\x12\x34\x56\x78".to_string());
let viewer = Bitviewer::build("rev: >32", { "rev": 0x12345678 })
inspect(viewer.raw(), content=b"\x12\x34\x56\x78".to_string())
}

///|
test "literal" {
let viewer = Bitviewer::build("a: 5; =011: 3; b: 6; =01: 2", {
"a": 0b10001,
"b": 0b010011
"b": 0b010011,
})
inspect(viewer.raw().to(0), content="\{0b10010011_11010001}")
}
67 changes: 34 additions & 33 deletions src/bitview/bitview.mbt
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
typealias @bitvec.Bitvector
traitalias @bitvec.BitvectorConvertible
///|
using @bitvec {type Bitvector, trait BitvectorConvertible}

///|
struct Bitviewer {
bytes: Bytes
bytes : Bytes
}

///|
/// Same as `from()`.
///
pub fn[T: BitvectorConvertible] Bitviewer::new(bytes: T) -> Bitviewer {
Bitviewer :: { bytes: T::from(bytes).out() }
pub fn[T : BitvectorConvertible] Bitviewer::new(bytes : T) -> Bitviewer {
Bitviewer::{ bytes: T::from(bytes).out() }
}

///|
/// Same as `new()`.
///
pub fn[T: BitvectorConvertible] Bitviewer::from(bytes: T) -> Bitviewer {
Bitviewer :: { bytes: T::from(bytes).out() }
pub fn[T : BitvectorConvertible] Bitviewer::from(bytes : T) -> Bitviewer {
Bitviewer::{ bytes: T::from(bytes).out() }
}

///|
Expand Down Expand Up @@ -44,46 +45,50 @@ pub fn[T: BitvectorConvertible] Bitviewer::from(bytes: T) -> Bitviewer {
///
/// For floating point fields, just declare their length; `Bitvector` type allows conversion to floating point.
///
pub fn Bitviewer::extract(self: Bitviewer, format: String) -> Map[String, Bitvector] {
let r = try? self.extract_malformable(format);
pub fn Bitviewer::extract(
self : Bitviewer,
format : String,
) -> Map[String, Bitvector] {
let r = try? self.extract_malformable(format)
r.unwrap()
}

///|
/// Works the same as `extract()`, except that it reports an error on malformed specification string.
///
pub fn Bitviewer::extract_malformable(self: Bitviewer, format: String) -> Map[String, Bitvector] raise Malformed {
let spec = parse_spec(format);
let result = {};
let mut from = 0;
pub fn Bitviewer::extract_malformable(
self : Bitviewer,
format : String,
) -> Map[String, Bitvector] raise Malformed {
let spec = parse_spec(format)
let result = {}
let mut from = 0
for k, v in spec {
if (v.from is Some(v)) {
from = eval(v, result);
if v.from is Some(v) {
from = eval(v, result)
}
let len = eval(v.len, result);

let mut bv = bits(self.bytes, from, len) |> bit2byte |> Bitvector::from;
let len = eval(v.len, result)
let mut bv = bits(self.bytes, from, len) |> bit2byte |> Bitvector::from
// Currently we default to little endian. Perhaps make it configurable in the future.
if (v.endian is Big) {
if (len % 8 != 0) {
raise Malformed("endianness only applies to whole-bytes");
if v.endian {
if len % 8 != 0 {
raise Malformed("endianness only applies to whole-bytes")
}
bv = bv.rev();
bv = bv.rev()
}
result[k] = bv;
from += len;
result[k] = bv
from += len
}
result
}

fn bits(bytes: Bytes, from: Int, len: Int) -> Array[Bool] {
///|
fn bits(bytes : Bytes, from : Int, len : Int) -> Array[Bool] {
let result = Array::new(capacity=len)

for i = 0; i < len; i = i + 1 {
let bit_pos = from + i
let ind = bit_pos / 8
let offset = bit_pos % 8

if ind < bytes.length() {
let byte_val = bytes[ind]
let bit = (byte_val.to_int() >> offset) & 1
Expand All @@ -92,26 +97,22 @@ fn bits(bytes: Bytes, from: Int, len: Int) -> Array[Bool] {
result.push(false)
}
}

result
}

fn bit2byte(bits: Array[Bool]) -> Array[Byte] {
///|
fn bit2byte(bits : Array[Bool]) -> Array[Byte] {
let byte_cnt = (bits.length() + 7) / 8
let result = Array::new(capacity=byte_cnt)

for i in 0..<byte_cnt {
let mut v = 0

for offset in 0..<8 {
let bit = i * 8 + offset
if bit < bits.length() && bits[bit] {
v = v | (1 << offset)
}
}

result.push(v.to_byte())
}

result
}
Loading