22 Mar 2013

C shared objects exported to python (ctypes)

Some C Projects need a quick extension which is not performance relevant. Sometimes I wish to use python because it is available on all modern *nix systems. Also, programming in python is very efficient.

To solve this I have created a small example project which puts the functionality into a shared object (dll in windows-speak) which can be used from python.

The library:

lib.c
This library contains a simple function which appends the string "->aaa" to an existing string. This is an interesting example since it involves dynamic memory allocation and pointers.


#include <stdlib.h>
#include <string.h>
#include "lib.h"

//int main() {return 0;}

char *append(char* str) {
 //char *n = "";
 char *b = "->aaa";
 char *n = malloc(strlen(str) + strlen(b) + 1);
 n[0] = '\0';
 strcat(n, str);
 strcat(n, b);
 return n;
}


lib.h
char *append(char* str);


test.c
used for testing the the library from C.

#include <stdio.h>
#include "lib.h"

int main(int argc, char* argv[]) {
 printf("%s\n", append(argv[1]));
 return 0;
}

Compiling
#!/usr/bin/env bash
gcc -std=c99 -Wall -c -fPIC lib.c -o lib.o
gcc -std=c99 -Wall -shared -Wl,-soname,libt.so -o libt.so lib.o
gcc -std=c99 -Wall -o test test.c -L. -lt
rm lib.o


pylibt.py
Because python uses dynamic data types we need to give some hints about data types. Also pointers need to be declared.
#!/usr/bin/env python

from ctypes import *

cdll.LoadLibrary("./libt.so")
_libt = CDLL("libt.so")

#def initpylibt():
# pass

def append(s):
 _append = _libt.append
 _append.argtypes = [c_char_p]
 _append.restype = c_char_p
 return _append(s)


test.py
let's use the python module from a script.

#!/usr/bin/env python

import pylibt

ret = pylibt.append("0123456789as dfasdf asdf asdf asdf asf asdf")
print ret


7 Mar 2013

gcc - compile shared library

the Program to compile as shared library

string.h

/** string.h
 *
 * $Id$
 */

#ifndef STINRG_H_
#define STINRG_H_

#ifndef DEFAULT_BLOCKSIZE
 // num. bytes of junks of memory to allocate from the heap
 #define DEFAULT_BLOCKSIZE 128
#endif

#include <stdio.h>
#include <stdlib.h>

/** datatype to hold the character data */
struct string_t {
 int memsize;
 int length;
 char *text;
 int error;
};
typedef struct string_t string;

// function declarations
string str_redfile(FILE *file, int bs);
void str_dump(string *s);
unsigned long str_memsize(string *s);

#endif /* STINRG_H_ */

string.c

/** C example for reading file and allocating memory dynamically.
 *
 * Will read a file from disk while dynamically allocating memory.
 *
 * Example usage: ./exe [file-name]
 *
 * $Id$
 */

#include "string.h"

/** Read file from FILE pointer
 *
 * Reads data from FILE *file and dynamically allocates memory from the heap
 * to store the read data. If memory allocation fails, the returned string
 * struct's member error will equal to 1.
 *
 * If reading the file works, string.error will be 0.
 */
string str_redfile(FILE *file, int bs) {
 int charsize = sizeof(char);
 int charcount = 0;
 int blocksize;
 if (!bs)
  blocksize = DEFAULT_BLOCKSIZE;
 else
  blocksize = bs;
 int memsize = blocksize;
 char *line = (char*) malloc(charsize * (memsize+1));
 char *linep = line;

 string l;
 l.error = 0;
 l.length = 0;
 l.memsize = memsize;
 l.length = charcount;
 l.text = linep;

 while (1) {

  if(charcount > memsize) {
   // reallocate memmory
   memsize = memsize+blocksize;
   char *al = (char*) realloc(linep, charsize * (memsize+1));

   if (al == NULL) {
    fprintf(stderr, "Error, out of memory.\n");
    *line = '\0';
    l.text = linep;
    l.error = 1;
    return l;
   }
  }

  char c = fgetc(file);

  //if (c == '\n' || c == '\r' || c == EOF) {
  if (c == EOF) {
   charcount--;
   break;
  }

  // remember input char
  *line = c;
  *(line++);
  charcount++;
 }

 *line = '\0';
 l.text = linep;
 l.memsize = memsize;
 l.length = charcount;

 return l;
}

/** return allocated memory of a string struct
 */
unsigned long str_memsize(string *s) {
 return sizeof(string) + (s->memsize) + 1;
}

/** print struct string info
 */
void str_dump(string *s) {
 if (s->error == 0) {
  printf("memsize: %d\n", s->memsize);
  printf("length:  %d\n", s->length);
  printf("error:   %d\n", s->error);
  printf("text:    %.*s\n", 10, s->text);
  printf("total:   %lu\n", str_memsize(s));
 } else
  printf("Error: %d.\n", s->error);
}

test.c

