1 2
5 6
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Didn't bother posting a new topic*, so I'll just add here. Self-modifying C code! It works on Linux and Windows. (32-bit only) Probably DOS too, if you remove the memory protection stuff. [code c]#include <stdio.h> #include <stdlib.h> #include <string.h> #ifndef _WIN32 # include <sys/mman.h> #else # include <windows.h> #endif static const char* const s = "gruu"; static const char* gruu(void) { return s; } int main(void) { const char*(*buf_cast)(); char *a = (char*)gruu, *b = (char*)main, *p, *buf; if(a>b) {p=a;a=b;b=p; } /* Note: For measuring the size of gruu(), we rely on the * assumption that gruu() and main() are placed adjacently * in the memory. But we don't know which way they are * ordered, so we swap those two if a>b, to ensure a<b. * That way, we can use (b-a) to calculate a rough estimate * of the size of the code within gruu() in bytes. */ /* Allocate enough memory and align it on page boundary */ buf = (char*)((((unsigned long)malloc(b-a+4096*2)) + 4095UL) &~ 4095UL); /* Create a copy of the function */ memcpy(buf, gruu, b-a); /* Change the function */ for(p=buf; p+sizeof(char*) <= buf+(b-a) ; ++p) { static const char* const h = "hello, world!"; if(*(const char**)p == s) { *(const char**)p = h; } /* case 1 */ if(*(const char***)p == &s) { *(const char*const **)p = &h; } /* case 2 */ } #ifndef _WIN32 mprotect(buf, 4096, PROT_READ | PROT_EXEC); /* Make it executable */ #else VirtualProtect(buf, 4096, PAGE_EXECUTE_READ, NULL); /* Make it executable */ #endif buf_cast = (const char*(*)()) buf; /* Cast the pointer into a function pointer */ printf("gruu() returns '%s'\n", gruu() ); printf("buf() returns '%s'\n", buf_cast() ); return printf("s is '%s'\n", s); }[/code] It creates a copy of the gruu() function, and modifies it to produce a different string. EDIT 2010-01-22: I just split the topic, so now it is a new topic after all. The old topic was the Hello, I suck at C:) thread.
Post subject: Re: Self-modifying C code
Tub
Joined: 6/25/2005
Posts: 1377
Bisqwit wrote:
It works on Linux and Windows. (32-bit only)
s/works/depends on random factors/ I just hope you're not planning to use it for something serious :)
m00
Post subject: Re: Self-modifying C code
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Tub wrote:
s/works/depends on random factors/
Well, deterministic random factors. Those being the assumption that the two functions are placed adjacently in the code -- which they are when compiled with current versions of GCC or CL. (ICC untested) I'm not planning to use it for anything serious, though.
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Bisqwit wrote:
I'm not planning to use it for anything serious, though.
THIS, however does something serious :P It's a RPN calculator that uses JIT to produce fast-executing code :) It works on both Linux and Windows in both 32-bit and 64-bit modes. SSE support required, SSE2 utilized if available. It uses double-precision (double) arithmetics if SSE2 is available, single-precision otherwise. [code c++]#include <string> #include <iostream> #include <vector> #include <ctype.h> #include <cstdlib> #include <cstring> #ifndef _WIN32 # include <sys/mman.h> #else # include <windows.h> #endif #ifdef __SSE2__ typedef double F; #else typedef float F; #endif int main() { std::vector<unsigned char> codebuf; int stacksize = 0, stackmax = 0, n_consts = 0; enum { Q = sizeof(long) == 8 ? 0x48 : 0x90 }; // A prefix for 64-bit opcodes, or nop on 32-bit enum { S = sizeof(F) == 8 ? 0xF2 : 0xF3 }; // 0xF3 = movss/addss/etc, 0xF2 = movsd/addsd/etc static const unsigned char BeginSeq[] = {0x55,Q,0x89,0xe5,Q,0x81,0xed}; // push bp;mov bp,sp;sub bp,N (ebp or rbp, etc) static const unsigned char EndSeq[] = {sizeof(F)==8 ? 0xDD : 0xD9,0x45,sizeof(F), // fldq[bp+8] or fldd[bp+4] S,0x0F,0x10,0x45,sizeof(F),0x5D,0xC3};// movss xmm0,[bp+8];pop bp;ret static const unsigned char PushSeq[] = {S,0x0F,0x11,0x45,0x00,Q,0x81,0xed,sizeof(F),0,0,0}; // movss[bp],xmm0;sub bp,8 static const unsigned char ConstSeq1[] = {0xE8}; // call $+N (remember to add enough Nop to make it aligned properly) static const unsigned char ConstSeq2[] = {0x5A}; // pop dx (edx or rdx) static const unsigned char ConstLoad[] = {S,0x0F,0x10,0x82}; // movsd xmm0,[dx+N] static const unsigned char LdOne[] = {Q,0x81,0xC5,sizeof(F),0,0,0, // add bp,8 S,0x0F,0x11,0x45,256-sizeof(F)}; // mov xmm0,[bp-8] static const unsigned char LdPair[] = {Q,0x81,0xC5,sizeof(F)*2,0,0,0, // add bp,16 S,0x0F,0x10,0x45,0x00, // mov xmm0,[bp] S,0x0F,0x10,0x4D,256-sizeof(F)}; // mov xmm1,[bp-8] static const unsigned char AddSeq[] = {S,0x0F,0x58,0xC1}; // addss xmm0,xmm1 (or addsd) static const unsigned char SubSeq[] = {S,0x0F,0x5C,0xC1}; // subss xmm0,xmm1 (or subsd) static const unsigned char MulSeq[] = {S,0x0F,0x59,0xC1}; // mulss xmm0,xmm1 (or mulsd) static const unsigned char DivSeq[] = {S,0x0F,0x5E,0xC1}; // divss xmm0,xmm1 (or divsd) static const unsigned char NopSeq[] = {0x90}; // nop #define EmitValAt(offs, ptr) \ codebuf.insert(codebuf.begin()+(offs), \ (const unsigned char*)&(ptr), \ (const unsigned char*)&(ptr) + sizeof(ptr)) #define Emit(seq) EmitValAt(codebuf.size(), seq) Emit(BeginSeq); Emit(ConstSeq1); Emit(ConstSeq2); std::string line; std::cout << "Enter RPN expression: "; std::getline(std::cin, line); for(size_t a=0; a<line.size(); ++a) { #define binop(op) { if(stacksize<=1) goto error; \ Emit(LdPair);Emit(op);Emit(PushSeq); --stacksize; continue; } if(line[a] == '+') { binop(AddSeq) } if(line[a] == '-' && !isdigit(line[a+1])) { binop(SubSeq); } if(line[a] == '*') { binop(MulSeq); } if(line[a] == '/') { binop(DivSeq); } if(isspace(line[a])) continue; char* endptr; F immed = std::strtod(&line[a],&endptr); if(endptr == &line[a]) goto error; a = (endptr - &line[0]) - 1; // Add the constant into the const array int di_offs = n_consts * sizeof(F); EmitValAt(sizeof(BeginSeq) + sizeof(ConstSeq1) + di_offs, immed); n_consts += 1; Emit(ConstLoad); EmitValAt(codebuf.size(), di_offs); Emit(PushSeq); ++stacksize; if(stacksize > stackmax) stackmax = stacksize; } //fprintf(stderr, "stacksize = %d\n", stacksize); if(stacksize != 1) { error: std::cerr << "Error\n"; return -1; } stackmax = (stackmax + 1) * sizeof(F); EmitValAt(sizeof(BeginSeq), stackmax); int constbuf_size = sizeof(F) * n_consts; EmitValAt(sizeof(BeginSeq) + 4+sizeof(ConstSeq1), constbuf_size); Emit(EndSeq); /*fwrite(&codebuf[0], 1, codebuf.size(), stdout); fflush(stdout);*/ char* buf = (char*)((((unsigned long)malloc(codebuf.size()+4096*2)) + 4095UL) &~ 4095UL); std::memcpy(buf, &codebuf[0], codebuf.size()); #ifndef _WIN32 mprotect(buf, codebuf.size(), PROT_READ | PROT_EXEC); #else VirtualProtect(buf, codebuf.size(), PAGE_EXECUTE_READ, NULL); #endif F (*buf_cast)() = (F(*)())buf; std::cerr << "result: " << buf_cast() << std::endl; }[/code] It doesn't produce optimized code, though. Redundant memory stores abound. Also the way of using the call opcode for constants is a bit perverse, but at least it's position-independent. An example of the code it produces for the expression "7 4 5 * 2 - +" (64-bit): [code asm]push rbp mov rbp,rsp sub rbp,0x20 call $+0x20 dq 7.0, 4.0, 5.0, 2.0 ; the call lands here, having skipped 0x20 bytes (4*8) pop rdx ; this receives the "calls"'s pushed immed table's begin address movsd xmm0,[rdx+0x0] : movsd [rbp+0x0], xmm0 : sub rbp,8 ; push const movsd xmm0,[rdx+0x8] : movsd [rbp+0x0], xmm0 : sub rbp,8 ; push const movsd xmm0,[rdx+0x10] : movsd [rbp+0], xmm0 : sub rbp,8 ; push const add rbp, 0x10: movsd xmm0, [rbp+0x0] : movsd xmm1, [rbp-0x8] ; load two operands mulsd xmm0, xmm1 : movsd [rbp+0x0], xmm0 : sub rbp, 8 ; mul, store result movsd xmm0,[rdx+0x18] : movsd [rbp+0x0], xmm0 : sub rbp, 8 ; push const add rbp, 0x10: movsd xmm0, [rbp+0x0] : movsd xmm1, [rbp-0x8] ; load two operands subsd xmm0, xmm1 : movsd [rbp+0x0], xmm0 : sub rbp, 8 ; sub, store result add rbp, 0x10: movsd xmm0, [rbp+0x0] : movsd xmm1, [rbp-0x8] ; load two operands addsd xmm0, xmm1 : movsd [rbp+0x0], xmm0 : sub rbp, 8 ; add, store result fld qword [rbp+0x8] : movsd xmm0, [rbp+0x8] ; load function return value pop rbp ret[/code]
Post subject: Re: Self-modifying C code
Tub
Joined: 6/25/2005
Posts: 1377
Bisqwit wrote:
Well, deterministic random factors.
If the address of s happens to equal a valid opcode that's used inside gruu(), it'll break. If you're using a more complex gruu() function, a simple search/replace won't do. For example, these won't work (though the second might if you disable compiler optimisations):
int gruu() { return sizeof(s); }
bool gruu() { return (s[3] == 'u'); }
m00
Post subject: Re: Self-modifying C code
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Tub wrote:
If the address of s happens to equal a valid opcode that's used inside gruu(), it'll break.
Ah, true, true. Can't believe I forgot that.
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Bisqwit wrote:
It doesn't produce optimized code, though. Redundant memory stores abound.
Fixing that problem is rather complicated as shown below, but oddly rewarding.
#include <string>
#include <iostream>
#include <vector>
#include <ctype.h>
#include <cstdlib>
#include <cstring>

