Skip to content

Commit

Permalink
Support OCT and BIN radix for read and print
Browse files Browse the repository at this point in the history
  • Loading branch information
tgtakaoka committed Jan 5, 2025
1 parent 4df3302 commit cd280ce
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 60 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@ course we can use `Serial.available()` in `loop()` and carefully use
So `libcli` library comes for help. This library offers a Cli instance
as a command line interface which can

- read a letter, a word string, a line of string, decimal and
hexadecimal number from serial console asynchronously.
- read a letter, a word string, a line of string, decimal,
hexadecimal, octal, and binary number from serial console
asynchronously.

- act as `Stream` and do any form of `print()` and `println()`.

- print decimal number with left or right aligned in a specified width
format, such as `"%-6d"` or `"%6d"` in `printf()`.

- print hexadecimal number in specified fixed-width format, such as
`"%08X"` in `printf()`.
- print hexadecimal, octal, and binary number in specified
fixed-width format, such as `"%08X"` `"%04O"` in `printf()`.

- print string with left or right aligned in a specified width, such
as `"%-8s"` or `"%8s"` in `printf()`.
Expand Down Expand Up @@ -87,16 +88,20 @@ void readHex(NumberCallback callback, uintptr_t context, uint32_t limit = UINT32
void readHex(NumberCallback callback, uintptr_t context, uint32_t limit, uint32_t defVal);
void readDec(NumberCallback callback, uintptr_t context, uint23_t limit = UINt32_MAX);
void readDec(NumberCallback callback, uintptr_t context, uint23_t limit, uint32_t defVal);
void readNum(NumberCallback callback, uintptr_t context, uint8_t radix = 10, uint23_t limit = UINt32_MAX);
void readNum(NumberCallback callback, uintptr_t context, uint8_t radix = 10, uint23_t limit, uint32_t defVal);
void printStr(const char *text, int8_t width = 0);
void printStr(const __FlashStringHelper *text, int8_t width = 0);
void printStr_P(const /*PROGMEM*/ char *text_P, int8_t width = 0);
void printHex(uint32_t number, int8_t width = 0);
void printDec(uint32_t number, int8_t width = 0);
void printNum(uint32_t number, uint8_t radix = 10, int8_t width = 0);
void printlnStr(const __FlashStringHelper *text, int8_t width = 0);
void printlnStr_P(const /*PROGMEM*/ char *text_P, int8_t width = 0);
void printlnHex(uint32_t number, int8_t width = 0);
void printlnDec(uint32_t number, int8_t width = 0);
void printlnNum(uint32_t number, uint8_t radix = 10, int8_t width = 0);
void backspace(int8_t n = 1);
```

Expand Down
13 changes: 9 additions & 4 deletions README_.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ course we can use `Serial.available()` in `loop()` and carefully use
So `libcli` library comes for help. This library offers a Cli instance
as a command line interface which can

* read a letter, a word string, a line of string, decimal and
hexadecimal number from serial console asynchronously.
* read a letter, a word string, a line of string, decimal,
hexadecimal, octal and binary number from serial console
asynchronously.
* act as `Stream` and do any form of `print()` and `println()`.
* print decimal number with left or right aligned in a specified width
format, such as `"%-6d"` or `"%6d"` in `printf()`.
* print hexadecimal number in specified fixed-width format, such as
`"%08X"` in `printf()`.
* print hexadecimal, octal, and binary number in specified fixed-width
format, such as `"%08X", "%04O"` in `printf()`.
* print string with left or right aligned in a specified width, such
as `"%-8s"` or `"%8s"` in `printf()`.

Expand Down Expand Up @@ -85,16 +86,20 @@ void readHex(NumberCallback callback, uintptr_t context, uint32_t limit = UINT32
void readHex(NumberCallback callback, uintptr_t context, uint32_t limit, uint32_t defVal);
void readDec(NumberCallback callback, uintptr_t context, uint23_t limit = UINt32_MAX);
void readDec(NumberCallback callback, uintptr_t context, uint23_t limit, uint32_t defVal);
void readNum(NumberCallback callback, uintptr_t context, uint8_t radix = 10, uint23_t limit = UINt32_MAX);
void readNum(NumberCallback callback, uintptr_t context, uint8_t radix, uint23_t limit, uint32_t defVal);
void printStr(const char *text, int8_t width = 0);
void printStr(const __FlashStringHelper *text, int8_t width = 0);
void printStr_P(const /*PROGMEM*/ char *text_P, int8_t width = 0);
void printHex(uint32_t number, int8_t width = 0);
void printDec(uint32_t number, int8_t width = 0);
void printNum(uint32_t number, uint8_t radix = 10, int8_t width = 0);
void printlnStr(const __FlashStringHelper *text, int8_t width = 0);
void printlnStr_P(const /*PROGMEM*/ char *text_P, int8_t width = 0);
void printlnHex(uint32_t number, int8_t width = 0);
void printlnDec(uint32_t number, int8_t width = 0);
void printlnNum(uint32_t number, uint8_t radix = 10, int8_t width = 0);
void backspace(int8_t n = 1);
----

Expand Down
33 changes: 25 additions & 8 deletions src/libcli/libcli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ Cli &Cli::instance() {
}

size_t Cli::printHex(uint32_t number, int8_t width) {
return _impl.printHex(number, width, false);
return _impl.printNum(number, width, 16, false);
}

size_t Cli::printDec(uint32_t number, int8_t width) {
return _impl.printDec(number, width, false);
return _impl.printNum(number, width, 10, false);
}

size_t Cli::printNum(uint32_t number, uint8_t radix, int8_t width) {
return _impl.printNum(number, width, radix, false);
}

size_t Cli::printStr(const __FlashStringHelper *text, int8_t width) {
Expand All @@ -48,11 +52,15 @@ size_t Cli::printStr_P(const /*PROGMEM*/ char *text_P, int8_t width) {
}

size_t Cli::printlnHex(uint32_t number, int8_t width) {
return _impl.printHex(number, width, true);
return _impl.printNum(number, width, 16, true);
}

size_t Cli::printlnDec(uint32_t number, int8_t width) {
return _impl.printDec(number, width, true);
return _impl.printNum(number, width, 10, true);
}

size_t Cli::printlnNum(uint32_t number, uint8_t radix, int8_t width) {
return _impl.printNum(number, width, radix, true);
}

size_t Cli::printlnStr(const __FlashStringHelper *text, int8_t width) {
Expand Down Expand Up @@ -86,19 +94,28 @@ void Cli::readLine(
}

void Cli::readHex(NumberCallback callback, uintptr_t context, uint32_t limit) {
_impl.setCallback(callback, context, limit, true);
_impl.setCallback(callback, context, limit, 16);
}

void Cli::readHex(NumberCallback callback, uintptr_t context, uint32_t limit, uint32_t defval) {
_impl.setCallback(callback, context, limit, defval, true);
_impl.setCallback(callback, context, limit, defval, 16);
}

void Cli::readDec(NumberCallback callback, uintptr_t context, uint32_t limit) {
_impl.setCallback(callback, context, limit, false);
_impl.setCallback(callback, context, limit, 10);
}

void Cli::readDec(NumberCallback callback, uintptr_t context, uint32_t limit, uint32_t defval) {
_impl.setCallback(callback, context, limit, defval, false);
_impl.setCallback(callback, context, limit, defval, 10);
}

void Cli::readNum(NumberCallback callback, uintptr_t context, uint8_t radix, uint32_t limit) {
_impl.setCallback(callback, context, limit, radix);
}

void Cli::readNum(NumberCallback callback, uintptr_t context, uint8_t radix, uint32_t limit,
uint32_t defval) {
_impl.setCallback(callback, context, limit, defval, radix);
}

} // namespace libcli
Expand Down
56 changes: 18 additions & 38 deletions src/libcli/libcli_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ bool isNewline(char c) {
}

/** Returns number of digits of |number| in |radix|. */
int8_t getDigits(uint32_t number, bool hex) {
const uint8_t radix = hex ? 16 : 10;
int8_t getDigits(uint32_t number, uint8_t radix) {
int8_t n = 0;
do {
n++;
Expand All @@ -62,20 +61,10 @@ size_t Impl::pad_right(int8_t len, int8_t width, char pad) {
return size;
}

size_t Impl::printHex(uint32_t number, int8_t width, bool newline) {
const auto len = getDigits(number, true);
size_t size = pad_left(len, width, '0');
size += console->print(number, HEX);
size += pad_right(len, width, ' ');
if (newline)
size += console->println();
return size;
}

size_t Impl::printDec(uint32_t number, int8_t width, bool newline) {
const auto len = getDigits(number, false);
size_t size = pad_left(len, width, ' ');
size += console->print(number, DEC);
size_t Impl::printNum(uint32_t number, int8_t width, uint8_t radix, bool newline) {
const auto len = getDigits(number, radix);
size_t size = pad_left(len, width, radix == 10 ? ' ' : '0');
size += console->print(number, radix);
size += pad_right(len, width, ' ');
if (newline)
size += console->println();
Expand Down Expand Up @@ -160,37 +149,33 @@ void Impl::processString(char c) {
}
}

void Impl::setCallback(NumberCallback callback, uintptr_t context, uint32_t limit, bool hex) {
void Impl::setCallback(NumberCallback callback, uintptr_t context, uint32_t limit, uint8_t radix) {
this->callback.number = callback;
num_width = getDigits(num_limit = limit, num_hex = hex);
num_width = getDigits(num_limit = limit, num_radix = radix);
num_value = 0;
num_len = 0;
setProcessor(&Impl::processNumber, context);
}

void Impl::setCallback(
NumberCallback callback, uintptr_t context, uint32_t limit, uint32_t defval, bool hex) {
setCallback(callback, context, limit, hex);
void Impl::setCallback(NumberCallback callback, uintptr_t context, uint32_t limit, uint32_t defval,
uint8_t radix) {
setCallback(callback, context, limit, radix);
backspace(num_width);
num_value = defval;
num_len = num_width;
if (hex) {
printHex(num_value, num_len, false);
} else {
printDec(num_value, num_len, false);
}
printNum(num_value, num_len, radix, false);
}

bool Impl::checkLimit(char c, uint8_t &n) const {
if (num_len >= num_width)
return false;
c = toUpperCase(c);
if (!num_hex && isDigit(c)) {
if (num_radix <= 10 && isDigit(c) && c < num_radix + '0') {
n = c - '0';
const uint32_t limit = num_limit / 10;
return num_value < limit || (num_value == limit && n <= (num_limit % 10));
const uint32_t limit = num_limit / num_radix;
return num_value < limit || (num_value == limit && n <= (num_limit % num_radix));
}
if (num_hex && isHexadecimalDigit(c)) {
if (num_radix == 16 && isHexadecimalDigit(c)) {
n = isDigit(c) ? c - '0' : c - 'A' + 10;
const uint32_t limit = num_limit / 16;
return num_value < limit || (num_value == limit && n <= (num_limit % 16));
Expand All @@ -199,10 +184,9 @@ bool Impl::checkLimit(char c, uint8_t &n) const {
}

void Impl::processNumber(char c) {
const uint8_t radix = num_hex ? 16 : 10;
uint8_t n;
if (checkLimit(c, n)) {
num_value *= radix;
num_value *= num_radix;
num_value += n;
num_len++;
console->print(c);
Expand All @@ -212,7 +196,7 @@ void Impl::processNumber(char c) {
State state;
if (isBackspace(c)) {
if (num_len) {
num_value /= radix;
num_value /= num_radix;
num_len--;
backspace(1);
return;
Expand All @@ -221,11 +205,7 @@ void Impl::processNumber(char c) {
} else if (isSpace(c) && num_len) {
backspace(num_len);
num_len = num_width;
if (num_hex) {
printHex(num_value, num_len, false);
} else {
printDec(num_value, num_len, false);
}
printNum(num_value, num_len, num_radix, false);
if (isNewline(c)) {
console->print(' ');
state = CLI_NEWLINE;
Expand Down
11 changes: 5 additions & 6 deletions src/libcli/libcli_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ struct Impl final {
void setCallback(LetterCallback callback, uintptr_t context);
void setCallback(StringCallback callback, uintptr_t context, char *buffer, size_t size,
bool hasDefval, bool word);
void setCallback(NumberCallback callback, uintptr_t context, uint32_t limit, bool hex);
void setCallback(NumberCallback callback, uintptr_t context, uint32_t limit,
uint32_t defval, bool hex);
void setCallback(NumberCallback callback, uintptr_t context, uint32_t limit, uint8_t radix);
void setCallback(NumberCallback callback, uintptr_t context, uint32_t limit, uint32_t defval,
uint8_t radix);

size_t backspace(int8_t n);
size_t printHex(uint32_t number, int8_t width, bool newline);
size_t printDec(uint32_t number, int8_t width, bool newline);
size_t printNum(uint32_t number, int8_t width, uint8_t radix, bool newline);
size_t printStr(const __FlashStringHelper *str, int8_t width, bool newline);
size_t printStr(const char *str, int8_t width, bool newline);

Expand Down Expand Up @@ -82,7 +81,7 @@ struct Impl final {

uint32_t num_value;
uint32_t num_limit;
bool num_hex;
uint8_t num_radix;
uint8_t num_len;
uint8_t num_width;

Expand Down
68 changes: 68 additions & 0 deletions test/PrintTest/PrintTest.ino
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,74 @@ test(printTest, printlnDec) {
stream.flush();
}

test(printTest, printNum) {
FakeStream stream;
Cli cli;
cli.begin(stream);

assertEqual(cli.printNum(1234), (size_t)4);
assertEqual(stream.printerText(), "1234");
stream.flush();

assertEqual(cli.printNum(01234, OCT), (size_t)4);
assertEqual(stream.printerText(), "1234");
stream.flush();

assertEqual(cli.printNum(0x123, BIN), (size_t)9);
assertEqual(stream.printerText(), "100100011");
stream.flush();

assertEqual(cli.printNum(0x123, BIN, 16), (size_t)16);
assertEqual(stream.printerText(), "0000000100100011");
stream.flush();

assertEqual(cli.printNum(01234, OCT, -8), (size_t)8);
assertEqual(stream.printerText(), "1234 ");
stream.flush();

assertEqual(cli.printNum(01234, OCT, 8), (size_t)8);
assertEqual(stream.printerText(), "00001234");
stream.flush();

assertEqual(cli.printNum(01234567, OCT, -4), (size_t)7);
assertEqual(stream.printerText(), "1234567");
stream.flush();
}

test(printTest, printlnNum) {
FakeStream stream;
Cli cli;
cli.begin(stream);

assertEqual(cli.printlnNum(1234), (size_t)6);
assertEqual(stream.printerText(), "1234" NL);
stream.flush();

assertEqual(cli.printlnNum(01234, OCT), (size_t)6);
assertEqual(stream.printerText(), "1234" NL);
stream.flush();

assertEqual(cli.printlnNum(0x123, BIN), (size_t)11);
assertEqual(stream.printerText(), "100100011" NL);
stream.flush();

assertEqual(cli.printlnNum(0x123, BIN, 16), (size_t)18);
assertEqual(stream.printerText(), "0000000100100011" NL);
stream.flush();

assertEqual(cli.printlnNum(01234, OCT, -8), (size_t)10);
assertEqual(stream.printerText(), "1234 " NL);
stream.flush();

assertEqual(cli.printlnNum(01234, OCT, 8), (size_t)10);
assertEqual(stream.printerText(), "00001234" NL);
stream.flush();

assertEqual(cli.printlnNum(01234567, OCT, -4), (size_t)9);
assertEqual(stream.printerText(), "1234567" NL);
stream.flush();
}

test(printTest, printStr) {
FakeStream stream;
Cli cli;
Expand Down
Loading

0 comments on commit cd280ce

Please sign in to comment.