aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2011-02-24 14:42:30 -0500
committerMarti Bolivar <mbolivar@leaflabs.com>2011-02-24 15:45:41 -0500
commit4e493c670aaabd8179976621d4b0bf3997fdc814 (patch)
treefebfef70c77381508cbcc19976c5eaf23c35ad09
parent7a5627be405c5f3353f58198ec4437a7e8138dff (diff)
downloadlibrambutan-4e493c670aaabd8179976621d4b0bf3997fdc814.tar.gz
librambutan-4e493c670aaabd8179976621d4b0bf3997fdc814.zip
Rewrote Print class.
The old Print class couldn't print uint64 values, and featured hand-hacked functionality better handled by snprintf(). Redid it using snprintf(), using "[u]int[8,16,32,64]" types for more clarity, and eliminated some private methods in favor of auxiliary functions in Print.cpp. Breaking compatibility with original implementation in three ways: - Print::print(double) is now accurate to 6 digits, rather than 2; this is consistent with the default behavior of the %f format specifier, and if you're using floating point, it's slow enough that you probably want the increased accuracy. - The only bases you can print a number to are 2, 8, 10, and 16. 8, 10, and 16 already have format specifiers, and 2 is an important special case; others complicate matters unnecessarily. - Printing numbers in bases other than 10 treats them as unsigned quantities (i.e., won't print '-' characters). This is more consistent with C++'s behavior for hexadecimal and octal literals (e.g., 0xFFFFFFFF has type uint32). Updated HardwareSerial and USBSerial class documentation to reflect the new behavior.
-rw-r--r--docs/source/lang/api/serial.rst35
-rw-r--r--docs/source/lang/api/serialusb.rst35
-rw-r--r--wirish/Print.cpp245
-rw-r--r--wirish/Print.h87
4 files changed, 235 insertions, 167 deletions
diff --git a/docs/source/lang/api/serial.rst b/docs/source/lang/api/serial.rst
index ca89b31..58002e3 100644
--- a/docs/source/lang/api/serial.rst
+++ b/docs/source/lang/api/serial.rst
@@ -113,25 +113,34 @@ means that you can use any of these functions on any of ``Serial1``,
Print the argument's digits over the USART, in decimal format.
-.. cpp:function:: HardwareSerial::print(long n)
+.. cpp:function:: HardwareSerial::print(long long n)
Print the argument's digits over the USART, in decimal format.
Negative values will be prefixed with a ``'-'`` character.
-.. cpp:function:: HardwareSerial::print(unsigned long n)
+.. cpp:function:: HardwareSerial::print(unsigned long long n)
Print the argument's digits over the USART, in decimal format.
-.. cpp:function:: HardwareSerial::print(long n, int base)
+.. _lang-serial-print-n-base:
- Print the digits of ``n`` over the USART, in base ``base`` (which
- may be between 2 and 16). The ``base`` value 2 corresponds to
- binary, 8 to octal, 10 to decimal, and 16 to hexadecimal. Negative
- values will be prefixed with a ``'-'`` character.
+.. cpp:function:: HardwareSerial::print(int n, int base)
+
+ Print the digits of ``n`` over the USART, in base ``base``. The
+ ``base`` value 2 corresponds to binary, 8 to octal, 10 to decimal,
+ and 16 to hexadecimal (you can also use the symbolic constants
+ ``BIN``, ``OCT``, ``DEC``, ``HEX``). If ``base`` is 10, negative
+ values will be prefixed with a ``'-'`` character (otherwise, ``n``
+ will be interpreted as an unsigned quantity).
+
+.. cpp:function:: HardwareSerial::print(long long n, int base)
+
+ Same behavior as the above :ref:`print(int n, int base)
+ <lang-serial-print-n-base>`, except with 64-bit values.
.. cpp:function:: HardwareSerial::print(double n)
- Print ``n``, accurate to 2 digits after the decimal point.
+ Print ``n``, accurate to 6 digits after the decimal point.
.. _lang-serial-println:
@@ -155,15 +164,19 @@ means that you can use any of these functions on any of ``Serial1``,
Like ``print(n)``, followed by ``"\r\n"``.
-.. cpp:function:: HardwareSerial::println(long n)
+.. cpp:function:: HardwareSerial::println(long long n)
Like ``print(n)``, followed by ``"\r\n"``.
-.. cpp:function:: HardwareSerial::println(unsigned long n)
+.. cpp:function:: HardwareSerial::println(unsigned long long n)
Like ``print(n)``, followed by ``"\r\n"``.
-.. cpp:function:: HardwareSerial::println(long n, int base)
+.. cpp:function:: HardwareSerial::println(int n, int base)
+
+ Like ``print(n, b)``, followed by ``"\r\n"``.
+
+.. cpp:function:: HardwareSerial::println(long long n, int base)
Like ``print(n, b)``, followed by ``"\r\n"``.
diff --git a/docs/source/lang/api/serialusb.rst b/docs/source/lang/api/serialusb.rst
index 3bb8c00..87fa641 100644
--- a/docs/source/lang/api/serialusb.rst
+++ b/docs/source/lang/api/serialusb.rst
@@ -109,26 +109,35 @@ world!")``.
Print the argument's digits over the USB connection, in decimal format.
-.. cpp:function:: USBSerial::print(long n)
+.. cpp:function:: USBSerial::print(long long n)
Print the argument's digits over the USB connection, in decimal
format. Negative values will be prefixed with a ``'-'`` character.
-.. cpp:function:: USBSerial::print(unsigned long n)
+.. cpp:function:: USBSerial::print(unsigned long long n)
Print the argument's digits over the USB connection, in decimal
format.
-.. cpp:function:: USBSerial::print(long n, int base)
+.. _lang-serial-print-n-base:
- Print the digits of ``n`` over the USB connection, in base ``base``
- (which may be between 2 and 16). The ``base`` value 2 corresponds
- to binary, 8 to octal, 10 to decimal, and 16 to hexadecimal.
- Negative values will be prefixed with a ``'-'`` character.
+.. cpp:function:: USBSerial::print(int n, int base)
+
+ Print the digits of ``n`` over USB, in base ``base``. The ``base``
+ value 2 corresponds to binary, 8 to octal, 10 to decimal, and 16 to
+ hexadecimal (you can also use the symbolic constants ``BIN``,
+ ``OCT``, ``DEC``, ``HEX``). If ``base`` is 10, negative values
+ will be prefixed with a ``'-'`` character (otherwise, ``n`` will be
+ interpreted as an unsigned quantity).
+
+.. cpp:function:: HardwareSerial::print(long long n, int base)
+
+ Same behavior as the above :ref:`print(int n, int base)
+ <lang-serialusb-print-n-base>`, except with 64-bit values.
.. cpp:function:: USBSerial::print(double n)
- Print ``n``, accurate to 2 digits after the decimal point.
+ Print ``n``, accurate to 6 digits after the decimal point.
.. _lang-serialusb-println:
@@ -152,15 +161,19 @@ world!")``.
Like ``print(n)``, followed by ``"\r\n"``.
-.. cpp:function:: USBSerial::println(long n)
+.. cpp:function:: USBSerial::println(long long n)
Like ``print(n)``, followed by ``"\r\n"``.
-.. cpp:function:: USBSerial::println(unsigned long n)
+.. cpp:function:: USBSerial::println(unsigned long long n)
Like ``print(n)``, followed by ``"\r\n"``.
-.. cpp:function:: USBSerial::println(long n, int base)
+.. cpp:function:: USBSerial::println(int n, int base)
+
+ Like ``print(n, b)``, followed by ``"\r\n"``.
+
+.. cpp:function:: USBSerial::println(long long n, int base)
Like ``print(n, b)``, followed by ``"\r\n"``.
diff --git a/wirish/Print.cpp b/wirish/Print.cpp
index c66ca61..9c52321 100644
--- a/wirish/Print.cpp
+++ b/wirish/Print.cpp
@@ -1,91 +1,127 @@
-/*
- * Print.cpp - Base class that provides print() and println()
- * Copyright (c) 2008 David A. Mellis. All right reserved.
+/******************************************************************************
+ * The MIT License
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
+ * Copyright (c) 2011 LeafLabs, LLC.
*
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
*
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
*
- * Modified 23 November 2006 by David A. Mellis
- */
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
-#include "wirish.h"
#include "Print.h"
-//------------------------------ Public Methods -------------------------------
+#include <cstdio>
+#include <climits>
+#include <cstring>
+
+// We'll allocate character buffers of size INT_BUF_SIZE to hold the
+// string representations of numbers; this value ensures that they're
+// big enough to accomodate the biggest integral value + null byte.
+//
+// E.g., consider -(2^63-1) = -9223372036854775807, which is 20
+// characters long, including the minus sign. The other edge cases
+// are similar.
+//
+// (Nonetheless, use snprintf everywhere, just in case of error).
+#define INT_BUF_SIZE 21
+
+// An IEEE-754 double buys you about 16 digits of precision; there's
+// the possibility of minus signs, a decimal point, 'e+'/'e-', etc.
+// While the Right Thing is to follow Steele and White, I'm just going
+// to double what I consider a safe number of bytes and hope for the
+// best.
+#define DOUBLE_BUF_SIZE 40
+
+static void fillBase(char *buf, int buf_size, int64 n,
+ uint8 n_real_bits, int base);
+static void fillBinary(char *buf, int64 n, int start_bit);
+static char baseToFmtSpec(int base);
void Print::write(const char *str) {
- while (*str)
- write(*str++);
+ for (const char *c = str; *c != '\0'; c++) {
+ write(*c);
+ }
}
void Print::write(void *buffer, uint32 size) {
- uint8 *ch = (uint8*)buffer;
- while (size--) {
- write(*ch++);
+ for (uint32 i = 0; i < size; i++) {
+ write(*((uint8*)buffer + i));
}
}
-void Print::print(uint8 b) {
- this->write(b);
-}
-
void Print::print(char c) {
- print((byte) c);
+ print((uint8) c);
}
void Print::print(const char str[]) {
write(str);
}
-void Print::print(int n) {
- print((long) n);
+void Print::print(uint8 b) {
+ write(b);
}
-void Print::print(unsigned int n) {
- print((unsigned long) n);
+void Print::print(int32 n) {
+ print(n, DEC);
}
-void Print::print(long n) {
- if (n < 0) {
- print('-');
- n = -n;
- }
- printNumber(n, 10);
+void Print::print(uint32 n) {
+ print((uint64) n);
}
-void Print::print(unsigned long n) {
- printNumber(n, 10);
+void Print::print(int64 n) {
+ print(n, DEC);
}
-void Print::print(long n, int base) {
- if (base == 0) {
- print((char) n);
- } else if (base == 10) {
- print(n);
- } else {
- printNumber(n, base);
- }
+void Print::print(uint64 n) {
+ char buf[INT_BUF_SIZE];
+ snprintf(buf, INT_BUF_SIZE, "%llu", n);
+ write(buf);
+}
+
+void Print::print(int32 n, int base) {
+ // Worst case: sign bit set && base == BIN: 32 bytes for digits +
+ // 1 null (base == BIN means no minus sign).
+ char buf[33];
+ fillBase(buf, sizeof(buf), (int64)n, 32, base);
+ write(buf);
+}
+
+void Print::print(int64 n, int base) {
+ // As above, but now 64 bytes for bits + 1 null
+ char buf[65];
+ fillBase(buf, sizeof(buf), n, 64, base);
+ write(buf);
}
void Print::print(double n) {
- printFloat(n, 2);
+ char buf[DOUBLE_BUF_SIZE];
+ // This breaks strict compliance with the Arduino library behavior
+ // (which is equivalent to using "%.2f"), but that's really not
+ // enough. According to Stroustrup, "%f" without precision is
+ // equivalent to ".6f", which is much better.
+ snprintf(buf, DOUBLE_BUF_SIZE, "%f", n);
+ write(buf);
}
void Print::println(void) {
- print('\r');
- print('\n');
+ print("\r\n");
}
void Print::println(char c) {
@@ -103,27 +139,32 @@ void Print::println(uint8 b) {
println();
}
-void Print::println(int n) {
+void Print::println(int32 n) {
print(n);
println();
}
-void Print::println(unsigned int n) {
+void Print::println(uint32 n) {
print(n);
println();
}
-void Print::println(long n) {
+void Print::println(int64 n) {
print(n);
println();
}
-void Print::println(unsigned long n) {
+void Print::println(uint64 n) {
print(n);
println();
}
-void Print::println(long n, int base) {
+void Print::println(int32 n, int base) {
+ print(n, base);
+ println();
+}
+
+void Print::println(int64 n, int base) {
print(n, base);
println();
}
@@ -133,58 +174,54 @@ void Print::println(double n) {
println();
}
-//------------------------------ Private Methods ------------------------------
+// -- Auxiliary functions -----------------------------------------------------
-void Print::printNumber(unsigned long n, uint8 base) {
- unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
- unsigned long i = 0;
-
- if (n == 0) {
- print('0');
- return;
- }
-
- while (n > 0) {
- buf[i++] = n % base;
- n /= base;
+static void fillBase(char *buf, int buf_size, int64 n,
+ uint8 n_real_bits, int base) {
+ if (base == BIN) {
+ fillBinary(buf, n, n_real_bits - 1);
+ } else {
+ char spec = baseToFmtSpec(base);
+ char fmt[5];
+
+ if (base == BYTE)
+ n = (uint8)n;
+
+ if (n_real_bits == 32) {
+ snprintf(fmt, sizeof(fmt), "%%l%c", spec);
+ snprintf(buf, buf_size, fmt, (int32)n);
+ } else {
+ snprintf(fmt, sizeof(fmt), "%%ll%c", spec);
+ snprintf(buf, buf_size, fmt, n);
+ }
}
-
- for (; i > 0; i--)
- print((char) (buf[i - 1] < 10 ?
- '0' + buf[i - 1] :
- 'A' + buf[i - 1] - 10));
}
-void Print::printFloat(double number, uint8 digits) {
- // Handle negative numbers
- if (number < 0.0) {
- print('-');
- number = -number;
+// Assumes sizeof(buf) > start_bit.
+static void fillBinary(char *buf, int64 n, int start_bit) {
+ int b = 0; // position in buf
+ int i = start_bit; // position in n's bits
+ while(!(n & (1 << i))) {
+ i--;
}
-
- // Round correctly so that print(1.999, 2) prints as "2.00"
- double rounding = 0.5;
- for (uint8 i=0; i<digits; ++i) {
- rounding /= 10.0;
- }
-
- number += rounding;
-
- // Extract the integer part of the number and print it
- unsigned long int_part = (unsigned long)number;
- double remainder = number - (double)int_part;
- print(int_part);
-
- // Print the decimal point, but only if there are digits beyond
- if (digits > 0) {
- print(".");
+ for(; i >= 0; i--) {
+ buf[b++] = '0' + ((n >> i) & 0x1);
}
-
- // Extract digits from the remainder one at a time
- while (digits-- > 0) {
- remainder *= 10.0;
- int toPrint = int(remainder);
- print(toPrint);
- remainder -= toPrint;
+ buf[b] = '\0';
+}
+
+static char baseToFmtSpec(int base) {
+ switch (base) {
+ case DEC:
+ return 'd';
+ case HEX:
+ return 'x';
+ case OCT:
+ return 'o';
+ case BYTE:
+ return 'd';
+ default:
+ // Shouldn't happen, but give a sensible default
+ return 'd';
}
}
diff --git a/wirish/Print.h b/wirish/Print.h
index dc21183..4527948 100644
--- a/wirish/Print.h
+++ b/wirish/Print.h
@@ -1,62 +1,67 @@
-/*
- * Print.h - Base class that provides print() and println()
- * Copyright (c) 2008 David A. Mellis. All right reserved.
+/******************************************************************************
+ * The MIT License
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
+ * Copyright (c) 2011 LeafLabs, LLC.
*
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
*
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA.
- */
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
-#ifndef Print_h
-#define Print_h
+#ifndef _PRINT_H_
+#define _PRINT_H_
-#include <libmaple_types.h>
-#include <stdio.h> // for size_t
+#include "libmaple_types.h"
-#define DEC 10
-#define HEX 16
-#define OCT 8
-#define BIN 2
-#define BYTE 0
+#define DEC 10
+#define HEX 16
+#define OCT 8
+#define BIN 2
+#define BYTE 0 // yuck
-class Print
-{
- private:
- void printNumber(unsigned long, uint8);
- void printFloat(double, uint8);
+class Print {
public:
virtual void write(uint8) = 0;
virtual void write(const char *str);
- virtual void write(void *, uint32);
+ virtual void write(void *buf, uint32 size);
+
void print(char);
void print(const char[]);
void print(uint8);
- void print(int);
- void print(unsigned int);
- void print(long);
- void print(unsigned long);
- void print(long, int);
+ void print(int32);
+ void print(uint32);
+ void print(int64);
+ void print(uint64);
+ void print(int32, int);
+ void print(int64, int);
void print(double);
+
void println(void);
void println(char);
void println(const char[]);
void println(uint8);
- void println(int);
- void println(unsigned int);
- void println(long);
- void println(unsigned long);
- void println(long, int);
+ void println(int32);
+ void println(uint32);
+ void println(int64);
+ void println(uint64);
+ void println(int32, int);
+ void println(int64, int);
void println(double);
};