[pypy-commit] pypy py3.5: Copy PyBytes_FromFormat from CPython 3.5 (c3da1ee47e6b)
rlamy
pypy.commits at gmail.com
Tue Dec 13 19:17:27 EST 2016
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.5
Changeset: r89053:f52e625db5b2
Date: 2016-12-14 00:16 +0000
http://bitbucket.org/pypy/pypy/changeset/f52e625db5b2/
Log: Copy PyBytes_FromFormat from CPython 3.5 (c3da1ee47e6b)
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -1346,6 +1346,7 @@
source_dir / "pythread.c",
source_dir / "missing.c",
source_dir / "pymem.c",
+ source_dir / "bytesobject.c",
]
def build_eci(building_bridge, export_symbols, code, use_micronumpy=False):
diff --git a/pypy/module/cpyext/include/bytesobject.h b/pypy/module/cpyext/include/bytesobject.h
--- a/pypy/module/cpyext/include/bytesobject.h
+++ b/pypy/module/cpyext/include/bytesobject.h
@@ -56,6 +56,9 @@
#define PyString_CHECK_INTERNED(op) (((PyStringObject *)(op))->ob_sstate)
+PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list);
+PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...);
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/src/bytesobject.c b/pypy/module/cpyext/src/bytesobject.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/bytesobject.c
@@ -0,0 +1,213 @@
+#include "Python.h"
+
+#if defined(Py_ISDIGIT) || defined(Py_ISALPHA)
+#error remove these definitions
+#endif
+#define Py_ISDIGIT isdigit
+#define Py_ISALPHA isalpha
+
+PyObject *
+PyBytes_FromFormatV(const char *format, va_list vargs)
+{
+ va_list count;
+ Py_ssize_t n = 0;
+ const char* f;
+ char *s;
+ PyObject* string;
+
+ Py_VA_COPY(count, vargs);
+ /* step 1: figure out how large a buffer we need */
+ for (f = format; *f; f++) {
+ if (*f == '%') {
+ const char* p = f;
+ while (*++f && *f != '%' && !Py_ISALPHA(*f))
+ ;
+
+ /* skip the 'l' or 'z' in {%ld, %zd, %lu, %zu} since
+ * they don't affect the amount of space we reserve.
+ */
+ if ((*f == 'l' || *f == 'z') &&
+ (f[1] == 'd' || f[1] == 'u'))
+ ++f;
+
+ switch (*f) {
+ case 'c':
+ {
+ int c = va_arg(count, int);
+ if (c < 0 || c > 255) {
+ PyErr_SetString(PyExc_OverflowError,
+ "PyBytes_FromFormatV(): %c format "
+ "expects an integer in range [0; 255]");
+ return NULL;
+ }
+ n++;
+ break;
+ }
+ case '%':
+ n++;
+ break;
+ case 'd': case 'u': case 'i': case 'x':
+ (void) va_arg(count, int);
+ /* 20 bytes is enough to hold a 64-bit
+ integer. Decimal takes the most space.
+ This isn't enough for octal. */
+ n += 20;
+ break;
+ case 's':
+ s = va_arg(count, char*);
+ n += strlen(s);
+ break;
+ case 'p':
+ (void) va_arg(count, int);
+ /* maximum 64-bit pointer representation:
+ * 0xffffffffffffffff
+ * so 19 characters is enough.
+ * XXX I count 18 -- what's the extra for?
+ */
+ n += 19;
+ break;
+ default:
+ /* if we stumble upon an unknown
+ formatting code, copy the rest of
+ the format string to the output
+ string. (we cannot just skip the
+ code, since there's no way to know
+ what's in the argument list) */
+ n += strlen(p);
+ goto expand;
+ }
+ } else
+ n++;
+ }
+ expand:
+ /* step 2: fill the buffer */
+ /* Since we've analyzed how much space we need for the worst case,
+ use sprintf directly instead of the slower PyOS_snprintf. */
+ string = PyBytes_FromStringAndSize(NULL, n);
+ if (!string)
+ return NULL;
+
+ s = PyBytes_AsString(string);
+
+ for (f = format; *f; f++) {
+ if (*f == '%') {
+ const char* p = f++;
+ Py_ssize_t i;
+ int longflag = 0;
+ int size_tflag = 0;
+ /* parse the width.precision part (we're only
+ interested in the precision value, if any) */
+ n = 0;
+ while (Py_ISDIGIT(*f))
+ n = (n*10) + *f++ - '0';
+ if (*f == '.') {
+ f++;
+ n = 0;
+ while (Py_ISDIGIT(*f))
+ n = (n*10) + *f++ - '0';
+ }
+ while (*f && *f != '%' && !Py_ISALPHA(*f))
+ f++;
+ /* handle the long flag, but only for %ld and %lu.
+ others can be added when necessary. */
+ if (*f == 'l' && (f[1] == 'd' || f[1] == 'u')) {
+ longflag = 1;
+ ++f;
+ }
+ /* handle the size_t flag. */
+ if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) {
+ size_tflag = 1;
+ ++f;
+ }
+
+ switch (*f) {
+ case 'c':
+ {
+ int c = va_arg(vargs, int);
+ /* c has been checked for overflow in the first step */
+ *s++ = (unsigned char)c;
+ break;
+ }
+ case 'd':
+ if (longflag)
+ sprintf(s, "%ld", va_arg(vargs, long));
+ else if (size_tflag)
+ sprintf(s, "%" PY_FORMAT_SIZE_T "d",
+ va_arg(vargs, Py_ssize_t));
+ else
+ sprintf(s, "%d", va_arg(vargs, int));
+ s += strlen(s);
+ break;
+ case 'u':
+ if (longflag)
+ sprintf(s, "%lu",
+ va_arg(vargs, unsigned long));
+ else if (size_tflag)
+ sprintf(s, "%" PY_FORMAT_SIZE_T "u",
+ va_arg(vargs, size_t));
+ else
+ sprintf(s, "%u",
+ va_arg(vargs, unsigned int));
+ s += strlen(s);
+ break;
+ case 'i':
+ sprintf(s, "%i", va_arg(vargs, int));
+ s += strlen(s);
+ break;
+ case 'x':
+ sprintf(s, "%x", va_arg(vargs, int));
+ s += strlen(s);
+ break;
+ case 's':
+ p = va_arg(vargs, char*);
+ i = strlen(p);
+ if (n > 0 && i > n)
+ i = n;
+ Py_MEMCPY(s, p, i);
+ s += i;
+ break;
+ case 'p':
+ sprintf(s, "%p", va_arg(vargs, void*));
+ /* %p is ill-defined: ensure leading 0x. */
+ if (s[1] == 'X')
+ s[1] = 'x';
+ else if (s[1] != 'x') {
+ memmove(s+2, s, strlen(s)+1);
+ s[0] = '0';
+ s[1] = 'x';
+ }
+ s += strlen(s);
+ break;
+ case '%':
+ *s++ = '%';
+ break;
+ default:
+ strcpy(s, p);
+ s += strlen(s);
+ goto end;
+ }
+ } else
+ *s++ = *f;
+ }
+
+ end:
+ _PyBytes_Resize(&string, s - PyBytes_AS_STRING(string));
+ return string;
+}
+
+PyObject *
+PyBytes_FromFormat(const char *format, ...)
+{
+ PyObject* ret;
+ va_list vargs;
+
+#ifdef HAVE_STDARG_PROTOTYPES
+ va_start(vargs, format);
+#else
+ va_start(vargs);
+#endif
+ ret = PyBytes_FromFormatV(format, vargs);
+ va_end(vargs);
+ return ret;
+}
+
diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py
--- a/pypy/module/cpyext/test/test_bytesobject.py
+++ b/pypy/module/cpyext/test/test_bytesobject.py
@@ -202,6 +202,19 @@
module.getbytes()
module.c_only()
+ def test_FromFormat(self):
+ module = self.import_extension('foo', [
+ ("fmt", "METH_VARARGS",
+ """
+ PyObject* fmt = PyTuple_GetItem(args, 0);
+ int n = PyLong_AsLong(PyTuple_GetItem(args, 1));
+ PyObject* result = PyBytes_FromFormat(PyBytes_AsString(fmt), n);
+ return result;
+ """),
+ ])
+ print(module.fmt(b'd:%d', 10))
+ assert module.fmt(b'd:%d', 10) == b'd:10'
+
class TestBytes(BaseApiTest):
def test_bytes_resize(self, space, api):
More information about the pypy-commit
mailing list