0.3.0 SHEX

This commit is contained in:
rob tillaart 2022-05-31 17:39:54 +02:00
parent 2e0ef7f994
commit fdb321e7e9
10 changed files with 358 additions and 86 deletions

View File

@ -13,16 +13,18 @@ Arduino library to generate hex dump over Serial (any stream).
## Description
SHEX is a simple library that wraps the Serial output side (by default) and
### SHEX
**SHEX** is a simple library that wraps the Serial output side (by default) and
generates an hex dump of all data that is printed. 16 bytes per row.
The default output format is
The default output format is (changed since 0.3.0)
```
0xABCDABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
0xABCDABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
0xABCDABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
0xABCDABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
```
with a separator line after each 8th line.
@ -35,38 +37,92 @@ This makes it possible to switch between the modes e.g. between 'debugging' and
One can toggle the character count at the start of the line.
### SHEXA
**SHEXA** (Serial HEX Ascii) is a derived class from **SHEX** that also
displays a column with printable characters.
The default output format is
```
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx abcdefgh ijklmnop
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx abcdefgh ijklmnop
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx abcdefgh ijklmnop
ABCD xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
```
To print this ASCII column extra RAM and code is used.
Therefore this is made a derived class from **SHEX**.
Furthermore **SHEXA** has a function **flushASCII()** to flush the ASCII column to output.
This is might be needed when HEX output is restarted.
## Interface
The **SHEX** and **SHEXA** share most of their interface.
### Defines
To be adjusted via command line (or in SHEX.h file)
- **SHEX_DEFAULT_LENGTH 16**
- **SHEX_MAX_LENGTH 32**
- **SHEX_DEFAULT_LENGTH 16**
- **SHEX_MAX_LENGTH 32**
- **SHEX_MIN_LENGTH 4**
- **SHEX_COUNTER_DIGITS 4**
- **SHEX_DEFAULT_VTAB 8**
### Constructor + Core
- **SHEX(Print \* stream = &Serial, uint8_t length = SHEX_DEFAULT_LENGTH)** Constructor, optional set the number of bytes per line.
- **SHEX(Print \* stream = &Serial, uint8_t length = SHEX_DEFAULT_LENGTH)** Constructor,
optional set the number of bytes per line.
default 16 bytes per line, forced multiple of 4, max SHEX_MAX_LENGTH = 32.
- **size_t write(uint8_t c)** implements the Print interface.
### Modifiers
### Output Modifiers
- **void setHEX(bool hexOutput = true)** switch between modi, HEX (true) or pass through (false).
- **bool getHEX()** returns mode set above.
- **void setBytesPerLine(uint8_t length = SHEX_DEFAULT_LENGTH)** idem, default 16 bytes per line, forced multiple of 4, max SHEX_MAX_LENGTH = 32.
- **void setBytesPerLine(uint8_t length = SHEX_DEFAULT_LENGTH)** idem, default 16 bytes per line,
forced multiple of 4, max SHEX_MAX_LENGTH = 32.
- **uint8_t getBytesPerLine()** returns number of bytes per line.
### Separator
- **void setSeparator(char c = ' ')** set the separator character, default a space.
Some people like a dot '.', or a tab '\t'. Feel free to experiment.
- **char getSeparator()** return the separator character set.
- **void setCountFlag(bool flag = true)** show the character count at begin of every line.
- **bool getCountFlag()** return flag set.
### Counter
- **void setCountDigits(uint8_t digits)** set the length of the counter, 8 or 6 or 4 (default).
Other numbers will be rounded up to 4, 6 or 8.
- **uint8_t getCountDigits()** returns idem.
- **void restartOutput()** restarts the counter from 0 and a new line.
Is automatically called if a setting is modified like **bytesPerLine**
**setVTAB** or **setCountDigits**
- **uint32_t getCounter()** return internal counter.
### VTAB
- **void setVTAB(uint8_t vtab = SHEX_DEFAULT_VTAB)** set the vertical separator line.
- **uint8_t getVTAB()** return the current vertical separator line.
### SHEXA specific
- **void flushASCII()** allows the user to flush the ASCII column to output.
This is typically used after a setting is changed, which causes a restart of
the HEX output. Best if followed by a **restartOutput()**.
_Note: it is not ideal but workable. This might change in the future._
## Operational
See examples.
@ -77,19 +133,29 @@ See examples.
Although no follow up release is planned, some ideas are kept here
so they won't get lost.
- Optional ASCII column in the output format ( . if not printable) e.g.
```
0xABCDABCD xx xx xx xx xx xx xx xx xx c.cc c..c
```
needs a line buffer to do that (double loop)
### Must
- optimize code
- print vs write
- more testing
- performance measurement
- different platforms.
- different streams incl SW Serial
### Could
- investigate **flushASCII()** for better solutions.
- HEX reader: **RHEX** converts dump format to a normal stream again.
- separate library.
### Wont
- line buffering for faster output (e.g Ethernet and SD card)
could it support **write(array, length)** call ?
- header line: runtime configurable; optional combined with separator
and after how many lines the header should repeat)
**header(str, lines)**; ???
- HEX reader: converts dump format to a normal stream again.
- better name for the class? - streamHex
- **showByteCount(bool)** is a better name than **setCountFlag()**
could it support **write(array, length)** call.
needs quite a rewrite..
Needs a big buffer: ~150 bytes. (counter 8 + hex 96 + printable 32 + extra sep)
- header line: runtime configurable;
optional combined with separator
and after how many lines the header should repeat
**header(str, lines)** ?
Can also be done by just calling Serial.print.

