Commits

Amaury Forgeot d'Arc  committed ec104e5

cpyext: add PyUnicode_CompareWithASCIIString

  • Participants
  • Parent commits 2919c69
  • Branches py3k

Comments (0)

Files changed (5)

File pypy/module/cpyext/src/pythread.c

-#include <Python.h>
-#include "pythread.h"
-#include "src/thread.h"
-
-long
-PyThread_get_thread_ident(void)
-{
-    return RPyThreadGetIdent();
-}
-
-PyThread_type_lock
-PyThread_allocate_lock(void)
-{
-    struct RPyOpaque_ThreadLock *lock;
-    lock = malloc(sizeof(struct RPyOpaque_ThreadLock));
-    if (lock == NULL)
-        return NULL;
-
-    if (RPyThreadLockInit(lock) == 0) {
-        free(lock);
-        return NULL;
-    }
-
-    return (PyThread_type_lock)lock;
-}
-
-void
-PyThread_free_lock(PyThread_type_lock lock)
-{
-    struct RPyOpaque_ThreadLock *real_lock = lock;
-    RPyThreadAcquireLock(real_lock, 0);
-    RPyThreadReleaseLock(real_lock);
-    RPyOpaqueDealloc_ThreadLock(real_lock);
-    free(lock);
-}
-
-int
-PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
-{
-    return RPyThreadAcquireLock((struct RPyOpaqueThreadLock*)lock, waitflag);
-}
-
-void
-PyThread_release_lock(PyThread_type_lock lock)
-{
-    RPyThreadReleaseLock((struct RPyOpaqueThreadLock*)lock);
-}
-
-
-/* ------------------------------------------------------------------------
-Per-thread data ("key") support.
-
-Use PyThread_create_key() to create a new key.  This is typically shared
-across threads.
-
-Use PyThread_set_key_value(thekey, value) to associate void* value with
-thekey in the current thread.  Each thread has a distinct mapping of thekey
-to a void* value.  Caution:  if the current thread already has a mapping
-for thekey, value is ignored.
-
-Use PyThread_get_key_value(thekey) to retrieve the void* value associated
-with thekey in the current thread.  This returns NULL if no value is
-associated with thekey in the current thread.
-
-Use PyThread_delete_key_value(thekey) to forget the current thread's associated
-value for thekey.  PyThread_delete_key(thekey) forgets the values associated
-with thekey across *all* threads.
-
-While some of these functions have error-return values, none set any
-Python exception.
-
-None of the functions does memory management on behalf of the void* values.
-You need to allocate and deallocate them yourself.  If the void* values
-happen to be PyObject*, these functions don't do refcount operations on
-them either.
-
-The GIL does not need to be held when calling these functions; they supply
-their own locking.  This isn't true of PyThread_create_key(), though (see
-next paragraph).
-
-There's a hidden assumption that PyThread_create_key() will be called before
-any of the other functions are called.  There's also a hidden assumption
-that calls to PyThread_create_key() are serialized externally.
------------------------------------------------------------------------- */
-
-#ifdef MS_WINDOWS
-#include <windows.h>
-
-/* use native Windows TLS functions */
-#define Py_HAVE_NATIVE_TLS
-
-int
-PyThread_create_key(void)
-{
-    return (int) TlsAlloc();
-}
-
-void
-PyThread_delete_key(int key)
-{
-    TlsFree(key);
-}
-
-/* We must be careful to emulate the strange semantics implemented in thread.c,
- * where the value is only set if it hasn't been set before.
- */
-int
-PyThread_set_key_value(int key, void *value)
-{
-    BOOL ok;
-    void *oldvalue;
-
-    assert(value != NULL);
-    oldvalue = TlsGetValue(key);
-    if (oldvalue != NULL)
-        /* ignore value if already set */
-        return 0;
-    ok = TlsSetValue(key, value);
-    if (!ok)
-        return -1;
-    return 0;
-}
-
-void *
-PyThread_get_key_value(int key)
-{
-    /* because TLS is used in the Py_END_ALLOW_THREAD macro,
-     * it is necessary to preserve the windows error state, because
-     * it is assumed to be preserved across the call to the macro.
-     * Ideally, the macro should be fixed, but it is simpler to
-     * do it here.
-     */
-    DWORD error = GetLastError();
-    void *result = TlsGetValue(key);
-    SetLastError(error);
-    return result;
-}
-
-void
-PyThread_delete_key_value(int key)
-{
-    /* NULL is used as "key missing", and it is also the default
-     * given by TlsGetValue() if nothing has been set yet.
-     */
-    TlsSetValue(key, NULL);
-}
-
-/* reinitialization of TLS is not necessary after fork when using
- * the native TLS functions.  And forking isn't supported on Windows either.
- */
-void
-PyThread_ReInitTLS(void)
-{}
-
-#else  /* MS_WINDOWS */
-
-/* A singly-linked list of struct key objects remembers all the key->value
- * associations.  File static keyhead heads the list.  keymutex is used
- * to enforce exclusion internally.
- */
-struct key {
-    /* Next record in the list, or NULL if this is the last record. */
-    struct key *next;
-
-    /* The thread id, according to PyThread_get_thread_ident(). */
-    long id;
-
-    /* The key and its associated value. */
-    int key;
-    void *value;
-};
-
-static struct key *keyhead = NULL;
-static PyThread_type_lock keymutex = NULL;
-static int nkeys = 0;  /* PyThread_create_key() hands out nkeys+1 next */
-
-/* Internal helper.
- * If the current thread has a mapping for key, the appropriate struct key*
- * is returned.  NB:  value is ignored in this case!
- * If there is no mapping for key in the current thread, then:
- *     If value is NULL, NULL is returned.
- *     Else a mapping of key to value is created for the current thread,
- *     and a pointer to a new struct key* is returned; except that if
- *     malloc() can't find room for a new struct key*, NULL is returned.
- * So when value==NULL, this acts like a pure lookup routine, and when
- * value!=NULL, this acts like dict.setdefault(), returning an existing
- * mapping if one exists, else creating a new mapping.
- *
- * Caution:  this used to be too clever, trying to hold keymutex only
- * around the "p->next = keyhead; keyhead = p" pair.  That allowed
- * another thread to mutate the list, via key deletion, concurrent with
- * find_key() crawling over the list.  Hilarity ensued.  For example, when
- * the for-loop here does "p = p->next", p could end up pointing at a
- * record that PyThread_delete_key_value() was concurrently free()'ing.
- * That could lead to anything, from failing to find a key that exists, to
- * segfaults.  Now we lock the whole routine.
- */
-static struct key *
-find_key(int key, void *value)
-{
-    struct key *p, *prev_p;
-    long id = PyThread_get_thread_ident();
-
-    if (!keymutex)
-        return NULL;
-    PyThread_acquire_lock(keymutex, 1);
-    prev_p = NULL;
-    for (p = keyhead; p != NULL; p = p->next) {
-        if (p->id == id && p->key == key)
-            goto Done;
-        /* Sanity check.  These states should never happen but if
-         * they do we must abort.  Otherwise we'll end up spinning in
-         * in a tight loop with the lock held.  A similar check is done
-         * in pystate.c tstate_delete_common().  */
-        if (p == prev_p)
-            Py_FatalError("tls find_key: small circular list(!)");
-        prev_p = p;
-        if (p->next == keyhead)
-            Py_FatalError("tls find_key: circular list(!)");
-    }
-    if (value == NULL) {
-        assert(p == NULL);
-        goto Done;
-    }
-    p = (struct key *)malloc(sizeof(struct key));
-    if (p != NULL) {
-        p->id = id;
-        p->key = key;
-        p->value = value;
-        p->next = keyhead;
-        keyhead = p;
-    }
- Done:
-    PyThread_release_lock(keymutex);
-    return p;
-}
-
-/* Return a new key.  This must be called before any other functions in
- * this family, and callers must arrange to serialize calls to this
- * function.  No violations are detected.
- */
-int
-PyThread_create_key(void)
-{
-    /* All parts of this function are wrong if it's called by multiple
-     * threads simultaneously.
-     */
-    if (keymutex == NULL)
-        keymutex = PyThread_allocate_lock();
-    return ++nkeys;
-}
-
-/* Forget the associations for key across *all* threads. */
-void
-PyThread_delete_key(int key)
-{
-    struct key *p, **q;
-
-    PyThread_acquire_lock(keymutex, 1);
-    q = &keyhead;
-    while ((p = *q) != NULL) {
-        if (p->key == key) {
-            *q = p->next;
-            free((void *)p);
-            /* NB This does *not* free p->value! */
-        }
-        else
-            q = &p->next;
-    }
-    PyThread_release_lock(keymutex);
-}
-
-/* Confusing:  If the current thread has an association for key,
- * value is ignored, and 0 is returned.  Else an attempt is made to create
- * an association of key to value for the current thread.  0 is returned
- * if that succeeds, but -1 is returned if there's not enough memory
- * to create the association.  value must not be NULL.
- */
-int
-PyThread_set_key_value(int key, void *value)
-{
-    struct key *p;
-
-    assert(value != NULL);
-    p = find_key(key, value);
-    if (p == NULL)
-        return -1;
-    else
-        return 0;
-}
-
-/* Retrieve the value associated with key in the current thread, or NULL
- * if the current thread doesn't have an association for key.
- */
-void *
-PyThread_get_key_value(int key)
-{
-    struct key *p = find_key(key, NULL);
-
-    if (p == NULL)
-        return NULL;
-    else
-        return p->value;
-}
-
-/* Forget the current thread's association for key, if any. */
-void
-PyThread_delete_key_value(int key)
-{
-    long id = PyThread_get_thread_ident();
-    struct key *p, **q;
-
-    PyThread_acquire_lock(keymutex, 1);
-    q = &keyhead;
-    while ((p = *q) != NULL) {
-        if (p->key == key && p->id == id) {
-            *q = p->next;
-            free((void *)p);
-            /* NB This does *not* free p->value! */
-            break;
-        }
-        else
-            q = &p->next;
-    }
-    PyThread_release_lock(keymutex);
-}
-
-/* Forget everything not associated with the current thread id.
- * This function is called from PyOS_AfterFork().  It is necessary
- * because other thread ids which were in use at the time of the fork
- * may be reused for new threads created in the forked process.
- */
-void
-PyThread_ReInitTLS(void)
-{
-    long id = PyThread_get_thread_ident();
-    struct key *p, **q;
-
-    if (!keymutex)
-        return;
-
-    /* As with interpreter_lock in PyEval_ReInitThreads()
-       we just create a new lock without freeing the old one */
-    keymutex = PyThread_allocate_lock();
-
-    /* Delete all keys which do not match the current thread id */
-    q = &keyhead;
-    while ((p = *q) != NULL) {
-        if (p->id != id) {
-            *q = p->next;
-            free((void *)p);
-            /* NB This does *not* free p->value! */
-        }
-        else
-            q = &p->next;
-    }
-}
-
-#endif  /* !MS_WINDOWS */

