From 88d7bba961cb38c594d72b6be5847cb1106f91b2 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Tue, 8 Apr 2014 23:30:46 +0100
Subject: [PATCH] py: Make it so that printing a small int does not allocate
 heap memory.

With the implementation of proper string formatting, code to print a
small int was delegated to mpz_as_str_inpl (after first converting the
small int to an mpz using stack memory).  But mpz_as_str_inpl allocates
heap memory to do the conversion, so small ints needed heap memory just
to be printed.

This fix has a separate function to print small ints, which does not
allocate heap, and allocates less stack.

String formatting, printf and pfenv are now large beasts, with some
semi-duplicated code.
---
 py/objint.c          | 44 ++++++++++++++++++++++++--------------------
 py/objint.h          |  2 ++
 py/objint_longlong.c |  8 ++++++++
 py/objint_mpz.c      | 26 +++++++++-----------------
 4 files changed, 43 insertions(+), 37 deletions(-)

diff --git a/py/objint.c b/py/objint.c
index 05269ce37..69954168d 100644
--- a/py/objint.c
+++ b/py/objint.c
@@ -70,15 +70,13 @@ void mp_obj_int_print(void (*print)(void *env, const char *fmt, ...), void *env,
     }
 }
 
-#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE || MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG
-
 #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG
-typedef mp_longint_impl_t   fmt_int_t;
+typedef mp_longint_impl_t fmt_int_t;
 #else
-typedef mp_small_int_t   fmt_int_t;
+typedef mp_small_int_t fmt_int_t;
 #endif
 