View File

@ -1,7 +1,7 @@
//
// FILE: SHEX.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.3
// VERSION: 0.3.0
// PURPOSE: Arduino library to generate hex dump over Serial
// DATE: 2020-05-24
// URL: https://github.com/RobTillaart/SHEX
@ -13,8 +13,17 @@
// 0.2.1 2021-12-28 update library.json, readme, license, minor edits
// 0.2.2 2022-05-27 fix #6 set default length
// add defines SHEX_DEFAULT_LENGTH + SHEX_MAX_LENGTH
// 0.2.3 2022-5-28 add setVTAB(vtab) getVTAB()
// 0.2.3 2022-05-28 add setVTAB(vtab) getVTAB()
// add define SHEX_DEFAULT_VTAB
// 0.3.0 2022-05-28 breaking!
// change default HEX output instead of pass through.
// add get / setCountDigits() =>
// #digits of count 4, 6 or 8 (4 = default)
// replaces get / setCounterFlag()
// add define SHEX_COUNTER_DIGITS + SHEX_MIN_LENGTH
// add restartOutput() and getCounter()
// add SHEXA class for ASCII column output
// add SHEXA::flushASCII().
#include "SHEX.h"
@ -30,16 +39,20 @@ SHEX::SHEX(Print* stream, uint8_t length)
{
_length = SHEX_MAX_LENGTH;
}
if (_length < SHEX_MIN_LENGTH)
{
_length = SHEX_MIN_LENGTH;
}
};
void SHEX::reset()
{
_hexOutput = false;
_hexOutput = true;
_length = SHEX_DEFAULT_LENGTH;
_charCount = 0;
_separator = ' ';
_countFlag = true;
_digits = SHEX_COUNTER_DIGITS;
_vtab = SHEX_DEFAULT_VTAB;
}
@ -50,24 +63,28 @@ void SHEX::reset()
//
size_t SHEX::write(uint8_t c)
{
// PASS THROUGH MODE
// PASS THROUGH MODE
if (_hexOutput == false) return _stream->write(c);
// HEX MODE
// handle end of line and position number
// HEX MODE
// handle end of line and position number
if ((_charCount % _length) == 0)
{
// insert ASCII array here
_stream->println();
// separator line every _vtab lines
// separator line every _vtab (default 8) lines
if ((_charCount % (_length * _vtab)) == 0)
{
_stream->println();
}
// next line
if (_countFlag)
// next line
if (_digits > 0)
{
uint32_t mask = 0xF0000000;
uint32_t mask = 0xF000;
if (_digits > 4) mask = 0xF00000;
if (_digits > 6) mask = 0xF0000000;
while((mask > 0xF) && (mask & _charCount) == 0)
{
_stream->print('0');
@ -78,7 +95,7 @@ size_t SHEX::write(uint8_t c)
}
}
// Print char as HEX
// Print char as HEX
if (c < 0x10) _stream->print('0');
_stream->print(c, HEX);
_stream->print(_separator);
@ -92,9 +109,7 @@ size_t SHEX::write(uint8_t c)
void SHEX::setHEX(bool hexOutput)
{
_hexOutput = hexOutput;
_charCount = 0;
// prevent change in middle of line
_stream->println();
restartOutput();
};
@ -106,20 +121,109 @@ void SHEX::setBytesPerLine(const uint8_t length)
{
_length = SHEX_MAX_LENGTH;
}
_charCount = 0;
// prevent change in middle of line
_stream->println();
if (_length < SHEX_MIN_LENGTH)
{
_length = SHEX_MIN_LENGTH;
}
restartOutput();
}
void SHEX::setVTAB(uint8_t vtab)
{
_vtab = vtab;
_charCount = 0;
// prevent change in middle of line
_stream->println();
restartOutput();
};
void SHEX::setCountDigits(uint8_t digits)
{
_digits = digits;
if (_digits == 0) return;
if (_digits < 4) _digits = 4;
if (_digits > 8) _digits = 8;
restartOutput();
};
void SHEX::restartOutput()
{
// prevent change in middle of line
_charCount = 0;
_stream->println();
}
///////////////////////////////////////////////////
//
// SHEXA
//
SHEXA::SHEXA(Print* stream, uint8_t length) : SHEX(stream, length)
{
}
size_t SHEXA::write(uint8_t c)
{
// PASS THROUGH MODE
if (_hexOutput == false) return _stream->write(c);
// HEX MODE
// handle end of line and position number
if ((_charCount % _length) == 0)
{
// printable ASCII column
if (_charCount != 0) flushASCII();
_stream->println();
// separator line every _vtab (default 8) lines
if ((_charCount % (_length * _vtab)) == 0)
{
_stream->println();
}
// next line
if (_digits > 0)
{
uint32_t mask = 0xF000;
if (_digits > 4) mask = 0xF00000;
if (_digits > 6) mask = 0xF0000000;
while((mask > 0xF) && (mask & _charCount) == 0)
{
_stream->print('0');
mask >>= 4;
}
_stream->print(_charCount, HEX);
_stream->print('\t');
}
}
// Print char as HEX
if (c < 0x10) _stream->print('0');
_stream->print(c, HEX);
_stream->print(_separator);
// Store in _txtbuf
_txtbuf[_charCount % _length] = isPrintable(c) ? c : '.';
_charCount++;
if ((_charCount % 4) == 0) _stream->print(_separator);
return 1;
}
void SHEXA::flushASCII()
{
int len = _charCount % _length;
if (len == 0) len = _length;
// else print about (_length - len) * 3 of spaces ...
for (uint8_t i = 0; i < len;)
{
_stream->write(_txtbuf[i++]);
if ((i % 8) == 0)_stream->print(" ");
}
}
// -- END OF FILE --

View File

@ -2,7 +2,7 @@
//
// FILE: SHEX.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.3
// VERSION: 0.3.0
// PURPOSE: Arduino library to generate hex dump over Serial
// DATE: 2020-05-24
// URL: https://github.com/RobTillaart/SHEX
@ -12,11 +12,12 @@
#include "Print.h"
#define SHEX_LIB_VERSION (F("0.2.3"))
#define SHEX_LIB_VERSION (F("0.3.0"))
#define SHEX_DEFAULT_LENGTH 16
#define SHEX_MAX_LENGTH 32
#define SHEX_MIN_LENGTH 4
#define SHEX_COUNTER_DIGITS 4
#define SHEX_DEFAULT_VTAB 8
@ -25,34 +26,60 @@ class SHEX: public Print
public:
SHEX(Print* stream = &Serial, uint8_t length = SHEX_DEFAULT_LENGTH);
void reset();
void reset();
size_t write(uint8_t c);
size_t write(uint8_t c);
void setHEX(bool hexOutput = true);
bool getHEX() { return _hexOutput; };
void setHEX(bool hexOutput = true);
bool getHEX() { return _hexOutput; };
void setBytesPerLine(const uint8_t length = SHEX_DEFAULT_LENGTH);
uint8_t getBytesPerLine() { return _length; };
void setBytesPerLine(const uint8_t length = SHEX_DEFAULT_LENGTH);
uint8_t getBytesPerLine() { return _length; };
void setSeparator(char c = ' ') { _separator = c; };
char getSeparator() { return _separator; };
void setSeparator(char c = ' ') { _separator = c; };
char getSeparator() { return _separator; };
void setCountFlag(bool flag = true) { _countFlag = flag; };
bool getCountFlag() {return _countFlag; };
// must be 0, 4, 6 or 8
void setCountDigits(uint8_t digits = SHEX_COUNTER_DIGITS);
uint8_t getCountDigits() { return _digits; }
// restarts the output - use with care
void restartOutput();
uint32_t getCounter() { return _charCount; };
void setVTAB(uint8_t vtab = SHEX_DEFAULT_VTAB);
uint8_t getVTAB() { return _vtab; };
void setVTAB(uint8_t vtab = SHEX_DEFAULT_VTAB);
uint8_t getVTAB() { return _vtab; };
private:
Print * _stream;
bool _hexOutput = false;
bool _countFlag = true;
protected:
Print * _stream = &Serial;
bool _hexOutput = true;
uint8_t _length = SHEX_DEFAULT_LENGTH;
uint8_t _vtab = SHEX_DEFAULT_VTAB;
uint32_t _charCount = 0;
char _separator = ' ';
uint32_t _charCount = 0;
uint32_t _digits = SHEX_COUNTER_DIGITS;
};
///////////////////////////////////////////////////
//
// SHEXA shows also ASCII dump
// derived class as it takes extra RAM
//
class SHEXA : public SHEX
{
public:
SHEXA(Print* stream = &Serial, uint8_t length = SHEX_DEFAULT_LENGTH);
size_t write(uint8_t c);
// flushes the ASCII column to output; not ideal but workable for now.
// use with care
void flushASCII();
protected:
uint8_t _txtbuf[SHEX_MAX_LENGTH];
bool _showtxt = true;
};

