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));