summaryrefslogtreecommitdiff
path: root/common.c
blob: a55f99ca709f7bc9afe2d4a4e444b2d55c20ff87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include "Python.h"
#include "structmember.h"

#include "common.h"

#include <stdlib.h>

key_t
get_random_key(void) {
    int key;

    /* ******************************************************************
    The inability to know the range of a key_t requires careful code here.
    Remember that KEY_MIN and KEY_MAX refer only to the limits inherent in the
    variable type I use internally when turning a key into a Python object and
    vice versa. Those limits may exceed the operating system's limits of key_t.

    For instance, if key_t is typedef-ed as uint, I should generate a key
    where 0 <= key <= UINT_MAX.

    Since I can't know what key_t is typedef-ed as, I take a conservative
    approach and generate only keys where
    1 <= key <= SHRT_MAX.

    Such values will work if key_t is typedef-ed as a short, int, uint,
    long or ulong.
    ****************************************************************** */
    do {
        // ref: http://www.c-faq.com/lib/randrange.html
        key = ((int)((double)rand() / ((double)RAND_MAX + 1) * (SHRT_MAX - 1))) + 1;
    } while (key == IPC_PRIVATE);

    return (key_t)key;
}

#if PY_MAJOR_VERSION < 3
PyObject *
py_int_or_long_from_ulong(unsigned long value) {
    // Python ints are guaranteed to accept up to LONG_MAX. Anything
    // larger needs to be a Python long.
    if (value > LONG_MAX)
        return PyLong_FromUnsignedLong(value);
    else
        return PyInt_FromLong(value);
}
#endif


int
convert_key_param(PyObject *py_key, void *converted_key) {
    // Converts a PyObject into a key if possible. Returns 0 on failure.
    // The converted_key param should point to a NoneableKey type.
    // None is an acceptable key, in which case converted_key->is_none
    // is non-zero and converted_key->value is undefined.
    int rc = 0;
    long key = 0;

    ((NoneableKey *)converted_key)->is_none = 0;

    if (py_key == Py_None) {
        rc = 1;
        ((NoneableKey *)converted_key)->is_none = 1;
    }
#if PY_MAJOR_VERSION < 3
    else if (PyInt_Check(py_key)) {
        rc = 1;
        key = PyInt_AsLong(py_key);
    }
#endif
    else if (PyLong_Check(py_key)) {
        rc = 1;
        key = PyLong_AsLong(py_key);
        if (PyErr_Occurred()) {
            // This happens when the Python long is too big for a C long.
            rc = 0;
            PyErr_Format(PyExc_ValueError,
                         "Key must be between %ld (KEY_MIN) and %ld (KEY_MAX)",
                         KEY_MIN, KEY_MAX);
        }
    }

    if (rc) {
        // Param is OK
        if (! ((NoneableKey *)converted_key)->is_none) {
            // It's not None; ensure it is in range
            if ((key >= KEY_MIN) && (key <= KEY_MAX))
                ((NoneableKey *)converted_key)->value = (key_t)key;
            else {
                rc = 0;
                PyErr_Format(PyExc_ValueError,
                             "Key must be between %ld (KEY_MIN) and %ld (KEY_MAX)",
                             KEY_MIN, KEY_MAX);
            }
        }
    }
    else
        PyErr_SetString(PyExc_TypeError, "Key must be an integer or None");

    return rc;
}