View File

@ -0,0 +1,47 @@
//
// FILE: SHEXA_demo.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo SHEX hex dump class
// DATE: 2022-05-28
#include "SHEX.h"
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("SHEX_LIB_VERSION: ");
Serial.println(SHEX_LIB_VERSION);
Serial.println("\nNORMAL\n");
for (int i = 0; i < 64; i++)
{
Serial.print(random(127));
Serial.print('\t');
if ((i % 16) == 0) Serial.println();
}
Serial.println("\n\nSHEX ASCII TEST");
SHEXA shex(&Serial, 16);
shex.setCountDigits(5);
shex.print("abcdefghijklmnopqrstuvwxyz");
shex.flushASCII();
shex.restartOutput();
shex.print("abcdefghijklmnopqrstuv");
shex.flushASCII();
shex.restartOutput();
Serial.println("\n Done...\n");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -23,21 +23,26 @@ void setup()
if ((i % 16) == 0) Serial.println();
}
Serial.println("\n\nSHEX ASCII TEST");
SHEX shex(&Serial, 16);
shex.setCountDigits(5);
shex.print("abcdefghijklmnopqrstuvwxyz");
shex.restartOutput();
shex.print("abcdefghijklmnopqrstuv");
shex.restartOutput();
Serial.println("\n\nSHEX\n");
SHEX shex(&Serial, 16);
for (int i = 0; i < 300; i++)
{
char c = random(150);
shex.print(c);
}
Serial.println("\n\nSHEX modified\n");
shex.setBytesPerLine(60);
shex.setSeparator('-');
shex.setCountFlag(false);
shex.setCountDigits(0);
for (int i = 0; i < 600; i++)
{
@ -55,4 +60,3 @@ void loop()
// -- END OF FILE --

View File

@ -26,6 +26,7 @@ void setup()
for (uint8_t vtab = 0; vtab < 6; vtab++)
{
// shex.flushASCII(); when using SHEXA
shex.setVTAB(vtab);
Serial.print("\n\nSHEX VTAB ");
Serial.println(shex.getVTAB());

View File

@ -3,6 +3,7 @@
# Data types (KEYWORD1)
SHEX KEYWORD1
SHEXA KEYWORD1
# Methods and Functions (KEYWORD2)
@ -17,8 +18,8 @@ getBytesPerLine KEYWORD2
setSeparator KEYWORD2
getSeparator KEYWORD2
setCountFlag KEYWORD2
getCountFlag KEYWORD2
setCountDigits KEYWORD2
getCountDigits KEYWORD2
setVTAB KEYWORD2
getVTAB KEYWORD2
@ -31,5 +32,6 @@ SHEX_DEFAULT_LENGTH LITERAL1
SHEX_MAX_LENGTH LITERAL1
SHEX_MIN_LENGTH LITERAL1
SHEX_COUNTER_DIGITS LITERAL1
SHEX_DEFAULT_VTAB LITERAL1

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/SHEX.git"
},
"version": "0.2.3",
"version": "0.3.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,9 +1,9 @@
name=SHEX
version=0.2.3
version=0.3.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library to generate hex dump over Serial
paragraph=
paragraph=SHEXA derived class with ASCII column.
category=Data Processing
url=https://github.com/RobTillaart/SHEX
architectures=*

View File

@ -50,6 +50,7 @@ unittest(test_constants)
assertEqual(16, SHEX_DEFAULT_LENGTH);
assertEqual(32, SHEX_MAX_LENGTH);
assertEqual(4, SHEX_MIN_LENGTH);
assertEqual(4, SHEX_COUNTER_DIGITS);
assertEqual(8, SHEX_DEFAULT_VTAB);
}
@ -57,9 +58,7 @@ unittest(test_constants)
unittest(test_set_hex)
{
SHEX shex;
assertFalse(shex.getHEX());
shex.setHEX();
assertTrue(shex.getHEX());
shex.setHEX(false);
@ -101,15 +100,37 @@ unittest(test_separator)
}
unittest(test_countFlag)
unittest(test_count_digits)
{
SHEX shex;
assertTrue(shex.getCountFlag());
shex.setCountFlag(false);
assertFalse(shex.getCountFlag());
shex.setCountFlag();
assertTrue(shex.getCountFlag());
assertEqual(SHEX_COUNTER_DIGITS, shex.getCountDigits());
shex.setCountDigits(0);
assertEqual(0, shex.getCountDigits());
shex.setCountDigits(1);
assertEqual(4, shex.getCountDigits());
shex.setCountDigits(2);
assertEqual(4, shex.getCountDigits());
shex.setCountDigits(3);
assertEqual(4, shex.getCountDigits());
shex.setCountDigits(4);
assertEqual(4, shex.getCountDigits());
shex.setCountDigits(6);
assertEqual(6, shex.getCountDigits());
shex.setCountDigits(8);
assertEqual(8, shex.getCountDigits());
// should be truncated to 8 == uint32_t counter)
shex.setCountDigits(9);
assertEqual(8, shex.getCountDigits());
}