#ifndef _WIN32
# include <sys/mman.h>
#else
# include <windows.h>
#endif

#ifdef __SSE2__
typedef double FloatType;
#else
typedef float FloatType;
#endif

enum {
    Reg64Prefix = sizeof(long) == 8 ? 0x48 : 0x90,   // A prefix for 64-bit opcodes, or nop on 32-bit
    SSEopCode = sizeof(FloatType) == 8 ? 0xF2 : 0xF3 // 0xF3 = movss/addss/etc, 0xF2 = movsd/addsd/etc
};

/* These functions synthesize some x86 opcodes. */
static unsigned char SSEopBuffer[9] = {SSEopCode,0x0F,0,0, 0,0,0,0};
static unsigned char* SSEop_regpair(int opno,int xmmreg1,int xmmreg2)
{
    SSEopBuffer[2] = opno;
    SSEopBuffer[3] = 0xC0 + xmmreg1*8 + xmmreg2;
    return SSEopBuffer;
}
static unsigned char* SSEop_regandmem8(int opno,int xmmreg, int indexreg,int offset)
{
    SSEopBuffer[2] = opno;
    SSEopBuffer[3] = 0x40 + xmmreg*8 + indexreg;
    if(indexreg == 4) /* sp */
        { SSEopBuffer[4] = 0x24;
          SSEopBuffer[5] = offset; }
    else
        SSEopBuffer[4] = offset;
    return SSEopBuffer;
}
static unsigned char* SSEop_regandmem32(int opno,int xmmreg, int indexreg,int offset)
{
    SSEopBuffer[2] = opno;
    SSEopBuffer[3] = 0x80 + xmmreg*8 + indexreg;
    if(indexreg == 4) /* sp */
        { SSEopBuffer[4] = 0x24;
          *(int*)&SSEopBuffer[5] = offset; }
    else
        *(int*)&SSEopBuffer[4] = offset;
    return SSEopBuffer;
}

/* This structure tells where the given value is stored */
struct Where
{
    enum {
        stack, // r = SP offset where the information is found
        immed, // r = DX offset where the information is found
        xmm    // r = XMM register number where the information is found
    } pos;
    int r; // type-specific information about the location
    
    struct stack_tag{}; struct immed_tag{}; struct xmm_tag{};
    Where() : pos(xmm),r(-1) { }
    Where(stack_tag, int o) : pos(stack),r(o) { }
    Where(immed_tag, int o) : pos(immed),r(o) { }
    Where(xmm_tag, int o) : pos(xmm),r(o) { }
};

#define EmitValAtN(offs, ptr, N) do { \
    const unsigned char* atp = (ptr); \
    codebuf.insert(codebuf.begin()+(offs), atp, atp+N); } while(0)
#define EmitValAt(offs, ptr) EmitValAtN(offs, (const unsigned char*)&(ptr), sizeof(ptr))
#define Emit(seq) EmitValAt(codebuf.size(), seq)
#define EmitN(seq,N) EmitValAtN(codebuf.size(), seq, N)
#define EmitSSEMemOp(opno,xmmreg,indexreg,offset) \
    if(offset >= -128 && offset < 128) \
        EmitN(SSEop_regandmem8(opno,xmmreg,indexreg,offset), 5+((indexreg)==4)); \
    else \
        EmitN(SSEop_regandmem32(opno,xmmreg,indexreg,offset), 8+((indexreg)==4))
#define EmitSSEMemOpWith(opno, xmmregno, what) \
    EmitSSEMemOp(opno, xmmregno, what.pos == Where::immed ? 2/*dx*/ : 5/*bp*/, what.r)

/* This object deals with the concept of "stack calculator".
 * Though it theoretically allows random access within
 * the stack, it is only used in pure stacky way.
 * Each slot in the stack may be stored in hardware stack,
 * in the table of constants, or in an XMM register.
 */
class Stack
{
    std::vector<Where> mathstack;
    size_t max_hwstack_size;
public:
    Stack() : mathstack(),max_hwstack_size(0) { }
    
    void Push(const Where& w)
    {
        mathstack.push_back(w);
    }
    
    /* Produces a mathematical binary operation between the two
     * topmost mathematical stack items.
     */
    void Binop(std::vector<unsigned char>& codebuf, int op, bool commutative)
    {
        Where top1 = mathstack[mathstack.size()-1];
        Where top2 = mathstack[mathstack.size()-2];
        if(commutative && top1.pos == Where::xmm && top2.pos != Where::xmm)
        {
            // Swap the operands to make it more favorable
            Where tmp = top1; top1 = top2; top2 = tmp;
        }
        // top2 is the "target" operand. For example, top2 /= top1.

        /*fprintf(stderr, "top1 is %s%d, top2 is %s%d\n",
            "stack\0immed\0xmm\0"+6*top1.pos, top1.r,
            "stack\0immed\0xmm\0"+6*top2.pos, top2.r);*/
        
        int result_reg = -1;
        if(top1.pos != Where::xmm) // if top1, i.e. rhs, is a memory operand.
        {
            // subss xmm_a, [top2]
            int xmm_a = Load(codebuf, top2, -1);
            EmitSSEMemOpWith(op, result_reg = xmm_a, top1);
        }
        else
        {
            // movss xmm_b, [top2] -- if needed
            // (movss xmm_a, [top1] -- not needed)
            // subss xmm_b, xmm_a
            int xmm_b = Load(codebuf, top2, top1.pos==Where::xmm ? top1.r : -1);
            int xmm_a = Load(codebuf, top1, xmm_b);
            if(commutative && xmm_a > xmm_b) { int p=xmm_a;xmm_a=xmm_b;xmm_b=p; }
            EmitN(SSEop_regpair(op, result_reg = xmm_b, xmm_a), 4);
        }
        mathstack.resize(mathstack.size()-1);
        mathstack.back() = Where(Where::xmm_tag(), result_reg);
    }
    
    /* Ensures that the mathematical stack top is in XMM0 and ST(0) */
    void LoadResult(std::vector<unsigned char>& codebuf)
    {
        Where& what = mathstack.back();

        const int wanted_xmm_register = 0;
        int stack_offs = what.pos == Where::stack ? what.r : -999;
        if(what.pos == Where::xmm)
        {
            if(what.r != wanted_xmm_register)
            {
                // Move the result to the correct XMM register
                EmitN(SSEop_regpair(0x10, wanted_xmm_register, what.r), 4);
                what = Where(Where::xmm_tag(), wanted_xmm_register); // now it's there.
            }
        }
        else
            LoadInto(codebuf, what, wanted_xmm_register); // Load into the XMM register
        
        // Now the value is in XMM0. Spill it so we get a copy in the stack.
        // It will stay in XMM0, though. (Only spill if we don't have it in stack.)
        if(stack_offs < -128 || stack_offs > 127)
        {
            ForgetHwStack();
            Spill(codebuf, what, GetFreeHwStackSlot());
            stack_offs = what.r;
        }

        // Load a copy of the value to ST(0) just in case we have 387-based fpmath.
        static unsigned char FLDseq[3] =
            { sizeof(FloatType)==8 ? 0xDD : 0xD9, 0x45, 0};
        FLDseq[2] = stack_offs;
        Emit(FLDseq);
    }

    int getmax_hwstack_size() const { return max_hwstack_size; }
    int size() const { return mathstack.size(); }

private:    
    int Load(std::vector<unsigned char>& codebuf, Where& what, int avoid_gobbling)
    {
        if(what.pos == Where::xmm) return what.r;
        
        // Figure out if there's a free register
        int xmmregno = FindFreeRegister();
        
        // Spill one register if no free was available
        if(xmmregno == -1)
            xmmregno = SpillOldestRegister(codebuf, avoid_gobbling);
        
        // Load the stuff into the register
        LoadInto(codebuf, what, xmmregno);
        
        return xmmregno;
    }

    int FindFreeRegister() const
    {
        bool used[8] = {false}; // Figure out which registers are used
        for(size_t p=mathstack.size(); p-->0; )
            if(mathstack[p].pos == Where::xmm)
                used[mathstack[p].r] = true;
        for(size_t r=0; r<8; ++r)
            if(!used[r])
                return r; // This register is free.
        return -1; // No free registers
    }
    
    int SpillOldestRegister(std::vector<unsigned char>& codebuf, int avoid_gobbling)
    {
        for(size_t p=0; p<mathstack.size(); ++p)
        {
            if(mathstack[p].pos == Where::xmm)
            {
                if(mathstack[p].r == avoid_gobbling) continue; // can't use this
                
                int xmmregno = mathstack[p].r;
                Spill(codebuf, mathstack[p], GetFreeHwStackSlot());
                return xmmregno;
            }
        }
        return -1;
    }
    