File pypy/module/cpyext/stubs.py

     raise NotImplementedError
     
 
-@cpython_api([PyObject, rffi.CCHARP], rffi.INT_real, error=-1)
-def PyUnicode_CompareWithASCIIString(space, uni, string):
-    """Compare a unicode object, uni, with string and return -1, 0, 1 for less
-    than, equal, and greater than, respectively. It is best to pass only
-    ASCII-encoded strings, but the function interprets the input string as
-    ISO-8859-1 if it contains non-ASCII characters"."""
-    raise NotImplementedError
-    
-
 @cpython_api([PyObject, PyObject, rffi.INT_real], PyObject)
 def PyUnicode_RichCompare(space, left, right, op):
     """Rich compare two unicode strings and return one of the following:

File pypy/module/cpyext/test/test_unicodeobject.py

         res = module.aswidecharstring("Caf\xe9")
         assert res == ("Caf\xe9\0", 4)
 
+    def test_CompareWithASCIIString(self):
+        module = self.import_extension('foo', [
+            ("compare", "METH_VARARGS",
+             '''
+             PyObject *uni;
+             const char* s;
+             int res;
+
+             if (!PyArg_ParseTuple(args, "Uy", &uni, &s))
+                 return NULL;
+
+             res = PyUnicode_CompareWithASCIIString(uni, s);
+             return PyLong_FromLong(res);
+             ''')])
+        assert module.compare("abc", b"abc") == 0
+        assert module.compare("abd", b"abc") == 1
+        assert module.compare("abb", b"abc") == -1
+        assert module.compare("caf\xe9", b"caf\xe9") == 0
+        assert module.compare("abc", b"ab") == 1
+        assert module.compare("ab\0", b"ab") == 1
+        assert module.compare("ab", b"abc") == -1
+        assert module.compare("", b"abc") == -1
+        assert module.compare("abc", b"") == 1
+
 
 class TestUnicode(BaseApiTest):
     def test_unicodeobject(self, space, api):

File pypy/module/cpyext/unicodeobject.py

         return 1
     return 0
 
+@cpython_api([PyObject, CONST_STRING], rffi.INT_real, error=CANNOT_FAIL)
+def PyUnicode_CompareWithASCIIString(space, w_uni, string):
+    """Compare a unicode object, uni, with string and return -1, 0, 1 for less
+    than, equal, and greater than, respectively. It is best to pass only
+    ASCII-encoded strings, but the function interprets the input string as
+    ISO-8859-1 if it contains non-ASCII characters."""
+    uni = space.unicode_w(w_uni)
+    i = 0
+    # Compare Unicode string and source character set string
+    while i < len(uni) and string[i] != '\0':
+        u = ord(uni[i])
+        s = ord(string[i])
+        if u != s:
+            if u < s:
+                return -1
+            else:
+                return 1
+        i += 1
+    if i < len(uni):
+        return 1  # uni is longer
+    if string[i] != '\0':
+        return -1  # str is longer
+    return 0
+    
+
 @cpython_api([rffi.CWCHARP, rffi.CWCHARP, Py_ssize_t], lltype.Void)
 def Py_UNICODE_COPY(space, target, source, length):
     """Roughly equivalent to memcpy() only the base size is Py_UNICODE

File pypy/translator/c/src/entrypoint.h

-#ifdef PYPY_STANDALONE
-
-#ifndef STANDALONE_ENTRY_POINT
-#  define STANDALONE_ENTRY_POINT   PYPY_STANDALONE
-#endif
-
-#ifndef PYPY_MAIN_FUNCTION
-#define PYPY_MAIN_FUNCTION main
-#endif
-
-char *RPython_StartupCode(void);
-int PYPY_MAIN_FUNCTION(int argc, char *argv[]);
-#endif  /* PYPY_STANDALONE */