/** test shared library
 *
 * an example how to use a shared library in a C program.
 *
 * $Id$
 */

#include "string.h"

/** main entry point
 */
int main(int argc, char **argv) {

 // we need 1 argument, the file name
 if(argc < 2) {
  fprintf(stderr, "Usage: %s [file]\n", argv[0]);
  exit(1);
 }

 // try to open the file
 FILE *f = fopen(argv[1], "r");
 if (f == NULL) {
  fprintf(stderr, "Error: failed to open file: %s\n", argv[1]);
  exit(2);
 }

 // allocate memory for storing the string and read contents
 string l = str_redfile(f, 0);

 // close file
 fclose(f);

 // check if reading worked
 if (l.error != 0) {
  fprintf(stderr, "Error: str_redfile() error: '%d'\n", l.error);
  fclose(f);
  exit(l.error);
 }

 // dump file infos
 str_dump(&l);

 // TODO: deallocate the memory
 // free memory
 //free(l.text);

 exit(EXIT_SUCCESS);
}

compiling the shared library and the test program from a bash:

#!/usr/bin/env bash
#
# gcc, compile a shared object file
#
# This example shows how to compile a shared object for C with gcc on linux. 
# required files: string.c, string.h test.
#
# $Id$

# configuration
bin=test
libname=string
v[0]=1; v[1]=0; v[2]=1;

# dynamic library
rm lib$libname* $bin
gcc -c -fPIC $libname.c -o $libname.o
gcc -shared -Wl,-soname,lib$libname.so -o lib$libname.so $libname.o
# create links for ld
#ln -s lib$libname.so lib$libname.so.${v[0]}
#ln -s lib$libname.so.${v[0]} lib$libname.so.${v[0]}.${v[1]}
#ln -s lib$libname.so.${v[0]}.${v[1]} lib$libname.so.${v[0]}.${v[1]}.${v[2]}

# compile executable
gcc $bin.c -o $bin -L. -l$libname

# run with proper LD_LIBRARY env variable, example:
# $ LD_LIBRARY_PATH=. ./test [path-to-file]

Running the executable. Make the newly compiled library available to ld through an environment variable.

$ LD_LIBRARY_PATH=. ./test [path-to-file]

6 Mar 2013

Reading file with dynamic memmory allocation in C

/*
 Will read a file from disk while dynamically allocating memory.

 Example usage: ./exe [file-name]
 */

#include <stdio.h>
#include <stdlib.h>

typedef struct string_t {
 int memsize;
 int length;
 char *text;
 int error;
};
typedef struct string_t string;

string redfile(FILE *file);
void str_dump(string *s);

string redfile(FILE *file) {
 int charsize = sizeof(char);
 int charcount = 0;
 int blocksize = 10;
 int memsize = blocksize;
 char *line = (char*) malloc(charsize * (memsize+1));
 char *linep = line;

 string l;
 l.error = 0;
 l.length = 0;
 l.memsize = memsize;
 l.length = charcount;
 l.text = linep;

 while (1) {

  if(charcount > memsize) {
   // reallocate memmory
   memsize = memsize+blocksize;
   char *al = realloc(linep, charsize * (memsize+1));

   if (al == NULL) {
    fprintf(stderr, "Error, out of memory.\n");
    *line = '\0';
    l.text = linep;
    l.error = 1;
    return l;
   }
  }

  char c = fgetc(file);

  //if (c == '\n' || c == '\r' || c == EOF) {
  if (c == EOF) {
   charcount--;
   break;
  }

  // remember input char
  *line = c;
  *(line++);
  charcount++;
 }

 *line = '\0';
 l.text = linep;
 l.memsize = memsize;
 l.length = charcount;

 return l;
}

void str_dump(string *s) {
 if (s->error == 0) {
  printf("memsize: %d\n", s->memsize);
  printf("length:  %d\n", s->length);
  printf("error:   %d\n", s->error);
  printf("text:    %s\n", s->text);
  printf("total:   %lu\n", sizeof(*s)+1);
 } else
  printf("Error: %d.\n", s->error);
}

int main(int argc, char **argv) {
 // string l = redfile(stdin);

 if(argc < 2) {
  fprintf(stderr, "Usage: %s [file]\n", argv[0]);
  exit(1);
 }

 FILE *f = fopen(argv[1], "r");
 if (f == NULL) {
  fprintf(stderr, "Error: failed to open file: %s\n", argv[1]);
  exit(2);
 }

 string l = redfile(f);

 if (l.error != 0) {
  fprintf(stderr, "Error: redfile() error: '%d'\n", l.error);
  fclose(f);
  exit(l.error);
 }

 str_dump(&l);

 fclose(f);

 exit(0);
}

5 Mar 2013

3sat shooting



We did some test flights today for the German TV 3sat. The shots will be aired in March this year. Just a couple of days before the shooting we got our very own FLIR PS thermal camera, yay.

Some footage of the shooting: