Search code examples
pythoncmemcpy

is there a Python Equivalent to Memcpy


I am trying to port some C Code but I am really stuck cause of the use of memcpy I tried with ctypes (did not work). I am hoping to find a python way of using an equivalent function of memcpy

Any ideas

Here is an example of the C Code I am trying to port

i = l + 5;
t = htons(atoi(port));
memcpy((buf+i), &t, 2);

Solution

  • You almost certainly don't need to call htons and then copy the 2 bytes into a buffer—see Keith's answer for why.

    However, if you do need to do this (maybe you're crafting IP packets to compare to captured wire packets as a test or something?), you can.

    First, if you're using a bytearray (or anything else that meets the writable buffer protocol), you just use normal list-style slice assignment:

    # like C's memcpy(buf+i, foo, 2)
    buf[i:i+2] = foo
    

    You don't have that two-byte string foo; you have a short integer. In C, you can turn that into a pointer to two bytes just by using the & operator to get its address, but Python can't do that. Fortunately, there's a standard library module called struct designed for exactly this kind of thing:

    t = socket.htons(int(port))
    buf[i:i+2] = struct.pack('h', t)
    

    Or, because struct can handle endianness for you:

    t = int(port)
    buf[i:i+2] = struct.pack('!h', t)
    

    However, often you don't even need the buffer copying; you can define the entire structure all at once inside struct. For example, if you're trying to pack an IP address and port into a 6-byte array, you could do this:

    buf = bytearray(6)
    i = 0
    addrbytes = [int(part) for part in addr.split('.')]
    buf[i:i+4] = struct.pack('4B', addrbytes[0], addrbytes[1], addrbytes[2], addrbytes[3])
    i += 4
    portshort = int(port)
    buf[i:i+2] = struct.pack('!h', portshort)
    

    But this is much simpler:

    addrbytes = [int(part) for part in addr.split('.')]
    portshort = int(port)
    buf = struct.pack('!4Bh', addrbytes[0], addrbytes[1], addrbytes[2], addrbytes[3], portshort)
    

    I've just defined a structure that's in network order, with four bytes followed by a short, and packed my data into it.

    One last thing to mention: If you really want to deal with C-style variables using C-style code, the ctypes module is another option. It's made specifically for interacting with C code, so in general it's pretty low-level (and the only module in the standard library that lets you segfault your code), but it let you build some nice mid-level stuff that looks a little more like C:

    class ADDRPORT(ctypes.BigEndianStructure):
        _fields_ = [("addr", ctypes.c_char*4),
                    ("port", ctypes.c_short)]
    
    addrport = ADDRPORT(addrbytes, portshort)
    

    Since your C code is progressively filling up a buffer, rather than setting elements of a struct, this probably isn't what you want. But it's worth being aware of, because it probably will be what you want at some point.