mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
47a53e143b
The APE_NO_MODIFY_SELF loader payload has been moved out of the examples folder and improved so that it works on BSD systems, and permits general elf program headers. This brings its quality up enough that it should be acceptable to use by default for many programs, e.g. Python, Lua, SQLite and Python. It's the responsibility of the user to define an appropriate TMPDIR if /tmp is considered an adversarial environment. Mac OS shall be supported by APE_NO_MODIFY_SELF soon. Fixes and improvements have been made to program_executable_name as it's now the one true way to get the absolute path of the executing image. This change fixes a memory leak in linenoise history loading, introduced by performance optimizations in51904e2687
This change fixes a longstanding regression with Mach system calls, that23ae9dfceb
back in February which impacted our sched_yield() implementation, which is why no one noticed until now. The Blinkenlights PC emulator has been improved. We now fix rendering on XNU and BSD by not making the assumption that the kernel terminal driver understands UTF8 since that seems to break its internal modeling of \r\n which is now being addressed by using \e[𝑦H instead. The paneling is now more compact in real mode so you won't need to make your font as tiny if you're only emulating an 8086 program. The CLMUL ISA is now emulated too This change also makes improvement to time. CLOCK_MONOTONIC now does the right thing on Windows NT. The nanosecond time module functions added in Python 3.7 have been backported. This change doubles the performance of Argon2 password stretching simply by not using its copy_block and xor_block helper functions, as they were trivial to inline thus resulting in us needing to iterate over each 1024 byte block four fewer times. This change makes code size improvements. _PyUnicode_ToNumeric() was 64k in size and now it's 10k. The CJK codec lookup tables now use lazy delta zigzag deflate (δzd) encoding which reduces their size from 600k to 200k plus the code bloat caused by macro abuse in _decimal.c is now addressed so our fully-loaded statically-linked hermetically-sealed Python virtual interpreter container is now 9.4 megs in the default build mode and 5.5m in MODE=tiny which leaves plenty of room for chibicc. The pydoc web server now accommodates the use case of people who work by SSH'ing into a different machine w/ python.com -m pydoc -p8080 -h0.0.0.0 Finally Python Capsulae delenda est and won't be supported in the future
5955 lines
169 KiB
C
5955 lines
169 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright (c) 2008-2016 Stefan Krah. All rights reserved. │
|
|
│ │
|
|
│ Redistribution and use in source and binary forms, with or without │
|
|
│ modification, are permitted provided that the following conditions │
|
|
│ are met: │
|
|
│ │
|
|
│ 1. Redistributions of source code must retain the above copyright │
|
|
│ notice, this list of conditions and the following disclaimer. │
|
|
│ │
|
|
│ 2. Redistributions in binary form must reproduce the above copyright │
|
|
│ notice, this list of conditions and the following disclaimer in │
|
|
│ the documentation and/or other materials provided with the │
|
|
│ distribution. │
|
|
│ │
|
|
│ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND │
|
|
│ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE │
|
|
│ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR │
|
|
│ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS │
|
|
│ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, │
|
|
│ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT │
|
|
│ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR │
|
|
│ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, │
|
|
│ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE │
|
|
│ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, │
|
|
│ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/log/libfatal.internal.h"
|
|
#include "third_party/python/Include/abstract.h"
|
|
#include "third_party/python/Include/boolobject.h"
|
|
#include "third_party/python/Include/complexobject.h"
|
|
#include "third_party/python/Include/descrobject.h"
|
|
#include "third_party/python/Include/dictobject.h"
|
|
#include "third_party/python/Include/floatobject.h"
|
|
#include "third_party/python/Include/import.h"
|
|
#include "third_party/python/Include/listobject.h"
|
|
#include "third_party/python/Include/longintrepr.h"
|
|
#include "third_party/python/Include/modsupport.h"
|
|
#include "third_party/python/Include/objimpl.h"
|
|
#include "third_party/python/Include/pyerrors.h"
|
|
#include "third_party/python/Include/pyhash.h"
|
|
#include "third_party/python/Include/pystate.h"
|
|
#include "third_party/python/Include/pythread.h"
|
|
#include "third_party/python/Include/structmember.h"
|
|
#include "third_party/python/Include/tupleobject.h"
|
|
#include "third_party/python/Include/yoink.h"
|
|
#include "third_party/python/Modules/_decimal/docstrings.h"
|
|
#include "third_party/python/Modules/_decimal/libmpdec/mpdecimal.h"
|
|
/* clang-format off */
|
|
|
|
PYTHON_PROVIDE("_decimal");
|
|
PYTHON_PROVIDE("_decimal.BasicContext");
|
|
PYTHON_PROVIDE("_decimal.Clamped");
|
|
PYTHON_PROVIDE("_decimal.Context");
|
|
PYTHON_PROVIDE("_decimal.ConversionSyntax");
|
|
PYTHON_PROVIDE("_decimal.Decimal");
|
|
PYTHON_PROVIDE("_decimal.DecimalException");
|
|
PYTHON_PROVIDE("_decimal.DecimalTuple");
|
|
PYTHON_PROVIDE("_decimal.DefaultContext");
|
|
PYTHON_PROVIDE("_decimal.DivisionByZero");
|
|
PYTHON_PROVIDE("_decimal.DivisionImpossible");
|
|
PYTHON_PROVIDE("_decimal.DivisionUndefined");
|
|
PYTHON_PROVIDE("_decimal.ExtendedContext");
|
|
PYTHON_PROVIDE("_decimal.FloatOperation");
|
|
PYTHON_PROVIDE("_decimal.HAVE_THREADS");
|
|
PYTHON_PROVIDE("_decimal.Inexact");
|
|
PYTHON_PROVIDE("_decimal.InvalidContext");
|
|
PYTHON_PROVIDE("_decimal.InvalidOperation");
|
|
PYTHON_PROVIDE("_decimal.MAX_EMAX");
|
|
PYTHON_PROVIDE("_decimal.MAX_PREC");
|
|
PYTHON_PROVIDE("_decimal.MIN_EMIN");
|
|
PYTHON_PROVIDE("_decimal.MIN_ETINY");
|
|
PYTHON_PROVIDE("_decimal.Overflow");
|
|
PYTHON_PROVIDE("_decimal.ROUND_05UP");
|
|
PYTHON_PROVIDE("_decimal.ROUND_CEILING");
|
|
PYTHON_PROVIDE("_decimal.ROUND_DOWN");
|
|
PYTHON_PROVIDE("_decimal.ROUND_FLOOR");
|
|
PYTHON_PROVIDE("_decimal.ROUND_HALF_DOWN");
|
|
PYTHON_PROVIDE("_decimal.ROUND_HALF_EVEN");
|
|
PYTHON_PROVIDE("_decimal.ROUND_HALF_UP");
|
|
PYTHON_PROVIDE("_decimal.ROUND_UP");
|
|
PYTHON_PROVIDE("_decimal.Rounded");
|
|
PYTHON_PROVIDE("_decimal.Subnormal");
|
|
PYTHON_PROVIDE("_decimal.Underflow");
|
|
PYTHON_PROVIDE("_decimal.__doc__");
|
|
PYTHON_PROVIDE("_decimal.__libmpdec_version__");
|
|
PYTHON_PROVIDE("_decimal.__loader__");
|
|
PYTHON_PROVIDE("_decimal.__name__");
|
|
PYTHON_PROVIDE("_decimal.__package__");
|
|
PYTHON_PROVIDE("_decimal.__spec__");
|
|
PYTHON_PROVIDE("_decimal.__version__");
|
|
PYTHON_PROVIDE("_decimal.getcontext");
|
|
PYTHON_PROVIDE("_decimal.localcontext");
|
|
PYTHON_PROVIDE("_decimal.setcontext");
|
|
|
|
PYTHON_YOINK("numbers");
|
|
PYTHON_YOINK("collections");
|
|
|
|
asm(".ident\t\"\\n\
|
|
libmpdec (BSD-2)\\n\
|
|
Copyright 2008-2016 Stefan Krah\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
|
|
#if !defined(MPD_VERSION_HEX) || MPD_VERSION_HEX < 0x02040100
|
|
#error "libmpdec version >= 2.4.1 required"
|
|
#endif
|
|
|
|
/*
|
|
* Type sizes with assertions in mpdecimal.h and pyport.h:
|
|
* sizeof(size_t) == sizeof(Py_ssize_t)
|
|
* sizeof(size_t) == sizeof(mpd_uint_t) == sizeof(mpd_ssize_t)
|
|
*/
|
|
|
|
#ifdef TEST_COVERAGE
|
|
#undef Py_LOCAL_INLINE
|
|
#define Py_LOCAL_INLINE Py_LOCAL
|
|
#endif
|
|
|
|
#define MPD_Float_operation MPD_Not_implemented
|
|
|
|
#define BOUNDS_CHECK(x, MIN, MAX) x = (x < MIN || MAX < x) ? MAX : x
|
|
|
|
|
|
/* _Py_DEC_MINALLOC >= MPD_MINALLOC */
|
|
#define _Py_DEC_MINALLOC 4
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
Py_hash_t hash;
|
|
mpd_t dec;
|
|
mpd_uint_t data[_Py_DEC_MINALLOC];
|
|
} PyDecObject;
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
uint32_t *flags;
|
|
} PyDecSignalDictObject;
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
mpd_context_t ctx;
|
|
PyObject *traps;
|
|
PyObject *flags;
|
|
int capitals;
|
|
#ifndef WITHOUT_THREADS
|
|
PyThreadState *tstate;
|
|
#endif
|
|
} PyDecContextObject;
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
PyObject *local;
|
|
PyObject *global;
|
|
} PyDecContextManagerObject;
|
|
|
|
|
|
#undef MPD
|
|
#undef CTX
|
|
static PyTypeObject PyDec_Type;
|
|
static PyTypeObject *PyDecSignalDict_Type;
|
|
static PyTypeObject PyDecContext_Type;
|
|
static PyTypeObject PyDecContextManager_Type;
|
|
#define PyDec_CheckExact(v) (Py_TYPE(v) == &PyDec_Type)
|
|
#define PyDec_Check(v) PyObject_TypeCheck(v, &PyDec_Type)
|
|
#define PyDecSignalDict_Check(v) (Py_TYPE(v) == PyDecSignalDict_Type)
|
|
#define PyDecContext_Check(v) PyObject_TypeCheck(v, &PyDecContext_Type)
|
|
#define MPD(v) (&((PyDecObject *)v)->dec)
|
|
#define SdFlagAddr(v) (((PyDecSignalDictObject *)v)->flags)
|
|
#define SdFlags(v) (*((PyDecSignalDictObject *)v)->flags)
|
|
#define CTX(v) (&((PyDecContextObject *)v)->ctx)
|
|
#define CtxCaps(v) (((PyDecContextObject *)v)->capitals)
|
|
|
|
|
|
Py_LOCAL_INLINE(PyObject *)
|
|
incr_true(void)
|
|
{
|
|
Py_INCREF(Py_True);
|
|
return Py_True;
|
|
}
|
|
|
|
Py_LOCAL_INLINE(PyObject *)
|
|
incr_false(void)
|
|
{
|
|
Py_INCREF(Py_False);
|
|
return Py_False;
|
|
}
|
|
|
|
|
|
#ifdef WITHOUT_THREADS
|
|
/* Default module context */
|
|
static PyObject *module_context = NULL;
|
|
#else
|
|
/* Key for thread state dictionary */
|
|
static PyObject *tls_context_key = NULL;
|
|
/* Invariant: NULL or the most recently accessed thread local context */
|
|
static PyDecContextObject *cached_context = NULL;
|
|
#endif
|
|
|
|
/* Template for creating new thread contexts, calling Context() without
|
|
* arguments and initializing the module_context on first access. */
|
|
static PyObject *default_context_template = NULL;
|
|
/* Basic and extended context templates */
|
|
static PyObject *basic_context_template = NULL;
|
|
static PyObject *extended_context_template = NULL;
|
|
|
|
|
|
/* Error codes for functions that return signals or conditions */
|
|
#define DEC_INVALID_SIGNALS (MPD_Max_status+1U)
|
|
#define DEC_ERR_OCCURRED (DEC_INVALID_SIGNALS<<1)
|
|
#define DEC_ERRORS (DEC_INVALID_SIGNALS|DEC_ERR_OCCURRED)
|
|
|
|
typedef struct {
|
|
const char *name; /* condition or signal name */
|
|
const char *fqname; /* fully qualified name */
|
|
uint32_t flag; /* libmpdec flag */
|
|
PyObject *ex; /* corresponding exception */
|
|
} DecCondMap;
|
|
|
|
/* Top level Exception; inherits from ArithmeticError */
|
|
static PyObject *DecimalException = NULL;
|
|
|
|
/* Exceptions that correspond to IEEE signals */
|
|
#define SUBNORMAL 5
|
|
#define INEXACT 6
|
|
#define ROUNDED 7
|
|
#define SIGNAL_MAP_LEN 9
|
|
static DecCondMap signal_map[] = {
|
|
{"InvalidOperation", "decimal.InvalidOperation", MPD_IEEE_Invalid_operation, NULL},
|
|
{"FloatOperation", "decimal.FloatOperation", MPD_Float_operation, NULL},
|
|
{"DivisionByZero", "decimal.DivisionByZero", MPD_Division_by_zero, NULL},
|
|
{"Overflow", "decimal.Overflow", MPD_Overflow, NULL},
|
|
{"Underflow", "decimal.Underflow", MPD_Underflow, NULL},
|
|
{"Subnormal", "decimal.Subnormal", MPD_Subnormal, NULL},
|
|
{"Inexact", "decimal.Inexact", MPD_Inexact, NULL},
|
|
{"Rounded", "decimal.Rounded", MPD_Rounded, NULL},
|
|
{"Clamped", "decimal.Clamped", MPD_Clamped, NULL},
|
|
{NULL}
|
|
};
|
|
|
|
/* Exceptions that inherit from InvalidOperation */
|
|
static DecCondMap cond_map[] = {
|
|
{"InvalidOperation", "decimal.InvalidOperation", MPD_Invalid_operation, NULL},
|
|
{"ConversionSyntax", "decimal.ConversionSyntax", MPD_Conversion_syntax, NULL},
|
|
{"DivisionImpossible", "decimal.DivisionImpossible", MPD_Division_impossible, NULL},
|
|
{"DivisionUndefined", "decimal.DivisionUndefined", MPD_Division_undefined, NULL},
|
|
{"InvalidContext", "decimal.InvalidContext", MPD_Invalid_context, NULL},
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
{"MallocError", "decimal.MallocError", MPD_Malloc_error, NULL},
|
|
#endif
|
|
{NULL}
|
|
};
|
|
|
|
static const char *dec_signal_string[MPD_NUM_FLAGS] = {
|
|
"Clamped",
|
|
"InvalidOperation",
|
|
"DivisionByZero",
|
|
"InvalidOperation",
|
|
"InvalidOperation",
|
|
"InvalidOperation",
|
|
"Inexact",
|
|
"InvalidOperation",
|
|
"InvalidOperation",
|
|
"InvalidOperation",
|
|
"FloatOperation",
|
|
"Overflow",
|
|
"Rounded",
|
|
"Subnormal",
|
|
"Underflow",
|
|
};
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
#define _PY_DEC_ROUND_GUARD MPD_ROUND_GUARD
|
|
#else
|
|
#define _PY_DEC_ROUND_GUARD (MPD_ROUND_GUARD-1)
|
|
#endif
|
|
static PyObject *round_map[_PY_DEC_ROUND_GUARD];
|
|
|
|
static const char *invalid_rounding_err =
|
|
"valid values for rounding are:\n\
|
|
[ROUND_CEILING, ROUND_FLOOR, ROUND_UP, ROUND_DOWN,\n\
|
|
ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN,\n\
|
|
ROUND_05UP]";
|
|
|
|
static const char *invalid_signals_err =
|
|
"valid values for signals are:\n\
|
|
[InvalidOperation, FloatOperation, DivisionByZero,\n\
|
|
Overflow, Underflow, Subnormal, Inexact, Rounded,\n\
|
|
Clamped]";
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
static const char *invalid_flags_err =
|
|
"valid values for _flags or _traps are:\n\
|
|
signals:\n\
|
|
[DecIEEEInvalidOperation, DecFloatOperation, DecDivisionByZero,\n\
|
|
DecOverflow, DecUnderflow, DecSubnormal, DecInexact, DecRounded,\n\
|
|
DecClamped]\n\
|
|
conditions which trigger DecIEEEInvalidOperation:\n\
|
|
[DecInvalidOperation, DecConversionSyntax, DecDivisionImpossible,\n\
|
|
DecDivisionUndefined, DecFpuError, DecInvalidContext, DecMallocError]";
|
|
#endif
|
|
|
|
static int
|
|
value_error_int(const char *mesg)
|
|
{
|
|
PyErr_SetString(PyExc_ValueError, mesg);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef CONFIG_32
|
|
static PyObject *
|
|
value_error_ptr(const char *mesg)
|
|
{
|
|
PyErr_SetString(PyExc_ValueError, mesg);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
type_error_int(const char *mesg)
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, mesg);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
runtime_error_int(const char *mesg)
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError, mesg);
|
|
return -1;
|
|
}
|
|
#define INTERNAL_ERROR_INT(funcname) \
|
|
return runtime_error_int("internal error in " funcname)
|
|
|
|
static PyObject *
|
|
runtime_error_ptr(const char *mesg)
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError, mesg);
|
|
return NULL;
|
|
}
|
|
#define INTERNAL_ERROR_PTR(funcname) \
|
|
return runtime_error_ptr("internal error in " funcname)
|
|
|
|
static void
|
|
dec_traphandler(mpd_context_t *ctx)
|
|
{
|
|
(void)ctx;
|
|
return; /* GCOV_NOT_REACHED */
|
|
}
|
|
|
|
static PyObject *
|
|
flags_as_exception(uint32_t flags)
|
|
{
|
|
DecCondMap *cm;
|
|
for (cm = signal_map; cm->name != NULL; cm++) {
|
|
if (flags&cm->flag) {
|
|
return cm->ex;
|
|
}
|
|
}
|
|
INTERNAL_ERROR_PTR("flags_as_exception");
|
|
}
|
|
|
|
Py_LOCAL_INLINE(uint32_t)
|
|
exception_as_flag(PyObject *ex)
|
|
{
|
|
DecCondMap *cm;
|
|
|
|
for (cm = signal_map; cm->name != NULL; cm++) {
|
|
if (cm->ex == ex) {
|
|
return cm->flag;
|
|
}
|
|
}
|
|
|
|
PyErr_SetString(PyExc_KeyError, invalid_signals_err);
|
|
return DEC_INVALID_SIGNALS;
|
|
}
|
|
|
|
static PyObject *
|
|
flags_as_list(uint32_t flags)
|
|
{
|
|
PyObject *list;
|
|
DecCondMap *cm;
|
|
|
|
list = PyList_New(0);
|
|
if (list == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (cm = cond_map; cm->name != NULL; cm++) {
|
|
if (flags&cm->flag) {
|
|
if (PyList_Append(list, cm->ex) < 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
for (cm = signal_map+1; cm->name != NULL; cm++) {
|
|
if (flags&cm->flag) {
|
|
if (PyList_Append(list, cm->ex) < 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
|
|
error:
|
|
Py_DECREF(list);
|
|
return NULL;
|
|
}
|
|
|
|
static PyObject *
|
|
signals_as_list(uint32_t flags)
|
|
{
|
|
PyObject *list;
|
|
DecCondMap *cm;
|
|
|
|
list = PyList_New(0);
|
|
if (list == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (cm = signal_map; cm->name != NULL; cm++) {
|
|
if (flags&cm->flag) {
|
|
if (PyList_Append(list, cm->ex) < 0) {
|
|
Py_DECREF(list);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
static uint32_t
|
|
list_as_flags(PyObject *list)
|
|
{
|
|
PyObject *item;
|
|
uint32_t flags, x;
|
|
Py_ssize_t n, j;
|
|
|
|
assert(PyList_Check(list));
|
|
|
|
n = PyList_Size(list);
|
|
flags = 0;
|
|
for (j = 0; j < n; j++) {
|
|
item = PyList_GetItem(list, j);
|
|
x = exception_as_flag(item);
|
|
if (x & DEC_ERRORS) {
|
|
return x;
|
|
}
|
|
flags |= x;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
static PyObject *
|
|
flags_as_dict(uint32_t flags)
|
|
{
|
|
DecCondMap *cm;
|
|
PyObject *dict;
|
|
|
|
dict = PyDict_New();
|
|
if (dict == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (cm = signal_map; cm->name != NULL; cm++) {
|
|
PyObject *b = flags&cm->flag ? Py_True : Py_False;
|
|
if (PyDict_SetItem(dict, cm->ex, b) < 0) {
|
|
Py_DECREF(dict);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return dict;
|
|
}
|
|
|
|
static uint32_t
|
|
dict_as_flags(PyObject *val)
|
|
{
|
|
PyObject *b;
|
|
DecCondMap *cm;
|
|
uint32_t flags = 0;
|
|
int x;
|
|
|
|
if (!PyDict_Check(val)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"argument must be a signal dict");
|
|
return DEC_INVALID_SIGNALS;
|
|
}
|
|
|
|
if (PyDict_Size(val) != SIGNAL_MAP_LEN) {
|
|
PyErr_SetString(PyExc_KeyError,
|
|
"invalid signal dict");
|
|
return DEC_INVALID_SIGNALS;
|
|
}
|
|
|
|
for (cm = signal_map; cm->name != NULL; cm++) {
|
|
b = PyDict_GetItemWithError(val, cm->ex);
|
|
if (b == NULL) {
|
|
if (PyErr_Occurred()) {
|
|
return DEC_ERR_OCCURRED;
|
|
}
|
|
PyErr_SetString(PyExc_KeyError,
|
|
"invalid signal dict");
|
|
return DEC_INVALID_SIGNALS;
|
|
}
|
|
|
|
x = PyObject_IsTrue(b);
|
|
if (x < 0) {
|
|
return DEC_ERR_OCCURRED;
|
|
}
|
|
if (x == 1) {
|
|
flags |= cm->flag;
|
|
}
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
static uint32_t
|
|
long_as_flags(PyObject *v)
|
|
{
|
|
long x;
|
|
|
|
x = PyLong_AsLong(v);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return DEC_ERR_OCCURRED;
|
|
}
|
|
if (x < 0 || x > (long)MPD_Max_status) {
|
|
PyErr_SetString(PyExc_TypeError, invalid_flags_err);
|
|
return DEC_INVALID_SIGNALS;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
dec_addstatus(PyObject *context, uint32_t status)
|
|
{
|
|
mpd_context_t *ctx = CTX(context);
|
|
|
|
ctx->status |= status;
|
|
if (status & (ctx->traps|MPD_Malloc_error)) {
|
|
PyObject *ex, *siglist;
|
|
|
|
if (status & MPD_Malloc_error) {
|
|
PyErr_NoMemory();
|
|
return 1;
|
|
}
|
|
|
|
ex = flags_as_exception(ctx->traps&status);
|
|
if (ex == NULL) {
|
|
return 1; /* GCOV_NOT_REACHED */
|
|
}
|
|
siglist = flags_as_list(ctx->traps&status);
|
|
if (siglist == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
PyErr_SetObject(ex, siglist);
|
|
Py_DECREF(siglist);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
getround(PyObject *v)
|
|
{
|
|
int i;
|
|
|
|
if (PyUnicode_Check(v)) {
|
|
for (i = 0; i < _PY_DEC_ROUND_GUARD; i++) {
|
|
if (v == round_map[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
for (i = 0; i < _PY_DEC_ROUND_GUARD; i++) {
|
|
if (PyUnicode_Compare(v, round_map[i]) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return type_error_int(invalid_rounding_err);
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* SignalDict Object */
|
|
/******************************************************************************/
|
|
|
|
/* The SignalDict is a MutableMapping that provides access to the
|
|
mpd_context_t flags, which reside in the context object. When a
|
|
new context is created, context.traps and context.flags are
|
|
initialized to new SignalDicts. Once a SignalDict is tied to
|
|
a context, it cannot be deleted. */
|
|
|
|
static int
|
|
signaldict_init(PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
(void)args;
|
|
(void)kwds;
|
|
SdFlagAddr(self) = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static Py_ssize_t
|
|
signaldict_len(PyObject *self)
|
|
{
|
|
(void)self;
|
|
return SIGNAL_MAP_LEN;
|
|
}
|
|
|
|
static PyObject *SignalTuple;
|
|
static PyObject *
|
|
signaldict_iter(PyObject *self)
|
|
{
|
|
(void)self;
|
|
return PyTuple_Type.tp_iter(SignalTuple);
|
|
}
|
|
|
|
static PyObject *
|
|
signaldict_getitem(PyObject *self, PyObject *key)
|
|
{
|
|
uint32_t flag;
|
|
|
|
flag = exception_as_flag(key);
|
|
if (flag & DEC_ERRORS) {
|
|
return NULL;
|
|
}
|
|
|
|
return SdFlags(self)&flag ? incr_true() : incr_false();
|
|
}
|
|
|
|
static int
|
|
signaldict_setitem(PyObject *self, PyObject *key, PyObject *value)
|
|
{
|
|
uint32_t flag;
|
|
int x;
|
|
|
|
if (value == NULL) {
|
|
return value_error_int("signal keys cannot be deleted");
|
|
}
|
|
|
|
flag = exception_as_flag(key);
|
|
if (flag & DEC_ERRORS) {
|
|
return -1;
|
|
}
|
|
|
|
x = PyObject_IsTrue(value);
|
|
if (x < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (x == 1) {
|
|
SdFlags(self) |= flag;
|
|
}
|
|
else {
|
|
SdFlags(self) &= ~flag;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PyObject *
|
|
signaldict_repr(PyObject *self)
|
|
{
|
|
DecCondMap *cm;
|
|
const char *n[SIGNAL_MAP_LEN]; /* name */
|
|
const char *b[SIGNAL_MAP_LEN]; /* bool */
|
|
int i;
|
|
|
|
assert(SIGNAL_MAP_LEN == 9);
|
|
|
|
for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) {
|
|
n[i] = cm->fqname;
|
|
b[i] = SdFlags(self)&cm->flag ? "True" : "False";
|
|
}
|
|
return PyUnicode_FromFormat(
|
|
"{<class '%s'>:%s, <class '%s'>:%s, <class '%s'>:%s, "
|
|
"<class '%s'>:%s, <class '%s'>:%s, <class '%s'>:%s, "
|
|
"<class '%s'>:%s, <class '%s'>:%s, <class '%s'>:%s}",
|
|
n[0], b[0], n[1], b[1], n[2], b[2],
|
|
n[3], b[3], n[4], b[4], n[5], b[5],
|
|
n[6], b[6], n[7], b[7], n[8], b[8]);
|
|
}
|
|
|
|
static PyObject *
|
|
signaldict_richcompare(PyObject *v, PyObject *w, int op)
|
|
{
|
|
PyObject *res = Py_NotImplemented;
|
|
|
|
assert(PyDecSignalDict_Check(v));
|
|
|
|
if (op == Py_EQ || op == Py_NE) {
|
|
if (PyDecSignalDict_Check(w)) {
|
|
res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False;
|
|
}
|
|
else if (PyDict_Check(w)) {
|
|
uint32_t flags = dict_as_flags(w);
|
|
if (flags & DEC_ERRORS) {
|
|
if (flags & DEC_INVALID_SIGNALS) {
|
|
/* non-comparable: Py_NotImplemented */
|
|
PyErr_Clear();
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
res = (SdFlags(v)==flags) ^ (op==Py_NE) ? Py_True : Py_False;
|
|
}
|
|
}
|
|
}
|
|
|
|
Py_INCREF(res);
|
|
return res;
|
|
}
|
|
|
|
static PyObject *
|
|
signaldict_copy(PyObject *self, PyObject *args)
|
|
{
|
|
(void)args;
|
|
return flags_as_dict(SdFlags(self));
|
|
}
|
|
|
|
|
|
static PyMappingMethods signaldict_as_mapping = {
|
|
(lenfunc)signaldict_len, /* mp_length */
|
|
(binaryfunc)signaldict_getitem, /* mp_subscript */
|
|
(objobjargproc)signaldict_setitem /* mp_ass_subscript */
|
|
};
|
|
|
|
static PyMethodDef signaldict_methods[] = {
|
|
{ "copy", (PyCFunction)signaldict_copy, METH_NOARGS, NULL},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
|
|
static PyTypeObject PyDecSignalDictMixin_Type =
|
|
{
|
|
PyVarObject_HEAD_INIT(0, 0)
|
|
"decimal.SignalDictMixin", /* tp_name */
|
|
sizeof(PyDecSignalDictObject), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
0, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
(getattrfunc) 0, /* tp_getattr */
|
|
(setattrfunc) 0, /* tp_setattr */
|
|
0, /* tp_reserved */
|
|
(reprfunc) signaldict_repr, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
&signaldict_as_mapping, /* tp_as_mapping */
|
|
PyObject_HashNotImplemented, /* tp_hash */
|
|
0, /* tp_call */
|
|
(reprfunc) 0, /* tp_str */
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
(setattrofunc) 0, /* tp_setattro */
|
|
(PyBufferProcs *) 0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|
|
|
Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
0, /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
signaldict_richcompare, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
(getiterfunc)signaldict_iter, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
signaldict_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
(initproc)signaldict_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
PyType_GenericNew, /* tp_new */
|
|
};
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Context Object, Part 1 */
|
|
/******************************************************************************/
|
|
|
|
#define Dec_CONTEXT_GET_SSIZE(mem) \
|
|
static PyObject * \
|
|
context_get##mem(PyObject *self, void *closure) \
|
|
{ \
|
|
(void)closure; \
|
|
return PyLong_FromSsize_t(mpd_get##mem(CTX(self))); \
|
|
}
|
|
|
|
#define Dec_CONTEXT_GET_ULONG(mem) \
|
|
static PyObject * \
|
|
context_get##mem(PyObject *self, void *closure) \
|
|
{ \
|
|
(void)closure; \
|
|
return PyLong_FromUnsignedLong(mpd_get##mem(CTX(self))); \
|
|
}
|
|
|
|
Dec_CONTEXT_GET_SSIZE(prec)
|
|
Dec_CONTEXT_GET_SSIZE(emax)
|
|
Dec_CONTEXT_GET_SSIZE(emin)
|
|
Dec_CONTEXT_GET_SSIZE(clamp)
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
Dec_CONTEXT_GET_ULONG(traps)
|
|
Dec_CONTEXT_GET_ULONG(status)
|
|
#endif
|
|
|
|
static PyObject *
|
|
context_getround(PyObject *self, void *closure)
|
|
{
|
|
int i = mpd_getround(CTX(self));
|
|
|
|
Py_INCREF(round_map[i]);
|
|
return round_map[i];
|
|
}
|
|
|
|
static PyObject *
|
|
context_getcapitals(PyObject *self, void *closure)
|
|
{
|
|
return PyLong_FromLong(CtxCaps(self));
|
|
}
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
static PyObject *
|
|
context_getallcr(PyObject *self, void *closure)
|
|
{
|
|
return PyLong_FromLong(mpd_getcr(CTX(self)));
|
|
}
|
|
#endif
|
|
|
|
static PyObject *
|
|
context_getetiny(PyObject *self, PyObject *dummy)
|
|
{
|
|
return PyLong_FromSsize_t(mpd_etiny(CTX(self)));
|
|
}
|
|
|
|
static PyObject *
|
|
context_getetop(PyObject *self, PyObject *dummy)
|
|
{
|
|
return PyLong_FromSsize_t(mpd_etop(CTX(self)));
|
|
}
|
|
|
|
static int
|
|
context_setprec(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetprec(ctx, x)) {
|
|
return value_error_int(
|
|
"valid range for prec is [1, MAX_PREC]");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
context_setemin(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetemin(ctx, x)) {
|
|
return value_error_int(
|
|
"valid range for Emin is [MIN_EMIN, 0]");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
context_setemax(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetemax(ctx, x)) {
|
|
return value_error_int(
|
|
"valid range for Emax is [0, MAX_EMAX]");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_32
|
|
static PyObject *
|
|
context_unsafe_setprec(PyObject *self, PyObject *value)
|
|
{
|
|
mpd_context_t *ctx = CTX(self);
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
|
|
if (x < 1 || x > 1070000000L) {
|
|
return value_error_ptr(
|
|
"valid range for unsafe prec is [1, 1070000000]");
|
|
}
|
|
|
|
ctx->prec = x;
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
context_unsafe_setemin(PyObject *self, PyObject *value)
|
|
{
|
|
mpd_context_t *ctx = CTX(self);
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
|
|
if (x < -1070000000L || x > 0) {
|
|
return value_error_ptr(
|
|
"valid range for unsafe emin is [-1070000000, 0]");
|
|
}
|
|
|
|
ctx->emin = x;
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
context_unsafe_setemax(PyObject *self, PyObject *value)
|
|
{
|
|
mpd_context_t *ctx = CTX(self);
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
|
|
if (x < 0 || x > 1070000000L) {
|
|
return value_error_ptr(
|
|
"valid range for unsafe emax is [0, 1070000000]");
|
|
}
|
|
|
|
ctx->emax = x;
|
|
Py_RETURN_NONE;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
context_setround(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
int x;
|
|
|
|
x = getround(value);
|
|
if (x == -1) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetround(ctx, x)) {
|
|
INTERNAL_ERROR_INT("context_setround"); /* GCOV_NOT_REACHED */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
context_setcapitals(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return -1;
|
|
}
|
|
|
|
if (x != 0 && x != 1) {
|
|
return value_error_int(
|
|
"valid values for capitals are 0 or 1");
|
|
}
|
|
CtxCaps(self) = (int)x;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
static int
|
|
context_settraps(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
uint32_t flags;
|
|
|
|
flags = long_as_flags(value);
|
|
if (flags & DEC_ERRORS) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsettraps(ctx, flags)) {
|
|
INTERNAL_ERROR_INT("context_settraps");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
context_settraps_list(PyObject *self, PyObject *value)
|
|
{
|
|
mpd_context_t *ctx;
|
|
uint32_t flags;
|
|
|
|
flags = list_as_flags(value);
|
|
if (flags & DEC_ERRORS) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsettraps(ctx, flags)) {
|
|
INTERNAL_ERROR_INT("context_settraps_list");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
context_settraps_dict(PyObject *self, PyObject *value)
|
|
{
|
|
mpd_context_t *ctx;
|
|
uint32_t flags;
|
|
|
|
if (PyDecSignalDict_Check(value)) {
|
|
flags = SdFlags(value);
|
|
}
|
|
else {
|
|
flags = dict_as_flags(value);
|
|
if (flags & DEC_ERRORS) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsettraps(ctx, flags)) {
|
|
INTERNAL_ERROR_INT("context_settraps_dict");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
static int
|
|
context_setstatus(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
uint32_t flags;
|
|
|
|
flags = long_as_flags(value);
|
|
if (flags & DEC_ERRORS) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetstatus(ctx, flags)) {
|
|
INTERNAL_ERROR_INT("context_setstatus");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
context_setstatus_list(PyObject *self, PyObject *value)
|
|
{
|
|
mpd_context_t *ctx;
|
|
uint32_t flags;
|
|
|
|
flags = list_as_flags(value);
|
|
if (flags & DEC_ERRORS) {
|
|
return -1;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetstatus(ctx, flags)) {
|
|
INTERNAL_ERROR_INT("context_setstatus_list");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
context_setstatus_dict(PyObject *self, PyObject *value)
|
|
{
|
|
mpd_context_t *ctx;
|
|
uint32_t flags;
|
|
|
|
if (PyDecSignalDict_Check(value)) {
|
|
flags = SdFlags(value);
|
|
}
|
|
else {
|
|
flags = dict_as_flags(value);
|
|
if (flags & DEC_ERRORS) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetstatus(ctx, flags)) {
|
|
INTERNAL_ERROR_INT("context_setstatus_dict");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
context_setclamp(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return -1;
|
|
}
|
|
BOUNDS_CHECK(x, INT_MIN, INT_MAX);
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetclamp(ctx, (int)x)) {
|
|
return value_error_int("valid values for clamp are 0 or 1");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
static int
|
|
context_setallcr(PyObject *self, PyObject *value, void *closure)
|
|
{
|
|
mpd_context_t *ctx;
|
|
mpd_ssize_t x;
|
|
|
|
x = PyLong_AsSsize_t(value);
|
|
if (x == -1 && PyErr_Occurred()) {
|
|
return -1;
|
|
}
|
|
BOUNDS_CHECK(x, INT_MIN, INT_MAX);
|
|
|
|
ctx = CTX(self);
|
|
if (!mpd_qsetcr(ctx, (int)x)) {
|
|
return value_error_int("valid values for _allcr are 0 or 1");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static PyObject *
|
|
context_getattr(PyObject *self, PyObject *name)
|
|
{
|
|
PyObject *retval;
|
|
|
|
if (PyUnicode_Check(name)) {
|
|
if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) {
|
|
retval = ((PyDecContextObject *)self)->traps;
|
|
Py_INCREF(retval);
|
|
return retval;
|
|
}
|
|
if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) {
|
|
retval = ((PyDecContextObject *)self)->flags;
|
|
Py_INCREF(retval);
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
return PyObject_GenericGetAttr(self, name);
|
|
}
|
|
|
|
static int
|
|
context_setattr(PyObject *self, PyObject *name, PyObject *value)
|
|
{
|
|
if (value == NULL) {
|
|
PyErr_SetString(PyExc_AttributeError,
|
|
"context attributes cannot be deleted");
|
|
return -1;
|
|
}
|
|
|
|
if (PyUnicode_Check(name)) {
|
|
if (PyUnicode_CompareWithASCIIString(name, "traps") == 0) {
|
|
return context_settraps_dict(self, value);
|
|
}
|
|
if (PyUnicode_CompareWithASCIIString(name, "flags") == 0) {
|
|
return context_setstatus_dict(self, value);
|
|
}
|
|
}
|
|
|
|
return PyObject_GenericSetAttr(self, name, value);
|
|
}
|
|
|
|
static PyObject *
|
|
context_clear_traps(PyObject *self, PyObject *dummy)
|
|
{
|
|
CTX(self)->traps = 0;
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
context_clear_flags(PyObject *self, PyObject *dummy)
|
|
{
|
|
CTX(self)->status = 0;
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
#define DEC_DFLT_EMAX 999999
|
|
#define DEC_DFLT_EMIN -999999
|
|
|
|
static mpd_context_t dflt_ctx = {
|
|
28, DEC_DFLT_EMAX, DEC_DFLT_EMIN,
|
|
MPD_IEEE_Invalid_operation|MPD_Division_by_zero|MPD_Overflow,
|
|
0, 0, MPD_ROUND_HALF_EVEN, 0, 1
|
|
};
|
|
|
|
static PyObject *
|
|
context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
PyDecContextObject *self = NULL;
|
|
mpd_context_t *ctx;
|
|
|
|
if (type == &PyDecContext_Type) {
|
|
self = PyObject_New(PyDecContextObject, &PyDecContext_Type);
|
|
}
|
|
else {
|
|
self = (PyDecContextObject *)type->tp_alloc(type, 0);
|
|
}
|
|
|
|
if (self == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
self->traps = PyObject_CallObject((PyObject *)PyDecSignalDict_Type, NULL);
|
|
if (self->traps == NULL) {
|
|
self->flags = NULL;
|
|
Py_DECREF(self);
|
|
return NULL;
|
|
}
|
|
self->flags = PyObject_CallObject((PyObject *)PyDecSignalDict_Type, NULL);
|
|
if (self->flags == NULL) {
|
|
Py_DECREF(self);
|
|
return NULL;
|
|
}
|
|
|
|
ctx = CTX(self);
|
|
|
|
if (default_context_template) {
|
|
*ctx = *CTX(default_context_template);
|
|
}
|
|
else {
|
|
*ctx = dflt_ctx;
|
|
}
|
|
|
|
SdFlagAddr(self->traps) = &ctx->traps;
|
|
SdFlagAddr(self->flags) = &ctx->status;
|
|
|
|
CtxCaps(self) = 1;
|
|
#ifndef WITHOUT_THREADS
|
|
self->tstate = NULL;
|
|
#endif
|
|
|
|
return (PyObject *)self;
|
|
}
|
|
|
|
static void
|
|
context_dealloc(PyDecContextObject *self)
|
|
{
|
|
#ifndef WITHOUT_THREADS
|
|
if (self == cached_context) {
|
|
cached_context = NULL;
|
|
}
|
|
#endif
|
|
Py_XDECREF(self->traps);
|
|
Py_XDECREF(self->flags);
|
|
Py_TYPE(self)->tp_free(self);
|
|
}
|
|
|
|
static int
|
|
context_init(PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {
|
|
"prec", "rounding", "Emin", "Emax", "capitals", "clamp",
|
|
"flags", "traps", NULL
|
|
};
|
|
PyObject *prec = Py_None;
|
|
PyObject *rounding = Py_None;
|
|
PyObject *emin = Py_None;
|
|
PyObject *emax = Py_None;
|
|
PyObject *capitals = Py_None;
|
|
PyObject *clamp = Py_None;
|
|
PyObject *status = Py_None;
|
|
PyObject *traps = Py_None;
|
|
int ret;
|
|
|
|
assert(PyTuple_Check(args));
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(
|
|
args, kwds,
|
|
"|OOOOOOOO", kwlist,
|
|
&prec, &rounding, &emin, &emax, &capitals, &clamp, &status, &traps
|
|
)) {
|
|
return -1;
|
|
}
|
|
|
|
if (prec != Py_None && context_setprec(self, prec, NULL) < 0) {
|
|
return -1;
|
|
}
|
|
if (rounding != Py_None && context_setround(self, rounding, NULL) < 0) {
|
|
return -1;
|
|
}
|
|
if (emin != Py_None && context_setemin(self, emin, NULL) < 0) {
|
|
return -1;
|
|
}
|
|
if (emax != Py_None && context_setemax(self, emax, NULL) < 0) {
|
|
return -1;
|
|
}
|
|
if (capitals != Py_None && context_setcapitals(self, capitals, NULL) < 0) {
|
|
return -1;
|
|
}
|
|
if (clamp != Py_None && context_setclamp(self, clamp, NULL) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (traps != Py_None) {
|
|
if (PyList_Check(traps)) {
|
|
ret = context_settraps_list(self, traps);
|
|
}
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
else if (PyLong_Check(traps)) {
|
|
ret = context_settraps(self, traps, NULL);
|
|
}
|
|
#endif
|
|
else {
|
|
ret = context_settraps_dict(self, traps);
|
|
}
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
if (status != Py_None) {
|
|
if (PyList_Check(status)) {
|
|
ret = context_setstatus_list(self, status);
|
|
}
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
else if (PyLong_Check(status)) {
|
|
ret = context_setstatus(self, status, NULL);
|
|
}
|
|
#endif
|
|
else {
|
|
ret = context_setstatus_dict(self, status);
|
|
}
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PyObject *
|
|
context_repr(PyDecContextObject *self)
|
|
{
|
|
mpd_context_t *ctx;
|
|
char flags[MPD_MAX_SIGNAL_LIST];
|
|
char traps[MPD_MAX_SIGNAL_LIST];
|
|
int n, mem;
|
|
|
|
assert(PyDecContext_Check(self));
|
|
ctx = CTX(self);
|
|
|
|
mem = MPD_MAX_SIGNAL_LIST;
|
|
n = mpd_lsnprint_signals(flags, mem, ctx->status, dec_signal_string);
|
|
if (n < 0 || n >= mem) {
|
|
INTERNAL_ERROR_PTR("context_repr");
|
|
}
|
|
|
|
n = mpd_lsnprint_signals(traps, mem, ctx->traps, dec_signal_string);
|
|
if (n < 0 || n >= mem) {
|
|
INTERNAL_ERROR_PTR("context_repr");
|
|
}
|
|
|
|
return PyUnicode_FromFormat(
|
|
"Context(prec=%zd, rounding=%s, Emin=%zd, Emax=%zd, "
|
|
"capitals=%d, clamp=%d, flags=%s, traps=%s)",
|
|
ctx->prec, mpd_round_string[ctx->round], ctx->emin, ctx->emax,
|
|
self->capitals, ctx->clamp, flags, traps);
|
|
}
|
|
|
|
static void
|
|
init_basic_context(PyObject *v)
|
|
{
|
|
mpd_context_t ctx = dflt_ctx;
|
|
|
|
ctx.prec = 9;
|
|
ctx.traps |= (MPD_Underflow|MPD_Clamped);
|
|
ctx.round = MPD_ROUND_HALF_UP;
|
|
|
|
*CTX(v) = ctx;
|
|
CtxCaps(v) = 1;
|
|
}
|
|
|
|
static void
|
|
init_extended_context(PyObject *v)
|
|
{
|
|
mpd_context_t ctx = dflt_ctx;
|
|
|
|
ctx.prec = 9;
|
|
ctx.traps = 0;
|
|
|
|
*CTX(v) = ctx;
|
|
CtxCaps(v) = 1;
|
|
}
|
|
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
/* Factory function for creating IEEE interchange format contexts */
|
|
static PyObject *
|
|
ieee_context(PyObject *dummy, PyObject *v)
|
|
{
|
|
PyObject *context;
|
|
mpd_ssize_t bits;
|
|
mpd_context_t ctx;
|
|
|
|
bits = PyLong_AsSsize_t(v);
|
|
if (bits == -1 && PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
if (bits <= 0 || bits > INT_MAX) {
|
|
goto error;
|
|
}
|
|
if (mpd_ieee_context(&ctx, (int)bits) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
context = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL);
|
|
if (context == NULL) {
|
|
return NULL;
|
|
}
|
|
*CTX(context) = ctx;
|
|
|
|
return context;
|
|
|
|
error:
|
|
PyErr_Format(PyExc_ValueError,
|
|
"argument must be a multiple of 32, with a maximum of %d",
|
|
MPD_IEEE_CONTEXT_MAX_BITS);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static PyObject *
|
|
context_copy(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *copy;
|
|
|
|
copy = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL);
|
|
if (copy == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
*CTX(copy) = *CTX(self);
|
|
CTX(copy)->newtrap = 0;
|
|
CtxCaps(copy) = CtxCaps(self);
|
|
|
|
return copy;
|
|
}
|
|
|
|
static PyObject *
|
|
context_reduce(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *flags;
|
|
PyObject *traps;
|
|
PyObject *ret;
|
|
mpd_context_t *ctx;
|
|
|
|
ctx = CTX(self);
|
|
|
|
flags = signals_as_list(ctx->status);
|
|
if (flags == NULL) {
|
|
return NULL;
|
|
}
|
|
traps = signals_as_list(ctx->traps);
|
|
if (traps == NULL) {
|
|
Py_DECREF(flags);
|
|
return NULL;
|
|
}
|
|
|
|
ret = Py_BuildValue(
|
|
"O(nsnniiOO)",
|
|
Py_TYPE(self),
|
|
ctx->prec, mpd_round_string[ctx->round], ctx->emin, ctx->emax,
|
|
CtxCaps(self), ctx->clamp, flags, traps
|
|
);
|
|
|
|
Py_DECREF(flags);
|
|
Py_DECREF(traps);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static PyGetSetDef context_getsets [] =
|
|
{
|
|
{ "prec", (getter)context_getprec, (setter)context_setprec, NULL, NULL},
|
|
{ "Emax", (getter)context_getemax, (setter)context_setemax, NULL, NULL},
|
|
{ "Emin", (getter)context_getemin, (setter)context_setemin, NULL, NULL},
|
|
{ "rounding", (getter)context_getround, (setter)context_setround, NULL, NULL},
|
|
{ "capitals", (getter)context_getcapitals, (setter)context_setcapitals, NULL, NULL},
|
|
{ "clamp", (getter)context_getclamp, (setter)context_setclamp, NULL, NULL},
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
{ "_allcr", (getter)context_getallcr, (setter)context_setallcr, NULL, NULL},
|
|
{ "_traps", (getter)context_gettraps, (setter)context_settraps, NULL, NULL},
|
|
{ "_flags", (getter)context_getstatus, (setter)context_setstatus, NULL, NULL},
|
|
#endif
|
|
{NULL}
|
|
};
|
|
|
|
|
|
#define CONTEXT_CHECK(obj) \
|
|
if (!PyDecContext_Check(obj)) { \
|
|
PyErr_SetString(PyExc_TypeError, \
|
|
"argument must be a context"); \
|
|
return NULL; \
|
|
}
|
|
|
|
#define CONTEXT_CHECK_VA(obj) \
|
|
if (obj == Py_None) { \
|
|
CURRENT_CONTEXT(obj); \
|
|
} \
|
|
else if (!PyDecContext_Check(obj)) { \
|
|
PyErr_SetString(PyExc_TypeError, \
|
|
"optional argument must be a context"); \
|
|
return NULL; \
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Global, thread local and temporary contexts */
|
|
/******************************************************************************/
|
|
|
|
#ifdef WITHOUT_THREADS
|
|
/* Return borrowed reference to the current context. When compiled
|
|
* without threads, this is always the module context. */
|
|
static int module_context_set = 0;
|
|
static PyObject *
|
|
current_context(void)
|
|
{
|
|
/* In decimal.py, the module context is automatically initialized
|
|
* from the DefaultContext when it is first accessed. This
|
|
* complicates the code and has a speed penalty of 1-2%. */
|
|
if (module_context_set) {
|
|
return module_context;
|
|
}
|
|
|
|
*CTX(module_context) = *CTX(default_context_template);
|
|
CTX(module_context)->status = 0;
|
|
CTX(module_context)->newtrap = 0;
|
|
CtxCaps(module_context) = CtxCaps(default_context_template);
|
|
|
|
module_context_set = 1;
|
|
return module_context;
|
|
}
|
|
|
|
/* ctxobj := borrowed reference to the current context */
|
|
#define CURRENT_CONTEXT(ctxobj) \
|
|
ctxobj = current_context()
|
|
|
|
/* ctx := pointer to the mpd_context_t struct of the current context */
|
|
#define CURRENT_CONTEXT_ADDR(ctx) \
|
|
ctx = CTX(current_context())
|
|
|
|
/* Return a new reference to the current context */
|
|
static PyObject *
|
|
PyDec_GetCurrentContext(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *context;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
|
|
Py_INCREF(context);
|
|
return context;
|
|
}
|
|
|
|
/* Set the module context to a new context, decrement old reference */
|
|
static PyObject *
|
|
PyDec_SetCurrentContext(PyObject *self, PyObject *v)
|
|
{
|
|
CONTEXT_CHECK(v);
|
|
|
|
/* If the new context is one of the templates, make a copy.
|
|
* This is the current behavior of decimal.py. */
|
|
if (v == default_context_template ||
|
|
v == basic_context_template ||
|
|
v == extended_context_template) {
|
|
v = context_copy(v, NULL);
|
|
if (v == NULL) {
|
|
return NULL;
|
|
}
|
|
CTX(v)->status = 0;
|
|
}
|
|
else {
|
|
Py_INCREF(v);
|
|
}
|
|
|
|
Py_XDECREF(module_context);
|
|
module_context = v;
|
|
module_context_set = 1;
|
|
Py_RETURN_NONE;
|
|
}
|
|
#else
|
|
/*
|
|
* Thread local storage currently has a speed penalty of about 4%.
|
|
* All functions that map Python's arithmetic operators to mpdecimal
|
|
* functions have to look up the current context for each and every
|
|
* operation.
|
|
*/
|
|
|
|
/* Get the context from the thread state dictionary. */
|
|
static PyObject *
|
|
current_context_from_dict(void)
|
|
{
|
|
PyObject *dict;
|
|
PyObject *tl_context;
|
|
PyThreadState *tstate;
|
|
|
|
dict = PyThreadState_GetDict();
|
|
if (dict == NULL) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"cannot get thread state");
|
|
return NULL;
|
|
}
|
|
|
|
tl_context = PyDict_GetItemWithError(dict, tls_context_key);
|
|
if (tl_context != NULL) {
|
|
/* We already have a thread local context. */
|
|
CONTEXT_CHECK(tl_context);
|
|
}
|
|
else {
|
|
if (PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Set up a new thread local context. */
|
|
tl_context = context_copy(default_context_template, NULL);
|
|
if (tl_context == NULL) {
|
|
return NULL;
|
|
}
|
|
CTX(tl_context)->status = 0;
|
|
|
|
if (PyDict_SetItem(dict, tls_context_key, tl_context) < 0) {
|
|
Py_DECREF(tl_context);
|
|
return NULL;
|
|
}
|
|
Py_DECREF(tl_context);
|
|
}
|
|
|
|
/* Cache the context of the current thread, assuming that it
|
|
* will be accessed several times before a thread switch. */
|
|
tstate = PyThreadState_GET();
|
|
if (tstate) {
|
|
cached_context = (PyDecContextObject *)tl_context;
|
|
cached_context->tstate = tstate;
|
|
}
|
|
|
|
/* Borrowed reference with refcount==1 */
|
|
return tl_context;
|
|
}
|
|
|
|
/* Return borrowed reference to thread local context. */
|
|
static PyObject *
|
|
current_context(void)
|
|
{
|
|
PyThreadState *tstate;
|
|
|
|
tstate = PyThreadState_GET();
|
|
if (cached_context && cached_context->tstate == tstate) {
|
|
return (PyObject *)cached_context;
|
|
}
|
|
|
|
return current_context_from_dict();
|
|
}
|
|
|
|
/* ctxobj := borrowed reference to the current context */
|
|
#define CURRENT_CONTEXT(ctxobj) \
|
|
ctxobj = current_context(); \
|
|
if (ctxobj == NULL) { \
|
|
return NULL; \
|
|
}
|
|
|
|
/* ctx := pointer to the mpd_context_t struct of the current context */
|
|
#define CURRENT_CONTEXT_ADDR(ctx) { \
|
|
PyObject *_c_t_x_o_b_j = current_context(); \
|
|
if (_c_t_x_o_b_j == NULL) { \
|
|
return NULL; \
|
|
} \
|
|
ctx = CTX(_c_t_x_o_b_j); \
|
|
}
|
|
|
|
/* Return a new reference to the current context */
|
|
static PyObject *
|
|
PyDec_GetCurrentContext(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *context;
|
|
|
|
context = current_context();
|
|
if (context == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Py_INCREF(context);
|
|
return context;
|
|
}
|
|
|
|
/* Set the thread local context to a new context, decrement old reference */
|
|
static PyObject *
|
|
PyDec_SetCurrentContext(PyObject *self, PyObject *v)
|
|
{
|
|
PyObject *dict;
|
|
|
|
CONTEXT_CHECK(v);
|
|
|
|
dict = PyThreadState_GetDict();
|
|
if (dict == NULL) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"cannot get thread state");
|
|
return NULL;
|
|
}
|
|
|
|
/* If the new context is one of the templates, make a copy.
|
|
* This is the current behavior of decimal.py. */
|
|
if (v == default_context_template ||
|
|
v == basic_context_template ||
|
|
v == extended_context_template) {
|
|
v = context_copy(v, NULL);
|
|
if (v == NULL) {
|
|
return NULL;
|
|
}
|
|
CTX(v)->status = 0;
|
|
}
|
|
else {
|
|
Py_INCREF(v);
|
|
}
|
|
|
|
cached_context = NULL;
|
|
if (PyDict_SetItem(dict, tls_context_key, v) < 0) {
|
|
Py_DECREF(v);
|
|
return NULL;
|
|
}
|
|
|
|
Py_DECREF(v);
|
|
Py_RETURN_NONE;
|
|
}
|
|
#endif
|
|
|
|
/* Context manager object for the 'with' statement. The manager
|
|
* owns one reference to the global (outer) context and one
|
|
* to the local (inner) context. */
|
|
static PyObject *
|
|
ctxmanager_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"ctx", NULL};
|
|
PyDecContextManagerObject *self;
|
|
PyObject *local = Py_None;
|
|
PyObject *global;
|
|
|
|
CURRENT_CONTEXT(global);
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &local)) {
|
|
return NULL;
|
|
}
|
|
if (local == Py_None) {
|
|
local = global;
|
|
}
|
|
else if (!PyDecContext_Check(local)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"optional argument must be a context");
|
|
return NULL;
|
|
}
|
|
|
|
self = PyObject_New(PyDecContextManagerObject,
|
|
&PyDecContextManager_Type);
|
|
if (self == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
self->local = context_copy(local, NULL);
|
|
if (self->local == NULL) {
|
|
self->global = NULL;
|
|
Py_DECREF(self);
|
|
return NULL;
|
|
}
|
|
self->global = global;
|
|
Py_INCREF(self->global);
|
|
|
|
return (PyObject *)self;
|
|
}
|
|
|
|
static void
|
|
ctxmanager_dealloc(PyDecContextManagerObject *self)
|
|
{
|
|
Py_XDECREF(self->local);
|
|
Py_XDECREF(self->global);
|
|
PyObject_Del(self);
|
|
}
|
|
|
|
static PyObject *
|
|
ctxmanager_set_local(PyDecContextManagerObject *self, PyObject *args)
|
|
{
|
|
PyObject *ret;
|
|
|
|
ret = PyDec_SetCurrentContext(NULL, self->local);
|
|
if (ret == NULL) {
|
|
return NULL;
|
|
}
|
|
Py_DECREF(ret);
|
|
|
|
Py_INCREF(self->local);
|
|
return self->local;
|
|
}
|
|
|
|
static PyObject *
|
|
ctxmanager_restore_global(PyDecContextManagerObject *self,
|
|
PyObject *args)
|
|
{
|
|
PyObject *ret;
|
|
|
|
ret = PyDec_SetCurrentContext(NULL, self->global);
|
|
if (ret == NULL) {
|
|
return NULL;
|
|
}
|
|
Py_DECREF(ret);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
static PyMethodDef ctxmanager_methods[] = {
|
|
{"__enter__", (PyCFunction)ctxmanager_set_local, METH_NOARGS, NULL},
|
|
{"__exit__", (PyCFunction)ctxmanager_restore_global, METH_VARARGS, NULL},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static PyTypeObject PyDecContextManager_Type =
|
|
{
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"decimal.ContextManager", /* tp_name */
|
|
sizeof(PyDecContextManagerObject), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
(destructor) ctxmanager_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
(getattrfunc) 0, /* tp_getattr */
|
|
(setattrfunc) 0, /* tp_setattr */
|
|
0, /* tp_reserved */
|
|
(reprfunc) 0, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
0, /* tp_hash */
|
|
0, /* tp_call */
|
|
0, /* tp_str */
|
|
(getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */
|
|
(setattrofunc) 0, /* tp_setattro */
|
|
(PyBufferProcs *) 0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
0, /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
ctxmanager_methods, /* tp_methods */
|
|
};
|
|
|
|
|
|
/******************************************************************************/
|
|
/* New Decimal Object */
|
|
/******************************************************************************/
|
|
|
|
static PyObject *
|
|
PyDecType_New(PyTypeObject *type)
|
|
{
|
|
PyDecObject *dec;
|
|
|
|
if (type == &PyDec_Type) {
|
|
dec = PyObject_New(PyDecObject, &PyDec_Type);
|
|
}
|
|
else {
|
|
dec = (PyDecObject *)type->tp_alloc(type, 0);
|
|
}
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
dec->hash = -1;
|
|
|
|
MPD(dec)->flags = MPD_STATIC|MPD_STATIC_DATA;
|
|
MPD(dec)->exp = 0;
|
|
MPD(dec)->digits = 0;
|
|
MPD(dec)->len = 0;
|
|
MPD(dec)->alloc = _Py_DEC_MINALLOC;
|
|
MPD(dec)->data = dec->data;
|
|
|
|
return (PyObject *)dec;
|
|
}
|
|
#define dec_alloc() PyDecType_New(&PyDec_Type)
|
|
|
|
static void
|
|
dec_dealloc(PyObject *dec)
|
|
{
|
|
mpd_del(MPD(dec));
|
|
Py_TYPE(dec)->tp_free(dec);
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Conversions to Decimal */
|
|
/******************************************************************************/
|
|
|
|
Py_LOCAL_INLINE(int)
|
|
is_space(enum PyUnicode_Kind kind, void *data, Py_ssize_t pos)
|
|
{
|
|
Py_UCS4 ch = PyUnicode_READ(kind, data, pos);
|
|
return Py_UNICODE_ISSPACE(ch);
|
|
}
|
|
|
|
/* Return the ASCII representation of a numeric Unicode string. The numeric
|
|
string may contain ascii characters in the range [1, 127], any Unicode
|
|
space and any unicode digit. If strip_ws is true, leading and trailing
|
|
whitespace is stripped. If ignore_underscores is true, underscores are
|
|
ignored.
|
|
|
|
Return NULL if malloc fails and an empty string if invalid characters
|
|
are found. */
|
|
static char *
|
|
numeric_as_ascii(const PyObject *u, int strip_ws, int ignore_underscores)
|
|
{
|
|
enum PyUnicode_Kind kind;
|
|
void *data;
|
|
Py_UCS4 ch;
|
|
char *res, *cp;
|
|
Py_ssize_t j, len;
|
|
int d;
|
|
if (PyUnicode_READY(u) == -1) {
|
|
return NULL;
|
|
}
|
|
kind = PyUnicode_KIND(u);
|
|
data = PyUnicode_DATA(u);
|
|
len = PyUnicode_GET_LENGTH(u);
|
|
cp = res = PyMem_Malloc(len+1);
|
|
if (res == NULL) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
j = 0;
|
|
if (strip_ws) {
|
|
while (len > 0 && is_space(kind, data, len-1)) {
|
|
len--;
|
|
}
|
|
while (j < len && is_space(kind, data, j)) {
|
|
j++;
|
|
}
|
|
}
|
|
for (; j < len; j++) {
|
|
ch = PyUnicode_READ(kind, data, j);
|
|
if (ignore_underscores && ch == '_') {
|
|
continue;
|
|
}
|
|
if (0 < ch && ch <= 127) {
|
|
*cp++ = ch;
|
|
continue;
|
|
}
|
|
if (Py_UNICODE_ISSPACE(ch)) {
|
|
*cp++ = ' ';
|
|
continue;
|
|
}
|
|
d = Py_UNICODE_TODECIMAL(ch);
|
|
if (d < 0) {
|
|
/* empty string triggers ConversionSyntax */
|
|
*res = '\0';
|
|
return res;
|
|
}
|
|
*cp++ = '0' + d;
|
|
}
|
|
*cp = '\0';
|
|
return res;
|
|
}
|
|
|
|
/* Return a new PyDecObject or a subtype from a C string. Use the context
|
|
during conversion. */
|
|
static PyObject *
|
|
PyDecType_FromCString(PyTypeObject *type, const char *s,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
dec = PyDecType_New(type);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
mpd_qset_string(MPD(dec), s, CTX(context), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
return dec;
|
|
}
|
|
|
|
/* Return a new PyDecObject or a subtype from a C string. Attempt exact
|
|
conversion. If the operand cannot be converted exactly, set
|
|
InvalidOperation. */
|
|
static PyObject *
|
|
PyDecType_FromCStringExact(PyTypeObject *type, const char *s,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
mpd_context_t maxctx;
|
|
dec = PyDecType_New(type);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
mpd_maxcontext(&maxctx);
|
|
mpd_qset_string(MPD(dec), s, &maxctx, &status);
|
|
if (status & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
|
|
/* we want exact results */
|
|
mpd_seterror(MPD(dec), MPD_Invalid_operation, &status);
|
|
}
|
|
status &= MPD_Errors;
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
return dec;
|
|
}
|
|
|
|
/* Return a new PyDecObject or a subtype from a PyUnicodeObject. */
|
|
static PyObject *
|
|
PyDecType_FromUnicode(PyTypeObject *type, const PyObject *u,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
char *s;
|
|
s = numeric_as_ascii(u, 0, 0);
|
|
if (s == NULL) {
|
|
return NULL;
|
|
}
|
|
dec = PyDecType_FromCString(type, s, context);
|
|
PyMem_Free(s);
|
|
return dec;
|
|
}
|
|
|
|
/* Return a new PyDecObject or a subtype from a PyUnicodeObject. Attempt exact
|
|
* conversion. If the conversion is not exact, fail with InvalidOperation.
|
|
* Allow leading and trailing whitespace in the input operand. */
|
|
static PyObject *
|
|
PyDecType_FromUnicodeExactWS(PyTypeObject *type, const PyObject *u,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
char *s;
|
|
s = numeric_as_ascii(u, 1, 1);
|
|
if (s == NULL) {
|
|
return NULL;
|
|
}
|
|
dec = PyDecType_FromCStringExact(type, s, context);
|
|
PyMem_Free(s);
|
|
return dec;
|
|
}
|
|
|
|
/* Set PyDecObject from triple without any error checking. */
|
|
Py_LOCAL_INLINE(void)
|
|
_dec_settriple(PyObject *dec, uint8_t sign, uint32_t v, mpd_ssize_t exp)
|
|
{
|
|
MPD(dec)->data[0] = v;
|
|
MPD(dec)->len = 1;
|
|
mpd_set_flags(MPD(dec), sign);
|
|
MPD(dec)->exp = exp;
|
|
mpd_setdigits(MPD(dec));
|
|
}
|
|
|
|
/* Return a new PyDecObject from an mpd_ssize_t. */
|
|
static PyObject *
|
|
PyDecType_FromSsize(PyTypeObject *type, mpd_ssize_t v, PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
|
|
dec = PyDecType_New(type);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qset_ssize(MPD(dec), v, CTX(context), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
return dec;
|
|
}
|
|
|
|
/* Return a new PyDecObject from an mpd_ssize_t. Conversion is exact. */
|
|
static PyObject *
|
|
PyDecType_FromSsizeExact(PyTypeObject *type, mpd_ssize_t v, PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
mpd_context_t maxctx;
|
|
|
|
dec = PyDecType_New(type);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_maxcontext(&maxctx);
|
|
|
|
mpd_qset_ssize(MPD(dec), v, &maxctx, &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
return dec;
|
|
}
|
|
|
|
/* Convert from a PyLongObject. The context is not modified; flags set
|
|
during conversion are accumulated in the status parameter. */
|
|
static PyObject *
|
|
dec_from_long(PyTypeObject *type, const PyObject *v,
|
|
const mpd_context_t *ctx, uint32_t *status)
|
|
{
|
|
PyObject *dec;
|
|
PyLongObject *l = (PyLongObject *)v;
|
|
Py_ssize_t ob_size;
|
|
size_t len;
|
|
uint8_t sign;
|
|
|
|
dec = PyDecType_New(type);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ob_size = Py_SIZE(l);
|
|
if (ob_size == 0) {
|
|
_dec_settriple(dec, MPD_POS, 0, 0);
|
|
return dec;
|
|
}
|
|
|
|
if (ob_size < 0) {
|
|
len = -ob_size;
|
|
sign = MPD_NEG;
|
|
}
|
|
else {
|
|
len = ob_size;
|
|
sign = MPD_POS;
|
|
}
|
|
|
|
if (len == 1) {
|
|
_dec_settriple(dec, sign, *l->ob_digit, 0);
|
|
mpd_qfinalize(MPD(dec), ctx, status);
|
|
return dec;
|
|
}
|
|
|
|
#if PYLONG_BITS_IN_DIGIT == 30
|
|
mpd_qimport_u32(MPD(dec), l->ob_digit, len, sign, PyLong_BASE,
|
|
ctx, status);
|
|
#elif PYLONG_BITS_IN_DIGIT == 15
|
|
mpd_qimport_u16(MPD(dec), l->ob_digit, len, sign, PyLong_BASE,
|
|
ctx, status);
|
|
#else
|
|
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
|
|
#endif
|
|
|
|
return dec;
|
|
}
|
|
|
|
/* Return a new PyDecObject from a PyLongObject. Use the context for
|
|
conversion. */
|
|
static PyObject *
|
|
PyDecType_FromLong(PyTypeObject *type, const PyObject *v, PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
|
|
if (!PyLong_Check(v)) {
|
|
PyErr_SetString(PyExc_TypeError, "argument must be an integer");
|
|
return NULL;
|
|
}
|
|
|
|
dec = dec_from_long(type, v, CTX(context), &status);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
|
|
return dec;
|
|
}
|
|
|
|
/* Return a new PyDecObject from a PyLongObject. Use a maximum context
|
|
for conversion. If the conversion is not exact, set InvalidOperation. */
|
|
static PyObject *
|
|
PyDecType_FromLongExact(PyTypeObject *type, const PyObject *v,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
mpd_context_t maxctx;
|
|
|
|
if (!PyLong_Check(v)) {
|
|
PyErr_SetString(PyExc_TypeError, "argument must be an integer");
|
|
return NULL;
|
|
}
|
|
|
|
mpd_maxcontext(&maxctx);
|
|
dec = dec_from_long(type, v, &maxctx, &status);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (status & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) {
|
|
/* we want exact results */
|
|
mpd_seterror(MPD(dec), MPD_Invalid_operation, &status);
|
|
}
|
|
status &= MPD_Errors;
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
|
|
return dec;
|
|
}
|
|
|
|
/* External C-API functions */
|
|
static binaryfunc _py_long_multiply;
|
|
static binaryfunc _py_long_floor_divide;
|
|
static ternaryfunc _py_long_power;
|
|
static unaryfunc _py_float_abs;
|
|
static PyCFunction _py_long_bit_length;
|
|
static PyCFunction _py_float_as_integer_ratio;
|
|
|
|
/* Return a PyDecObject or a subtype from a PyFloatObject.
|
|
Conversion is exact. */
|
|
static PyObject *
|
|
PyDecType_FromFloatExact(PyTypeObject *type, PyObject *v,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dec, *tmp;
|
|
PyObject *n, *d, *n_d;
|
|
mpd_ssize_t k;
|
|
double x;
|
|
int sign;
|
|
mpd_t *d1, *d2;
|
|
uint32_t status = 0;
|
|
mpd_context_t maxctx;
|
|
|
|
|
|
assert(PyType_IsSubtype(type, &PyDec_Type));
|
|
|
|
if (PyLong_Check(v)) {
|
|
return PyDecType_FromLongExact(type, v, context);
|
|
}
|
|
if (!PyFloat_Check(v)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"argument must be int of float");
|
|
return NULL;
|
|
}
|
|
|
|
x = PyFloat_AsDouble(v);
|
|
if (x == -1.0 && PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
sign = (copysign(1.0, x) == 1.0) ? 0 : 1;
|
|
|
|
if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) {
|
|
dec = PyDecType_New(type);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
if (Py_IS_NAN(x)) {
|
|
/* decimal.py calls repr(float(+-nan)),
|
|
* which always gives a positive result. */
|
|
mpd_setspecial(MPD(dec), MPD_POS, MPD_NAN);
|
|
}
|
|
else {
|
|
mpd_setspecial(MPD(dec), sign, MPD_INF);
|
|
}
|
|
return dec;
|
|
}
|
|
|
|
/* absolute value of the float */
|
|
tmp = _py_float_abs(v);
|
|
if (tmp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* float as integer ratio: numerator/denominator */
|
|
n_d = _py_float_as_integer_ratio(tmp, NULL);
|
|
Py_DECREF(tmp);
|
|
if (n_d == NULL) {
|
|
return NULL;
|
|
}
|
|
n = PyTuple_GET_ITEM(n_d, 0);
|
|
d = PyTuple_GET_ITEM(n_d, 1);
|
|
|
|
tmp = _py_long_bit_length(d, NULL);
|
|
if (tmp == NULL) {
|
|
Py_DECREF(n_d);
|
|
return NULL;
|
|
}
|
|
k = PyLong_AsSsize_t(tmp);
|
|
Py_DECREF(tmp);
|
|
if (k == -1 && PyErr_Occurred()) {
|
|
Py_DECREF(n_d);
|
|
return NULL;
|
|
}
|
|
k--;
|
|
|
|
dec = PyDecType_FromLongExact(type, n, context);
|
|
Py_DECREF(n_d);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
d1 = mpd_qnew();
|
|
if (d1 == NULL) {
|
|
Py_DECREF(dec);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
d2 = mpd_qnew();
|
|
if (d2 == NULL) {
|
|
mpd_del(d1);
|
|
Py_DECREF(dec);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
mpd_maxcontext(&maxctx);
|
|
mpd_qset_uint(d1, 5, &maxctx, &status);
|
|
mpd_qset_ssize(d2, k, &maxctx, &status);
|
|
mpd_qpow(d1, d1, d2, &maxctx, &status);
|
|
if (dec_addstatus(context, status)) {
|
|
mpd_del(d1);
|
|
mpd_del(d2);
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
|
|
/* result = n * 5**k */
|
|
mpd_qmul(MPD(dec), MPD(dec), d1, &maxctx, &status);
|
|
mpd_del(d1);
|
|
mpd_del(d2);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
/* result = +- n * 5**k * 10**-k */
|
|
mpd_set_sign(MPD(dec), sign);
|
|
MPD(dec)->exp = -k;
|
|
|
|
return dec;
|
|
}
|
|
|
|
static PyObject *
|
|
PyDecType_FromFloat(PyTypeObject *type, PyObject *v,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
|
|
dec = PyDecType_FromFloatExact(type, v, context);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qfinalize(MPD(dec), CTX(context), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
|
|
return dec;
|
|
}
|
|
|
|
/* Return a new PyDecObject or a subtype from a Decimal. */
|
|
static PyObject *
|
|
PyDecType_FromDecimalExact(PyTypeObject *type, PyObject *v, PyObject *context)
|
|
{
|
|
PyObject *dec;
|
|
uint32_t status = 0;
|
|
|
|
if (type == &PyDec_Type && PyDec_CheckExact(v)) {
|
|
Py_INCREF(v);
|
|
return v;
|
|
}
|
|
|
|
dec = PyDecType_New(type);
|
|
if (dec == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qcopy(MPD(dec), MPD(v), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(dec);
|
|
return NULL;
|
|
}
|
|
|
|
return dec;
|
|
}
|
|
|
|
static PyObject *
|
|
sequence_as_tuple(PyObject *v, PyObject *ex, const char *mesg)
|
|
{
|
|
if (PyTuple_Check(v)) {
|
|
Py_INCREF(v);
|
|
return v;
|
|
}
|
|
if (PyList_Check(v)) {
|
|
return PyList_AsTuple(v);
|
|
}
|
|
|
|
PyErr_SetString(ex, mesg);
|
|
return NULL;
|
|
}
|
|
|
|
/* Return a new C string representation of a DecimalTuple. */
|
|
static char *
|
|
dectuple_as_str(PyObject *dectuple)
|
|
{
|
|
PyObject *digits = NULL, *tmp;
|
|
char *decstring = NULL;
|
|
char sign_special[6];
|
|
char *cp;
|
|
long sign, l;
|
|
mpd_ssize_t exp = 0;
|
|
Py_ssize_t i, mem, tsize;
|
|
int is_infinite = 0;
|
|
int n;
|
|
|
|
assert(PyTuple_Check(dectuple));
|
|
|
|
if (PyTuple_Size(dectuple) != 3) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"argument must be a sequence of length 3");
|
|
goto error;
|
|
}
|
|
|
|
/* sign */
|
|
tmp = PyTuple_GET_ITEM(dectuple, 0);
|
|
if (!PyLong_Check(tmp)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"sign must be an integer with the value 0 or 1");
|
|
goto error;
|
|
}
|
|
sign = PyLong_AsLong(tmp);
|
|
if (sign == -1 && PyErr_Occurred()) {
|
|
goto error;
|
|
}
|
|
if (sign != 0 && sign != 1) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"sign must be an integer with the value 0 or 1");
|
|
goto error;
|
|
}
|
|
sign_special[0] = sign ? '-' : '+';
|
|
sign_special[1] = '\0';
|
|
|
|
/* exponent or encoding for a special number */
|
|
tmp = PyTuple_GET_ITEM(dectuple, 2);
|
|
if (PyUnicode_Check(tmp)) {
|
|
/* special */
|
|
if (PyUnicode_CompareWithASCIIString(tmp, "F") == 0) {
|
|
strcat(sign_special, "Inf");
|
|
is_infinite = 1;
|
|
}
|
|
else if (PyUnicode_CompareWithASCIIString(tmp, "n") == 0) {
|
|
strcat(sign_special, "NaN");
|
|
}
|
|
else if (PyUnicode_CompareWithASCIIString(tmp, "N") == 0) {
|
|
strcat(sign_special, "sNaN");
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"string argument in the third position "
|
|
"must be 'F', 'n' or 'N'");
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
/* exponent */
|
|
if (!PyLong_Check(tmp)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"exponent must be an integer");
|
|
goto error;
|
|
}
|
|
exp = PyLong_AsSsize_t(tmp);
|
|
if (exp == -1 && PyErr_Occurred()) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* coefficient */
|
|
digits = sequence_as_tuple(PyTuple_GET_ITEM(dectuple, 1), PyExc_ValueError,
|
|
"coefficient must be a tuple of digits");
|
|
if (digits == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
tsize = PyTuple_Size(digits);
|
|
/* [sign][coeffdigits+1][E][-][expdigits+1]['\0'] */
|
|
mem = 1 + tsize + 3 + MPD_EXPDIGITS + 2;
|
|
cp = decstring = PyMem_Malloc(mem);
|
|
if (decstring == NULL) {
|
|
PyErr_NoMemory();
|
|
goto error;
|
|
}
|
|
|
|
n = snprintf(cp, mem, "%s", sign_special);
|
|
if (n < 0 || n >= mem) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"internal error in dec_sequence_as_str");
|
|
goto error;
|
|
}
|
|
cp += n;
|
|
|
|
if (tsize == 0 && sign_special[1] == '\0') {
|
|
/* empty tuple: zero coefficient, except for special numbers */
|
|
*cp++ = '0';
|
|
}
|
|
for (i = 0; i < tsize; i++) {
|
|
tmp = PyTuple_GET_ITEM(digits, i);
|
|
if (!PyLong_Check(tmp)) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"coefficient must be a tuple of digits");
|
|
goto error;
|
|
}
|
|
l = PyLong_AsLong(tmp);
|
|
if (l == -1 && PyErr_Occurred()) {
|
|
goto error;
|
|
}
|
|
if (l < 0 || l > 9) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"coefficient must be a tuple of digits");
|
|
goto error;
|
|
}
|
|
if (is_infinite) {
|
|
/* accept but ignore any well-formed coefficient for compatibility
|
|
with decimal.py */
|
|
continue;
|
|
}
|
|
*cp++ = (char)l + '0';
|
|
}
|
|
*cp = '\0';
|
|
|
|
if (sign_special[1] == '\0') {
|
|
/* not a special number */
|
|
*cp++ = 'E';
|
|
n = snprintf(cp, MPD_EXPDIGITS+2, "%" PRI_mpd_ssize_t, exp);
|
|
if (n < 0 || n >= MPD_EXPDIGITS+2) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"internal error in dec_sequence_as_str");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
Py_XDECREF(digits);
|
|
return decstring;
|
|
|
|
|
|
error:
|
|
Py_XDECREF(digits);
|
|
if (decstring) PyMem_Free(decstring);
|
|
return NULL;
|
|
}
|
|
|
|
/* Currently accepts tuples and lists. */
|
|
static PyObject *
|
|
PyDecType_FromSequence(PyTypeObject *type, PyObject *v,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dectuple;
|
|
PyObject *dec;
|
|
char *s;
|
|
|
|
dectuple = sequence_as_tuple(v, PyExc_TypeError,
|
|
"argument must be a tuple or list");
|
|
if (dectuple == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
s = dectuple_as_str(dectuple);
|
|
Py_DECREF(dectuple);
|
|
if (s == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
dec = PyDecType_FromCString(type, s, context);
|
|
|
|
PyMem_Free(s);
|
|
return dec;
|
|
}
|
|
|
|
/* Currently accepts tuples and lists. */
|
|
static PyObject *
|
|
PyDecType_FromSequenceExact(PyTypeObject *type, PyObject *v,
|
|
PyObject *context)
|
|
{
|
|
PyObject *dectuple;
|
|
PyObject *dec;
|
|
char *s;
|
|
|
|
dectuple = sequence_as_tuple(v, PyExc_TypeError,
|
|
"argument must be a tuple or list");
|
|
if (dectuple == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
s = dectuple_as_str(dectuple);
|
|
Py_DECREF(dectuple);
|
|
if (s == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
dec = PyDecType_FromCStringExact(type, s, context);
|
|
|
|
PyMem_Free(s);
|
|
return dec;
|
|
}
|
|
|
|
#define PyDec_FromCString(str, context) \
|
|
PyDecType_FromCString(&PyDec_Type, str, context)
|
|
#define PyDec_FromCStringExact(str, context) \
|
|
PyDecType_FromCStringExact(&PyDec_Type, str, context)
|
|
|
|
#define PyDec_FromUnicode(unicode, context) \
|
|
PyDecType_FromUnicode(&PyDec_Type, unicode, context)
|
|
#define PyDec_FromUnicodeExact(unicode, context) \
|
|
PyDecType_FromUnicodeExact(&PyDec_Type, unicode, context)
|
|
#define PyDec_FromUnicodeExactWS(unicode, context) \
|
|
PyDecType_FromUnicodeExactWS(&PyDec_Type, unicode, context)
|
|
|
|
#define PyDec_FromSsize(v, context) \
|
|
PyDecType_FromSsize(&PyDec_Type, v, context)
|
|
#define PyDec_FromSsizeExact(v, context) \
|
|
PyDecType_FromSsizeExact(&PyDec_Type, v, context)
|
|
|
|
#define PyDec_FromLong(pylong, context) \
|
|
PyDecType_FromLong(&PyDec_Type, pylong, context)
|
|
#define PyDec_FromLongExact(pylong, context) \
|
|
PyDecType_FromLongExact(&PyDec_Type, pylong, context)
|
|
|
|
#define PyDec_FromFloat(pyfloat, context) \
|
|
PyDecType_FromFloat(&PyDec_Type, pyfloat, context)
|
|
#define PyDec_FromFloatExact(pyfloat, context) \
|
|
PyDecType_FromFloatExact(&PyDec_Type, pyfloat, context)
|
|
|
|
#define PyDec_FromSequence(sequence, context) \
|
|
PyDecType_FromSequence(&PyDec_Type, sequence, context)
|
|
#define PyDec_FromSequenceExact(sequence, context) \
|
|
PyDecType_FromSequenceExact(&PyDec_Type, sequence, context)
|
|
|
|
/* class method */
|
|
static PyObject *
|
|
dec_from_float(PyObject *type, PyObject *pyfloat)
|
|
{
|
|
PyObject *context;
|
|
PyObject *result;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
result = PyDecType_FromFloatExact(&PyDec_Type, pyfloat, context);
|
|
if (type != (PyObject *)&PyDec_Type && result != NULL) {
|
|
Py_SETREF(result, PyObject_CallFunctionObjArgs(type, result, NULL));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* create_decimal_from_float */
|
|
static PyObject *
|
|
ctx_from_float(PyObject *context, PyObject *v)
|
|
{
|
|
return PyDec_FromFloat(v, context);
|
|
}
|
|
|
|
/* Apply the context to the input operand. Return a new PyDecObject. */
|
|
static PyObject *
|
|
dec_apply(PyObject *v, PyObject *context)
|
|
{
|
|
PyObject *result;
|
|
uint32_t status = 0;
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qcopy(MPD(result), MPD(v), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qfinalize(MPD(result), CTX(context), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* 'v' can have any type accepted by the Decimal constructor. Attempt
|
|
an exact conversion. If the result does not meet the restrictions
|
|
for an mpd_t, fail with InvalidOperation. */
|
|
static PyObject *
|
|
PyDecType_FromObjectExact(PyTypeObject *type, PyObject *v, PyObject *context)
|
|
{
|
|
if (v == NULL) {
|
|
return PyDecType_FromSsizeExact(type, 0, context);
|
|
}
|
|
else if (PyDec_Check(v)) {
|
|
return PyDecType_FromDecimalExact(type, v, context);
|
|
}
|
|
else if (PyUnicode_Check(v)) {
|
|
return PyDecType_FromUnicodeExactWS(type, v, context);
|
|
}
|
|
else if (PyLong_Check(v)) {
|
|
return PyDecType_FromLongExact(type, v, context);
|
|
}
|
|
else if (PyTuple_Check(v) || PyList_Check(v)) {
|
|
return PyDecType_FromSequenceExact(type, v, context);
|
|
}
|
|
else if (PyFloat_Check(v)) {
|
|
if (dec_addstatus(context, MPD_Float_operation)) {
|
|
return NULL;
|
|
}
|
|
return PyDecType_FromFloatExact(type, v, context);
|
|
}
|
|
else {
|
|
PyErr_Format(PyExc_TypeError,
|
|
"conversion from %s to Decimal is not supported",
|
|
v->ob_type->tp_name);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* The context is used during conversion. This function is the
|
|
equivalent of context.create_decimal(). */
|
|
static PyObject *
|
|
PyDec_FromObject(PyObject *v, PyObject *context)
|
|
{
|
|
if (v == NULL) {
|
|
return PyDec_FromSsize(0, context);
|
|
}
|
|
else if (PyDec_Check(v)) {
|
|
mpd_context_t *ctx = CTX(context);
|
|
if (mpd_isnan(MPD(v)) &&
|
|
MPD(v)->digits > ctx->prec - ctx->clamp) {
|
|
/* Special case: too many NaN payload digits */
|
|
PyObject *result;
|
|
if (dec_addstatus(context, MPD_Conversion_syntax)) {
|
|
return NULL;
|
|
}
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
mpd_setspecial(MPD(result), MPD_POS, MPD_NAN);
|
|
return result;
|
|
}
|
|
return dec_apply(v, context);
|
|
}
|
|
else if (PyUnicode_Check(v)) {
|
|
return PyDec_FromUnicode(v, context);
|
|
}
|
|
else if (PyLong_Check(v)) {
|
|
return PyDec_FromLong(v, context);
|
|
}
|
|
else if (PyTuple_Check(v) || PyList_Check(v)) {
|
|
return PyDec_FromSequence(v, context);
|
|
}
|
|
else if (PyFloat_Check(v)) {
|
|
if (dec_addstatus(context, MPD_Float_operation)) {
|
|
return NULL;
|
|
}
|
|
return PyDec_FromFloat(v, context);
|
|
}
|
|
else {
|
|
PyErr_Format(PyExc_TypeError,
|
|
"conversion from %s to Decimal is not supported",
|
|
v->ob_type->tp_name);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static PyObject *
|
|
dec_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"value", "context", NULL};
|
|
PyObject *v = NULL;
|
|
PyObject *context = Py_None;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
|
|
&v, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
|
|
return PyDecType_FromObjectExact(type, v, context);
|
|
}
|
|
|
|
static PyObject *
|
|
ctx_create_decimal(PyObject *context, PyObject *args)
|
|
{
|
|
PyObject *v = NULL;
|
|
|
|
if (!PyArg_ParseTuple(args, "|O", &v)) {
|
|
return NULL;
|
|
}
|
|
|
|
return PyDec_FromObject(v, context);
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Implicit conversions to Decimal */
|
|
/******************************************************************************/
|
|
|
|
/* Try to convert PyObject v to a new PyDecObject conv. If the conversion
|
|
fails, set conv to NULL (exception is set). If the conversion is not
|
|
implemented, set conv to Py_NotImplemented. */
|
|
#define NOT_IMPL 0
|
|
#define TYPE_ERR 1
|
|
Py_LOCAL_INLINE(int)
|
|
convert_op(int type_err, PyObject **conv, PyObject *v, PyObject *context)
|
|
{
|
|
|
|
if (PyDec_Check(v)) {
|
|
*conv = v;
|
|
Py_INCREF(v);
|
|
return 1;
|
|
}
|
|
if (PyLong_Check(v)) {
|
|
*conv = PyDec_FromLongExact(v, context);
|
|
if (*conv == NULL) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (type_err) {
|
|
PyErr_Format(PyExc_TypeError,
|
|
"conversion from %s to Decimal is not supported",
|
|
v->ob_type->tp_name);
|
|
}
|
|
else {
|
|
Py_INCREF(Py_NotImplemented);
|
|
*conv = Py_NotImplemented;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Return NotImplemented for unsupported types. */
|
|
#define CONVERT_OP(a, v, context) \
|
|
if (!convert_op(NOT_IMPL, a, v, context)) { \
|
|
return *(a); \
|
|
}
|
|
|
|
#define CONVERT_BINOP(a, b, v, w, context) \
|
|
if (!convert_op(NOT_IMPL, a, v, context)) { \
|
|
return *(a); \
|
|
} \
|
|
if (!convert_op(NOT_IMPL, b, w, context)) { \
|
|
Py_DECREF(*(a)); \
|
|
return *(b); \
|
|
}
|
|
|
|
#define CONVERT_TERNOP(a, b, c, v, w, x, context) \
|
|
if (!convert_op(NOT_IMPL, a, v, context)) { \
|
|
return *(a); \
|
|
} \
|
|
if (!convert_op(NOT_IMPL, b, w, context)) { \
|
|
Py_DECREF(*(a)); \
|
|
return *(b); \
|
|
} \
|
|
if (!convert_op(NOT_IMPL, c, x, context)) { \
|
|
Py_DECREF(*(a)); \
|
|
Py_DECREF(*(b)); \
|
|
return *(c); \
|
|
}
|
|
|
|
/* Raise TypeError for unsupported types. */
|
|
#define CONVERT_OP_RAISE(a, v, context) \
|
|
if (!convert_op(TYPE_ERR, a, v, context)) { \
|
|
return NULL; \
|
|
}
|
|
|
|
#define CONVERT_BINOP_RAISE(a, b, v, w, context) \
|
|
if (!convert_op(TYPE_ERR, a, v, context)) { \
|
|
return NULL; \
|
|
} \
|
|
if (!convert_op(TYPE_ERR, b, w, context)) { \
|
|
Py_DECREF(*(a)); \
|
|
return NULL; \
|
|
}
|
|
|
|
#define CONVERT_TERNOP_RAISE(a, b, c, v, w, x, context) \
|
|
if (!convert_op(TYPE_ERR, a, v, context)) { \
|
|
return NULL; \
|
|
} \
|
|
if (!convert_op(TYPE_ERR, b, w, context)) { \
|
|
Py_DECREF(*(a)); \
|
|
return NULL; \
|
|
} \
|
|
if (!convert_op(TYPE_ERR, c, x, context)) { \
|
|
Py_DECREF(*(a)); \
|
|
Py_DECREF(*(b)); \
|
|
return NULL; \
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Implicit conversions to Decimal for comparison */
|
|
/******************************************************************************/
|
|
|
|
/* Convert rationals for comparison */
|
|
static PyObject *Rational = NULL;
|
|
static PyObject *
|
|
multiply_by_denominator(PyObject *v, PyObject *r, PyObject *context)
|
|
{
|
|
PyObject *result;
|
|
PyObject *tmp = NULL;
|
|
PyObject *denom = NULL;
|
|
uint32_t status = 0;
|
|
mpd_context_t maxctx;
|
|
mpd_ssize_t exp;
|
|
mpd_t *vv;
|
|
|
|
/* v is not special, r is a rational */
|
|
tmp = PyObject_GetAttrString(r, "denominator");
|
|
if (tmp == NULL) {
|
|
return NULL;
|
|
}
|
|
denom = PyDec_FromLongExact(tmp, context);
|
|
Py_DECREF(tmp);
|
|
if (denom == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
vv = mpd_qncopy(MPD(v));
|
|
if (vv == NULL) {
|
|
Py_DECREF(denom);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(denom);
|
|
mpd_del(vv);
|
|
return NULL;
|
|
}
|
|
|
|
mpd_maxcontext(&maxctx);
|
|
/* Prevent Overflow in the following multiplication. The result of
|
|
the multiplication is only used in mpd_qcmp, which can handle
|
|
values that are technically out of bounds, like (for 32-bit)
|
|
99999999999999999999...99999999e+425000000. */
|
|
exp = vv->exp;
|
|
vv->exp = 0;
|
|
mpd_qmul(MPD(result), vv, MPD(denom), &maxctx, &status);
|
|
MPD(result)->exp = exp;
|
|
|
|
Py_DECREF(denom);
|
|
mpd_del(vv);
|
|
/* If any status has been accumulated during the multiplication,
|
|
the result is invalid. This is very unlikely, since even the
|
|
32-bit version supports 425000000 digits. */
|
|
if (status) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"exact conversion for comparison failed");
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
numerator_as_decimal(PyObject *r, PyObject *context)
|
|
{
|
|
PyObject *tmp, *num;
|
|
|
|
tmp = PyObject_GetAttrString(r, "numerator");
|
|
if (tmp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
num = PyDec_FromLongExact(tmp, context);
|
|
Py_DECREF(tmp);
|
|
return num;
|
|
}
|
|
|
|
/* Convert v and w for comparison. v is a Decimal. If w is a Rational, both
|
|
v and w have to be transformed. Return 1 for success, with new references
|
|
to the converted objects in vcmp and wcmp. Return 0 for failure. In that
|
|
case wcmp is either NULL or Py_NotImplemented (new reference) and vcmp
|
|
is undefined. */
|
|
static int
|
|
convert_op_cmp(PyObject **vcmp, PyObject **wcmp, PyObject *v, PyObject *w,
|
|
int op, PyObject *context)
|
|
{
|
|
mpd_context_t *ctx = CTX(context);
|
|
|
|
*vcmp = v;
|
|
|
|
if (PyDec_Check(w)) {
|
|
Py_INCREF(w);
|
|
*wcmp = w;
|
|
}
|
|
else if (PyLong_Check(w)) {
|
|
*wcmp = PyDec_FromLongExact(w, context);
|
|
}
|
|
else if (PyFloat_Check(w)) {
|
|
if (op != Py_EQ && op != Py_NE &&
|
|
dec_addstatus(context, MPD_Float_operation)) {
|
|
*wcmp = NULL;
|
|
}
|
|
else {
|
|
ctx->status |= MPD_Float_operation;
|
|
*wcmp = PyDec_FromFloatExact(w, context);
|
|
}
|
|
}
|
|
else if (PyComplex_Check(w) && (op == Py_EQ || op == Py_NE)) {
|
|
Py_complex c = PyComplex_AsCComplex(w);
|
|
if (c.real == -1.0 && PyErr_Occurred()) {
|
|
*wcmp = NULL;
|
|
}
|
|
else if (c.imag == 0.0) {
|
|
PyObject *tmp = PyFloat_FromDouble(c.real);
|
|
if (tmp == NULL) {
|
|
*wcmp = NULL;
|
|
}
|
|
else {
|
|
ctx->status |= MPD_Float_operation;
|
|
*wcmp = PyDec_FromFloatExact(tmp, context);
|
|
Py_DECREF(tmp);
|
|
}
|
|
}
|
|
else {
|
|
Py_INCREF(Py_NotImplemented);
|
|
*wcmp = Py_NotImplemented;
|
|
}
|
|
}
|
|
else {
|
|
int is_rational = PyObject_IsInstance(w, Rational);
|
|
if (is_rational < 0) {
|
|
*wcmp = NULL;
|
|
}
|
|
else if (is_rational > 0) {
|
|
*wcmp = numerator_as_decimal(w, context);
|
|
if (*wcmp && !mpd_isspecial(MPD(v))) {
|
|
*vcmp = multiply_by_denominator(v, w, context);
|
|
if (*vcmp == NULL) {
|
|
Py_CLEAR(*wcmp);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Py_INCREF(Py_NotImplemented);
|
|
*wcmp = Py_NotImplemented;
|
|
}
|
|
}
|
|
|
|
if (*wcmp == NULL || *wcmp == Py_NotImplemented) {
|
|
return 0;
|
|
}
|
|
if (*vcmp == v) {
|
|
Py_INCREF(v);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#define CONVERT_BINOP_CMP(vcmp, wcmp, v, w, op, ctx) \
|
|
if (!convert_op_cmp(vcmp, wcmp, v, w, op, ctx)) { \
|
|
return *(wcmp); \
|
|
} \
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Conversions from decimal */
|
|
/******************************************************************************/
|
|
|
|
static PyObject *
|
|
unicode_fromascii(const char *s, Py_ssize_t size)
|
|
{
|
|
PyObject *res;
|
|
|
|
res = PyUnicode_New(size, 127);
|
|
if (res == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(PyUnicode_1BYTE_DATA(res), s, size);
|
|
return res;
|
|
}
|
|
|
|
/* PyDecObject as a string. The default module context is only used for
|
|
the value of 'capitals'. */
|
|
static PyObject *
|
|
dec_str(PyObject *dec)
|
|
{
|
|
PyObject *res, *context;
|
|
mpd_ssize_t size;
|
|
char *cp;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
size = mpd_to_sci_size(&cp, MPD(dec), CtxCaps(context));
|
|
if (size < 0) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
res = unicode_fromascii(cp, size);
|
|
mpd_free(cp);
|
|
return res;
|
|
}
|
|
|
|
/* Representation of a PyDecObject. */
|
|
static PyObject *
|
|
dec_repr(PyObject *dec)
|
|
{
|
|
PyObject *res, *context;
|
|
char *cp;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
cp = mpd_to_sci(MPD(dec), CtxCaps(context));
|
|
if (cp == NULL) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
res = PyUnicode_FromFormat("Decimal('%s')", cp);
|
|
mpd_free(cp);
|
|
return res;
|
|
}
|
|
|
|
/* Return a duplicate of src, copy embedded null characters. */
|
|
static char *
|
|
dec_strdup(const char *src, Py_ssize_t size)
|
|
{
|
|
char *dest = PyMem_Malloc(size+1);
|
|
if (dest == NULL) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(dest, src, size);
|
|
dest[size] = '\0';
|
|
return dest;
|
|
}
|
|
|
|
static void
|
|
dec_replace_fillchar(char *dest)
|
|
{
|
|
while (*dest != '\0') {
|
|
if (*dest == '\xff') *dest = '\0';
|
|
dest++;
|
|
}
|
|
}
|
|
|
|
/* Convert decimal_point or thousands_sep, which may be multibyte or in
|
|
the range [128, 255], to a UTF8 string. */
|
|
static PyObject *
|
|
dotsep_as_utf8(const char *s)
|
|
{
|
|
PyObject *utf8;
|
|
PyObject *tmp;
|
|
wchar_t buf[2];
|
|
size_t n;
|
|
|
|
n = mbstowcs(buf, s, 2);
|
|
if (n != 1) { /* Issue #7442 */
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"invalid decimal point or unsupported "
|
|
"combination of LC_CTYPE and LC_NUMERIC");
|
|
return NULL;
|
|
}
|
|
tmp = PyUnicode_FromWideChar(buf, n);
|
|
if (tmp == NULL) {
|
|
return NULL;
|
|
}
|
|
utf8 = PyUnicode_AsUTF8String(tmp);
|
|
Py_DECREF(tmp);
|
|
return utf8;
|
|
}
|
|
|
|
/* Formatted representation of a PyDecObject. */
|
|
static PyObject *
|
|
dec_format(PyObject *dec, PyObject *args)
|
|
{
|
|
PyObject *result = NULL;
|
|
PyObject *override = NULL;
|
|
PyObject *dot = NULL;
|
|
PyObject *sep = NULL;
|
|
PyObject *grouping = NULL;
|
|
PyObject *fmtarg;
|
|
PyObject *context;
|
|
mpd_spec_t spec;
|
|
char *fmt;
|
|
char *decstring = NULL;
|
|
uint32_t status = 0;
|
|
int replace_fillchar = 0;
|
|
Py_ssize_t size;
|
|
|
|
|
|
CURRENT_CONTEXT(context);
|
|
if (!PyArg_ParseTuple(args, "O|O", &fmtarg, &override)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (PyUnicode_Check(fmtarg)) {
|
|
fmt = PyUnicode_AsUTF8AndSize(fmtarg, &size);
|
|
if (fmt == NULL) {
|
|
return NULL;
|
|
}
|
|
if (size > 0 && fmt[0] == '\0') {
|
|
/* NUL fill character: must be replaced with a valid UTF-8 char
|
|
before calling mpd_parse_fmt_str(). */
|
|
replace_fillchar = 1;
|
|
fmt = dec_strdup(fmt, size);
|
|
if (fmt == NULL) {
|
|
return NULL;
|
|
}
|
|
fmt[0] = '_';
|
|
}
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"format arg must be str");
|
|
return NULL;
|
|
}
|
|
|
|
if (!mpd_parse_fmt_str(&spec, fmt, CtxCaps(context))) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"invalid format string");
|
|
goto finish;
|
|
}
|
|
if (replace_fillchar) {
|
|
/* In order to avoid clobbering parts of UTF-8 thousands separators or
|
|
decimal points when the substitution is reversed later, the actual
|
|
placeholder must be an invalid UTF-8 byte. */
|
|
spec.fill[0] = '\xff';
|
|
spec.fill[1] = '\0';
|
|
}
|
|
|
|
if (override) {
|
|
/* Values for decimal_point, thousands_sep and grouping can
|
|
be explicitly specified in the override dict. These values
|
|
take precedence over the values obtained from localeconv()
|
|
in mpd_parse_fmt_str(). The feature is not documented and
|
|
is only used in test_decimal. */
|
|
if (!PyDict_Check(override)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"optional argument must be a dict");
|
|
goto finish;
|
|
}
|
|
if ((dot = PyDict_GetItemString(override, "decimal_point"))) {
|
|
if ((dot = PyUnicode_AsUTF8String(dot)) == NULL) {
|
|
goto finish;
|
|
}
|
|
spec.dot = PyBytes_AS_STRING(dot);
|
|
}
|
|
if ((sep = PyDict_GetItemString(override, "thousands_sep"))) {
|
|
if ((sep = PyUnicode_AsUTF8String(sep)) == NULL) {
|
|
goto finish;
|
|
}
|
|
spec.sep = PyBytes_AS_STRING(sep);
|
|
}
|
|
if ((grouping = PyDict_GetItemString(override, "grouping"))) {
|
|
if ((grouping = PyUnicode_AsUTF8String(grouping)) == NULL) {
|
|
goto finish;
|
|
}
|
|
spec.grouping = PyBytes_AS_STRING(grouping);
|
|
}
|
|
if (mpd_validate_lconv(&spec) < 0) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"invalid override dict");
|
|
goto finish;
|
|
}
|
|
}
|
|
else {
|
|
size_t n = strlen(spec.dot);
|
|
if (n > 1 || (n == 1 && !isascii((uchar)spec.dot[0]))) {
|
|
/* fix locale dependent non-ascii characters */
|
|
dot = dotsep_as_utf8(spec.dot);
|
|
if (dot == NULL) {
|
|
goto finish;
|
|
}
|
|
spec.dot = PyBytes_AS_STRING(dot);
|
|
}
|
|
n = strlen(spec.sep);
|
|
if (n > 1 || (n == 1 && !isascii((uchar)spec.sep[0]))) {
|
|
/* fix locale dependent non-ascii characters */
|
|
sep = dotsep_as_utf8(spec.sep);
|
|
if (sep == NULL) {
|
|
goto finish;
|
|
}
|
|
spec.sep = PyBytes_AS_STRING(sep);
|
|
}
|
|
}
|
|
|
|
|
|
decstring = mpd_qformat_spec(MPD(dec), &spec, CTX(context), &status);
|
|
if (decstring == NULL) {
|
|
if (status & MPD_Malloc_error) {
|
|
PyErr_NoMemory();
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"format specification exceeds internal limits of _decimal");
|
|
}
|
|
goto finish;
|
|
}
|
|
size = strlen(decstring);
|
|
if (replace_fillchar) {
|
|
dec_replace_fillchar(decstring);
|
|
}
|
|
|
|
result = PyUnicode_DecodeUTF8(decstring, size, NULL);
|
|
|
|
|
|
finish:
|
|
Py_XDECREF(grouping);
|
|
Py_XDECREF(sep);
|
|
Py_XDECREF(dot);
|
|
if (replace_fillchar) PyMem_Free(fmt);
|
|
if (decstring) mpd_free(decstring);
|
|
return result;
|
|
}
|
|
|
|
/* Return a PyLongObject from a PyDecObject, using the specified rounding
|
|
* mode. The context precision is not observed. */
|
|
static PyObject *
|
|
dec_as_long(PyObject *dec, PyObject *context, int round)
|
|
{
|
|
PyLongObject *pylong;
|
|
digit *ob_digit;
|
|
size_t n;
|
|
Py_ssize_t i;
|
|
mpd_t *x;
|
|
mpd_context_t workctx;
|
|
uint32_t status = 0;
|
|
|
|
if (mpd_isspecial(MPD(dec))) {
|
|
if (mpd_isnan(MPD(dec))) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"cannot convert NaN to integer");
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_OverflowError,
|
|
"cannot convert Infinity to integer");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
x = mpd_qnew();
|
|
if (x == NULL) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
workctx = *CTX(context);
|
|
workctx.round = round;
|
|
mpd_qround_to_int(x, MPD(dec), &workctx, &status);
|
|
if (dec_addstatus(context, status)) {
|
|
mpd_del(x);
|
|
return NULL;
|
|
}
|
|
|
|
status = 0;
|
|
ob_digit = NULL;
|
|
#if PYLONG_BITS_IN_DIGIT == 30
|
|
n = mpd_qexport_u32(&ob_digit, 0, PyLong_BASE, x, &status);
|
|
#elif PYLONG_BITS_IN_DIGIT == 15
|
|
n = mpd_qexport_u16(&ob_digit, 0, PyLong_BASE, x, &status);
|
|
#else
|
|
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
|
|
#endif
|
|
|
|
if (n == SIZE_MAX) {
|
|
PyErr_NoMemory();
|
|
mpd_del(x);
|
|
return NULL;
|
|
}
|
|
|
|
assert(n > 0);
|
|
pylong = _PyLong_New(n);
|
|
if (pylong == NULL) {
|
|
mpd_free(ob_digit);
|
|
mpd_del(x);
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(pylong->ob_digit, ob_digit, n * sizeof(digit));
|
|
mpd_free(ob_digit);
|
|
|
|
i = n;
|
|
while ((i > 0) && (pylong->ob_digit[i-1] == 0)) {
|
|
i--;
|
|
}
|
|
|
|
Py_SIZE(pylong) = i;
|
|
if (mpd_isnegative(x) && !mpd_iszero(x)) {
|
|
Py_SIZE(pylong) = -i;
|
|
}
|
|
|
|
mpd_del(x);
|
|
return (PyObject *) pylong;
|
|
}
|
|
|
|
/* Convert a Decimal to its exact integer ratio representation. */
|
|
static PyObject *
|
|
dec_as_integer_ratio(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *numerator = NULL;
|
|
PyObject *denominator = NULL;
|
|
PyObject *exponent = NULL;
|
|
PyObject *result = NULL;
|
|
PyObject *tmp;
|
|
mpd_ssize_t exp;
|
|
PyObject *context;
|
|
uint32_t status = 0;
|
|
|
|
if (mpd_isspecial(MPD(self))) {
|
|
if (mpd_isnan(MPD(self))) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"cannot convert NaN to integer ratio");
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_OverflowError,
|
|
"cannot convert Infinity to integer ratio");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CURRENT_CONTEXT(context);
|
|
|
|
tmp = dec_alloc();
|
|
if (tmp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!mpd_qcopy(MPD(tmp), MPD(self), &status)) {
|
|
Py_DECREF(tmp);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
exp = mpd_iszero(MPD(tmp)) ? 0 : MPD(tmp)->exp;
|
|
MPD(tmp)->exp = 0;
|
|
|
|
/* context and rounding are unused here: the conversion is exact */
|
|
numerator = dec_as_long(tmp, context, MPD_ROUND_FLOOR);
|
|
Py_DECREF(tmp);
|
|
if (numerator == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
exponent = PyLong_FromSsize_t(exp < 0 ? -exp : exp);
|
|
if (exponent == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
tmp = PyLong_FromLong(10);
|
|
if (tmp == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
Py_SETREF(exponent, _py_long_power(tmp, exponent, Py_None));
|
|
Py_DECREF(tmp);
|
|
if (exponent == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
if (exp >= 0) {
|
|
Py_SETREF(numerator, _py_long_multiply(numerator, exponent));
|
|
if (numerator == NULL) {
|
|
goto error;
|
|
}
|
|
denominator = PyLong_FromLong(1);
|
|
if (denominator == NULL) {
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
denominator = exponent;
|
|
exponent = NULL;
|
|
tmp = _PyLong_GCD(numerator, denominator);
|
|
if (tmp == NULL) {
|
|
goto error;
|
|
}
|
|
Py_SETREF(numerator, _py_long_floor_divide(numerator, tmp));
|
|
Py_SETREF(denominator, _py_long_floor_divide(denominator, tmp));
|
|
Py_DECREF(tmp);
|
|
if (numerator == NULL || denominator == NULL) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
result = PyTuple_Pack(2, numerator, denominator);
|
|
|
|
|
|
error:
|
|
Py_XDECREF(exponent);
|
|
Py_XDECREF(denominator);
|
|
Py_XDECREF(numerator);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"rounding", "context", NULL};
|
|
PyObject *result;
|
|
PyObject *rounding = Py_None;
|
|
PyObject *context = Py_None;
|
|
uint32_t status = 0;
|
|
mpd_context_t workctx;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
|
|
&rounding, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
|
|
workctx = *CTX(context);
|
|
if (rounding != Py_None) {
|
|
int round = getround(rounding);
|
|
if (round < 0) {
|
|
return NULL;
|
|
}
|
|
if (!mpd_qsetround(&workctx, round)) {
|
|
INTERNAL_ERROR_PTR("PyDec_ToIntegralValue"); /* GCOV_NOT_REACHED */
|
|
}
|
|
}
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qround_to_int(MPD(result), MPD(dec), &workctx, &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"rounding", "context", NULL};
|
|
PyObject *result;
|
|
PyObject *rounding = Py_None;
|
|
PyObject *context = Py_None;
|
|
uint32_t status = 0;
|
|
mpd_context_t workctx;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist,
|
|
&rounding, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
|
|
workctx = *CTX(context);
|
|
if (rounding != Py_None) {
|
|
int round = getround(rounding);
|
|
if (round < 0) {
|
|
return NULL;
|
|
}
|
|
if (!mpd_qsetround(&workctx, round)) {
|
|
INTERNAL_ERROR_PTR("PyDec_ToIntegralExact"); /* GCOV_NOT_REACHED */
|
|
}
|
|
}
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qround_to_intx(MPD(result), MPD(dec), &workctx, &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
PyDec_AsFloat(PyObject *dec)
|
|
{
|
|
PyObject *f, *s;
|
|
|
|
if (mpd_isnan(MPD(dec))) {
|
|
if (mpd_issnan(MPD(dec))) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"cannot convert signaling NaN to float");
|
|
return NULL;
|
|
}
|
|
if (mpd_isnegative(MPD(dec))) {
|
|
s = PyUnicode_FromString("-nan");
|
|
}
|
|
else {
|
|
s = PyUnicode_FromString("nan");
|
|
}
|
|
}
|
|
else {
|
|
s = dec_str(dec);
|
|
}
|
|
|
|
if (s == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
f = PyFloat_FromString(s);
|
|
Py_DECREF(s);
|
|
|
|
return f;
|
|
}
|
|
|
|
static PyObject *
|
|
PyDec_Round(PyObject *dec, PyObject *args)
|
|
{
|
|
PyObject *result;
|
|
PyObject *x = NULL;
|
|
uint32_t status = 0;
|
|
PyObject *context;
|
|
|
|
|
|
CURRENT_CONTEXT(context);
|
|
if (!PyArg_ParseTuple(args, "|O", &x)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (x) {
|
|
mpd_uint_t dq[1] = {1};
|
|
mpd_t q = {MPD_STATIC|MPD_CONST_DATA,0,1,1,1,dq};
|
|
mpd_ssize_t y;
|
|
|
|
if (!PyLong_Check(x)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"optional arg must be an integer");
|
|
return NULL;
|
|
}
|
|
|
|
y = PyLong_AsSsize_t(x);
|
|
if (y == -1 && PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
q.exp = (y == MPD_SSIZE_MIN) ? MPD_SSIZE_MAX : -y;
|
|
mpd_qquantize(MPD(result), MPD(dec), &q, CTX(context), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else {
|
|
return dec_as_long(dec, context, MPD_ROUND_HALF_EVEN);
|
|
}
|
|
}
|
|
|
|
static PyTypeObject *DecimalTuple = NULL;
|
|
/* Return the DecimalTuple representation of a PyDecObject. */
|
|
static PyObject *
|
|
PyDec_AsTuple(PyObject *dec, PyObject *dummy)
|
|
{
|
|
PyObject *result = NULL;
|
|
PyObject *sign = NULL;
|
|
PyObject *coeff = NULL;
|
|
PyObject *expt = NULL;
|
|
PyObject *tmp = NULL;
|
|
mpd_t *x = NULL;
|
|
char *intstring = NULL;
|
|
Py_ssize_t intlen, i;
|
|
|
|
|
|
x = mpd_qncopy(MPD(dec));
|
|
if (x == NULL) {
|
|
PyErr_NoMemory();
|
|
goto out;
|
|
}
|
|
|
|
sign = PyLong_FromUnsignedLong(mpd_sign(MPD(dec)));
|
|
if (sign == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
if (mpd_isinfinite(x)) {
|
|
expt = PyUnicode_FromString("F");
|
|
if (expt == NULL) {
|
|
goto out;
|
|
}
|
|
/* decimal.py has non-compliant infinity payloads. */
|
|
coeff = Py_BuildValue("(i)", 0);
|
|
if (coeff == NULL) {
|
|
goto out;
|
|
}
|
|
}
|
|
else {
|
|
if (mpd_isnan(x)) {
|
|
expt = PyUnicode_FromString(mpd_isqnan(x)?"n":"N");
|
|
}
|
|
else {
|
|
expt = PyLong_FromSsize_t(MPD(dec)->exp);
|
|
}
|
|
if (expt == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
/* coefficient is defined */
|
|
if (x->len > 0) {
|
|
|
|
/* make an integer */
|
|
x->exp = 0;
|
|
/* clear NaN and sign */
|
|
mpd_clear_flags(x);
|
|
intstring = mpd_to_sci(x, 1);
|
|
if (intstring == NULL) {
|
|
PyErr_NoMemory();
|
|
goto out;
|
|
}
|
|
|
|
intlen = strlen(intstring);
|
|
coeff = PyTuple_New(intlen);
|
|
if (coeff == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < intlen; i++) {
|
|
tmp = PyLong_FromLong(intstring[i]-'0');
|
|
if (tmp == NULL) {
|
|
goto out;
|
|
}
|
|
PyTuple_SET_ITEM(coeff, i, tmp);
|
|
}
|
|
}
|
|
else {
|
|
coeff = PyTuple_New(0);
|
|
if (coeff == NULL) {
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
result = PyObject_CallFunctionObjArgs((PyObject *)DecimalTuple,
|
|
sign, coeff, expt, NULL);
|
|
|
|
out:
|
|
if (x) mpd_del(x);
|
|
if (intstring) mpd_free(intstring);
|
|
Py_XDECREF(sign);
|
|
Py_XDECREF(coeff);
|
|
Py_XDECREF(expt);
|
|
return result;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Macros for converting mpdecimal functions to Decimal methods */
|
|
/******************************************************************************/
|
|
|
|
static PyObject *
|
|
_Dec_UnaryNumberMethod(PyObject *self,
|
|
void mpdfunc(mpd_t *, const mpd_t *,
|
|
const mpd_context_t *,
|
|
uint32_t *))
|
|
{
|
|
PyObject *result;
|
|
PyObject *context;
|
|
uint32_t status = 0;
|
|
CURRENT_CONTEXT(context);
|
|
if ((result = dec_alloc()) == NULL) {
|
|
return NULL;
|
|
}
|
|
mpdfunc(MPD(result), MPD(self), CTX(context), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Unary number method that uses the default module context. */
|
|
#define Dec_UnaryNumberMethod(MPDFUNC) \
|
|
static PyObject * \
|
|
nm_##MPDFUNC(PyObject *self) \
|
|
{ \
|
|
return _Dec_UnaryNumberMethod(self, MPDFUNC); \
|
|
}
|
|
|
|
static PyObject *
|
|
_Dec_BinaryNumberMethod(PyObject *self, PyObject *other,
|
|
void mpdfunc(mpd_t *, const mpd_t *, const mpd_t *,
|
|
const mpd_context_t *, uint32_t *))
|
|
{
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
PyObject *context;
|
|
uint32_t status = 0;
|
|
CURRENT_CONTEXT(context) ;
|
|
CONVERT_BINOP(&a, &b, self, other, context);
|
|
if ((result = dec_alloc()) == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
mpdfunc(MPD(result), MPD(a), MPD(b), CTX(context), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Binary number method that uses default module context. */
|
|
#define Dec_BinaryNumberMethod(MPDFUNC) \
|
|
static PyObject * \
|
|
nm_##MPDFUNC(PyObject *self, PyObject *other) \
|
|
{ \
|
|
return _Dec_BinaryNumberMethod(self, other, MPDFUNC); \
|
|
}
|
|
|
|
/* Boolean function without a context arg. */
|
|
#define Dec_BoolFunc(MPDFUNC) \
|
|
static PyObject * \
|
|
dec_##MPDFUNC(PyObject *self, PyObject *dummy) \
|
|
{ \
|
|
return MPDFUNC(MPD(self)) ? incr_true() : incr_false(); \
|
|
}
|
|
|
|
/* Boolean function with an optional context arg. */
|
|
#define Dec_BoolFuncVA(MPDFUNC) \
|
|
static PyObject * \
|
|
dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
|
|
{ \
|
|
static char *kwlist[] = {"context", NULL}; \
|
|
PyObject *context = Py_None; \
|
|
\
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \
|
|
&context)) { \
|
|
return NULL; \
|
|
} \
|
|
CONTEXT_CHECK_VA(context); \
|
|
\
|
|
return MPDFUNC(MPD(self), CTX(context)) ? incr_true() : incr_false(); \
|
|
}
|
|
|
|
static PyObject *
|
|
_Dec_UnaryFuncVA(PyObject *self, PyObject *args, PyObject *kwds,
|
|
void mpdfunc(mpd_t *, const mpd_t *, const mpd_context_t *,
|
|
uint32_t *))
|
|
{
|
|
static char *kwlist[] = {"context", NULL};
|
|
PyObject *result;
|
|
PyObject *context = Py_None;
|
|
uint32_t status = 0;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
|
|
&context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
if ((result = dec_alloc()) == NULL) {
|
|
return NULL;
|
|
}
|
|
mpdfunc(MPD(result), MPD(self), CTX(context), &status);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Unary function with an optional context arg. */
|
|
#define Dec_UnaryFuncVA(MPDFUNC) \
|
|
static PyObject * \
|
|
dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
|
|
{ \
|
|
return _Dec_UnaryFuncVA(self, args, kwds, MPDFUNC); \
|
|
}
|
|
|
|
static PyObject *
|
|
_Dec_BinaryFuncVA(PyObject *self, PyObject *args, PyObject *kwds,
|
|
void mpdfunc(mpd_t *, const mpd_t *, const mpd_t *,
|
|
const mpd_context_t *, uint32_t *))
|
|
{
|
|
static char *kwlist[] = {"other", "context", NULL};
|
|
PyObject *other;
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
PyObject *context = Py_None;
|
|
uint32_t status = 0;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
|
|
&other, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
CONVERT_BINOP_RAISE(&a, &b, self, other, context);
|
|
if ((result = dec_alloc()) == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
mpdfunc(MPD(result), MPD(a), MPD(b), CTX(context), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Binary function with an optional context arg. */
|
|
#define Dec_BinaryFuncVA(MPDFUNC) \
|
|
static PyObject * \
|
|
dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
|
|
{ \
|
|
return _Dec_BinaryFuncVA(self, args, kwds, (void *)MPDFUNC); \
|
|
}
|
|
|
|
static PyObject *
|
|
_Dec_BinaryFuncVA_NO_CTX(PyObject *self, PyObject *args, PyObject *kwds,
|
|
int mpdfunc(mpd_t *, const mpd_t *, const mpd_t *))
|
|
{
|
|
static char *kwlist[] = {"other", "context", NULL};
|
|
PyObject *context = Py_None;
|
|
PyObject *other;
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
|
|
&other, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
CONVERT_BINOP_RAISE(&a, &b, self, other, context);
|
|
if ((result = dec_alloc()) == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
mpdfunc(MPD(result), MPD(a), MPD(b));
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return result;
|
|
}
|
|
|
|
/* Binary function with an optional context arg. Actual MPDFUNC does
|
|
NOT take a context. The context is used to record InvalidOperation
|
|
if the second operand cannot be converted exactly. */
|
|
#define Dec_BinaryFuncVA_NO_CTX(MPDFUNC) \
|
|
static PyObject * \
|
|
dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
|
|
{ \
|
|
return _Dec_BinaryFuncVA_NO_CTX(self, args, kwds, MPDFUNC); \
|
|
}
|
|
|
|
/* Ternary function with an optional context arg. */
|
|
#define Dec_TernaryFuncVA(MPDFUNC) \
|
|
static PyObject * \
|
|
dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \
|
|
{ \
|
|
static char *kwlist[] = {"other", "third", "context", NULL}; \
|
|
PyObject *other, *third; \
|
|
PyObject *a, *b, *c; \
|
|
PyObject *result; \
|
|
PyObject *context = Py_None; \
|
|
uint32_t status = 0; \
|
|
\
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, \
|
|
&other, &third, &context)) { \
|
|
return NULL; \
|
|
} \
|
|
CONTEXT_CHECK_VA(context); \
|
|
CONVERT_TERNOP_RAISE(&a, &b, &c, self, other, third, context); \
|
|
\
|
|
if ((result = dec_alloc()) == NULL) { \
|
|
Py_DECREF(a); \
|
|
Py_DECREF(b); \
|
|
Py_DECREF(c); \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
MPDFUNC(MPD(result), MPD(a), MPD(b), MPD(c), CTX(context), &status); \
|
|
Py_DECREF(a); \
|
|
Py_DECREF(b); \
|
|
Py_DECREF(c); \
|
|
if (dec_addstatus(context, status)) { \
|
|
Py_DECREF(result); \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
return result; \
|
|
}
|
|
|
|
|
|
/**********************************************/
|
|
/* Number methods */
|
|
/**********************************************/
|
|
|
|
Dec_UnaryNumberMethod(mpd_qminus)
|
|
Dec_UnaryNumberMethod(mpd_qplus)
|
|
Dec_UnaryNumberMethod(mpd_qabs)
|
|
|
|
Dec_BinaryNumberMethod(mpd_qadd)
|
|
Dec_BinaryNumberMethod(mpd_qsub)
|
|
Dec_BinaryNumberMethod(mpd_qmul)
|
|
Dec_BinaryNumberMethod(mpd_qdiv)
|
|
Dec_BinaryNumberMethod(mpd_qrem)
|
|
Dec_BinaryNumberMethod(mpd_qdivint)
|
|
|
|
static PyObject *
|
|
nm_dec_as_long(PyObject *dec)
|
|
{
|
|
PyObject *context;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
return dec_as_long(dec, context, MPD_ROUND_DOWN);
|
|
}
|
|
|
|
static int
|
|
nm_nonzero(PyObject *v)
|
|
{
|
|
return !mpd_iszero(MPD(v));
|
|
}
|
|
|
|
static PyObject *
|
|
nm_mpd_qdivmod(PyObject *v, PyObject *w)
|
|
{
|
|
PyObject *a, *b;
|
|
PyObject *q, *r;
|
|
PyObject *context;
|
|
uint32_t status = 0;
|
|
PyObject *ret;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
CONVERT_BINOP(&a, &b, v, w, context);
|
|
|
|
q = dec_alloc();
|
|
if (q == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
r = dec_alloc();
|
|
if (r == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
Py_DECREF(q);
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qdivmod(MPD(q), MPD(r), MPD(a), MPD(b), CTX(context), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(r);
|
|
Py_DECREF(q);
|
|
return NULL;
|
|
}
|
|
|
|
ret = Py_BuildValue("(OO)", q, r);
|
|
Py_DECREF(r);
|
|
Py_DECREF(q);
|
|
return ret;
|
|
}
|
|
|
|
static PyObject *
|
|
nm_mpd_qpow(PyObject *base, PyObject *exp, PyObject *mod)
|
|
{
|
|
PyObject *a, *b, *c = NULL;
|
|
PyObject *result;
|
|
PyObject *context;
|
|
uint32_t status = 0;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
CONVERT_BINOP(&a, &b, base, exp, context);
|
|
|
|
if (mod != Py_None) {
|
|
if (!convert_op(NOT_IMPL, &c, mod, context)) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return c;
|
|
}
|
|
}
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
Py_XDECREF(c);
|
|
return NULL;
|
|
}
|
|
|
|
if (c == NULL) {
|
|
mpd_qpow(MPD(result), MPD(a), MPD(b),
|
|
CTX(context), &status);
|
|
}
|
|
else {
|
|
mpd_qpowmod(MPD(result), MPD(a), MPD(b), MPD(c),
|
|
CTX(context), &status);
|
|
Py_DECREF(c);
|
|
}
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Decimal Methods */
|
|
/******************************************************************************/
|
|
|
|
/* Unary arithmetic functions, optional context arg */
|
|
Dec_UnaryFuncVA(mpd_qexp)
|
|
Dec_UnaryFuncVA(mpd_qln)
|
|
Dec_UnaryFuncVA(mpd_qlog10)
|
|
Dec_UnaryFuncVA(mpd_qnext_minus)
|
|
Dec_UnaryFuncVA(mpd_qnext_plus)
|
|
Dec_UnaryFuncVA(mpd_qreduce)
|
|
Dec_UnaryFuncVA(mpd_qsqrt)
|
|
|
|
/* Binary arithmetic functions, optional context arg */
|
|
Dec_BinaryFuncVA(mpd_qcompare)
|
|
Dec_BinaryFuncVA(mpd_qcompare_signal)
|
|
Dec_BinaryFuncVA(mpd_qmax)
|
|
Dec_BinaryFuncVA(mpd_qmax_mag)
|
|
Dec_BinaryFuncVA(mpd_qmin)
|
|
Dec_BinaryFuncVA(mpd_qmin_mag)
|
|
Dec_BinaryFuncVA(mpd_qnext_toward)
|
|
Dec_BinaryFuncVA(mpd_qrem_near)
|
|
|
|
/* Ternary arithmetic functions, optional context arg */
|
|
Dec_TernaryFuncVA(mpd_qfma)
|
|
|
|
/* Boolean functions, no context arg */
|
|
Dec_BoolFunc(mpd_iscanonical)
|
|
Dec_BoolFunc(mpd_isfinite)
|
|
Dec_BoolFunc(mpd_isinfinite)
|
|
Dec_BoolFunc(mpd_isnan)
|
|
Dec_BoolFunc(mpd_isqnan)
|
|
Dec_BoolFunc(mpd_issnan)
|
|
Dec_BoolFunc(mpd_issigned)
|
|
Dec_BoolFunc(mpd_iszero)
|
|
|
|
/* Boolean functions, optional context arg */
|
|
Dec_BoolFuncVA(mpd_isnormal)
|
|
Dec_BoolFuncVA(mpd_issubnormal)
|
|
|
|
/* Unary functions, no context arg */
|
|
static PyObject *
|
|
dec_mpd_adjexp(PyObject *self, PyObject *dummy)
|
|
{
|
|
mpd_ssize_t retval;
|
|
|
|
if (mpd_isspecial(MPD(self))) {
|
|
retval = 0;
|
|
}
|
|
else {
|
|
retval = mpd_adjexp(MPD(self));
|
|
}
|
|
|
|
return PyLong_FromSsize_t(retval);
|
|
}
|
|
|
|
static PyObject *
|
|
dec_canonical(PyObject *self, PyObject *dummy)
|
|
{
|
|
Py_INCREF(self);
|
|
return self;
|
|
}
|
|
|
|
static PyObject *
|
|
dec_conjugate(PyObject *self, PyObject *dummy)
|
|
{
|
|
Py_INCREF(self);
|
|
return self;
|
|
}
|
|
|
|
static PyObject *
|
|
dec_mpd_radix(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *result;
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
_dec_settriple(result, MPD_POS, 10, 0);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
dec_mpd_qcopy_abs(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *result;
|
|
uint32_t status = 0;
|
|
|
|
if ((result = dec_alloc()) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qcopy_abs(MPD(result), MPD(self), &status);
|
|
if (status & MPD_Malloc_error) {
|
|
Py_DECREF(result);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
dec_mpd_qcopy_negate(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *result;
|
|
uint32_t status = 0;
|
|
|
|
if ((result = dec_alloc()) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qcopy_negate(MPD(result), MPD(self), &status);
|
|
if (status & MPD_Malloc_error) {
|
|
Py_DECREF(result);
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Unary functions, optional context arg */
|
|
Dec_UnaryFuncVA(mpd_qinvert)
|
|
Dec_UnaryFuncVA(mpd_qlogb)
|
|
|
|
static PyObject *
|
|
dec_mpd_class(PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"context", NULL};
|
|
PyObject *context = Py_None;
|
|
const char *cp;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
|
|
&context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
|
|
cp = mpd_class(MPD(self), CTX(context));
|
|
return PyUnicode_FromString(cp);
|
|
}
|
|
|
|
static PyObject *
|
|
dec_mpd_to_eng(PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"context", NULL};
|
|
PyObject *result;
|
|
PyObject *context = Py_None;
|
|
mpd_ssize_t size;
|
|
char *s;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist,
|
|
&context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
|
|
size = mpd_to_eng_size(&s, MPD(self), CtxCaps(context));
|
|
if (size < 0) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
|
|
result = unicode_fromascii(s, size);
|
|
mpd_free(s);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Binary functions, optional context arg for conversion errors */
|
|
Dec_BinaryFuncVA_NO_CTX(mpd_compare_total)
|
|
Dec_BinaryFuncVA_NO_CTX(mpd_compare_total_mag)
|
|
|
|
static PyObject *
|
|
dec_mpd_qcopy_sign(PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"other", "context", NULL};
|
|
PyObject *other;
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
PyObject *context = Py_None;
|
|
uint32_t status = 0;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
|
|
&other, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
CONVERT_BINOP_RAISE(&a, &b, self, other, context);
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qcopy_sign(MPD(result), MPD(a), MPD(b), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
dec_mpd_same_quantum(PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"other", "context", NULL};
|
|
PyObject *other;
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
PyObject *context = Py_None;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist,
|
|
&other, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
CONVERT_BINOP_RAISE(&a, &b, self, other, context);
|
|
|
|
result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false();
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Binary functions, optional context arg */
|
|
Dec_BinaryFuncVA(mpd_qand)
|
|
Dec_BinaryFuncVA(mpd_qor)
|
|
Dec_BinaryFuncVA(mpd_qxor)
|
|
|
|
Dec_BinaryFuncVA(mpd_qrotate)
|
|
Dec_BinaryFuncVA(mpd_qscaleb)
|
|
Dec_BinaryFuncVA(mpd_qshift)
|
|
|
|
static PyObject *
|
|
dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"exp", "rounding", "context", NULL};
|
|
PyObject *rounding = Py_None;
|
|
PyObject *context = Py_None;
|
|
PyObject *w, *a, *b;
|
|
PyObject *result;
|
|
uint32_t status = 0;
|
|
mpd_context_t workctx;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist,
|
|
&w, &rounding, &context)) {
|
|
return NULL;
|
|
}
|
|
CONTEXT_CHECK_VA(context);
|
|
|
|
workctx = *CTX(context);
|
|
if (rounding != Py_None) {
|
|
int round = getround(rounding);
|
|
if (round < 0) {
|
|
return NULL;
|
|
}
|
|
if (!mpd_qsetround(&workctx, round)) {
|
|
INTERNAL_ERROR_PTR("dec_mpd_qquantize"); /* GCOV_NOT_REACHED */
|
|
}
|
|
}
|
|
|
|
CONVERT_BINOP_RAISE(&a, &b, v, w, context);
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qquantize(MPD(result), MPD(a), MPD(b), &workctx, &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Special methods */
|
|
static PyObject *
|
|
dec_richcompare(PyObject *v, PyObject *w, int op)
|
|
{
|
|
PyObject *a;
|
|
PyObject *b;
|
|
PyObject *context;
|
|
uint32_t status = 0;
|
|
int a_issnan, b_issnan;
|
|
int r;
|
|
|
|
assert(PyDec_Check(v));
|
|
|
|
CURRENT_CONTEXT(context);
|
|
CONVERT_BINOP_CMP(&a, &b, v, w, op, context);
|
|
|
|
a_issnan = mpd_issnan(MPD(a));
|
|
b_issnan = mpd_issnan(MPD(b));
|
|
|
|
r = mpd_qcmp(MPD(a), MPD(b), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (r == INT_MAX) {
|
|
/* sNaNs or op={le,ge,lt,gt} always signal. */
|
|
if (a_issnan || b_issnan || (op != Py_EQ && op != Py_NE)) {
|
|
if (dec_addstatus(context, status)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
/* qNaN comparison with op={eq,ne} or comparison
|
|
* with InvalidOperation disabled. */
|
|
return (op == Py_NE) ? incr_true() : incr_false();
|
|
}
|
|
|
|
switch (op) {
|
|
case Py_EQ:
|
|
r = (r == 0);
|
|
break;
|
|
case Py_NE:
|
|
r = (r != 0);
|
|
break;
|
|
case Py_LE:
|
|
r = (r <= 0);
|
|
break;
|
|
case Py_GE:
|
|
r = (r >= 0);
|
|
break;
|
|
case Py_LT:
|
|
r = (r == -1);
|
|
break;
|
|
case Py_GT:
|
|
r = (r == 1);
|
|
break;
|
|
}
|
|
|
|
return PyBool_FromLong(r);
|
|
}
|
|
|
|
/* __ceil__ */
|
|
static PyObject *
|
|
dec_ceil(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *context;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
return dec_as_long(self, context, MPD_ROUND_CEILING);
|
|
}
|
|
|
|
/* __complex__ */
|
|
static PyObject *
|
|
dec_complex(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *f;
|
|
double x;
|
|
|
|
f = PyDec_AsFloat(self);
|
|
if (f == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
x = PyFloat_AsDouble(f);
|
|
Py_DECREF(f);
|
|
if (x == -1.0 && PyErr_Occurred()) {
|
|
return NULL;
|
|
}
|
|
|
|
return PyComplex_FromDoubles(x, 0);
|
|
}
|
|
|
|
/* __copy__ and __deepcopy__ */
|
|
static PyObject *
|
|
dec_copy(PyObject *self, PyObject *dummy)
|
|
{
|
|
Py_INCREF(self);
|
|
return self;
|
|
}
|
|
|
|
/* __floor__ */
|
|
static PyObject *
|
|
dec_floor(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *context;
|
|
CURRENT_CONTEXT(context);
|
|
return dec_as_long(self, context, MPD_ROUND_FLOOR);
|
|
}
|
|
|
|
/* Always uses the module context */
|
|
static Py_hash_t
|
|
_dec_hash(PyDecObject *v)
|
|
{
|
|
/* 2**61 - 1 */
|
|
mpd_uint_t p_data[1] = {2305843009213693951ULL};
|
|
mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 19, 1, 1, p_data};
|
|
/* Inverse of 10 modulo p */
|
|
mpd_uint_t inv10_p_data[1] = {2075258708292324556ULL};
|
|
mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
|
|
0, 19, 1, 1, inv10_p_data};
|
|
const Py_hash_t py_hash_inf = 314159;
|
|
const Py_hash_t py_hash_nan = 0;
|
|
mpd_uint_t ten_data[1] = {10};
|
|
mpd_t ten = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
|
|
0, 2, 1, 1, ten_data};
|
|
Py_hash_t result;
|
|
mpd_t *exp_hash = NULL;
|
|
mpd_t *tmp = NULL;
|
|
mpd_ssize_t exp;
|
|
uint32_t status = 0;
|
|
mpd_context_t maxctx;
|
|
PyObject *context;
|
|
|
|
|
|
context = current_context();
|
|
if (context == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (mpd_isspecial(MPD(v))) {
|
|
if (mpd_issnan(MPD(v))) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"Cannot hash a signaling NaN value");
|
|
return -1;
|
|
}
|
|
else if (mpd_isnan(MPD(v))) {
|
|
return py_hash_nan;
|
|
}
|
|
else {
|
|
return py_hash_inf * mpd_arith_sign(MPD(v));
|
|
}
|
|
}
|
|
|
|
mpd_maxcontext(&maxctx);
|
|
exp_hash = mpd_qnew();
|
|
if (exp_hash == NULL) {
|
|
goto malloc_error;
|
|
}
|
|
tmp = mpd_qnew();
|
|
if (tmp == NULL) {
|
|
goto malloc_error;
|
|
}
|
|
|
|
/*
|
|
* exp(v): exponent of v
|
|
* int(v): coefficient of v
|
|
*/
|
|
exp = MPD(v)->exp;
|
|
if (exp >= 0) {
|
|
/* 10**exp(v) % p */
|
|
mpd_qsset_ssize(tmp, exp, &maxctx, &status);
|
|
mpd_qpowmod(exp_hash, &ten, tmp, &p, &maxctx, &status);
|
|
}
|
|
else {
|
|
/* inv10_p**(-exp(v)) % p */
|
|
mpd_qsset_ssize(tmp, -exp, &maxctx, &status);
|
|
mpd_qpowmod(exp_hash, &inv10_p, tmp, &p, &maxctx, &status);
|
|
}
|
|
|
|
/* hash = (int(v) * exp_hash) % p */
|
|
if (!mpd_qcopy(tmp, MPD(v), &status)) {
|
|
goto malloc_error;
|
|
}
|
|
tmp->exp = 0;
|
|
mpd_set_positive(tmp);
|
|
|
|
maxctx.prec = MPD_MAX_PREC + 21;
|
|
maxctx.emax = MPD_MAX_EMAX + 21;
|
|
maxctx.emin = MPD_MIN_EMIN - 21;
|
|
|
|
mpd_qmul(tmp, tmp, exp_hash, &maxctx, &status);
|
|
mpd_qrem(tmp, tmp, &p, &maxctx, &status);
|
|
|
|
result = mpd_qget_ssize(tmp, &status);
|
|
result = mpd_ispositive(MPD(v)) ? result : -result;
|
|
result = (result == -1) ? -2 : result;
|
|
|
|
if (status != 0) {
|
|
if (status & MPD_Malloc_error) {
|
|
goto malloc_error;
|
|
}
|
|
else {
|
|
PyErr_SetString(PyExc_RuntimeError, /* GCOV_NOT_REACHED */
|
|
"dec_hash: internal error: please report"); /* GCOV_NOT_REACHED */
|
|
}
|
|
result = -1; /* GCOV_NOT_REACHED */
|
|
}
|
|
|
|
|
|
finish:
|
|
if (exp_hash) mpd_del(exp_hash);
|
|
if (tmp) mpd_del(tmp);
|
|
return result;
|
|
|
|
malloc_error:
|
|
PyErr_NoMemory();
|
|
result = -1;
|
|
goto finish;
|
|
}
|
|
|
|
static Py_hash_t
|
|
dec_hash(PyDecObject *self)
|
|
{
|
|
if (self->hash == -1) {
|
|
self->hash = _dec_hash(self);
|
|
}
|
|
|
|
return self->hash;
|
|
}
|
|
|
|
/* __reduce__ */
|
|
static PyObject *
|
|
dec_reduce(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *result, *str;
|
|
|
|
str = dec_str(self);
|
|
if (str == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
result = Py_BuildValue("O(O)", Py_TYPE(self), str);
|
|
Py_DECREF(str);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* __sizeof__ */
|
|
static PyObject *
|
|
dec_sizeof(PyObject *v, PyObject *dummy)
|
|
{
|
|
Py_ssize_t res;
|
|
|
|
res = _PyObject_SIZE(Py_TYPE(v));
|
|
if (mpd_isdynamic_data(MPD(v))) {
|
|
res += MPD(v)->alloc * sizeof(mpd_uint_t);
|
|
}
|
|
return PyLong_FromSsize_t(res);
|
|
}
|
|
|
|
/* __trunc__ */
|
|
static PyObject *
|
|
dec_trunc(PyObject *self, PyObject *dummy)
|
|
{
|
|
PyObject *context;
|
|
|
|
CURRENT_CONTEXT(context);
|
|
return dec_as_long(self, context, MPD_ROUND_DOWN);
|
|
}
|
|
|
|
/* real and imag */
|
|
static PyObject *
|
|
dec_real(PyObject *self, void *closure)
|
|
{
|
|
Py_INCREF(self);
|
|
return self;
|
|
}
|
|
|
|
static PyObject *
|
|
dec_imag(PyObject *self, void *closure)
|
|
{
|
|
PyObject *result;
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
_dec_settriple(result, MPD_POS, 0, 0);
|
|
return result;
|
|
}
|
|
|
|
|
|
static PyGetSetDef dec_getsets [] =
|
|
{
|
|
{ "real", (getter)dec_real, NULL, NULL, NULL},
|
|
{ "imag", (getter)dec_imag, NULL, NULL, NULL},
|
|
{NULL}
|
|
};
|
|
|
|
static PyNumberMethods dec_number_methods =
|
|
{
|
|
(binaryfunc) nm_mpd_qadd,
|
|
(binaryfunc) nm_mpd_qsub,
|
|
(binaryfunc) nm_mpd_qmul,
|
|
(binaryfunc) nm_mpd_qrem,
|
|
(binaryfunc) nm_mpd_qdivmod,
|
|
(ternaryfunc) nm_mpd_qpow,
|
|
(unaryfunc) nm_mpd_qminus,
|
|
(unaryfunc) nm_mpd_qplus,
|
|
(unaryfunc) nm_mpd_qabs,
|
|
(inquiry) nm_nonzero,
|
|
(unaryfunc) 0, /* no bit-complement */
|
|
(binaryfunc) 0, /* no shiftl */
|
|
(binaryfunc) 0, /* no shiftr */
|
|
(binaryfunc) 0, /* no bit-and */
|
|
(binaryfunc) 0, /* no bit-xor */
|
|
(binaryfunc) 0, /* no bit-ior */
|
|
(unaryfunc) nm_dec_as_long,
|
|
0, /* nb_reserved */
|
|
(unaryfunc) PyDec_AsFloat,
|
|
0, /* binaryfunc nb_inplace_add; */
|
|
0, /* binaryfunc nb_inplace_subtract; */
|
|
0, /* binaryfunc nb_inplace_multiply; */
|
|
0, /* binaryfunc nb_inplace_remainder; */
|
|
0, /* ternaryfunc nb_inplace_power; */
|
|
0, /* binaryfunc nb_inplace_lshift; */
|
|
0, /* binaryfunc nb_inplace_rshift; */
|
|
0, /* binaryfunc nb_inplace_and; */
|
|
0, /* binaryfunc nb_inplace_xor; */
|
|
0, /* binaryfunc nb_inplace_or; */
|
|
(binaryfunc) nm_mpd_qdivint, /* binaryfunc nb_floor_divide; */
|
|
(binaryfunc) nm_mpd_qdiv, /* binaryfunc nb_true_divide; */
|
|
0, /* binaryfunc nb_inplace_floor_divide; */
|
|
0, /* binaryfunc nb_inplace_true_divide; */
|
|
};
|
|
|
|
static PyMethodDef dec_methods [] =
|
|
{
|
|
/* Unary arithmetic functions, optional context arg */
|
|
{ "exp", (PyCFunction)dec_mpd_qexp, METH_VARARGS|METH_KEYWORDS, doc_exp },
|
|
{ "ln", (PyCFunction)dec_mpd_qln, METH_VARARGS|METH_KEYWORDS, doc_ln },
|
|
{ "log10", (PyCFunction)dec_mpd_qlog10, METH_VARARGS|METH_KEYWORDS, doc_log10 },
|
|
{ "next_minus", (PyCFunction)dec_mpd_qnext_minus, METH_VARARGS|METH_KEYWORDS, doc_next_minus },
|
|
{ "next_plus", (PyCFunction)dec_mpd_qnext_plus, METH_VARARGS|METH_KEYWORDS, doc_next_plus },
|
|
{ "normalize", (PyCFunction)dec_mpd_qreduce, METH_VARARGS|METH_KEYWORDS, doc_normalize },
|
|
{ "to_integral", (PyCFunction)PyDec_ToIntegralValue, METH_VARARGS|METH_KEYWORDS, doc_to_integral },
|
|
{ "to_integral_exact", (PyCFunction)PyDec_ToIntegralExact, METH_VARARGS|METH_KEYWORDS, doc_to_integral_exact },
|
|
{ "to_integral_value", (PyCFunction)PyDec_ToIntegralValue, METH_VARARGS|METH_KEYWORDS, doc_to_integral_value },
|
|
{ "sqrt", (PyCFunction)dec_mpd_qsqrt, METH_VARARGS|METH_KEYWORDS, doc_sqrt },
|
|
|
|
/* Binary arithmetic functions, optional context arg */
|
|
{ "compare", (PyCFunction)dec_mpd_qcompare, METH_VARARGS|METH_KEYWORDS, doc_compare },
|
|
{ "compare_signal", (PyCFunction)dec_mpd_qcompare_signal, METH_VARARGS|METH_KEYWORDS, doc_compare_signal },
|
|
{ "max", (PyCFunction)dec_mpd_qmax, METH_VARARGS|METH_KEYWORDS, doc_max },
|
|
{ "max_mag", (PyCFunction)dec_mpd_qmax_mag, METH_VARARGS|METH_KEYWORDS, doc_max_mag },
|
|
{ "min", (PyCFunction)dec_mpd_qmin, METH_VARARGS|METH_KEYWORDS, doc_min },
|
|
{ "min_mag", (PyCFunction)dec_mpd_qmin_mag, METH_VARARGS|METH_KEYWORDS, doc_min_mag },
|
|
{ "next_toward", (PyCFunction)dec_mpd_qnext_toward, METH_VARARGS|METH_KEYWORDS, doc_next_toward },
|
|
{ "quantize", (PyCFunction)dec_mpd_qquantize, METH_VARARGS|METH_KEYWORDS, doc_quantize },
|
|
{ "remainder_near", (PyCFunction)dec_mpd_qrem_near, METH_VARARGS|METH_KEYWORDS, doc_remainder_near },
|
|
|
|
/* Ternary arithmetic functions, optional context arg */
|
|
{ "fma", (PyCFunction)dec_mpd_qfma, METH_VARARGS|METH_KEYWORDS, doc_fma },
|
|
|
|
/* Boolean functions, no context arg */
|
|
{ "is_canonical", dec_mpd_iscanonical, METH_NOARGS, doc_is_canonical },
|
|
{ "is_finite", dec_mpd_isfinite, METH_NOARGS, doc_is_finite },
|
|
{ "is_infinite", dec_mpd_isinfinite, METH_NOARGS, doc_is_infinite },
|
|
{ "is_nan", dec_mpd_isnan, METH_NOARGS, doc_is_nan },
|
|
{ "is_qnan", dec_mpd_isqnan, METH_NOARGS, doc_is_qnan },
|
|
{ "is_snan", dec_mpd_issnan, METH_NOARGS, doc_is_snan },
|
|
{ "is_signed", dec_mpd_issigned, METH_NOARGS, doc_is_signed },
|
|
{ "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero },
|
|
|
|
/* Boolean functions, optional context arg */
|
|
{ "is_normal", (PyCFunction)dec_mpd_isnormal, METH_VARARGS|METH_KEYWORDS, doc_is_normal },
|
|
{ "is_subnormal", (PyCFunction)dec_mpd_issubnormal, METH_VARARGS|METH_KEYWORDS, doc_is_subnormal },
|
|
|
|
/* Unary functions, no context arg */
|
|
{ "adjusted", dec_mpd_adjexp, METH_NOARGS, doc_adjusted },
|
|
{ "canonical", dec_canonical, METH_NOARGS, doc_canonical },
|
|
{ "conjugate", dec_conjugate, METH_NOARGS, doc_conjugate },
|
|
{ "radix", dec_mpd_radix, METH_NOARGS, doc_radix },
|
|
|
|
/* Unary functions, optional context arg for conversion errors */
|
|
{ "copy_abs", dec_mpd_qcopy_abs, METH_NOARGS, doc_copy_abs },
|
|
{ "copy_negate", dec_mpd_qcopy_negate, METH_NOARGS, doc_copy_negate },
|
|
|
|
/* Unary functions, optional context arg */
|
|
{ "logb", (PyCFunction)dec_mpd_qlogb, METH_VARARGS|METH_KEYWORDS, doc_logb },
|
|
{ "logical_invert", (PyCFunction)dec_mpd_qinvert, METH_VARARGS|METH_KEYWORDS, doc_logical_invert },
|
|
{ "number_class", (PyCFunction)dec_mpd_class, METH_VARARGS|METH_KEYWORDS, doc_number_class },
|
|
{ "to_eng_string", (PyCFunction)dec_mpd_to_eng, METH_VARARGS|METH_KEYWORDS, doc_to_eng_string },
|
|
|
|
/* Binary functions, optional context arg for conversion errors */
|
|
{ "compare_total", (PyCFunction)dec_mpd_compare_total, METH_VARARGS|METH_KEYWORDS, doc_compare_total },
|
|
{ "compare_total_mag", (PyCFunction)dec_mpd_compare_total_mag, METH_VARARGS|METH_KEYWORDS, doc_compare_total_mag },
|
|
{ "copy_sign", (PyCFunction)dec_mpd_qcopy_sign, METH_VARARGS|METH_KEYWORDS, doc_copy_sign },
|
|
{ "same_quantum", (PyCFunction)dec_mpd_same_quantum, METH_VARARGS|METH_KEYWORDS, doc_same_quantum },
|
|
|
|
/* Binary functions, optional context arg */
|
|
{ "logical_and", (PyCFunction)dec_mpd_qand, METH_VARARGS|METH_KEYWORDS, doc_logical_and },
|
|
{ "logical_or", (PyCFunction)dec_mpd_qor, METH_VARARGS|METH_KEYWORDS, doc_logical_or },
|
|
{ "logical_xor", (PyCFunction)dec_mpd_qxor, METH_VARARGS|METH_KEYWORDS, doc_logical_xor },
|
|
{ "rotate", (PyCFunction)dec_mpd_qrotate, METH_VARARGS|METH_KEYWORDS, doc_rotate },
|
|
{ "scaleb", (PyCFunction)dec_mpd_qscaleb, METH_VARARGS|METH_KEYWORDS, doc_scaleb },
|
|
{ "shift", (PyCFunction)dec_mpd_qshift, METH_VARARGS|METH_KEYWORDS, doc_shift },
|
|
|
|
/* Miscellaneous */
|
|
{ "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float },
|
|
{ "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple },
|
|
{ "as_integer_ratio", dec_as_integer_ratio, METH_NOARGS, doc_as_integer_ratio },
|
|
|
|
/* Special methods */
|
|
{ "__copy__", dec_copy, METH_NOARGS, NULL },
|
|
{ "__deepcopy__", dec_copy, METH_O, NULL },
|
|
{ "__format__", dec_format, METH_VARARGS, NULL },
|
|
{ "__reduce__", dec_reduce, METH_NOARGS, NULL },
|
|
{ "__round__", PyDec_Round, METH_VARARGS, NULL },
|
|
{ "__ceil__", dec_ceil, METH_NOARGS, NULL },
|
|
{ "__floor__", dec_floor, METH_NOARGS, NULL },
|
|
{ "__trunc__", dec_trunc, METH_NOARGS, NULL },
|
|
{ "__complex__", dec_complex, METH_NOARGS, NULL },
|
|
{ "__sizeof__", dec_sizeof, METH_NOARGS, NULL },
|
|
|
|
{ NULL, NULL, 1 }
|
|
};
|
|
|
|
static PyTypeObject PyDec_Type =
|
|
{
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"decimal.Decimal", /* tp_name */
|
|
sizeof(PyDecObject), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
(destructor) dec_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
(getattrfunc) 0, /* tp_getattr */
|
|
(setattrfunc) 0, /* tp_setattr */
|
|
0, /* tp_reserved */
|
|
(reprfunc) dec_repr, /* tp_repr */
|
|
&dec_number_methods, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
(hashfunc) dec_hash, /* tp_hash */
|
|
0, /* tp_call */
|
|
(reprfunc) dec_str, /* tp_str */
|
|
(getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */
|
|
(setattrofunc) 0, /* tp_setattro */
|
|
(PyBufferProcs *) 0, /* tp_as_buffer */
|
|
(Py_TPFLAGS_DEFAULT|
|
|
Py_TPFLAGS_BASETYPE), /* tp_flags */
|
|
doc_decimal, /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
dec_richcompare, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
dec_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
dec_getsets, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0, /* tp_alloc */
|
|
dec_new, /* tp_new */
|
|
PyObject_Del, /* tp_free */
|
|
};
|
|
|
|
|
|
/******************************************************************************/
|
|
/* Context Object, Part 2 */
|
|
/******************************************************************************/
|
|
|
|
|
|
/************************************************************************/
|
|
/* Macros for converting mpdecimal functions to Context methods */
|
|
/************************************************************************/
|
|
|
|
/* Boolean context method. */
|
|
#define DecCtx_BoolFunc(MPDFUNC) \
|
|
static PyObject * \
|
|
ctx_##MPDFUNC(PyObject *context, PyObject *v) \
|
|
{ \
|
|
PyObject *ret; \
|
|
PyObject *a; \
|
|
CONVERT_OP_RAISE(&a, v, context); \
|
|
ret = MPDFUNC(MPD(a), CTX(context)) ? incr_true() : incr_false(); \
|
|
Py_DECREF(a); \
|
|
return ret; \
|
|
}
|
|
|
|
static PyObject *
|
|
_DecCtx_BoolFunc_NO_CTX(PyObject *context, PyObject *v,
|
|
int mpdfunc(const mpd_t *))
|
|
{
|
|
PyObject *ret;
|
|
PyObject *a;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
ret = mpdfunc(MPD(a)) ? incr_true() : incr_false();
|
|
Py_DECREF(a);
|
|
return ret;
|
|
}
|
|
|
|
/* Boolean context method. MPDFUNC does NOT use a context. */
|
|
#define DecCtx_BoolFunc_NO_CTX(MPDFUNC) \
|
|
static PyObject * \
|
|
ctx_##MPDFUNC(PyObject *context, PyObject *v) \
|
|
{ \
|
|
return _DecCtx_BoolFunc_NO_CTX(context, v, MPDFUNC); \
|
|
}
|
|
|
|
static PyObject *
|
|
_DecCtx_UnaryFunc(PyObject *context, PyObject *v,
|
|
void mpdfunc(mpd_t *, const mpd_t *, const mpd_context_t *,
|
|
uint32_t *))
|
|
{
|
|
PyObject *result, *a;
|
|
uint32_t status = 0;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
if ((result = dec_alloc()) == NULL) {
|
|
Py_DECREF(a);
|
|
return NULL;
|
|
}
|
|
mpdfunc(MPD(result), MPD(a), CTX(context), &status);
|
|
Py_DECREF(a);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Unary context method. */
|
|
#define DecCtx_UnaryFunc(MPDFUNC) \
|
|
static PyObject * \
|
|
ctx_##MPDFUNC(PyObject *context, PyObject *v) \
|
|
{ \
|
|
return _DecCtx_UnaryFunc(context, v, MPDFUNC); \
|
|
}
|
|
|
|
static PyObject *
|
|
_DecCtx_BinaryFunc(PyObject *context, PyObject *args,
|
|
void mpdfunc(mpd_t *, const mpd_t *, const mpd_t *,
|
|
const mpd_context_t *, uint32_t *))
|
|
{
|
|
PyObject *v, *w;
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
uint32_t status = 0;
|
|
if (!PyArg_ParseTuple(args, "OO", &v, &w)) {
|
|
return NULL;
|
|
}
|
|
CONVERT_BINOP_RAISE(&a, &b, v, w, context);
|
|
if ((result = dec_alloc()) == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
mpdfunc(MPD(result), MPD(a), MPD(b), CTX(context), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Binary context method. */
|
|
#define DecCtx_BinaryFunc(MPDFUNC) \
|
|
static PyObject * \
|
|
ctx_##MPDFUNC(PyObject *context, PyObject *args) \
|
|
{ \
|
|
return _DecCtx_BinaryFunc(context, args, (void *)MPDFUNC); \
|
|
}
|
|
|
|
/*
|
|
* Binary context method. The context is only used for conversion.
|
|
* The actual MPDFUNC does NOT take a context arg.
|
|
*/
|
|
#define DecCtx_BinaryFunc_NO_CTX(MPDFUNC) \
|
|
static PyObject * \
|
|
ctx_##MPDFUNC(PyObject *context, PyObject *args) \
|
|
{ \
|
|
PyObject *v, *w; \
|
|
PyObject *a, *b; \
|
|
PyObject *result; \
|
|
\
|
|
if (!PyArg_ParseTuple(args, "OO", &v, &w)) { \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
CONVERT_BINOP_RAISE(&a, &b, v, w, context); \
|
|
\
|
|
if ((result = dec_alloc()) == NULL) { \
|
|
Py_DECREF(a); \
|
|
Py_DECREF(b); \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
MPDFUNC(MPD(result), MPD(a), MPD(b)); \
|
|
Py_DECREF(a); \
|
|
Py_DECREF(b); \
|
|
\
|
|
return result; \
|
|
}
|
|
|
|
/* Ternary context method. */
|
|
#define DecCtx_TernaryFunc(MPDFUNC) \
|
|
static PyObject * \
|
|
ctx_##MPDFUNC(PyObject *context, PyObject *args) \
|
|
{ \
|
|
PyObject *v, *w, *x; \
|
|
PyObject *a, *b, *c; \
|
|
PyObject *result; \
|
|
uint32_t status = 0; \
|
|
\
|
|
if (!PyArg_ParseTuple(args, "OOO", &v, &w, &x)) { \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
CONVERT_TERNOP_RAISE(&a, &b, &c, v, w, x, context); \
|
|
\
|
|
if ((result = dec_alloc()) == NULL) { \
|
|
Py_DECREF(a); \
|
|
Py_DECREF(b); \
|
|
Py_DECREF(c); \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
MPDFUNC(MPD(result), MPD(a), MPD(b), MPD(c), CTX(context), &status); \
|
|
Py_DECREF(a); \
|
|
Py_DECREF(b); \
|
|
Py_DECREF(c); \
|
|
if (dec_addstatus(context, status)) { \
|
|
Py_DECREF(result); \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
return result; \
|
|
}
|
|
|
|
/* Unary arithmetic functions */
|
|
DecCtx_UnaryFunc(mpd_qabs)
|
|
DecCtx_UnaryFunc(mpd_qexp)
|
|
DecCtx_UnaryFunc(mpd_qln)
|
|
DecCtx_UnaryFunc(mpd_qlog10)
|
|
DecCtx_UnaryFunc(mpd_qminus)
|
|
DecCtx_UnaryFunc(mpd_qnext_minus)
|
|
DecCtx_UnaryFunc(mpd_qnext_plus)
|
|
DecCtx_UnaryFunc(mpd_qplus)
|
|
DecCtx_UnaryFunc(mpd_qreduce)
|
|
DecCtx_UnaryFunc(mpd_qround_to_int)
|
|
DecCtx_UnaryFunc(mpd_qround_to_intx)
|
|
DecCtx_UnaryFunc(mpd_qsqrt)
|
|
|
|
/* Binary arithmetic functions */
|
|
DecCtx_BinaryFunc(mpd_qadd)
|
|
DecCtx_BinaryFunc(mpd_qcompare)
|
|
DecCtx_BinaryFunc(mpd_qcompare_signal)
|
|
DecCtx_BinaryFunc(mpd_qdiv)
|
|
DecCtx_BinaryFunc(mpd_qdivint)
|
|
DecCtx_BinaryFunc(mpd_qmax)
|
|
DecCtx_BinaryFunc(mpd_qmax_mag)
|
|
DecCtx_BinaryFunc(mpd_qmin)
|
|
DecCtx_BinaryFunc(mpd_qmin_mag)
|
|
DecCtx_BinaryFunc(mpd_qmul)
|
|
DecCtx_BinaryFunc(mpd_qnext_toward)
|
|
DecCtx_BinaryFunc(mpd_qquantize)
|
|
DecCtx_BinaryFunc(mpd_qrem)
|
|
DecCtx_BinaryFunc(mpd_qrem_near)
|
|
DecCtx_BinaryFunc(mpd_qsub)
|
|
|
|
static PyObject *
|
|
ctx_mpd_qdivmod(PyObject *context, PyObject *args)
|
|
{
|
|
PyObject *v, *w;
|
|
PyObject *a, *b;
|
|
PyObject *q, *r;
|
|
uint32_t status = 0;
|
|
PyObject *ret;
|
|
if (!PyArg_ParseTuple(args, "OO", &v, &w)) {
|
|
return NULL;
|
|
}
|
|
CONVERT_BINOP_RAISE(&a, &b, v, w, context);
|
|
q = dec_alloc();
|
|
if (q == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
r = dec_alloc();
|
|
if (r == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
Py_DECREF(q);
|
|
return NULL;
|
|
}
|
|
mpd_qdivmod(MPD(q), MPD(r), MPD(a), MPD(b), CTX(context), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(r);
|
|
Py_DECREF(q);
|
|
return NULL;
|
|
}
|
|
ret = Py_BuildValue("(OO)", q, r);
|
|
Py_DECREF(r);
|
|
Py_DECREF(q);
|
|
return ret;
|
|
}
|
|
|
|
/* Binary or ternary arithmetic functions */
|
|
static PyObject *
|
|
ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *kwlist[] = {"a", "b", "modulo", NULL};
|
|
PyObject *base, *exp, *mod = Py_None;
|
|
PyObject *a, *b, *c = NULL;
|
|
PyObject *result;
|
|
uint32_t status = 0;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist,
|
|
&base, &exp, &mod)) {
|
|
return NULL;
|
|
}
|
|
CONVERT_BINOP_RAISE(&a, &b, base, exp, context);
|
|
if (mod != Py_None) {
|
|
if (!convert_op(TYPE_ERR, &c, mod, context)) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return c;
|
|
}
|
|
}
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
Py_XDECREF(c);
|
|
return NULL;
|
|
}
|
|
if (c == NULL) {
|
|
mpd_qpow(MPD(result), MPD(a), MPD(b),
|
|
CTX(context), &status);
|
|
}
|
|
else {
|
|
mpd_qpowmod(MPD(result), MPD(a), MPD(b), MPD(c),
|
|
CTX(context), &status);
|
|
Py_DECREF(c);
|
|
}
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Ternary arithmetic functions */
|
|
DecCtx_TernaryFunc(mpd_qfma)
|
|
|
|
/* No argument */
|
|
static PyObject *
|
|
ctx_mpd_radix(PyObject *context, PyObject *dummy)
|
|
{
|
|
return dec_mpd_radix(context, dummy);
|
|
}
|
|
|
|
/* Boolean functions: single decimal argument */
|
|
DecCtx_BoolFunc(mpd_isnormal)
|
|
DecCtx_BoolFunc(mpd_issubnormal)
|
|
DecCtx_BoolFunc_NO_CTX(mpd_isfinite)
|
|
DecCtx_BoolFunc_NO_CTX(mpd_isinfinite)
|
|
DecCtx_BoolFunc_NO_CTX(mpd_isnan)
|
|
DecCtx_BoolFunc_NO_CTX(mpd_isqnan)
|
|
DecCtx_BoolFunc_NO_CTX(mpd_issigned)
|
|
DecCtx_BoolFunc_NO_CTX(mpd_issnan)
|
|
DecCtx_BoolFunc_NO_CTX(mpd_iszero)
|
|
|
|
static PyObject *
|
|
ctx_iscanonical(PyObject *context, PyObject *v)
|
|
{
|
|
if (!PyDec_Check(v)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"argument must be a Decimal");
|
|
return NULL;
|
|
}
|
|
return mpd_iscanonical(MPD(v)) ? incr_true() : incr_false();
|
|
}
|
|
|
|
/* Functions with a single decimal argument */
|
|
static PyObject *
|
|
PyDecContext_Apply(PyObject *context, PyObject *v)
|
|
{
|
|
PyObject *result, *a;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
result = dec_apply(a, context);
|
|
Py_DECREF(a);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
ctx_canonical(PyObject *context, PyObject *v)
|
|
{
|
|
if (!PyDec_Check(v)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"argument must be a Decimal");
|
|
return NULL;
|
|
}
|
|
Py_INCREF(v);
|
|
return v;
|
|
}
|
|
|
|
static PyObject *
|
|
ctx_mpd_qcopy_abs(PyObject *context, PyObject *v)
|
|
{
|
|
PyObject *result, *a;
|
|
uint32_t status = 0;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(a);
|
|
return NULL;
|
|
}
|
|
mpd_qcopy_abs(MPD(result), MPD(a), &status);
|
|
Py_DECREF(a);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
ctx_copy_decimal(PyObject *context, PyObject *v)
|
|
{
|
|
PyObject *result;
|
|
CONVERT_OP_RAISE(&result, v, context);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
ctx_mpd_qcopy_negate(PyObject *context, PyObject *v)
|
|
{
|
|
PyObject *result, *a;
|
|
uint32_t status = 0;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(a);
|
|
return NULL;
|
|
}
|
|
mpd_qcopy_negate(MPD(result), MPD(a), &status);
|
|
Py_DECREF(a);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DecCtx_UnaryFunc(mpd_qlogb)
|
|
DecCtx_UnaryFunc(mpd_qinvert)
|
|
|
|
static PyObject *
|
|
ctx_mpd_class(PyObject *context, PyObject *v)
|
|
{
|
|
PyObject *a;
|
|
const char *cp;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
cp = mpd_class(MPD(a), CTX(context));
|
|
Py_DECREF(a);
|
|
return PyUnicode_FromString(cp);
|
|
}
|
|
|
|
static PyObject *
|
|
ctx_mpd_to_sci(PyObject *context, PyObject *v)
|
|
{
|
|
PyObject *result;
|
|
PyObject *a;
|
|
mpd_ssize_t size;
|
|
char *s;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
size = mpd_to_sci_size(&s, MPD(a), CtxCaps(context));
|
|
Py_DECREF(a);
|
|
if (size < 0) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
result = unicode_fromascii(s, size);
|
|
mpd_free(s);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
ctx_mpd_to_eng(PyObject *context, PyObject *v)
|
|
{
|
|
PyObject *result;
|
|
PyObject *a;
|
|
mpd_ssize_t size;
|
|
char *s;
|
|
CONVERT_OP_RAISE(&a, v, context);
|
|
size = mpd_to_eng_size(&s, MPD(a), CtxCaps(context));
|
|
Py_DECREF(a);
|
|
if (size < 0) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
result = unicode_fromascii(s, size);
|
|
mpd_free(s);
|
|
return result;
|
|
}
|
|
|
|
/* Functions with two decimal arguments */
|
|
DecCtx_BinaryFunc_NO_CTX(mpd_compare_total)
|
|
DecCtx_BinaryFunc_NO_CTX(mpd_compare_total_mag)
|
|
|
|
static PyObject *
|
|
ctx_mpd_qcopy_sign(PyObject *context, PyObject *args)
|
|
{
|
|
PyObject *v, *w;
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
uint32_t status = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "OO", &v, &w)) {
|
|
return NULL;
|
|
}
|
|
|
|
CONVERT_BINOP_RAISE(&a, &b, v, w, context);
|
|
|
|
result = dec_alloc();
|
|
if (result == NULL) {
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
return NULL;
|
|
}
|
|
|
|
mpd_qcopy_sign(MPD(result), MPD(a), MPD(b), &status);
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
if (dec_addstatus(context, status)) {
|
|
Py_DECREF(result);
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DecCtx_BinaryFunc(mpd_qand)
|
|
DecCtx_BinaryFunc(mpd_qor)
|
|
DecCtx_BinaryFunc(mpd_qxor)
|
|
|
|
DecCtx_BinaryFunc(mpd_qrotate)
|
|
DecCtx_BinaryFunc(mpd_qscaleb)
|
|
DecCtx_BinaryFunc(mpd_qshift)
|
|
|
|
static PyObject *
|
|
ctx_mpd_same_quantum(PyObject *context, PyObject *args)
|
|
{
|
|
PyObject *v, *w;
|
|
PyObject *a, *b;
|
|
PyObject *result;
|
|
|
|
if (!PyArg_ParseTuple(args, "OO", &v, &w)) {
|
|
return NULL;
|
|
}
|
|
|
|
CONVERT_BINOP_RAISE(&a, &b, v, w, context);
|
|
|
|
result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false();
|
|
Py_DECREF(a);
|
|
Py_DECREF(b);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static PyMethodDef context_methods [] =
|
|
{
|
|
/* Unary arithmetic functions */
|
|
{ "abs", ctx_mpd_qabs, METH_O, doc_ctx_abs },
|
|
{ "exp", ctx_mpd_qexp, METH_O, doc_ctx_exp },
|
|
{ "ln", ctx_mpd_qln, METH_O, doc_ctx_ln },
|
|
{ "log10", ctx_mpd_qlog10, METH_O, doc_ctx_log10 },
|
|
{ "minus", ctx_mpd_qminus, METH_O, doc_ctx_minus },
|
|
{ "next_minus", ctx_mpd_qnext_minus, METH_O, doc_ctx_next_minus },
|
|
{ "next_plus", ctx_mpd_qnext_plus, METH_O, doc_ctx_next_plus },
|
|
{ "normalize", ctx_mpd_qreduce, METH_O, doc_ctx_normalize },
|
|
{ "plus", ctx_mpd_qplus, METH_O, doc_ctx_plus },
|
|
{ "to_integral", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral },
|
|
{ "to_integral_exact", ctx_mpd_qround_to_intx, METH_O, doc_ctx_to_integral_exact },
|
|
{ "to_integral_value", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral_value },
|
|
{ "sqrt", ctx_mpd_qsqrt, METH_O, doc_ctx_sqrt },
|
|
|
|
/* Binary arithmetic functions */
|
|
{ "add", ctx_mpd_qadd, METH_VARARGS, doc_ctx_add },
|
|
{ "compare", ctx_mpd_qcompare, METH_VARARGS, doc_ctx_compare },
|
|
{ "compare_signal", ctx_mpd_qcompare_signal, METH_VARARGS, doc_ctx_compare_signal },
|
|
{ "divide", ctx_mpd_qdiv, METH_VARARGS, doc_ctx_divide },
|
|
{ "divide_int", ctx_mpd_qdivint, METH_VARARGS, doc_ctx_divide_int },
|
|
{ "divmod", ctx_mpd_qdivmod, METH_VARARGS, doc_ctx_divmod },
|
|
{ "max", ctx_mpd_qmax, METH_VARARGS, doc_ctx_max },
|
|
{ "max_mag", ctx_mpd_qmax_mag, METH_VARARGS, doc_ctx_max_mag },
|
|
{ "min", ctx_mpd_qmin, METH_VARARGS, doc_ctx_min },
|
|
{ "min_mag", ctx_mpd_qmin_mag, METH_VARARGS, doc_ctx_min_mag },
|
|
{ "multiply", ctx_mpd_qmul, METH_VARARGS, doc_ctx_multiply },
|
|
{ "next_toward", ctx_mpd_qnext_toward, METH_VARARGS, doc_ctx_next_toward },
|
|
{ "quantize", ctx_mpd_qquantize, METH_VARARGS, doc_ctx_quantize },
|
|
{ "remainder", ctx_mpd_qrem, METH_VARARGS, doc_ctx_remainder },
|
|
{ "remainder_near", ctx_mpd_qrem_near, METH_VARARGS, doc_ctx_remainder_near },
|
|
{ "subtract", ctx_mpd_qsub, METH_VARARGS, doc_ctx_subtract },
|
|
|
|
/* Binary or ternary arithmetic functions */
|
|
{ "power", (PyCFunction)ctx_mpd_qpow, METH_VARARGS|METH_KEYWORDS, doc_ctx_power },
|
|
|
|
/* Ternary arithmetic functions */
|
|
{ "fma", ctx_mpd_qfma, METH_VARARGS, doc_ctx_fma },
|
|
|
|
/* No argument */
|
|
{ "Etiny", context_getetiny, METH_NOARGS, doc_ctx_Etiny },
|
|
{ "Etop", context_getetop, METH_NOARGS, doc_ctx_Etop },
|
|
{ "radix", ctx_mpd_radix, METH_NOARGS, doc_ctx_radix },
|
|
|
|
/* Boolean functions */
|
|
{ "is_canonical", ctx_iscanonical, METH_O, doc_ctx_is_canonical },
|
|
{ "is_finite", ctx_mpd_isfinite, METH_O, doc_ctx_is_finite },
|
|
{ "is_infinite", ctx_mpd_isinfinite, METH_O, doc_ctx_is_infinite },
|
|
{ "is_nan", ctx_mpd_isnan, METH_O, doc_ctx_is_nan },
|
|
{ "is_normal", ctx_mpd_isnormal, METH_O, doc_ctx_is_normal },
|
|
{ "is_qnan", ctx_mpd_isqnan, METH_O, doc_ctx_is_qnan },
|
|
{ "is_signed", ctx_mpd_issigned, METH_O, doc_ctx_is_signed },
|
|
{ "is_snan", ctx_mpd_issnan, METH_O, doc_ctx_is_snan },
|
|
{ "is_subnormal", ctx_mpd_issubnormal, METH_O, doc_ctx_is_subnormal },
|
|
{ "is_zero", ctx_mpd_iszero, METH_O, doc_ctx_is_zero },
|
|
|
|
/* Functions with a single decimal argument */
|
|
{ "_apply", PyDecContext_Apply, METH_O, NULL }, /* alias for apply */
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
{ "apply", PyDecContext_Apply, METH_O, doc_ctx_apply },
|
|
#endif
|
|
{ "canonical", ctx_canonical, METH_O, doc_ctx_canonical },
|
|
{ "copy_abs", ctx_mpd_qcopy_abs, METH_O, doc_ctx_copy_abs },
|
|
{ "copy_decimal", ctx_copy_decimal, METH_O, doc_ctx_copy_decimal },
|
|
{ "copy_negate", ctx_mpd_qcopy_negate, METH_O, doc_ctx_copy_negate },
|
|
{ "logb", ctx_mpd_qlogb, METH_O, doc_ctx_logb },
|
|
{ "logical_invert", ctx_mpd_qinvert, METH_O, doc_ctx_logical_invert },
|
|
{ "number_class", ctx_mpd_class, METH_O, doc_ctx_number_class },
|
|
{ "to_sci_string", ctx_mpd_to_sci, METH_O, doc_ctx_to_sci_string },
|
|
{ "to_eng_string", ctx_mpd_to_eng, METH_O, doc_ctx_to_eng_string },
|
|
|
|
/* Functions with two decimal arguments */
|
|
{ "compare_total", ctx_mpd_compare_total, METH_VARARGS, doc_ctx_compare_total },
|
|
{ "compare_total_mag", ctx_mpd_compare_total_mag, METH_VARARGS, doc_ctx_compare_total_mag },
|
|
{ "copy_sign", ctx_mpd_qcopy_sign, METH_VARARGS, doc_ctx_copy_sign },
|
|
{ "logical_and", ctx_mpd_qand, METH_VARARGS, doc_ctx_logical_and },
|
|
{ "logical_or", ctx_mpd_qor, METH_VARARGS, doc_ctx_logical_or },
|
|
{ "logical_xor", ctx_mpd_qxor, METH_VARARGS, doc_ctx_logical_xor },
|
|
{ "rotate", ctx_mpd_qrotate, METH_VARARGS, doc_ctx_rotate },
|
|
{ "same_quantum", ctx_mpd_same_quantum, METH_VARARGS, doc_ctx_same_quantum },
|
|
{ "scaleb", ctx_mpd_qscaleb, METH_VARARGS, doc_ctx_scaleb },
|
|
{ "shift", ctx_mpd_qshift, METH_VARARGS, doc_ctx_shift },
|
|
|
|
/* Set context values */
|
|
{ "clear_flags", context_clear_flags, METH_NOARGS, doc_ctx_clear_flags },
|
|
{ "clear_traps", context_clear_traps, METH_NOARGS, doc_ctx_clear_traps },
|
|
|
|
/* Miscellaneous */
|
|
{ "__copy__", (PyCFunction)context_copy, METH_NOARGS, NULL },
|
|
{ "__reduce__", context_reduce, METH_NOARGS, NULL },
|
|
{ "copy", (PyCFunction)context_copy, METH_NOARGS, doc_ctx_copy },
|
|
{ "create_decimal", ctx_create_decimal, METH_VARARGS, doc_ctx_create_decimal },
|
|
{ "create_decimal_from_float", ctx_from_float, METH_O, doc_ctx_create_decimal_from_float },
|
|
|
|
{ NULL, NULL, 1 }
|
|
};
|
|
|
|
static PyTypeObject PyDecContext_Type =
|
|
{
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"decimal.Context", /* tp_name */
|
|
sizeof(PyDecContextObject), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
(destructor) context_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
(getattrfunc) 0, /* tp_getattr */
|
|
(setattrfunc) 0, /* tp_setattr */
|
|
0, /* tp_reserved */
|
|
(reprfunc) context_repr, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
(hashfunc) 0, /* tp_hash */
|
|
0, /* tp_call */
|
|
(reprfunc) context_repr, /* tp_str */
|
|
(getattrofunc) context_getattr, /* tp_getattro */
|
|
(setattrofunc) context_setattr, /* tp_setattro */
|
|
(PyBufferProcs *) 0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
doc_context, /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
context_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
context_getsets, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
context_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
context_new, /* tp_new */
|
|
PyObject_Del, /* tp_free */
|
|
};
|
|
|
|
|
|
static PyMethodDef _decimal_methods [] =
|
|
{
|
|
{ "getcontext", (PyCFunction)PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext},
|
|
{ "setcontext", (PyCFunction)PyDec_SetCurrentContext, METH_O, doc_setcontext},
|
|
{ "localcontext", (PyCFunction)ctxmanager_new, METH_VARARGS|METH_KEYWORDS, doc_localcontext},
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
{ "IEEEContext", (PyCFunction)ieee_context, METH_O, doc_ieee_context},
|
|
#endif
|
|
{ NULL, NULL, 1, NULL }
|
|
};
|
|
|
|
static struct PyModuleDef _decimal_module = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"decimal",
|
|
doc__decimal,
|
|
-1,
|
|
_decimal_methods,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
struct ssize_constmap { const char *name; mpd_ssize_t val; };
|
|
static struct ssize_constmap ssize_constants [] = {
|
|
{"MAX_PREC", MPD_MAX_PREC},
|
|
{"MAX_EMAX", MPD_MAX_EMAX},
|
|
{"MIN_EMIN", MPD_MIN_EMIN},
|
|
{"MIN_ETINY", MPD_MIN_ETINY},
|
|
{NULL}
|
|
};
|
|
|
|
struct int_constmap { const char *name; int val; };
|
|
static struct int_constmap int_constants [] = {
|
|
/* int constants */
|
|
#ifdef EXTRA_FUNCTIONALITY
|
|
{"DECIMAL32", MPD_DECIMAL32},
|
|
{"DECIMAL64", MPD_DECIMAL64},
|
|
{"DECIMAL128", MPD_DECIMAL128},
|
|
{"IEEE_CONTEXT_MAX_BITS", MPD_IEEE_CONTEXT_MAX_BITS},
|
|
/* int condition flags */
|
|
{"DecClamped", MPD_Clamped},
|
|
{"DecConversionSyntax", MPD_Conversion_syntax},
|
|
{"DecDivisionByZero", MPD_Division_by_zero},
|
|
{"DecDivisionImpossible", MPD_Division_impossible},
|
|
{"DecDivisionUndefined", MPD_Division_undefined},
|
|
{"DecFpuError", MPD_Fpu_error},
|
|
{"DecInexact", MPD_Inexact},
|
|
{"DecInvalidContext", MPD_Invalid_context},
|
|
{"DecInvalidOperation", MPD_Invalid_operation},
|
|
{"DecIEEEInvalidOperation", MPD_IEEE_Invalid_operation},
|
|
{"DecMallocError", MPD_Malloc_error},
|
|
{"DecFloatOperation", MPD_Float_operation},
|
|
{"DecOverflow", MPD_Overflow},
|
|
{"DecRounded", MPD_Rounded},
|
|
{"DecSubnormal", MPD_Subnormal},
|
|
{"DecUnderflow", MPD_Underflow},
|
|
{"DecErrors", MPD_Errors},
|
|
{"DecTraps", MPD_Traps},
|
|
#endif
|
|
{NULL}
|
|
};
|
|
|
|
|
|
#define CHECK_INT(expr) \
|
|
do { if ((expr) < 0) goto error; } while (0)
|
|
#define ASSIGN_PTR(result, expr) \
|
|
do { result = (expr); if (result == NULL) goto error; } while (0)
|
|
#define CHECK_PTR(expr) \
|
|
do { if ((expr) == NULL) goto error; } while (0)
|
|
|
|
|
|
static PyCFunction
|
|
cfunc_noargs(PyTypeObject *t, const char *name)
|
|
{
|
|
struct PyMethodDef *m;
|
|
|
|
if (t->tp_methods == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
for (m = t->tp_methods; m->ml_name != NULL; m++) {
|
|
if (strcmp(name, m->ml_name) == 0) {
|
|
if (!(m->ml_flags & METH_NOARGS)) {
|
|
goto error;
|
|
}
|
|
return m->ml_meth;
|
|
}
|
|
}
|
|
|
|
error:
|
|
PyErr_Format(PyExc_RuntimeError,
|
|
"internal error: could not find method %s", name);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PyMODINIT_FUNC
|
|
PyInit__decimal(void)
|
|
{
|
|
PyObject *m = NULL;
|
|
PyObject *numbers = NULL;
|
|
PyObject *Number = NULL;
|
|
PyObject *collections = NULL;
|
|
PyObject *MutableMapping = NULL;
|
|
PyObject *obj = NULL;
|
|
DecCondMap *cm;
|
|
struct ssize_constmap *ssize_cm;
|
|
struct int_constmap *int_cm;
|
|
int i;
|
|
|
|
|
|
/* Init libmpdec */
|
|
mpd_traphandler = dec_traphandler;
|
|
mpd_mallocfunc = PyMem_Malloc;
|
|
mpd_reallocfunc = PyMem_Realloc;
|
|
mpd_callocfunc = mpd_callocfunc_em;
|
|
mpd_free = PyMem_Free;
|
|
mpd_setminalloc(_Py_DEC_MINALLOC);
|
|
|
|
|
|
/* Init external C-API functions */
|
|
_py_long_multiply = PyLong_Type.tp_as_number->nb_multiply;
|
|
_py_long_floor_divide = PyLong_Type.tp_as_number->nb_floor_divide;
|
|
_py_long_power = PyLong_Type.tp_as_number->nb_power;
|
|
_py_float_abs = PyFloat_Type.tp_as_number->nb_absolute;
|
|
ASSIGN_PTR(_py_float_as_integer_ratio, cfunc_noargs(&PyFloat_Type,
|
|
"as_integer_ratio"));
|
|
ASSIGN_PTR(_py_long_bit_length, cfunc_noargs(&PyLong_Type, "bit_length"));
|
|
|
|
|
|
/* Init types */
|
|
PyDec_Type.tp_base = &PyBaseObject_Type;
|
|
PyDecContext_Type.tp_base = &PyBaseObject_Type;
|
|
PyDecContextManager_Type.tp_base = &PyBaseObject_Type;
|
|
PyDecSignalDictMixin_Type.tp_base = &PyBaseObject_Type;
|
|
|
|
CHECK_INT(PyType_Ready(&PyDec_Type));
|
|
CHECK_INT(PyType_Ready(&PyDecContext_Type));
|
|
CHECK_INT(PyType_Ready(&PyDecSignalDictMixin_Type));
|
|
CHECK_INT(PyType_Ready(&PyDecContextManager_Type));
|
|
|
|
ASSIGN_PTR(obj, PyUnicode_FromString("decimal"));
|
|
CHECK_INT(PyDict_SetItemString(PyDec_Type.tp_dict, "__module__", obj));
|
|
CHECK_INT(PyDict_SetItemString(PyDecContext_Type.tp_dict,
|
|
"__module__", obj));
|
|
Py_CLEAR(obj);
|
|
|
|
|
|
/* Numeric abstract base classes */
|
|
ASSIGN_PTR(numbers, PyImport_ImportModule("numbers"));
|
|
ASSIGN_PTR(Number, PyObject_GetAttrString(numbers, "Number"));
|
|
/* Register Decimal with the Number abstract base class */
|
|
ASSIGN_PTR(obj, PyObject_CallMethod(Number, "register", "(O)",
|
|
(PyObject *)&PyDec_Type));
|
|
Py_CLEAR(obj);
|
|
/* Rational is a global variable used for fraction comparisons. */
|
|
ASSIGN_PTR(Rational, PyObject_GetAttrString(numbers, "Rational"));
|
|
/* Done with numbers, Number */
|
|
Py_CLEAR(numbers);
|
|
Py_CLEAR(Number);
|
|
|
|
/* DecimalTuple */
|
|
ASSIGN_PTR(collections, PyImport_ImportModule("collections"));
|
|
ASSIGN_PTR(DecimalTuple, (PyTypeObject *)PyObject_CallMethod(collections,
|
|
"namedtuple", "(ss)", "DecimalTuple",
|
|
"sign digits exponent"));
|
|
|
|
ASSIGN_PTR(obj, PyUnicode_FromString("decimal"));
|
|
CHECK_INT(PyDict_SetItemString(DecimalTuple->tp_dict, "__module__", obj));
|
|
Py_CLEAR(obj);
|
|
|
|
/* MutableMapping */
|
|
ASSIGN_PTR(MutableMapping, PyObject_GetAttrString(collections,
|
|
"MutableMapping"));
|
|
/* Create SignalDict type */
|
|
ASSIGN_PTR(PyDecSignalDict_Type,
|
|
(PyTypeObject *)PyObject_CallFunction(
|
|
(PyObject *)&PyType_Type, "s(OO){}",
|
|
"SignalDict", &PyDecSignalDictMixin_Type,
|
|
MutableMapping));
|
|
|
|
/* Done with collections, MutableMapping */
|
|
Py_CLEAR(collections);
|
|
Py_CLEAR(MutableMapping);
|
|
|
|
|
|
/* Create the module */
|
|
ASSIGN_PTR(m, PyModule_Create(&_decimal_module));
|
|
|
|
|
|
/* Add types to the module */
|
|
Py_INCREF(&PyDec_Type);
|
|
CHECK_INT(PyModule_AddObject(m, "Decimal", (PyObject *)&PyDec_Type));
|
|
Py_INCREF(&PyDecContext_Type);
|
|
CHECK_INT(PyModule_AddObject(m, "Context",
|
|
(PyObject *)&PyDecContext_Type));
|
|
Py_INCREF(DecimalTuple);
|
|
CHECK_INT(PyModule_AddObject(m, "DecimalTuple", (PyObject *)DecimalTuple));
|
|
|
|
|
|
/* Create top level exception */
|
|
ASSIGN_PTR(DecimalException, PyErr_NewException(
|
|
"decimal.DecimalException",
|
|
PyExc_ArithmeticError, NULL));
|
|
Py_INCREF(DecimalException);
|
|
CHECK_INT(PyModule_AddObject(m, "DecimalException", DecimalException));
|
|
|
|
/* Create signal tuple */
|
|
ASSIGN_PTR(SignalTuple, PyTuple_New(SIGNAL_MAP_LEN));
|
|
|
|
/* Add exceptions that correspond to IEEE signals */
|
|
for (i = SIGNAL_MAP_LEN-1; i >= 0; i--) {
|
|
PyObject *base;
|
|
|
|
cm = signal_map + i;
|
|
|
|
switch (cm->flag) {
|
|
case MPD_Float_operation:
|
|
base = PyTuple_Pack(2, DecimalException, PyExc_TypeError);
|
|
break;
|
|
case MPD_Division_by_zero:
|
|
base = PyTuple_Pack(2, DecimalException, PyExc_ZeroDivisionError);
|
|
break;
|
|
case MPD_Overflow:
|
|
base = PyTuple_Pack(2, signal_map[INEXACT].ex,
|
|
signal_map[ROUNDED].ex);
|
|
break;
|
|
case MPD_Underflow:
|
|
base = PyTuple_Pack(3, signal_map[INEXACT].ex,
|
|
signal_map[ROUNDED].ex,
|
|
signal_map[SUBNORMAL].ex);
|
|
break;
|
|
default:
|
|
base = PyTuple_Pack(1, DecimalException);
|
|
break;
|
|
}
|
|
|
|
if (base == NULL) {
|
|
goto error; /* GCOV_NOT_REACHED */
|
|
}
|
|
|
|
ASSIGN_PTR(cm->ex, PyErr_NewException(cm->fqname, base, NULL));
|
|
Py_DECREF(base);
|
|
|
|
/* add to module */
|
|
Py_INCREF(cm->ex);
|
|
CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex));
|
|
|
|
/* add to signal tuple */
|
|
Py_INCREF(cm->ex);
|
|
PyTuple_SET_ITEM(SignalTuple, i, cm->ex);
|
|
}
|
|
|
|
/*
|
|
* Unfortunately, InvalidOperation is a signal that comprises
|
|
* several conditions, including InvalidOperation! Naming the
|
|
* signal IEEEInvalidOperation would prevent the confusion.
|
|
*/
|
|
cond_map[0].ex = signal_map[0].ex;
|
|
|
|
/* Add remaining exceptions, inherit from InvalidOperation */
|
|
for (cm = cond_map+1; cm->name != NULL; cm++) {
|
|
PyObject *base;
|
|
if (cm->flag == MPD_Division_undefined) {
|
|
base = PyTuple_Pack(2, signal_map[0].ex, PyExc_ZeroDivisionError);
|
|
}
|
|
else {
|
|
base = PyTuple_Pack(1, signal_map[0].ex);
|
|
}
|
|
if (base == NULL) {
|
|
goto error; /* GCOV_NOT_REACHED */
|
|
}
|
|
|
|
ASSIGN_PTR(cm->ex, PyErr_NewException(cm->fqname, base, NULL));
|
|
Py_DECREF(base);
|
|
|
|
Py_INCREF(cm->ex);
|
|
CHECK_INT(PyModule_AddObject(m, cm->name, cm->ex));
|
|
}
|
|
|
|
|
|
/* Init default context template first */
|
|
ASSIGN_PTR(default_context_template,
|
|
PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
|
|
Py_INCREF(default_context_template);
|
|
CHECK_INT(PyModule_AddObject(m, "DefaultContext",
|
|
default_context_template));
|
|
|
|
#ifdef WITHOUT_THREADS
|
|
/* Init module context */
|
|
ASSIGN_PTR(module_context,
|
|
PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
|
|
Py_INCREF(Py_False);
|
|
CHECK_INT(PyModule_AddObject(m, "HAVE_THREADS", Py_False));
|
|
#else
|
|
ASSIGN_PTR(tls_context_key, PyUnicode_FromString("___DECIMAL_CTX__"));
|
|
Py_INCREF(Py_True);
|
|
CHECK_INT(PyModule_AddObject(m, "HAVE_THREADS", Py_True));
|
|
#endif
|
|
|
|
/* Init basic context template */
|
|
ASSIGN_PTR(basic_context_template,
|
|
PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
|
|
init_basic_context(basic_context_template);
|
|
Py_INCREF(basic_context_template);
|
|
CHECK_INT(PyModule_AddObject(m, "BasicContext",
|
|
basic_context_template));
|
|
|
|
/* Init extended context template */
|
|
ASSIGN_PTR(extended_context_template,
|
|
PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
|
|
init_extended_context(extended_context_template);
|
|
Py_INCREF(extended_context_template);
|
|
CHECK_INT(PyModule_AddObject(m, "ExtendedContext",
|
|
extended_context_template));
|
|
|
|
|
|
/* Init mpd_ssize_t constants */
|
|
for (ssize_cm = ssize_constants; ssize_cm->name != NULL; ssize_cm++) {
|
|
ASSIGN_PTR(obj, PyLong_FromSsize_t(ssize_cm->val));
|
|
CHECK_INT(PyModule_AddObject(m, ssize_cm->name, obj));
|
|
obj = NULL;
|
|
}
|
|
|
|
/* Init int constants */
|
|
for (int_cm = int_constants; int_cm->name != NULL; int_cm++) {
|
|
CHECK_INT(PyModule_AddIntConstant(m, int_cm->name,
|
|
int_cm->val));
|
|
}
|
|
|
|
/* Init string constants */
|
|
for (i = 0; i < _PY_DEC_ROUND_GUARD; i++) {
|
|
ASSIGN_PTR(round_map[i], PyUnicode_InternFromString(mpd_round_string[i]));
|
|
Py_INCREF(round_map[i]);
|
|
CHECK_INT(PyModule_AddObject(m, mpd_round_string[i], round_map[i]));
|
|
}
|
|
|
|
/* Add specification version number */
|
|
CHECK_INT(PyModule_AddStringConstant(m, "__version__", "1.70"));
|
|
CHECK_INT(PyModule_AddStringConstant(m, "__libmpdec_version__", mpd_version()));
|
|
|
|
|
|
return m;
|
|
|
|
|
|
error:
|
|
Py_CLEAR(obj); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(numbers); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(Number); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(Rational); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(collections); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(MutableMapping); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(SignalTuple); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(DecimalTuple); /* GCOV_NOT_REACHED */
|
|
#ifdef WITHOUT_THREADS
|
|
Py_CLEAR(module_context); /* GCOV_NOT_REACHED */
|
|
#else
|
|
Py_CLEAR(default_context_template); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(tls_context_key); /* GCOV_NOT_REACHED */
|
|
#endif
|
|
Py_CLEAR(basic_context_template); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(extended_context_template); /* GCOV_NOT_REACHED */
|
|
Py_CLEAR(m); /* GCOV_NOT_REACHED */
|
|
|
|
return NULL; /* GCOV_NOT_REACHED */
|
|
}
|
|
|
|
_Section(".rodata.pytab.1") const struct _inittab _PyImport_Inittab__decimal = {
|
|
"_decimal",
|
|
PyInit__decimal,
|
|
};
|