import collections
import itertools
from cffi import FFI
class StringMap(collections.Mapping):
def __init__(self, *stuff, **values):
ptrs = ((ffi.new('const char[]', k), ffi.new('const char[]', v))
for (k, v) in StringMap._unstuff(*stuff, **values))
ptrs = zip(*ptrs)
keys, vals = [ffi.new('const char*[]', list(arr)) for arr in ptrs]
count = len(keys)
ptr = lib.stringmap_new(count, keys, vals)
self._underyling = ffi.gc(ptr, lib.stringmap_free)
def keys(self):
return list(self)
def get(self, key):
ptr = lib.stringmap_get(self._underyling, ffi.new('const char[]', key))
if ptr != ffi.NULL:
return ffi.string(ptr)
def __len__(self):
return lib.stringmap_len(self._underyling)
def __iter__(self):
n = 0
ptr = lib.stringmap_keys(self._underyling)
while ptr[n] != ffi.NULL:
yield ffi.string(ptr[n])
n += 1
def __getitem__(self, key):
item = self.get(key)
if item is not None:
return item
raise KeyError(key)
def items(self):
return list(self.iter_items())
def iter_items(self):
return ((k, self[k]) for k in self)
@staticmethod
def _unstuff(*stuff, **values):
acc = []
for stuff in stuff:
acc += [_unstuff_it(stuff)]
acc += [values.items()]
return list(itertools.chain(*acc))
def _unstuff_it(stuff):
if isinstance(stuff, collections.Mapping):
return stuff.items()
if isinstance(stuff, collections.Iterable):
return stuff
raise ValueError('Please pass only sequences of pairs or actual mappings.')
ffi = FFI()
ffi.cdef("""
typedef struct stringmap_t stringmap_t;
stringmap_t* stringmap_new(size_t count, char const* const* keys, char const* const* values);
char const* const* stringmap_keys(stringmap_t const* m);
char const* stringmap_get(stringmap_t const* m, char const* key);
size_t stringmap_len(stringmap_t const* m);
void stringmap_free(stringmap_t* m);
""")
lib = ffi.dlopen('target/debug/libstringmap.dylib')