Idiomatic way to port C bitfiddling to Python - python

How would you port the following C code in a Pythonic way (especially the bit fiddling part in Get2, Get3, ...)
switch(mem[pos-1])
{
...
case 0x10: pos+=Get2(&mem[pos+0x02])+0x04; break;
case 0x11: pos+=Get3(&mem[pos+0x0F])+0x12; break;
case 0x16: pos+=Get4(&mem[pos+0x00])+0x04; break;
...
case 0x20: pos+=0x02; break;
}
...
//////////////////////////////////////////////////////////
// Conversion routines to fetch bytes in Big Endian order
//////////////////////////////////////////////////////////
unsigned int Get2(unsigned char *pointer)
{
return (pointer[0] | (pointer[1]<<8));
}
unsigned int Get3(unsigned char *pointer)
{
return (pointer[0] | (pointer[1]<<8) | (pointer[2]<<16));
}
unsigned int Get4(unsigned char *pointer)
{
return (pointer[0] | (pointer[1]<<8) | (pointer[2]<<16) | (pointer[3]<<24));
}
This is what I've got so far:
x = struct.unpack('B', mem[pos-1])[0]
if x == 0x10:
# pos += ???
continue
if x == 0x11:
# pos += ???
continue
if x == 0x16:
# pos += ???
continue
if x == 0x20:
pos += 0x02
continue

If you're just getting one unsigned byte, just do
x = ord(mem[pos - 1])
on Python 2 or
x = mem[pos - 1]
on Python 3.
Instead of select / case, You want a dictionary.
positions = {0x10: do_10, 0x11: do_12, 0x16: do_16}
Where do_10 etc. are functions:
def do_10(pos):
# This actually would need an endianness character
return struct.unpack('H', mem[pos + 0x02])[0] + 0x04
You use it like this:
pos += positions[mem[pos - 1]](pos)
If you want to define the functions right in the dictionary, you can:
positions = {
# This actually would need an endianness character
0x10: (lambda pos: struct.unpack('H', mem[pos + 0x02])[0] + 0x04)
# ...
}

This is a guess, but based on your code, I think this is what you're looking for:
x = struct.unpack('B', mem[pos-1])[0]
if x == 0x10:
pos += 0x04 + struct.unpack('>h' mem[pos+0x04])
elif x == 0x11:
pos += 0x12 + (0x00FFFFFF & struct.unpack('>i' mem[pos+0x0F]))
elif x == 0x16:
pos += 0x04 + struct.unpack('>i' mem[pos])
elif x == 0x20:
pos += 0x02

Your four bit-fiddlers could almost transliterated in python
def bigstr_to_int2(str) :
return (ord(str[0])<<8) | ord(str[1])
Seems to work, but is perhaps not "idiomatic". Ideally, you should try to use the struct.unpack to do the whole thing in bulk, but I can't tell you how to do that, because I don't understand the main function up the top.

Related

Python read character

I would like to know how can I read the charaters into a buffer in Python?
In C code, I can easy decralare the buffer character like char buffer[256];:
void read_char(int x, char buffer[], int *flag_stop) {
int i, length;
char character;
i = 0;
bzero(buffer, 256);
do {
if ((length = read(x, &character, 1)) <= 0)
{
*flag_stop = 1;
break;
}
buffer[i] = character;
i++;
}
while(c != 0x0A);
}
But I don't know how to do in Python so the code is something like this:
def read_char(x,buffer,**flag_stop):
i = 0
buffer = np.array([], dtype='S64')
while True:
if os.read(x, character, 1) <= 0:
**flag_stop == 1
break
buffer[i] = str(character)
i=i+1
if(character != 0x0A):
break
I have tried with numpy.chararray but I did not work. Any idea for this problem? thank you very much!
Your main problem is that you want in Python write code exactly like in C.
Python would need something like this
def read_char(x):
flag_stop = False # or 0
i = 0
buffer = np.array([], dtype='S64')
while True:
character = os.read(x, 1)
if not character: # if len(character) < 0
flag_stop = True # or 1
break
buffer[i] = character
i += 1
if character != 0x0A:
break
return buffer, flag_stop
# ---
buffer, flag_stop = read_char(file_handler)
I don't like str() in your code - you may get single byte which is part of mulit-bytes code (like utf-8) and converting to str() can only mess it.

Negative number causes VarInt's encoding function to crash

I try to implement the function below in Python but as soon as I enter a negative number the function crashes. Does anyone understand why?
public static void writeVarInt(int value) {
do {
byte temp = (byte)(value & 0b01111111);
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
writeByte(temp);
} while (value != 0);
}
Here my function :
def write_varint(array, data):
first_pass = True
while data != 0 or first_pass:
first_pass = False
temp = (data & 0b01111111)
data >>= 7
if data != 0:
temp |= 0b10000000
print(temp)
write_byte(array, temp)
def write_byte(array, data):
write_bytes(array, struct.pack('>b', data))
def write_bytes(array, source_bytes):
for byte in source_bytes:
array.append(byte)
if data != 0:
temp |= 0b10000000
When data value is -1 this conditional does not get called and therefore temp ends up out of range of the needed [-128, 127], and wounds up crashing when struct.pack() function gets called (which requires a number within that range).

C++ equivalent of python's expandtabs() function?

I need to implement the C++ equivalent of the expandtabs() function. Can someone help me with converting this code to C++?
def expandtabs(string, n):
result = ""
pos = 0
for char in string:
if char == "\t":
# instead of the tab character, append the
# number of spaces to the next tab stop
char = " " * (n - pos % n)
pos = 0
elif char == "\n":
pos = 0
else:
pos += 1
result += char
return result
This is what I have:
std::string ExpandTabs(const std::string &str, int tabsize =4){
std::string ReturnString = str;
std::string result = " ";
int pos = 0;
for(std::string::size_type i = 0; i < ReturnString.size(); ++i) {
if (ReturnString[i] == '\t'){
int spaces = tabsize - pos % tabsize ;
ReturnString.append(" ", spaces);
pos = 0;
}
else{
pos+=1;
}
}
return ReturnString;
You need to build up the string character by character. Currently you assign str to ReturnString at the start of the function and then append whatever spaces you decide are necessary to the end of the string, instead of in place of the tabs.
There are no doubt more idiomatic ways to achieve the same result, but a like for like conversion of the python might look like.
#include <iostream>
#include <string>
std::string expand_tabs(const std::string &str, int tabsize=4)
{
std::string result = "";
int pos = 0;
for(char c: str)
{
if(c == '\t')
{
// append the spaces here.
result.append(tabsize - pos % tabsize, ' ');
pos = 0;
} else
{
result += c;
pos = (c == '\n') ? 0: pos + 1;
}
}
return result;
}
int main()
{
std::cout << expand_tabs("i\tam\ta\tstring\twith\ttabs") << '\n';
std::cout << expand_tabs("1\t2\t3\t4", 2) << '\n';
}
It basically steps through the input forwarding on any non tab characters to the result string, otherwise it adds the correct number of spaces to the result.
Output:
i am a string with tabs
1 2 3 4
A straight translation of the python code is problematic, since char cannot be both a string and a single character, but apart from that it's straightforward:
std::string expandtabs(std::string const&str, std::string::size_type tabsize=8)
{
std::string result;
std::string::size_type pos = 0
result.reserve(str.size()); // avoid re-allocations in case there are no tabs
for(c : str)
switch(c) {
default:
result += c;
++pos;
break;
case '\n':
result += c;
pos = 0;
break;
case '\t':
result.append(tabsize - pos % tabsize,' ');
pos = 0;
}
return result
}

Trying to port my interactive ncurses game of life from C to python

I have a single out of range bug when trying to iterate over the grid to count neighbor cells. It's almost done, but I've spent a couple hours on this one bug already. I'm determined to deal with the rest of them if any after this. lol. I've never asked for help on stack exchange before, so be sure that I wouldn't if not having tried everything and being thoroughly frustrated. Go easy on me. lol
The C code works. If anybody wants the declarations for the C implementation I would be happy to post them.
To reproduce the bug, fire it up with python3 in a *nix terminal, and press "enter". You might do it on windows, but I guess you need to do some work with pdcurses.
It seems possible now that the C code has undetected errors despite working, and throwing no compiler warnings.
When iterating over the 2D array, the code is supposed to work by adding +1 to the grid(grid.cell) index, so as not to go out of bounds when counting cells just out of the screen. From the rudimentary debug output I added to grid.neighbor_count, I can see that it gets all the way to the far side of the screen on the x axis before it throws an error, so going negative on the y axis seems to work as expected. I've seen several other ways to do this, but this seems the most elegant to me, and it works in C already.
Error message
Traceback (most recent call last):
File "./life.py", line 118, in <module>
main()
File "./life.py", line 100, in main
grid.draw_next(term)
File "./life.py", line 49, in draw_next
j = self.neighbor_count(y,x)
File "./life.py", line 38, in neighbor_count
count += self.cell[i+1][j+1]
IndexError: list index out of range
Python
from blessed import Terminal
#import signal
import sys
def echo(text):
print(text, end='', flush=True)
class Grid(object):
def __init__(self, y, x):
self.cell = [([0] * x) for i in range(y)]
def resize(self, y, x):
pass
def width(self, x=0):
if not x: return len(self.cell[0][:])
def height(self, y=0):
if not y: return len(self.cell[:][:])
def toggle_cell(self, y, x):
i = self.cell[y][x]
if i:
self.cell[y][x] = 0
return 0
else:
self.cell[y][x] = 1
return 1
def neighbor_count(self, y, x):
count = 0
for i in range(y-1,y+2):
for j in range(x-1,x+2):
# debug sys.stderr.write('i: {0} j: {1} '.format(i, j))
# debug sys.stderr.write('y: {0} x: {1}\n'.format(y, x))
count += self.cell[i+1][j+1]
return count - self.cell[y][x]
class Grid(Grid):
lifeform = '+'
def draw_next(self, term):
new = [([0] * (term.width)) for i in range(term.height)]
for y in range(term.height):
for x in range(term.width):
j = self.neighbor_count(y,x)
if j == 2:
new[y+1][x+1] = self.cell[y+1][x+1]
elif j == 3:
new[y+1][x+1] = 1;
with term.location(x=x, y=y):
echo(self.lifeform)
else:
new[y+1][x+1] = 0
with term.location(x=x, y=y):
echo(' ')
self.cell = new
def main():
"""Program entry point."""
term = Terminal()
y_pos,x_pos = 0,0
def draw(y, x, string):
with term.location(y=y, x=x):
echo(string)
def move(y, x):
echo(term.move(y, x))
grid = Grid(term.height, term.width)
with term.cbreak(), term.fullscreen():
inp, inp0 = '',''
echo(term.clear)
while inp0 not in ('q', 'Q', 'KEY_ESCAPE'):
inp0 = term.inkey() # timeout=1
inp = repr(inp0)
if not inp:
# timeout
break
elif inp0 == ' ':
if grid.toggle_cell(y_pos, x_pos):
draw(y_pos, x_pos, grid.lifeform)
else: draw(y_pos, x_pos, ' ')
elif inp == 'KEY_UP' and y_pos > 0:
y_pos -= 1
move(y_pos, x_pos)
elif inp == 'KEY_DOWN' and y_pos < term.height-1:
y_pos += 1
move(y_pos, x_pos)
elif inp == 'KEY_RIGHT' and x_pos < term.width-1:
x_pos += 1
move(y_pos, x_pos)
elif inp == 'KEY_LEFT' and x_pos > 0:
x_pos -= 1
move(y_pos, x_pos)
elif inp == 'KEY_ENTER':
grid.draw_next(term)
# everything down here was for debugging info
elif inp0 == 'x':
draw(y_pos, x_pos, x_pos)
elif inp0 == 'y':
draw(y_pos, x_pos, y_pos)
elif inp0 == 'w':
draw(y_pos, x_pos, grid.width())
elif inp0 == 'h':
draw(y_pos, x_pos, grid.height())
elif inp0 == 'W':
draw(y_pos, x_pos, term.width)
elif inp0 == 'H':
draw(y_pos, x_pos, term.height)
elif inp0 == 'g':
draw(y_pos, x_pos, term.get_location())
if __name__ == '__main__':
main()
C
#include"life.h"
int main(){
init_win();
game_on = 1; //init_game();
while(game_on){
//get_color();
input();
wrefresh(stdscr);
}
quit();
return 0;
}
void init_win(){
initscr();
nodelay(stdscr,TRUE); // don't block for input, getch()
raw();
cbreak(); // take input chars one at a time, no wait for \n
noecho(); // don't print input directly
curs_set(2);
keypad(stdscr,TRUE); // enable keyboard mapping
}
void init_game(){
want_color = 1;
srand(time(NULL));
if (has_colors() && want_color) { start_color(); }
for (int i = 0; i < 8; i++)
init_pair(i, color_table[i], COLOR_BLACK);
game_on = 1;
}
void input(){
switch(getch()){
case 'q':
nodelay(stdscr,FALSE);
mvprintw(0,0,"Really quit? [y/N]");
refresh();
if(getch()=='y'){
game_on = 0;
}
else{
move(y_pos,x_pos);
refresh();
nodelay(stdscr,TRUE);
}
break;
case 'c':
clear_grid();
break;
case ' ':
toggle_cell(y_pos,x_pos);
break;
case KEY_UP:
if(y_pos > 0){
y_pos--;
move(y_pos,x_pos);
}
break;
case KEY_DOWN:
if(y_pos < LINES-1){
y_pos++;
move(y_pos,x_pos);
}
break;
case KEY_LEFT:
if(x_pos > 0){
x_pos--;
move(y_pos,x_pos);
}
break;
case KEY_RIGHT:
if(x_pos < COLS-1){
x_pos++;
move(y_pos,x_pos);
}
break;
case 10: // 10 == newline
// leaveok(stdscr,1);
update_generation();
move(y_pos,x_pos);
//leaveok(stdscr,0);
break;
}
}
void clear_grid(){
for (int y=0;y<1000;y++)
for (int x=0;x<1000;x++)
grid[y][x] = 0;
erase();
}
void draw(int y,int x,char z){
move(y,x);
delch();
insch(z);
}
void get_color(){
chtype bold = (rand() % 2) ? A_BOLD : A_NORMAL;
attrset(COLOR_PAIR(rand() % 8) | bold);
}
void toggle_cell(int y,int x){
if (grid[y+1][x+1]==1){
grid[y+1][x+1] = 0;
draw(y,x,' ');
} else {
grid[y+1][x+1] = 1;
draw(y,x,CELL);
}
}
int neighbor_count(int y,int x){
int count = 0;
for(int i=y-1;i<=y+1;i++){
for(int j=x-1;j<=x+1;j++){
count += grid[i+1][j+1];
}
}
count -= grid[y+1][x+1];
return(count);
}
void update_generation(){
int new_grid[1000][1000];
for (int y=0;y<LINES;y++)
for (int x=0;x<COLS;x++)
switch(neighbor_count(y,x)){
case 2:
new_grid[y+1][x+1] = grid[y+1][x+1];
break;
case 3:
new_grid[y+1][x+1] = 1;
draw(y,x,CELL);
break;
default:
new_grid[y+1][x+1] = 0;
draw(y,x,' ');
}
for (int y=0;y<LINES;y++)
for (int x=0;x<COLS;x++)
grid[y+1][x+1] = new_grid[y+1][x+1];
}
void quit(){
nocbreak();
echo();
endwin();
}

Python - Ported AS2 function doesn't work

This game uses this encryption to stop people from being able to connect bots easily the function used by the game is:
function djinndecrypt(string, key) {
var crypt = "";
var part = "";
var parts = 0;
var keyGen = key.length;
for(var i = 0; i < string.length; i++) {
var char = string.charAt(i);
var chrNum = string.charCodeAt(i);
var keyChr = key.charAt(parts % key.length);
var keyNum = key.charCodeAt(parts % key.length);
part += char;
if((i+1) % 3 == 0 && i > 0) {
crypt += String.fromCharCode((coreHash.indexOf(part)+2) - keyNum - keyGen);
part = "";
parts++;
}
}
return crypt;
I have attempted to port this to python my code is:
coreHash = "4Qiqc3mpjw2jFLJBaV9ANsWoYDR8ktvZIMeJTfOd1lyH5P7XKxhU6rCbzuE10nS5Ks7rPj1YvnDlbX4m2jI0NkyOL3hHU6FRVduJJoCwfWiABzqSaETMet1pZ89xQc81ieHA0F5EuNnWCwJvMJLQhV3UxPYRB76pbfIy2dcoaT14XkqlZsSzj9KDtrmjO9fjdMPERB12TYzDJrNxc3QajKU0kbvu4F56yHAXWO7tlwni8mZqCSosLVIehJ1pzT1kEsSXAl1O45CdLnRwQeM06ZqxJfJayBN8FWj2prYjuKIhiHU7VbomDPvct935yULcOHNJZsa31DeSlmnPBxJkYbTdAFRotIizv2VpXuf9w0KqQ8Ehjr4MW7Cj164Y1ukWxp9EjOwDPF8rsX5vlZN7QJqfteCATKncUoLVaM16Shy";
def djinndecrypt(string, key):
crypt = ""
part = ""
parts = 0;
keyGen = len(key)
for i in xrange(len(string)):
char = string[i]
chrNum = ord(char)
keyChr = key[parts % len(key)]
keyNum = ord(keyChr)
part += char
if((i + 1) % 3 == 0 and i > 0):
crypt += chr((coreHash.find(part) + 2) - keyNum - keyGen)
part = ""
parts += 1
print crypt
djinndecrypt("zQDHzljoHM6RhuhuhWzQDA5dgOAzQDlbAqkgOA57mM", "69338277581336797325449966279465")
The code ends up raising an exception:
ValueError: chr() arg not in range(256)
chr() only accepts up to 255 as described by the error. You can get more distance using unichr() but that returns Unicode characters instead of ACSII like chr().
However, given that this is supposed to result in an English string, the problem appears to be in the coreHash, as results above 122 [ chr(122) is 'z' ] will be out of alphabetical range.

Categories