    void Spill(std::vector<unsigned char>& codebuf, Where& what, int stackindex)
    {
        if(what.pos != Where::xmm) return;
        
        int bp_offs = (stackindex+1) * -sizeof(FloatType), xmmregno = what.r;
        
        // 0x11 = mov m,r
        EmitSSEMemOp(0x11, xmmregno, 5, bp_offs);
        what = Where(Where::stack_tag(), bp_offs);
    }
    
    void LoadInto(std::vector<unsigned char>& codebuf, Where& what, int xmmregno)
    {
        // 0x10 = mov r,m
        EmitSSEMemOpWith(0x10, xmmregno, what);
        what = Where(Where::xmm_tag(), xmmregno);
    }
    
    void ForgetHwStack()
    {
        for(size_t a=0; a<mathstack.size(); ++a)
            if(mathstack[a].pos == Where::stack)
                mathstack[a].pos = Where::immed; // dummy
    }
    int GetFreeHwStackSlot()
    {
        std::vector<bool> used;
        for(size_t a=0; a<mathstack.size(); ++a)
            if(mathstack[a].pos == Where::stack)
            {
                size_t offs = -1 - mathstack[a].r / (int)sizeof(FloatType);
                if(offs >= used.size()) used.resize(offs+1);
                used[offs] = true;
            }
        for(size_t a=0; a<used.size(); ++a)
            if(!used[a])
                return a;
        
        size_t result = used.size();
        if(result >= max_hwstack_size) max_hwstack_size = result+1;
        return result;
    }
};

int main()
{
    std::vector<unsigned char> codebuf;
    Stack stack;
    int n_consts = 0;
    static const unsigned char BeginSeq1[] = {0xC8}; // enter N
    static const unsigned char BeginSeq2[] = {0x00}; // ,0
    static const unsigned char EndSeq[]    = {0xC9,0xC3}; // leave;ret
    static const unsigned char ConstSeq1[] = {0x90,0x90,0x90,0x90,0x90,0xE8}; // call $+N (remember to add enough Nop to make it aligned properly)
    static const unsigned char ConstSeq2[] = {0x5A}; // pop dx (edx or rdx)
    Emit(BeginSeq1);
    Emit(BeginSeq2);
    Emit(ConstSeq1);
    Emit(ConstSeq2);
    std::string line;
    std::cout << "Enter RPN expression: ";
    std::getline(std::cin, line);
    std::cout << "\n";
    for(size_t a=0; a<line.size(); ++a)
    {
        int binop = 0;
        // Non-commutative operands, indicated by negative binop:
        if(line[a] == '-' && !isdigit(line[a+1])) binop=-0x5C; // subss/subsd
        else if(line[a] == '/') binop=-0x5E; // divss/divsd
        // Commutative operands, indicated by positive binop:
        else if(line[a] == '+') binop=0x58; // addss/addsd
        else if(line[a] == '*') binop=0x59; // mulss/mulsd
        // Check if we got an operand:
        if(binop)
        {
            if(stack.size() <= 1) goto error;
            stack.Binop(codebuf, binop<0?-binop:binop, binop>0);
            continue;
        }
        if(isspace(line[a])) continue;
        char* endptr;
        FloatType immed = std::strtod(&line[a],&endptr);
        if(endptr == &line[a]) goto error;
        a = (endptr - &line[0]) - 1;
        
        // Add the constant into the const array
        int dx_offs = n_consts++ * sizeof(immed);
        EmitValAt(sizeof(BeginSeq1) + sizeof(BeginSeq2) + sizeof(ConstSeq1) + dx_offs, immed);
        stack.Push( Where(Where::immed_tag(), dx_offs) );
    }
    if(stack.size() != 1) { error: std::cerr << "Error\n"; return -1; }
    
    stack.LoadResult(codebuf);

    // Complete the "enter N,0" opcode in the beginning:
    unsigned short stackmax = stack.getmax_hwstack_size() * sizeof(FloatType);
    EmitValAt(sizeof(BeginSeq1), stackmax);

    // Complete the "call $+N" opcode in the beginning:
    int constbuf_size = sizeof(FloatType) * n_consts;
    EmitValAt(sizeof(BeginSeq1)+sizeof(stackmax)+sizeof(BeginSeq2)
             +sizeof(ConstSeq1), constbuf_size);
    
    // Complete the code by adding the function exit mantra:
    Emit(EndSeq);
    
    /*fwrite(&codebuf[0], 1, codebuf.size(), stdout);
    fflush(stdout); //- Use this to "ndisasm" the output
    */

    /* Allocate a buffer where the JIT code will be stored to.
     * Align it on page boundary and set it executable. */
    char* buf = (char*)((((unsigned long)malloc(codebuf.size()+4096*2)) + 4095UL) &~ 4095UL);
    std::memcpy(buf, &codebuf[0], codebuf.size());

#ifndef _WIN32
    mprotect(buf, codebuf.size(), PROT_READ | PROT_EXEC);
#else
    VirtualProtect(buf, codebuf.size(), PAGE_EXECUTE_READ, NULL);
#endif
    FloatType (*buf_cast)() = (FloatType(*)())buf;
    /* Call the code */
    std::cerr << "result: " << buf_cast() << std::endl;
}
An example of the code it produces for the expression "1 2 3 4 * / 5 - 6 7 + 9 * 2 + + *" (64-bit):
enter 0x8,0
call $+0x48
dq 1.0, 2.0, 3.0, 4.0
dq 5.0, 6.0, 7.0, 9.0
dq 2.0
; the call lands here, having skipped 0x48 bytes (9*8)
pop rdx ; this receives the "calls"'s pushed immed table's begin address
movsd xmm0, [rdx+0x10]
mulsd xmm0, [rdx+0x18]
movsd xmm1, [rdx+0x8]
divsd xmm1, xmm0
subsd xmm1, [rdx+0x20]
movsd xmm0, [rdx+0x28]
addsd xmm0, [rdx+0x30]
mulsd xmm0, [rdx+0x38]
addsd xmm0, [rdx+0x40]
addsd xmm1, xmm0
mulsd xmm1, [rdx+0x0]
movsd xmm0, xmm1
movsd [rbp-0x8], xmm0
fld qword [rbp-0x8]
leave
ret
Post subject: Re: Little JIT calculator
Banned User, Former player
Joined: 3/10/2004
Posts: 7698
Location: Finland
Bisqwit wrote:
It's a RPN calculator that uses JIT to produce fast-executing code :)
I'm not sure JIT-compiling is considered self-modifying code per se...
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
I wrote a little adventure game in C++.
#include <string>
#include <utility>
#include <deque>
#include <cstdlib>
#include <cstdarg>
#include <iostream>
#include <cmath>
#include <cstdio>
#include <map>

static const struct { const char* a; float b, c; } MoneyTypes[] =
  { {"platinum",10,0}, {"gold",1,0}, {"silver",.6},
    {"bronze",.4,0}, {"copper",.2,0}, {"wood",.01} },
  BuildTypes[] =
  { {"iron",.4,3}, {"fur",.01,.2}, {"platinum",2,4}, {"gold",1,3.5},
    {"chromium",.9,2}, {"bronze",.1,2.7}, {"pewter",.05,2},
    {"bamboo",.01,1}, {"silk",.01,.1},  {"steel",.7,3},
    {"leather",.09,.5}, {"glass",.04,2} },
  ItemTypes[] =
  { {"shirt",1}, {"tie",.25}, {"bracelet",.2}, {"shoe",.4}, {"leggings",.8},
    {"battlesuit",10}, {"hammer",.4}, {"dagger",.1}, {"cap", .6},
    {"crown",3}, {"sceptre",4}, {"cape",.7}, {"overalls",4} },
  CondTypes[] =
  { {"awesome",1.2}, {"excellent",1}, {"good",.9}, {"average",.75},
    {"bad",.6}, {"poor",.5}, {"thrashed",.4} },
  WallTypes[] = { {"open",1}, {"collapsed",.5}, {"blocked"} },
  EnvTypes[] = { {"dark"}, {"humid"}, {"tall"}, {"beautiful"}, {"narrow"} },
  BodyParts[] = { {"finger",10}, {"teeth",30}, {"elbow",100}, {"toe",40} },
  FoodTypes[] = {
    {"b akbdl epqfts dblf",50000}, {"b kbqhf okbsf pe dgjdlfm kfht",35000},
    {"b dbvkcqpm pe dpplfc opsbspft",20000}, {"b dgjdlfm gps cph",10000},
    {"dgfftf bmc nbdbqpmj",6000}, {"b avssfqnjkl ajtdvjs",3000}, {"b apjkfc fhh",2000},
    {"tpnf kjdgfm tsfx",1000}, {"b xppc dpqsfw tbmcxjdg",700},
    {"b dvo pe ujmfhbq", 500}, {"b dvo pe bookf tffct", 300},
    {"b qpssfm dbqqps", 200}, {"b nvh pe nvccz xbsfq",110}, {"tpnf qbaajs cqpoojmht",70},
    {"b cfbc dpdlqpbdg", 50}, {"b npmsg pkc tojcfq mfs", 30},
    {"b gjkk pe cvts", 16}, {"b gfbo pe cvts", 8}, {"b ajh ojkf pe cvts", 4},
    {"b ojkf pe cvts", 2}, {"b tofdlkf pe cvts", 1}
  }; // badcfehgjilknmporqtsvuxwzy

#define NItems(x) (sizeof((x))/sizeof(*(x)))
static double frand() { return std::rand() / ((double)RAND_MAX+1.0); }
static const char* Sep(size_t pos, size_t max) { return pos?pos==max-1?" and ":", ":""; }