-static const uint log_base2_floor[] = {
+STATIC const uint log_base2_floor[] = {
     0,
     0, 1, 1, 2,
     2, 2, 2, 3,
@@ -90,7 +88,7 @@ static const uint log_base2_floor[] = {
     4, 4, 4, 5
 };
 
-uint int_as_str_size_formatted(uint base, const char *prefix, char comma) {
+STATIC uint int_as_str_size_formatted(uint base, const char *prefix, char comma) {
     if (base < 2 || base > 32) {
         return 0;
     }
@@ -110,22 +108,29 @@ uint int_as_str_size_formatted(uint base, const char *prefix, char comma) {
 // formatted size will be in *fmt_size.
 char *mp_obj_int_formatted(char **buf, int *buf_size, int *fmt_size, mp_obj_t self_in,
                            int base, const char *prefix, char base_char, char comma) {
-    if (!MP_OBJ_IS_INT(self_in)) {
-        buf[0] = '\0';
-        *fmt_size = 0;
-        return *buf;
-    }
     fmt_int_t num;
+    if (MP_OBJ_IS_SMALL_INT(self_in)) {
+        // A small int; get the integer value to format.
+        num = mp_obj_get_int(self_in);
+#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
+    } else if (MP_OBJ_IS_TYPE(self_in, &mp_type_int)) {
+        // Not a small int.
 #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG
-    mp_obj_int_t *self = self_in;
-    if (MP_OBJ_IS_TYPE(self_in, &mp_type_int)) {
-        // mp_obj_get_int truncates to machine_int_t
+        mp_obj_int_t *self = self_in;
+        // Get the value to format; mp_obj_get_int truncates to machine_int_t.
         num = self->val;
-    } else
+#else
+        // Delegate to the implementation for the long int.
+        return mp_obj_int_formatted_impl(buf, buf_size, fmt_size, self_in, base, prefix, base_char, comma);
 #endif
-    {
-        num = mp_obj_get_int(self_in);
+#endif
+    } else {
+        // Not an int.
+        buf[0] = '\0';
+        *fmt_size = 0;
+        return *buf;
     }
+
     char sign = '\0';
     if (num < 0) {
         num = -num;
@@ -180,12 +185,11 @@ char *mp_obj_int_formatted(char **buf, int *buf_size, int *fmt_size, mp_obj_t se
     return b;
 }
 
+#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE
+
 bool mp_obj_int_is_positive(mp_obj_t self_in) {
     return mp_obj_get_int(self_in) >= 0;
 }
-#endif  // LONGLONG or NONE
-
-#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE
 
 // This is called for operations on SMALL_INT that are not handled by mp_unary_op
 mp_obj_t mp_obj_int_unary_op(int op, mp_obj_t o_in) {
diff --git a/py/objint.h b/py/objint.h
index 7ee476269..5b21d55fe 100644
--- a/py/objint.h
+++ b/py/objint.h
@@ -10,6 +10,8 @@ typedef struct _mp_obj_int_t {
 void mp_obj_int_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind);
 char *mp_obj_int_formatted(char **buf, int *buf_size, int *fmt_size, mp_obj_t self_in,
                            int base, const char *prefix, char base_char, char comma);
+char *mp_obj_int_formatted_impl(char **buf, int *buf_size, int *fmt_size, mp_obj_t self_in,
+                                int base, const char *prefix, char base_char, char comma);
 bool mp_obj_int_is_positive(mp_obj_t self_in);
 mp_obj_t mp_obj_int_unary_op(int op, mp_obj_t o_in);
 mp_obj_t mp_obj_int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in);
diff --git a/py/objint_longlong.c b/py/objint_longlong.c
index 332f0bbb8..24435415f 100644
--- a/py/objint_longlong.c
+++ b/py/objint_longlong.c
@@ -22,6 +22,14 @@
 #define SUFFIX ""
 #endif
 
+bool mp_obj_int_is_positive(mp_obj_t self_in) {
+    if (MP_OBJ_IS_SMALL_INT(self_in)) {
+        return MP_OBJ_SMALL_INT_VALUE(self_in) >= 0;
+    }
+    mp_obj_int_t *self = self_in;
+    return self->val >= 0;
+}
+
 mp_obj_t mp_obj_int_unary_op(int op, mp_obj_t o_in) {
     mp_obj_int_t *o = o_in;
     switch (op) {
diff --git a/py/objint_mpz.c b/py/objint_mpz.c
index 6410ecc64..583ce4cb7 100644
--- a/py/objint_mpz.c
+++ b/py/objint_mpz.c
@@ -1,6 +1,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
+#include <assert.h>
 
 #include "nlr.h"
 #include "misc.h"
@@ -29,30 +30,21 @@ STATIC mp_obj_int_t *mp_obj_int_new_mpz(void) {
 //
 // The resulting formatted string will be returned from this function and the
 // formatted size will be in *fmt_size.
-char *mp_obj_int_formatted(char **buf, int *buf_size, int *fmt_size, mp_obj_t self_in,
-                           int base, const char *prefix, char base_char, char comma) {
-    mpz_t small_mpz;
-    mpz_t *mpz;
-    mpz_dig_t small_dig[(sizeof(mp_small_int_t) * 8 + MPZ_DIG_SIZE - 1) / MPZ_DIG_SIZE];
-
-    if (MP_OBJ_IS_SMALL_INT(self_in)) {
-        mpz_init_fixed_from_int(&small_mpz, small_dig,
-                                sizeof(small_dig) / sizeof(small_dig[0]),
-                                MP_OBJ_SMALL_INT_VALUE(self_in));
-        mpz = &small_mpz;
-    } else {
-        mp_obj_int_t *self = self_in;
-        mpz = &self->mpz;
-    }
+//
+// This particular routine should only be called for the mpz representation of the int.
+char *mp_obj_int_formatted_impl(char **buf, int *buf_size, int *fmt_size, mp_obj_t self_in,
+                                int base, const char *prefix, char base_char, char comma) {
+    assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int));
+    mp_obj_int_t *self = self_in;
 
-    uint needed_size = mpz_as_str_size_formatted(mpz, base, prefix, comma);
+    uint needed_size = mpz_as_str_size_formatted(&self->mpz, base, prefix, comma);
     if (needed_size > *buf_size) {
         *buf = m_new(char, needed_size);
         *buf_size = needed_size;
     }
     char *str = *buf;
 
-    *fmt_size = mpz_as_str_inpl(mpz, base, prefix, base_char, comma, str);
+    *fmt_size = mpz_as_str_inpl(&self->mpz, base, prefix, base_char, comma, str);
 
     return str;
 }
-- 
GitLab