• Facebook
  • Twitter
  • Reddit
  • StumbleUpon
  • Digg
  • email

#! /usr/bin/env python
# ______________________________________________________________________
"""Module ANOICSpace
 
Ctypes-based OO wrapper for the ANOI C space implementation.
 
Jonathan Riehl
 
$Id: ANOICSpace.py 6 2008-06-20 22:08:55Z jriehl $
"""
# ______________________________________________________________________
# Module imports
 
import ctypes
import ANOIBaseSpace
 
# ______________________________________________________________________
# Module data
 
# This assumes that libANOI is in your library path!
try:
    _anoic = ctypes.CDLL("libANOI.so")
except OSError:
    _anoic = ctypes.CDLL("libANOI.dll")
 
# TODO: Add ctypes type checking wrappers for the C API.
 
# ______________________________________________________________________
# Class definitions
 
class CUID (ctypes.Structure):
    # ____________________________________________________________
    _fields_ = [("size", ctypes.c_int),
                ("data", ctypes.c_void_p)]
 
    # ____________________________________________________________
    def getUid (self):
        """CUID.getUid()
        """
        ret_val = 0L
        if self.size > ctypes.sizeof(ctypes.c_void_p):
            raise NotImplemented("FIXME")
        elif self.data is not None:
            ret_val = int(self.data)
        return ret_val
 
    # ____________________________________________________________
    def setUid (self, pyuid):
        # XXX Truncation pinch point.  Fix this!
        _anoic.ANOIUidLoadValue(ctypes.byref(self), pyuid)
 
CUIDPtr = ctypes.POINTER(CUID)
 
# ______________________________________________________________________
 
class CATOM (ctypes.Structure):
    # ____________________________________________________________
    _fields_ = [("uid", CUID),
                ("relations", ctypes.c_void_p),
                ("length", ctypes.c_long),
                ("offset", ctypes.c_long)]
 
    # ____________________________________________________________
    def getKeys (self):
        """CATOM.getKeys()
        """
        key_count = _anoic.ANOIAtomGetKeyCount(ctypes.byref(self))
        keys = (CUID * key_count)()
        assert key_count == _anoic.ANOIAtomGetKeys(ctypes.byref(self),
                                                   ctypes.byref(keys))
        return [cuid.getUid() for cuid in keys]
 
CATOMPtr = ctypes.POINTER(CATOM)
 
# ______________________________________________________________________
 
class CUIDVEC (ctypes.Structure):
    # ____________________________________________________________
    _fields_ = [("size", ctypes.c_int),
                ("uids", CUIDPtr)]
 
    # ____________________________________________________________
    def getUids (self):
        """CUIDVEC.getUids()
        """
        ret_val = []
        for index in xrange(self.size):
            ret_val.append(self.uids[index].getUid())
        return ret_val
 
CUIDVECPtr = ctypes.POINTER(CUIDVEC)
 
# ______________________________________________________________________
 
class ANOICUid (object):
    """Class ANOICUid
    """
    # ____________________________________________________________
    def __init__ (self, pyuid = 0):
        """ANOIUid.__init__()
 
        Constructor for the ANOICUid class."""
        self._cuid = _anoic.ANOINewUid(None)
        assert self._cuid is not None
        self._as_parameter_ = self._cuid
        # XXX: This is NOT going to work for arbitrarily large
        # numbers!  Will need to detect native word size, and then use
        # a combination of ANOIUidLoadValue and ANOIUidShiftValue.
        _anoic.ANOIUidLoadValue(self, ctypes.c_int(pyuid))
        self._ptr = ctypes.cast(self._cuid, CUIDPtr)
        self._obj = self._ptr.contents
 
    # ____________________________________________________________
    def __del__ (self):
        """ANOIUid.__del__()
 
        Destructor for the ANOICUid class.  Frees the underlying C
        data structure."""
        if self._cuid is not None:
            _anoic.ANOIFreeUid(self)
 
    # ____________________________________________________________
    def set (self, pyuid):
        """ANOIUid.set()
 
        Set the contained C UID object to the given value."""
        assert self._cuid is not None
        self._obj.setUid(pyuid)
 
    # ____________________________________________________________
    def get (self):
        """ANOIUid.get()
 
        Get a Python number for the contained C UID object."""
        assert self._cuid is not None
        return self._obj.getUid()
 
# ______________________________________________________________________
 
class ANOICUidVec (object):
    # ____________________________________________________________
    def __init__ (self, size = 0):
        self._cuidvec = _anoic.ANOINewUidVec(size)
        assert self._cuidvec is not None
        self._as_parameter_ = self._cuidvec
        self._ptr = ctypes.cast(self._cuidvec, CUIDVECPtr)
        self._obj = self._ptr.contents
 
    # ____________________________________________________________
    def __del__ (self):
        if self._cuidvec is not None:
            _anoic.ANOIFreeUidVec(self)
 
    # ____________________________________________________________
    def clear (self):
        assert self._cuidvec is not None
        assert 0 == _anoic.ANOIDelUidVec(self)
 
    # ____________________________________________________________
    def setVec (self, uids):
        size = len(uids)
        assert _anoic.ANOIInitUidVec(self, size) == 0
        for index in xrange(size):
 
            _anoic.ANOIUidLoadValue(ctypes.byref(self._obj.uids[index]),
                                    uids[index])
 
    # ____________________________________________________________
    def getVec (self):
        return self._obj.getUids()
 