struct ItemType {
    size_t build, item, cond; // Indexes.
    const std::string Name1() const { return ItemTypes[item].a; }
    const std::string Name2() const { return std::string(BuildTypes[build].a)+" "+Name1(); }
    const std::string Name3() const { return std::string(CondTypes[cond].a)+" "+Name2(); }
    const std::string Name1c() const { return std::string(CondTypes[cond].a)+" "+Name1(); }
    const std::string Name3b(bool condition = false) const {
        std::string n = condition ? Name3() : Name2();
        if(n[n.size()-1]=='s') return n;
        return (n[0]=='a'||n[0]=='e'||n[0]=='i'||n[0]=='o'||n[0]=='u' ? "an ":"a ")+n;
    }
    const std::string Name(int level) const
        { return level==1 ? Name1() : level==2 ? Name2() : level==3 ? Name3() : Name1c(); }
    ItemType()
        : build ( std::rand() % (frand() > 0.4 ? NItems(BuildTypes) : 2) ),
          item ( std::rand() % (frand() > 0.4 ? NItems(ItemTypes) : 4) ),
          cond ( std::rand() % (frand() > 0.8 ? NItems(CondTypes) : 3) ) { }
    double value() const {
        return 200.0 * ItemTypes[item].b * BuildTypes[build].b * CondTypes[cond].b;
    }
    double weight() const { return BuildTypes[build].c; }
};
static std::string Appraise(double value,int v=1,int maxi=3) {
    std::deque<std::string> list; redo:
    for(size_t a=0; a<NItems(FoodTypes); ++a)
        if(value >= FoodTypes[a].b*3.0) {
            std::string k = FoodTypes[a].a;
            for(size_t b=0; b<k.size(); ++b) if(k[b]-' ') k[b]=1+((k[b]-1)^v);
            list.push_back(k); value -= FoodTypes[a].b*3.0;
            if((int)list.size() < maxi) goto redo; break;
        }
    if(list.empty()) list.push_back("only hungry dreams");
    std::string resp;
    for(size_t a=0; a<list.size(); ++a) resp += Sep(a,list.size()) + list[a];
    return resp;
}
static struct Eq {
    std::deque<ItemType> Items;
    long Money[ NItems(MoneyTypes) ];
    
    template<typename PrFunc>
    bool print(bool is_inv, PrFunc& P) const {
        double moneyvalue = 0;
        for(size_t a=0; a<Items.size(); ++a)
            P("%s\n", Items[a].Name3b(!is_inv).c_str()),
            moneyvalue += Items[a].value();
        if(is_inv && moneyvalue) P("The total value of your items is %.2f gold.\n", moneyvalue);
        moneyvalue = 0;
        for(size_t a=0; a<NItems(Money); ++a)
            if(Money[a])
                P("%ld %s coin%s\n",
                    Money[a], MoneyTypes[a].a, "s"+(Money[a]==1)),
                moneyvalue += Money[a] * MoneyTypes[a].b;
        if(is_inv && moneyvalue) P("The coins are worth %.2f gold total\n", moneyvalue);
        if(is_inv)
        {
            P("Your possessions wear you down %d points every step you take.\n", burden());
            P("You estimate that these possessions could earn you %s.\n", Appraise(value()).c_str());
        }
        return moneyvalue > 0 || !Items.empty();
    }
    double value() const {
        double res = 0;
        for(size_t a=0; a<Items.size(); ++a) res += Items[a].value();
        for(size_t a=0; a<NItems(Money); ++a) res += Money[a] * MoneyTypes[a].b;
        return res;
    }
    int burden() const {
        double weight = 0;
        for(size_t a=0; a<NItems(Money); ++a) weight += Money[a] / 100.0;
        for(size_t a=0; a<Items.size(); ++a) weight += Items[a].weight();
        return 1 + weight;
    }
    double look_item(long no, bool in, bool all) const {
        if(all) std::printf("You see a %s made of %s, in %s condition. ",
            ItemTypes[Items[no].item].a,
            BuildTypes[Items[no].build].a,
            CondTypes[Items[no].cond].a);
        else std::printf("It is a %s made of %s. It is in %s condition.\n",
            ItemTypes[Items[no].item].a,
            BuildTypes[Items[no].build].a,
            CondTypes[Items[no].cond].a);
        if(in) std::printf("It does not hide anything interesting inside thereof.\n");
        else if(all) std::printf("\n");
        else std::printf(
            "You estimate that with it you could probably buy %s.\n",
            Appraise(Items[no].value(),1, 1).c_str());
        return Items[no].value();
    }
    void look_money(long a, bool in, bool all) const {
        std::printf("%ld %s coin%s\n", Money[a], MoneyTypes[a].a, "s"+(Money[a]==1));
        if(in) std::printf("They are just normal coins.\n");
        else if(!all) std::printf("The coins are worth %.2f gold total\n", Money[a]*MoneyTypes[a].b);
    }
    void clear(size_t n_items = 0) {
        Items.resize(n_items);
        for(size_t a=0; a<NItems(Money); ++a) Money[a] = 0;
    }
    std::deque<std::string> move(Eq& target, const std::string& what, bool all) {
        std::deque<std::string> movelist;
        Eq target_backup = target, me_backup = *this;
        bool ok = true;
        if(all)
            ok = move_one(target, what, movelist, all);
        else
            for(size_t begin = 0; begin != what.size(); ) {
                while(what[begin]==' ') ++begin;
                size_t end = what.find(',', begin);
                if(end == what.npos) end = what.size();
                size_t e = end;
                while(e > begin && what[e-1] == ' ') --e;
                ok = ok && move_one(target, what.substr(begin, e-begin),
                                    movelist, all);
                begin = end; if(begin<what.size()&&what[begin]==',') ++begin;
            }
        if(!ok) movelist.clear();
        if(movelist.empty()) {
            std::printf("Nothing moved!\n");
            target = target_backup;
            *this = me_backup;
        }
        return movelist;
    }
    long find_item(const std::string& what, bool all, size_t first=0) const {
        const char* number_begin = what.c_str() + what.size();
        while(number_begin > what.c_str()
          &&  number_begin[-1] >= '0' && number_begin[-1] <= '9') --number_begin;
        long index = std::strtol(number_begin, 0, 10);
        while(number_begin > what.c_str() && number_begin[-1] == ' ') --number_begin;
        std::string itemname = what.substr(0, number_begin-what.c_str());
        bool skip = itemname.size() != what.size();
        for(int level=4; level>=1; --level)
        {
            long occurrences = 0;
            for(size_t a=first; a<Items.size(); ++a)
                if((itemname == "" && all)
                || itemname == "a "+Items[a].Name(level)
                || itemname == "an "+Items[a].Name(level)
                || itemname == Items[a].Name(level))
                {
                    if(skip && ++occurrences != index) continue;
                    return a;
                }
        }
        return -1;
    }
    long find_money(const std::string& what, bool all, size_t first=0) const {
        for(size_t moneytype = first; moneytype < NItems(MoneyTypes); ++moneytype) {
            if(Money[moneytype] <= 0) continue;
            if((what == "" && all)
            || what == "coins"
            || what == MoneyTypes[moneytype].a
            || what == MoneyTypes[moneytype].a+std::string(" coin")
            || what == MoneyTypes[moneytype].a+std::string(" coins")) {
                return moneytype;
            }
        }
        return -1;
    }
    bool move_one(Eq& target, const std::string& what,
                  std::deque<std::string>& movelist,
                  bool all) {
        bool found_item = false, found_money = false;
        for(long item_id; (item_id=find_item(what, all)) >= 0;) {
            target.Items.push_front(Items[item_id]);
            movelist.push_back(Items[item_id].Name3b());
            Items.erase(Items.begin()+item_id);
            found_item = true;
            if(!all) break;
        }
        char* end = 0, * begin = (char*)what.c_str();
        long numeral = std::strtol(begin, &end, 10);
        if(!end || end == begin) numeral = 0;
        else if(numeral < 1) return found_item;
        for(begin=end; *begin==' '; )++begin;
        std::string moneyname = begin;
        for(long moneytype; (moneytype=find_money(moneyname,all)) >= 0;) {
            long mynumeral = numeral;
            if(mynumeral == 0) mynumeral = Money[moneytype];
            if(mynumeral > Money[moneytype]) return false;
            target.Money[moneytype] += mynumeral;
            Money[moneytype] -= mynumeral;
            char explanation[512];
            std::sprintf(explanation, "%ld %s coin%s",
                mynumeral, MoneyTypes[moneytype].a, "s"+(mynumeral==1));
            movelist.push_back(explanation);
            found_money = true;
            if(!all) break;
        }
        return found_money || found_item;
    }
} eq;
static struct Room {
    size_t Wall, Env; // Indexes
    float Chest;      // 0=no chest
    int seed;         // For maze generation
    Eq items;         // What's lying on the floor
    Room() : Wall(0),Env(0),Chest(0),seed(),items() {}
} const defaultroom;
static struct Maze {
    std::map<long/*x*/,std::map<long/*y*/,Room> > rooms;
    Room& GenerateRoom(long x,long y,const Room& model, int seed) {
        std::srand((x<<12)^y);
        std::pair<std::map<long/*y*/,Room>::iterator, bool>
          insres ( rooms[x].insert(std::make_pair(y, model)) );
        Room& room = insres.first->second;
        if(insres.second) {
            room.Chest = frand() > 0.7 ? 1.0 : 0.0;
            room.seed = (seed + (frand() > 0.9 ? rand() : 0)) & 3;
            if(frand() > 0.9) room.Env = rand() % NItems(EnvTypes);
            if(frand() > (seed==model.seed ? 0.9 : 0.2))
                room.Wall = frand() > 0.9 ? 0 : 2;
            room.items.clear(unsigned(std::pow(frand(),90.0) * 2.5));
        } return room;
    }
    char Char(long x,long y) const
    {
        std::map<long/*x*/,std::map<long/*y*/,Room> >::const_iterator
            i = rooms.find(x);
        if(i == rooms.end()) return ' ';
        std::map<long/*y*/,Room>::const_iterator j = i->second.find(y);
        if(j == i->second.end()) return ' ';
        if(j->second.Wall) return '#';
        if(j->second.Chest > 0.0) return 'c';
        return (j->second.items.value() != 0.0) ? 'i' : '.';
    }
} maze;
#define SpawnRooms(x,y) \
    const Room \
        &room   = maze.GenerateRoom(x,y, defaultroom, 0), \
        &room_n = maze.GenerateRoom(x,y-1, room, 0), \
        &room_s = maze.GenerateRoom(x,y+1, room, 1), \
        &room_w = maze.GenerateRoom(x-1,y, room, 2), \
        &room_e = maze.GenerateRoom(x+1,y, room, 3)
