1 /// Range-based iteration over disassembled instructions
2 module capstone.range;
3
4 import std.exception: enforce, assertThrown;
5 import std.range: isInputRange;
6 import std.format: format;
7
8 import capstone.api;
9 import capstone.capstone;
10 import capstone.internal;
11 import capstone.instruction;
12 import capstone.error;
13
14 /// An extended `InstructionRange` that provides architecture-specific instructions
15 class InstructionImplRange(TInstruction) : InstructionRange {
16 import core.exception: RangeError;
17 private{
18 const Capstone cs;
19 const ubyte[] code; // Keep ref, s.t. it cannot be deallocated externally
20 const(ubyte)* pCode;
21 ulong codeLength;
22 ulong address;
23
24 TInstruction instr;
25 cs_insn* pInsn;
26
27 bool hasFront;
28 }
29
30 package this(in Capstone cs, in ubyte[] code, in ulong address){
31 this.cs = cs;
32 this.code = code;
33 this.pCode = code.ptr;
34 this.codeLength = code.length;
35 this.address = address;
36 this.hasFront = true;
37
38 popFront;
39 }
40
41 /// True if no disassemblable instructions remain
42 override bool empty() const {return !hasFront;}
43
44 /** The latest disassembled instruction
45
46 Throws if called on an `empty` range.
47 */
48 override TInstruction front() {
49 enforce!RangeError(!empty, "Trying to access an empty range (%s)".format(typeof(this).stringof));
50 return instr;
51 }
52
53 /** Advances the range, disassembling the next instruction
54
55 Throws if called on an `empty` range.
56 */
57 override void popFront(){
58 enforce!RangeError(!empty, "Trying to access an empty range (%s)".format(typeof(this).stringof));
59 pInsn = cs_malloc(cs.handle); // Is freed by Instruction
60 if(!pInsn)
61 throw new CapstoneException("Insufficient memory to allocate an instruction", ErrorCode.OutOfMemory);
62 hasFront = cs_disasm_iter(cs.handle, &pCode, &codeLength, &address, pInsn);
63 if(hasFront)
64 instr = new TInstruction(cs, pInsn); // Instruction takes ownership of pointer
65 else
66 cs_errno(cs.handle).checkErrno;
67 }
68 }
69 static assert(isInputRange!(InstructionRange));