# ______________________________________________________________________
 
class ANOICSpace (ANOIBaseSpace.ANOIBaseSpace):
    """Class ANOICSpace
    """
    # ____________________________________________________________
    def __init__ (self, fd = None):
        """ANOICSpace.__init__()
 
        Constructor for the ANOI C space wrapper object."""
        self._file = None
        if fd is None:
            # FIXME: Go find a "safe" temporary file name generator and use
            # that; can't use stdout since the C space uses seek() to keep
            # atom content...might want to change that.
            self._file = open("space.dat", "w")
            fd = self._file.fileno()
        self._space = _anoic.ANOINewSpace(fd)
        assert self._space is not None
        self._as_parameter_ = self._space
        # Scratch C UID's (avoid construction and destruction costs).
        self.uid0 = ANOICUid()
        self.uid1 = ANOICUid()
        self.uid2 = ANOICUid()
        self.vec = ANOICUidVec()
 
    # ____________________________________________________________
    def __del__ (self):
        """ANOICSpace.__del__()
        Be sure to tidy up the C data when the Python object is collected.
        """
        if self._space is not None:
            _anoic.ANOIFreeSpace(self)
            del self.uid0
            del self.uid1
            del self.uid2
            del self.vec
            if self._file is not None:
                self._file.close()
 
    # ____________________________________________________________
    def cross (self, uid0, uid1):
        """ANOICSpace.cross()
        Supposed to return UID0 x UID1.
        """
        self.uid0.set(uid0)
        self.uid1.set(uid1)
        # XXX This is not uniform; would expect 0 for success.
        cross_result = _anoic.ANOICross(self, self.uid0, self.uid1, self.uid2)
        assert cross_result >= 0
        return self.uid2.get()
 
    # ____________________________________________________________
    def crossEquals (self, uid0, uid1, uid2):
        """ANOICSpace.crossEquals()
        Supposed to set UID0 x UID1 = UID2.
        """
        self.uid0.set(uid0)
        self.uid1.set(uid1)
        self.uid2.set(uid2)
        return _anoic.ANOICrossEquals(self, self.uid0, self.uid1, self.uid2)
 
    # ____________________________________________________________
    def getUid (self):
        """ANOICSpace.getUid()
        Supposed to return a free UID, allocating any needed records
        for it."""
        atom_ptr = ctypes.cast(_anoic.ANOISpaceGetAtom(self), CATOMPtr)
        return atom_ptr.contents.uid.getUid()
 
    # ____________________________________________________________
    def freeUid (self, uid):
        """ANOICSpace.freeUid()
        Mark the UID passed as being free, removing any records for
        it.  It is implementation specific how this is done; the space
        may implement garbage collection, reference counting, or take
        it on faith that nothing else references the passed UID."""
        self.uid0.set(uid)
        return _anoic.ANOISpaceFreeAtom(self, self.uid0)
 
    # ____________________________________________________________
    def getUidContent (self, uid):
        """ANOICSpace.getUidContent()
        Return a UID vector associated with the given UID."""
        self.uid0.set(uid)
        assert _anoic.ANOISpaceGetUidContent(self, self.uid0, self.vec) == 0
        return self.vec.getVec()
 
    # ____________________________________________________________
    def setUidContent (self, uid, contentVector):
        """ANOICSpace.setUidContent()
        Associate the passed UID vector with the UID.  Destroys previous
        content."""
        self.uid0.set(uid)
        self.vec.setVec(contentVector)
        assert _anoic.ANOISpaceSetUidContent(self, self.uid0, self.vec) == 0
 
    # ____________________________________________________________
    def isValidUid (self, uid):
        """ANOICSpace.isValidUid()
        Return a true value if the UID is defined in the space, false
        otherwise."""
        self.uid0.set(uid)
        return _anoic.ANOISpaceFindAtom(self, self.uid0) is not None
 
    # ____________________________________________________________
    def getUidKeys (self, uid):
        """ANOICSpace.getUidKeys()
        Return a vector of valid right hand cross product operands for the
        given UID."""
        ret_val = []
        self.uid0.set(uid)
        atom_ptr = ctypes.cast(_anoic.ANOISpaceFindAtom(self, self.uid0),
                               CATOMPtr)
        return atom_ptr.contents.getKeys()
 
# ______________________________________________________________________
# Main (test) routine
 
def main ():
    import ANOInit
    cspace = ANOICSpace()
    ANOInit.createSpace(cspace, "basicSpace.py")
    del cspace
 
# ______________________________________________________________________
 
if __name__ == "__main__":
    main()
 
# ______________________________________________________________________
# End of ANOICSpace.py