static long x=0, y=0, life=1000;
struct VecPrint: public std::deque<std::string>
{
    void operator() (const char* fmt, ...)
    {
        if(pos < size()) printf("%s | ", operator[](pos++).c_str());
        else if(!empty()) printf("%*s | ", (int)operator[](0).size(), "");
        va_list ap;
        va_start(ap, fmt);
        std::vprintf(fmt, ap);
        va_end(ap);
    }
    size_t pos; VecPrint() : pos(0) { }
    ~VecPrint() /* flush */
    { while(pos < size()) printf("%s |\n", operator[](pos++).c_str()); }
};
static void Look() {
    SpawnRooms(x,y);
    for(int o=1; o<5&&!maze.GenerateRoom(x,y+o, room, 0).Wall; ++o) { SpawnRooms(x,y+o); }
    for(int o=1; o<5&&!maze.GenerateRoom(x,y-o, room, 0).Wall; ++o) { SpawnRooms(x,y-o); }
    for(int o=1; o<6&&!maze.GenerateRoom(x-o,y, room, 0).Wall; ++o) { SpawnRooms(x-o,y); }
    for(int o=1; o<6&&!maze.GenerateRoom(x+o,y, room, 0).Wall; ++o) { SpawnRooms(x+o,y); }
    VecPrint mapgraph;
    for(long yo=-4; yo<=4; ++yo)
    {
        std::string line;
        for(long xo=-5; xo<=5; ++xo)
            line += ((xo==0&&yo==0) ? '@' : maze.Char(x+xo, y+yo));
        mapgraph.push_back(line);
    }
    mapgraph("In a %s tunnel at %+3ld,%+3ld\n", EnvTypes[room.Env].a, x,-y);
    mapgraph("Exits:%s%s%s%s\n",
        room_n.Wall?"":" north",
        room_s.Wall?"":" south",
        room_w.Wall?"":" west",
        room_e.Wall?"":" east");
    if(room.Chest > 0.0 || room.items.value() > 0.0) mapgraph("\n");
    if(room.Chest > 0.0) mapgraph("a chest\n");
    room.items.print(false, mapgraph);
}
static void Inv() {
    if(!eq.print(true, std::printf))
        std::printf("You are carrying nothing.\n");
}
static void LookAt(std::string what, bool in=false) {
    const Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    bool all = false;
    if(what == "all") { all=true; what=""; }
    else if(what.substr(0,4) == "all ") { all=true; what.erase(0,4); }
    long no,c=0;
    double value=0;
    if(all)
    {
        for(no=0; (no = room.items.find_item(what,all,no)) >= 0; ++c) value += room.items.look_item(no++,in,all);
        for(no=0; (no = room.items.find_money(what,all,no)) >= 0; ++c) room.items.look_money(no++,in,all);
        if(!c)
        {
          for(no=0; (no = eq.find_item(what,all,no)) >= 0; ++c) value += eq.look_item(no++,in,all);
          for(no=0; (no = eq.find_money(what,all,no)) >= 0; ++c) eq.look_money(no++,in,all);
        }
    }
    else
    {
        for(no=0; (no = eq.find_item(what,all,no)) >= 0; ++c) value += eq.look_item(no++,in,all);
        if(!c) for(no=0; (no = eq.find_money(what,all,no)) >= 0; ++c) eq.look_money(no++,in,all);
        if(!c) for(no=0; (no = room.items.find_item(what,all,no)) >= 0; ++c) value += room.items.look_item(no++,in,all);
        if(!c) for(no=0; (no = room.items.find_money(what,all,no)) >= 0; ++c) room.items.look_money(no++,in,all);
    }
    if(c&&all) std::printf(
        "You estimate that with them you could probably buy %s.\n",
        Appraise(value,1, 1).c_str());
    if(c) return;

    if(what == "" && all && room.Chest > 0)
        std::printf("You see a closed chest here. You can try to <open> it.\n");
    else if(what == "chest" && room.Chest > 0)
        std::printf(in
            ?"The chest is closed. You cannot see inside. You can try to <open> it.\n"
            :"There is a chest here. It is closed. You can try to <open> it.\n");
    else
        std::printf("There %s no %s here that you can look at.\n",
            what[what.size()-1]=='s' ? "are" : "is", what.c_str());
}
static void EatLife(long l) {
    if(life>=800 && life-l<800) std::printf("You are so hungry!\n");
    if(life>=150 && life-l<150) std::printf("You are famished!\n");
    if(life>=70 && life-l<70) std::printf("You are about to collapse any second!\n");
    life -= l;
}
static void OpenChest() {
    Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    if(room.Chest <= 0.0) { std::printf("Open what? There's nothing openable here.\n"); return; }
    std::printf("You try to pry the chest open.\n");
    EatLife(8);
    room.Chest -= 0.1 + std::pow(frand(), 4.0);
    if(frand() > 0.96) {
        unsigned sprain = rand()%NItems(BodyParts);
        EatLife(BodyParts[sprain].b);
        std::printf("You sprain your %s!\n", BodyParts[sprain].a);
    }
    if(room.Chest <= 0.0) {
        std::printf("The chest bursts into pieces!\nEverything it contained is scattered on the ground.\n");
        std::srand((y<<12)^x);
        do
            if(frand() > 0.96) { // pure money is rare.
                unsigned moneytype((1.0-std::pow(frand(), 4)) * (NItems(MoneyTypes)-1));
                room.items.Money[moneytype] += rand() % long(600/MoneyTypes[moneytype].b);
            }
            else
                room.items.Items.push_front(ItemType());
        while(frand() > 0.3);
    }
    else std::printf("The chest resists your meddling! Try harder.\n");
}
static void Get(std::string what) {
    Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    bool all = false;
    if(what == "all") { all=true; what=""; }
    else if(what.substr(0,4) == "all ") { all=true; what.erase(0,4); }
    
    if(what == "chest" && room.Chest > 0)
        { std::printf("You cannot take the chest.\n"); return; }
    std::deque<std::string> moved = room.items.move(eq, what, all);
    if(moved.empty())
        std::printf("There %s no %s here!\n",
            what[what.size()-1]=='s'?"are":"is", what.c_str());
    else {
        std::printf("You take ");
        for(size_t a=0; a<moved.size(); ++a)
            std::printf("%s%s", Sep(a,moved.size()), moved[a].c_str());
        std::printf(".\n");
        EatLife(moved.size() * 2);
    }
}
static void Drop(std::string what) {
    Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    bool all = false;
    if(what == "all") { all=true; what=""; }
    else if(what.substr(0,4) == "all ") { all=true; what.erase(0,4); }
    std::deque<std::string> moved = eq.move(room.items, what, all);
    if(moved.empty())
        std::printf("You don't have %s!\n", what.c_str());
    else {
        std::printf("You drop ");
        for(size_t a=0; a<moved.size(); ++a)
            std::printf("%s%s", Sep(a,moved.size()), moved[a].c_str());
        std::printf(".\n");
        EatLife(moved.size() * 1);
    }
}
static bool TryMove(int xd,int yd) {
    if(maze.GenerateRoom(x+xd, y+yd, defaultroom, 0).Wall)
        { std::printf("You cannot go that way.\n"); return false; }
    x += xd; y += yd; return true;
}
int main() {
    std::string lastcmd, cmd;
    std::printf("Welcome to the death trap.\n\n");
    help: std::printf(
        "Available commands:\n"
        "\tl/look\n\tla/look at <item>\n\tn/s/w/e for moving\n\tget <item>/get all/ga for short,\n"
        "\tdrop <item>/drop all\n\ti/inv/inventory\n\tquit\n\thelp\n\n"
        "You are starving. You are trying to find enough stuff to sell\n"
        "for food before you die. Beware, food is very expensive here.\n\n");
    for(Look(); life > 0; ) {
        std::printf("[life:%ld]> ", life); fflush(stdout);
        std::getline(std::cin, cmd);
        if(!std::cin.good() || cmd == "quit") break;
        if(cmd == "!") cmd = lastcmd; else lastcmd = cmd;
        if(cmd == "i" || cmd == "inv" || cmd == "inventory") Inv();
        else if(cmd == "n" || cmd == "north" || cmd == "go n" || cmd == "go north")
            { if(TryMove(0,-1)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "s" || cmd == "south" || cmd == "go s" || cmd == "go south")
            { if(TryMove(0,1)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "w" || cmd == "west" || cmd == "go w" || cmd == "go west")
            { if(TryMove(-1,0)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "e" || cmd == "east" || cmd == "go e" || cmd == "go east")
            { if(TryMove(1,0)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "sw" || cmd == "nw" || cmd == "se" || cmd == "ne")
            std::printf("The concept of diagonal movements makes you squeasy.\n");
        else if(cmd.substr(0,4) == "wear" || cmd.substr(0,5) == "wield")
            std::printf("You are scavenging for survival and not playing an RPG character.\n");
        else if(cmd == "l" || cmd == "look")             Look();
        else if(cmd.substr(0,3) == "la ")                LookAt(cmd.substr(3));
        else if(cmd.substr(0,8) == "look at ")           LookAt(cmd.substr(8));
        else if(cmd.substr(0,8) == "look in ")           LookAt(cmd.substr(8),true);
        else if(cmd.substr(0,5) == "look ")              LookAt(cmd.substr(5));
        else if(cmd.substr(0,5) == "l at ")              LookAt(cmd.substr(5));
        else if(cmd.substr(0,5) == "l in ")              LookAt(cmd.substr(5),true);
        else if(cmd.substr(0,2) == "l ")                 LookAt(cmd.substr(2));
        else if(cmd == "open" || cmd == "open chest" || cmd == "pry") OpenChest();
        else if(cmd == "ga")                             Get("all");
        else if(cmd.substr(0,3) == "ga ")                Get("all "+cmd.substr(3));
        else if(cmd.substr(0,4) == "get ")               Get(cmd.substr(4));
        else if(cmd.substr(0,5) == "drop ")              Drop(cmd.substr(5));
        else if(cmd == "help" || cmd == "what" || cmd == "?") goto help;
        else std::printf("what?\n");
    }
    double value = eq.value();
    return std::printf(
        "%s\n[life:%ld] Game over\nYou managed to collect stuff worth %.2f gold.\n"
        "With all your possessions, you purchase %s. You consume your reward eagerly.\nYOU %s\n",
        life<0?"You are pulled out from the maze by a supernatural force!":"byebye",
        life, value,
        Appraise(value).c_str(),
        value<10000.0
            ? "DID NOT SURVIVE. Hint: Learn to judge the value/weight ratio."
            : "SURVIVED! CONGRATULATION. ;)") < 0;
}
A pre-compiled Windows binary can be found in this zip: http://bisqwit.iki.fi/kala/bisqwit-adv.zip In this game you are a starving adventurer whose goal is to scange the dungeon for artifacts worth enough to buy him a meal -- and to do so before he is too famished to do anything. EDIT: Improved the parser a bit, motivated by subtle persuation at TVTropes
SXL
Joined: 2/7/2005
Posts: 571
It is pitch dark. You are likely to be eaten by a grue.
> what is a grue?
The grue is a sinister, lurking presence in the dark places of the earth. Its favorite diet is adventurers, but its insatiable appetite is tempered by its fear of light. No grue has ever been seen by the light of day, and few have survived its fearsome jaws to tell the tale. 
I never sleep, 'cause sleep is the cousin of death - NAS
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Alas, no grue there. The game is already too difficult :)
Post subject: Bytecodemachinehelloworldwithanarrayofconstmethodpointers
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Again a pointless exercise at various topics. Thoughtfodder. Language: C++
#include <vector>
#include <iostream>
enum { opPush, opCopy, opSwap, opPutchar, opSub,
       opJumpIfNonZero, opGosub, opReturn, nOpcodes };
class Interpreter
{
public:
    static void (Interpreter::*const opcodes[nOpcodes]) (const int[], int&);
private:
    void copy(const int[], int&)       { S.push_back(at(pop())); }
    void swap(const int[], int&)       { int &a=at(pop()), b=at(0); at(0)=a; a=b; }
    void sub(const int[], int&)        { int a = pop(); at(0) -= a; }
    void jz(const int b[],int& ip)     { int v = b[++ip]; if(pop() != 0) ip = v-1; }
    void putchar(const int[], int&)    { std::cout << char(pop()); }
    void push(const int b[],int& ip)   { S.push_back(b[++ip]); }
    void gosub(const int b[],int& ip)  { S.push_back(ip+2); ip = b[ip+1]-1; }
    void ret(const int[], int& ip)     { ip = pop()-1; }
    int& at(int v) { return S[S.size() - 1 - v]; }
    int pop() { int ret = at(0); S.pop_back(); return ret; }
    std::vector<int> S;
};

void (Interpreter::*const Interpreter::opcodes[nOpcodes]) (const int[], int&) =
{
    &Interpreter::push, &Interpreter::copy, &Interpreter::swap,
    &Interpreter::putchar, &Interpreter::sub, &Interpreter::jz,
    &Interpreter::gosub, &Interpreter::ret
};

int main()
{
    static const int code[] =
    {
        opPush, 'H',  // 0
        opPush, 'e',  // 2
        opPush, 'l',  // 4
        opPush, 'l',  // 6
        opPush, 'o',  // 8
        opPush, 5,    // 10 - length of string
        opGosub, 40,   // 12
        opPush, ',',  // 14
        opPush, ' ',  // 16
        opPush, 'w',  // 18
        opPush, 'o',  // 20
        opPush, 'r',  // 22
        opPush, 'l',  // 24
        opPush, 'd',  // 26
        opPush, '.',  // 28
        opPush, 8,   // 30 - length of string
        opGosub, 40,  // 32
        opPush, '\n', opPutchar, // 34
        opPush, -1, opReturn, // 37 - end
        opPush, 1, opSwap, // 40
        opPush, 0, opCopy,
        opPush, 0, opCopy,
        opPush, -2, opSub,
        opSwap, opPutchar,
        opPush, 1, opSub,
        opPush, 0, opCopy,
        opJumpIfNonZero, 43,
        opPush, 1, opSwap,
        opReturn
    };
    Interpreter machine;
    for(int ip=0; ip>=0; ip += 1)
        (machine.*Interpreter::opcodes[code[ip]])(code, ip);
}
Rather than spoil the presentation of the program with long blobs of comments, I'll just explain the curious lines here separately:
    static void (Interpreter::*const opcodes[nOpcodes]) (const int[], int&);
    // ^   ^     ^^^^^^^^^^^^^^      ^      ^ an array   ^^^^^^^^^^^^^^^^^
    // ^   ^     ^                   ^ called "opcodes"                 ^
    // ^   ^     ^ consists of pointers to methods of class Interpreter ^
    // ^   ^ returning nothing (void)                                   ^
    // ^                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // ^                            ^ and they take these parameters
    // ^^ rather than a per-instance property, the array
    //    is shared to all instances of class Interpreter
void (Interpreter::*const Interpreter::opcodes[nOpcodes]) (const int[], int&) =
// ^  ^^^^^^^^^^^^^^      ^            ^      ^ an array     ^
// ^  ^                   ^            ^ called "opcodes"    ^
// ^  ^                   ^ within class Interpreter         ^
// ^  ^ consists of pointers to methods of class Interpreter ^
// ^ returning nothing (void)                                ^ taking these
//                                                             parameters
// is initialized to this list:
{
    // Repeating "&Interpreter::" sucks in the array definition,
    // but here is what happens, if one tries to do without (gcc 4.4):
    //  ret: error: argument of type 'void (Interpreter::)(const int*, int&)' does not match 'void (Interpreter::* const)(const int*, int&)'
    // &ret: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function.  Say '&Interpreter::ret'
        (machine.*Interpreter::opcodes[code[ip]])(code, ip);
        // ^      ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^   ^^^^^^^^
        // ^      ^                     ^ array index ^
        // ^      ^ Access the method pointer table   ^
        // ^                                          ^
        // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        // ^ Call the member function using the instance in "machine"
Post subject: Bytecodemachinehelloworldwithanarrayofconstfunctionpointers
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Bisqwit wrote:
Again a pointless exercise at various topics. Thoughtfodder. Language: C++
Equivalent code in ANSI C.
#include <stdio.h>
#include <stdlib.h>
enum { opPush, opCopy, opSwap, opPutchar, opSub,
       opJumpIfNonZero, opGosub, opReturn, nOpcodes };
typedef struct Stack
{
    int L/*limit*/,*P/*pointer*/,C/*cap*/;
} Stack;
static void push(Stack* S, int v)
  { if(S->L >= S->C)
        S->P = (int *) realloc(S->P, (S->C = (S->C + 1) * 2) * sizeof( *S->P ));
    S->P[S->L++] = v; }
static int* at(Stack* S, int v) { return &S->P[S->L - 1 - v]; }
static void dispose(Stack* S) { if(S->P) free(S->P); S->L=S->C=0; S->P=(int*)0; }
static int pop(Stack* S) { return S->P[--S->L]; }
static void copy(Stack* S, const int b[], int*ip)  { push(S,*at(S,pop(S))); b=b; ip=ip; }
static void swap(Stack* S, const int b[], int*ip)  { int *a=at(S,pop(S)), c=*at(S,0); *at(S,0)=*a; *a=c; b=b; ip=ip; }
static void sub(Stack* S, const int b[], int*ip)   { int a = pop(S); *at(S,0) -= a; b=b; ip=ip; }
static void jz(Stack* S, const int b[],int*ip)     { int v = b[++*ip]; if(pop(S) != 0) *ip = v-1; }
static void outc(Stack* S, const int b[], int*ip)  { putchar(pop(S)); b=b; ip=ip; }
static void put(Stack* S, const int b[],int*ip)    { push(S,b[++*ip]); }
static void gosub(Stack* S, const int b[],int*ip)  { push(S,*ip+2); *ip = b[*ip+1]-1; }
static void ret(Stack* S, const int b[], int*ip)   { *ip = pop(S)-1; b=b; }
static void (*const opcodes[nOpcodes]) (Stack* S, const int b[], int*ip) =
{ put,copy,swap,outc,sub,jz,gosub,ret };

int main(void)
{
    static const int code[] =
    {
        opPush, 'H',  /* 0 */
        opPush, 'e',  /* 2 */
        opPush, 'l',  /* 4 */
        opPush, 'l',  /* 6 */
        opPush, 'o',  /* 8 */
        opPush, 5,    /* 10 - length of string */
        opGosub, 40,   /* 12 */
        opPush, ',',  /* 14 */
        opPush, ' ',  /* 16 */
        opPush, 'w',  /* 18 */
        opPush, 'o',  /* 20 */
        opPush, 'r',  /* 22 */
        opPush, 'l',  /* 24 */
        opPush, 'd',  /* 26 */
        opPush, '.',  /* 28 */
        opPush, 8,   /* 30 - length of string */
        opGosub, 40,  /* 32 */
        opPush, '\n', opPutchar, /* 34 */
        opPush, -1, opReturn, /* 37 - end */
        opPush, 1, opSwap, /* 40 */
        opPush, 0, opCopy,
        opPush, 0, opCopy,
        opPush, -2, opSub,
        opSwap, opPutchar,
        opPush, 1, opSub,
        opPush, 0, opCopy,
        opJumpIfNonZero, 43,
        opPush, 1, opSwap,
        opReturn
    };
    Stack S = {0, (int*)0, 0 };
    int ip;
    for(ip=0; ip>=0; ip += 1)
        opcodes[code[ip]](&S, code, &ip);
    dispose(&S);
    return 0;
}
This is probably faster than the C++ code above, because this uses function pointers rather than method pointers. Method pointers have to support virtual functions as well. A C++ version without method pointers, without looking like the C code, could be like this:
#include <vector>
#include <iostream>
enum { opPush, opCopy, opSwap, opPutchar, opSub,
       opJumpIfNonZero, opGosub, opReturn, nOpcodes };
class Interpreter
{
private:
    std::vector<int> S;
    int& at(int v) { return S[S.size() - 1 - v]; }
    int pop() { int ret = at(0); S.pop_back(); return ret; }
public:
    void Iterate(const int code[], int& ip)
    {
        int a,*c;
        switch(code[ip])
        {
        case opPush:          S.push_back(code[++ip]); break;
        case opCopy:          S.push_back(at(pop())); break; 
        case opSwap:          c=&at(pop()); a=at(0); at(0)=*c; *c=a; break;
        case opPutchar:       std::cout << char(pop()); break;
        case opSub:           a = pop(); at(0) -= a; break;   
        case opJumpIfNonZero: a = code[++ip]; if(pop() != 0) ip = a-1; break;
        case opGosub:         S.push_back(ip+2); ip = code[ip+1]-1; break;   
        case opReturn:        ip = pop()-1; break;
}   }   };
int main()
{
    static const int code[] =
    {
        /* same as above, omitted */
    };
    Interpreter machine;
    for(int ip=0; ip>=0; ip += 1)
        machine.Iterate(code, ip);
}
Banned User, Former player
Joined: 3/10/2004
Posts: 7698
Location: Finland
Playing with bytecode interpreters, eh?-)
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Warp wrote:
Playing with bytecode interpreters, eh?-)
Playing with method pointers, too. This was mildly unrelated to fparser. A pointless exercise.
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Since people like to analyze source code… What does this do?
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* for inet_ntoa */
#include <string.h> /* for memcpy */
#include <stdio.h> /* for fwrite etc, fdopen */
#include <unistd.h> /* for usleep */
#include <signal.h>

/* Data is PCX image with header (128 bytes) and redundant palette entries
 * stripped, RLE and padding undone, further compressed using a custom
 * RLE scheme */
static const unsigned char compdata[] =
"fe@@LNceMAAme@@LNceMAAje@@LNceMAAke@U^@NLMAMA^`bMMje@U^@NLMAMA^`bMMge@U^@NLM"
"AMA^`bMMje@^LNLeMG`bMje@^LNLeMG`bMge@^LNLeMG`bMie@UHNLL@Ub^`bMG`bie@UHNLL@Ub"
"^`bMG`bfe@UHNLL@Ub^`bMG`bie@^H@^Ub^bM`bG^ie@^H@^Ub^bM`bG^fe@^H@^Ub^bM`bG^ie@"
"KH@U^UU^M`bMG^je@KH@U^UU^M`bMG^ge@KH@U^UU^M`bMG^je@K@@UU^UUMM`b^Bje@K@@UU^UU"
"MM`b^Bge@K@@UU^UUMM`b^Bje@UHG@^^MD^AJ`bDje@UHG@^^MD^AJ`bDge@UHG@^^MD^AJ`bDie"
"@H_@G`e@H^WWVP``Cje@^HG`e@H^WWVP``Cge@K@G`e@H^WWVP``Che@HK@Cae@H^DW`dA``Cje@"
"U@ae@H^DW`dA``CgeH_@ae@H^DW`dA``Che@__@ce@HDWb\\Cie@K@@be@HDWb\\Cge@Uce@HDWb"
"\\Cie@UU@beCICW\\\\Cie@H_@deICW\\\\Cge@K@ceCICW\\\\Cje@UH@aeCKKRCCke@K@CceIK"
"KCCieH_@beCKKXCCle@H@aeCRKKRWCje@H_@ceIKXCWChe@K@CaeCbKCWCme@beIWRRCacCie@_U"
"U@beIKXCacCfe@H_@beCKKXCacCoeCW\\\\WCbcKAge@@UH@beIXXCbcCee@_UU@aeCKXXCbcCme"
"CS\\\\DCac]]D\\Age@H@ceI\\XC`c]]Cfe@UH@aeCX`aQC`c]]CmeAHSDcCQC\\``HCge@deI`a"
"\\ICQCge@H@beC\\`abCQCmeCHHWDHHCW\\\\AIW\\CleI`a\\IW\\\\Age@ceCYHHCW\\\\AleC"
"WWHCCIAW\\Q`eCHWCleIHHQ@\\@AmeCHWWC\\QIleCIHICOdAaeCCmeIWWQX@T@meCHW`aWCXIme"
"bCOIX@T]XApeIW`aQNFPCmeICH`aWC[IpeIXNFPTXAqebIFPECmeIKcCXIpeIXFPENXAreI@PE@C"
"meIXKFPXCIpeICPEXNXAIqeCZ@RHAmeICXPXC\\IIkeCAAW\\\\WPEXA``\\CoeAHWIW\\CmeI\\"
"CQC\\`a``QIieCHHIW\\WCaeCW\\WAneCHIHIIAkeCIW\\``\\CaeC\\``\\CheCHWHDACceCWIH"
"AmeAHWIIWCjeCIWIW\\CceC\\IHACCeeCHIHIeeAIHWICCkeAHHIHCjeCbHWACdeCWHHIWWAdeAI"
"WAfeAHHIbWCkeAIWICjeAHWIHCfeAHWWHAeeAIHHIfeAHWWHHAkeCHHWWAieAIWHCgeAHWHAgejA"
"IbHAAkeAAIIHWHAieAHWHCdebAIHAceb@b`@``;bi\"H\22;HX\303H33`iP;`iPb\1b\22\205`"
"``i\375`yH\313\22P\246`i`q``\334@\324\12\225\303;\1\266\32H\324\303\32\225"  
"\22\205\375\303`y;\313\266\246\303\22\277\313H\225\344\"\256\334;b\375\266"
"\354\324\225\364\277\277\215`q\266\215\313\277\215\375\277\246\225\277\266"
"\354c\266d\277";
#define n_palette_colors 38
static unsigned char pcxdata[32*24*3 + n_palette_colors*3];
#define palette (&pcxdata[sizeof(pcxdata)-n_palette_colors*3])
static void DecodeImage()
{
    unsigned srcpos, limit=sizeof(compdata), outpos=0, d, value, e;
    for(srcpos=0; srcpos<limit; ++srcpos)
        for(d = compdata[srcpos] ^ 64, value = d,
            e = (d >= 32 && d < 64) ? (value = compdata[++srcpos] ^ 64, d-32+1) : 1;
            e-- > 0; ) pcxdata[outpos++] = value;
}
static void PutWord(char* target, unsigned dword, int msb_first)
{
    int p;
    for(p=0; p<4; ++p) target[p] = dword >> (msb_first ? 24-p*8 : p*8);
}
static unsigned chksum32(const char* data, unsigned size, unsigned res, int crc)
{
    if(size == 0) return res;
    if(!crc) { unsigned s1 = ((res & 0xFFFF) + (*data & 0xFF)) % 65521; /* adler32 */
    return chksum32(data+1, size-1, s1 + ((((res>>16) + s1) % 65521) << 16), 0); }
    return data ? chksum32(data+1,size-1, ~((~res>>8) ^ chksum32(0,8,(~res ^ *data) & 0xFF,1)), 1)
                : chksum32(0,size-1, (res>>1) ^ ((res&1) ? 0xEDB88320u : 0), 1); /* crc32 */
}
static unsigned EncodeImageData(char* target,
                                unsigned (*Compress)(char*,const char*,unsigned))
{
    static int framenumber = 0, xoffsets[4] = {0,25,47,25};
    char databuf[64*(46+1)]; /* width: 46, height: 64 */
    int frame_x = xoffsets[framenumber++ % 4], frame_y = 0, y,x;
    for(y=0; y<64; ++y)
        for(databuf[y*(46+1)]=x=0; x<46; ++x)
            databuf[y*(46+1)+1+x] = pcxdata[(frame_x+x/2) + (frame_y+y/2)*72];
    return Compress(target, databuf, sizeof(databuf));
}
static unsigned Deflate(char* target, const char* source, unsigned srcsize)
{
    /* A bare-bones deflate-compressor (as in gzip) */
    int algo=8, windowbits=8 /* 8 to 15 allowed */, flevel=0 /* 0 to 3 */;
    /* Put RFC1950 header: */
    target[0] = algo + (windowbits-8)*16;
    target[1] = flevel*64 + 31-((256*target[0])%31); /* checksum and compression level */
    /* Compress data using a lossless algorithm (RFC1951): */
    target[2] = 1;  /* bfinal bit and btype: 0=uncompressed */
    PutWord(target+3, srcsize|(~srcsize<<16), 0);
    memcpy(target+7, source, srcsize);
    /* After compressed data, put the checksum (adler-32): */
    PutWord(target+7+srcsize, chksum32(source, srcsize, 1, 0), 1);
    return srcsize + 7+4;
}
static void EncodeImage(char** target)
{
    #define AddedChunk(type,n) do { unsigned ncopy=(n); \
        memcpy(*target+4, (type), 4); \
        PutWord(*target, ncopy, 1); /* in the chunk, put crc32 of the type and data (below) */ \
        PutWord(*target + 8 + ncopy, chksum32(*target + 4, 4 + ncopy, 0, 1), 1); \
        *target += 12+ncopy; } while(0)
    memcpy(*target, "\x89PNG\15\12\x1A\12", 8); /* Put header - always const */
    *target += 8;
      PutWord(*target+8 + 0, 46, 1); /* Put image width    */
      PutWord(*target+8 + 4, 64, 1); /* and height in IHDR */
      memcpy(*target+8 + 8, "\x8\3\0\0\0", 5);
      /* Meaning of above: 8-bit,paletted,deflate,std filters,no interlacing */
    AddedChunk("IHDR", 13);
      memcpy(*target+8, palette, n_palette_colors * 3);
    AddedChunk("PLTE", n_palette_colors*3);
    AddedChunk("IDAT", EncodeImageData(*target+8, Deflate) );
    AddedChunk("IEND", 0);
    #undef AddedChunk
}
static void Serve(int sock)
{
    static const char bound[] = "qegkqpekga";
    char Buf[20000];
    signal(SIGPIPE, SIG_IGN);
    FILE* fp = fdopen(sock, "r+");
    fgets(Buf, sizeof(Buf), fp); /* Get the request */
    do {
        fgets(Buf, sizeof(Buf), fp);
        /* Read until an empty line is received */
    } while(*Buf != '\n' && *Buf != '\r');
    fprintf(fp,
       "HTTP/1.0 200 OK\n"
        "Content-type: multipart/x-mixed-replace; boundary=\"%s\"\r\n"
        "\r\n", bound);
    printf("Connection received\n");
    for(;;)
    {
        char* EndPtr = Buf;
        EncodeImage(&EndPtr);
        if(fprintf(fp,
            "--%s\n"
            "Content-type: image/png\r\n"
            "Content-disposition: inline\r\n"
            "Content-length: %lu\r\n"
            "\r\n", bound, (unsigned long)(EndPtr-Buf)) <= 0
        || fwrite(Buf, EndPtr-Buf, 1, fp) < 1) break;
        if(fflush(fp) != 0) break;
        usleep(200000);
    }
    printf("Connection closed\n");
    fclose(fp);
}
int main()
{
    struct sockaddr_in MyAddr, They;
    int Plug = socket(AF_INET, SOCK_STREAM, 0), AddrLen = sizeof(They), Port = 8800;
    if(Plug < 0) { perror("socket"); return -1; }
    DecodeImage();
    memset(&MyAddr,0,sizeof(MyAddr));
    MyAddr.sin_port = htons(Port);
    MyAddr.sin_family = AF_INET;
    MyAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    { int tmp=1; setsockopt(Plug, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)); }
    if(bind(Plug, (struct sockaddr*)&MyAddr, sizeof(struct sockaddr)) < 0)
       { perror("bind"); return -1; }
    if(listen(Plug, 10) < 0)
       { perror("listen"); return -1; }
    for(;;)
    {
        int Sock = accept(Plug, (struct sockaddr*)&They, (unsigned*)&AddrLen);
        if(Sock < 0) { perror("accept"); sleep(1); continue; }
        Serve(Sock);
        close(Sock);
    }
    return 0;
}
(Tested, compiles and runs on at least Linux and Cygwin.) EDIT: Updated to use Chrono Trigger's data compression algorithm to reduce the size of the literal array embedded in the source. EDIT: Optimized data array for source code size. EDIT: Undid compression and used xor-by-64 and custom RLE instead. Also now using recursive crc32() and adler32() checksum functions…
Emulator Coder
Joined: 3/9/2004
Posts: 4588
Location: In his lab studying psychology to find new ways to torture TASers and forumers
If we're going to be posting what does this do riddles, let us at least start off with some simple stuff. [code c] uint32_t f(uint32_t x) { return((x&UINT32_C(037777777776))?(f(x-1)<<1)-f(x-2)+2:x); } [/code] What does f() do?
Warning: Opinions expressed by Nach or others in this post do not necessarily reflect the views, opinions, or position of Nach himself on the matter(s) being discussed therein.
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Nach wrote:
x&UINT32_C(037777777776)
That's the most complex way of saying x > 1 that I have ever seen... Not that the rest of the function is much better. ;) You could have at least said x & ~unsigned(1) and replaced the awkward uint32_t types with unsigned int.
Emulator Coder
Joined: 3/9/2004
Posts: 4588
Location: In his lab studying psychology to find new ways to torture TASers and forumers
Bisqwit wrote:
Nach wrote:
x&UINT32_C(037777777776)
That's the most complex way of saying x > 1 that I have ever seen... Not that the rest of the function is much better. ;)
Glad you liked it. Most people are shocked when they realize what the function actually does. Then they really get perplexed when working out the math.
Warning: Opinions expressed by Nach or others in this post do not necessarily reflect the views, opinions, or position of Nach himself on the matter(s) being discussed therein.
Joined: 8/29/2006
Posts: 68
Nach wrote:
If we're going to be posting what does this do riddles, let us at least start off with some simple stuff. [code c] uint32_t f(uint32_t x) { return((x&UINT32_C(037777777776))?(f(x-1)<<1)-f(x-2)+2:x); } [/code] What does f() do?
That's simple. Everyone knows that the square of a number is the same as the square of one less than that number times two minus the square of two less than that number plus 2. The part about checking whether it's greater than 1 is only there because the recursion has to know when to stop. Now i have one: [code c++] template <typename T> T f(T x) { if(sizeof(T)>=8) { x=((x&UINT64_C(0xFFFFFFFF00000000))>>32)|((x&UINT64_C(0x00000000FFFFFFFF))<<32); } if(sizeof(T)>=4) { x=((x&UINT64_C(0xFFFF0000FFFF0000))>>16)|((x&UINT64_C(0x0000FFFF0000FFFF))<<16); } if(sizeof(T)>=2) { x=((x&UINT64_C(0xFF00FF00FF00FF00))>>8)|((x&UINT64_C(0x00FF00FF00FF00FF))<<8); } x=((x&UINT64_C(0xF0F0F0F0F0F0F0F0))>>4)|((x&UINT64_C(0x0F0F0F0F0F0F0F0F))<<4); x=((x&UINT64_C(0xCCCCCCCCCCCCCCCC))>>2)|((x&UINT64_C(0x3333333333333333))<<2); x=((x&UINT64_C(0xAAAAAAAAAAAAAAAA))>>1)|((x&UINT64_C(0x5555555555555555))<<1); return x; } [/code] Good luck.
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
DeFender1031 wrote:
Now i have one:
Too well-known. Only that you've made it into a template function. ;) No takers for my program? It contains education in small size about a number of popular standards, about Chrono Trigger, and about one particular feature of WWW that is very rarely used.
Banned User, Former player
Joined: 3/10/2004
Posts: 7698
Location: Finland
Bisqwit wrote:
No takers for my program?
I think that the program is too large to raise interest. A wall of code is rather daunting. I'd say something like 15 lines of code is a good upper limit for such exercises.
Emulator Coder
Joined: 3/9/2004
Posts: 4588
Location: In his lab studying psychology to find new ways to torture TASers and forumers
Bisqwit wrote:
No takers for my program? It contains education in small size about a number of popular standards, about Chrono Trigger, and about one particular feature of WWW that is very rarely used.
Well I thought it was obvious for the most part, with good function names and clear strings which says what it does. You got a minimalistic HTTP Server using x-mixed-replace which I wasn't even sure what it was till I looked it up. You also have a PCX decoder which is rather trivial. What I do find very enlightening about your mini program is that it has a PNG encoder. I never considered doing that without using libpng. However, since you requested it, I decided to actually download the code, compile it, and test it out. Here's where I got a bit shocked. You have an animated image, I'm still not sure how you pulled that off, and I even have the code in front of me.
Warning: Opinions expressed by Nach or others in this post do not necessarily reflect the views, opinions, or position of Nach himself on the matter(s) being discussed therein.
arflech
He/Him
Joined: 5/3/2008
Posts: 1120
Bisqwit wrote:
(Tested, compiles and runs on at least Linux and Cygwin.)
The code needs some serious hacking before it can compile with TCC or MinGW GCC in Windows, because Windows uses a slightly different implementation of sockets. I tried compiling TCC in Cygwin (because I love that compiler so much), and then I learned that you should never run UPX on Cygwin binaries. (the same goes for Java, AbiWord, Recuva, Defraggler, and Google Chrome...even though Java runs after UPX, I couldn't upgrade it without un-compressing it) Then after that I found TCC still wouldn't compile, probably because Cygwin puts all the good stuff in /usr/local and for some reason the TCC configure script couldn't find it, so I bit the bullet and used GCC...but I did not get to see the magic. What other components do I need to see the animated image?
i imgur com/QiCaaH8 png
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
GMan wrote:
EDIT: wtf? I can't put "T" after "typename".
It's not because of "typename", but because you're telling phpBB you are posting HTML. Stop claiming you're posting HTML, and it will stop making assumptions on your <s and >s.
arflech wrote:
Windows uses a slightly different implementation of sockets.
I know. Maybe I should have been more explicit, but I never claimed to have implemented Microsoft Winsock support; only POSIX standard. Which is supported by Cygwin.
1 2
5 6