Skip to content

Commit

Permalink
Merge pull request #9 from pauloavelar/feature/mvp
Browse files Browse the repository at this point in the history
Test coverage
  • Loading branch information
pauloavelar authored Mar 14, 2021
2 parents 7e69f5e + dea50ff commit 8e27aa7
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 49 deletions.
51 changes: 33 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,28 @@ n.GetPaddedUint8() // parses the value as uint8 and pads it if too small
// all available types: bool, uint8, uint16, uint32, uint64, string, time.Time and Nodes
```

### Supported types

| Type | Max Length (bytes) | Notes |
|----------|-------------------:|-------------------------------------------------------------------|
| `bool` | 1 | Any **non-zero** value is treated as `true` |
| `uint8` | 1 | |
| `uint16` | 2 | |
| `uint32` | 4 | |
| `uint64` | 8 | |
| `Time` | 8 | Value is parsed as padded `uint64` and then as **Unix** (seconds) |
| `string` | **Unlimited** | Value is parsed as **UTF-8** |
| `Nodes` | **Unlimited** | |

> If the **value** is bigger than the **max length**, only the first _n_ bytes are used.
## Important details

### Tags are non-unique in TLV messages

When parsing a value to multiple nodes, tags can be **repeated** and will be returned by
the parser. Use `Nodes#GetByTag(tlv.Tag)` and `Nodes#GetFirstByTag(tlv.Tag)` to fetch **all**
or **one** node, respectively.
When parsing a value to multiple nodes, tags can be **repeated** and will be returned by the parser.
Use `Nodes#GetByTag(tlv.Tag)` and `Nodes#GetFirstByTag(tlv.Tag)` to fetch **all** or **one** node,
respectively.

#### Example:

Expand All @@ -89,30 +104,29 @@ message:
### The parser supports multiple root level messages
After reading a TLV-encoded message from a byte-array, when using `tlv.ParseBytes([]byte)`
the parser will continue reading the array until it reaches the end. The returned structure
will have **all the nodes** found in the payload.
After reading a TLV-encoded message from a byte-array, when using `tlv.ParseBytes([]byte)` the parser
will continue reading the array until it reaches the end. The returned structure will have **all the
nodes** found in the payload.

> ⚠️ The parser works in an all or none strategy when dealing with multiple messages.
> ⚠️  The parser works in an all or none strategy when dealing with multiple messages.

## Caveats

### No bit parity or checksum

The encoding scheme itself does *not* provide any **bit parity** or **checksum** to ensure
the integrity of received payloads. It is up to the upper layer or to the payload design
to add these features.
The encoding scheme itself does *not* provide **bit parity** or **checksum** to ensure the integrity
of received payloads. It is up to the upper layer or to the payload design to add these features.

### Errors with multiple messages are hard to pinpoint

The bigger the payload, more likely errors will *not* be identified by the parser. The
**only** failproof hint of a malformed payload is a mismatch between the read length and
the remaining bytes in the stream. When that happens, a reading error may have happened
*anywhere* in the payload, which means none of it can be trusted.
The bigger the payload, more likely errors will *not* be identified by the parser. The **only**
failproof hint of a malformed payload is a mismatch between the read length and the remaining bytes
in the stream. When that happens, a reading error may have happened *anywhere* in the payload, which
means none of it can be trusted.

> If by the end of the byte stream there is a mismatch between the provided length and
> the remaining bytes, the whole payload is invalidated, and the parser will return an
> error -- regardless of how many successful messages it has read.
> ⚠️  If by the end of the stream there is a mismatch between the **provided length** and the
> **remaining bytes**, the whole payload is invalidated, and the parser will return an error,
> **regardless of how many successful messages it has read**.

## Roadmap

Expand All @@ -121,5 +135,6 @@ the remaining bytes in the stream. When that happens, a reading error may have h

## Changelog

* **`v1.0.0`** (2021-03-14)
* **`v1.0.0-alpha1`** (2021-03-14)
* First release with basic parsing support
* ⚠️  Methods and structs may change completely
2 changes: 1 addition & 1 deletion tlv/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func newPushNotification(nodes Nodes) pushNotification {
}
if timestamp, ok := nodes.GetFirstByTag(tagTimestamp); ok {
ts, _ := timestamp.GetDate()
pn.Timestamp = ts.UTC()
pn.Timestamp = ts
}

return pn
Expand Down
60 changes: 30 additions & 30 deletions tlv/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,36 @@ func (n Node) GetNodes() (Nodes, error) {
return ParseBytes(n.Value)
}

// GetBool parses the value as boolean if it has enough bytes
func (n Node) GetBool() (res, ok bool) {
if len(n.Value) < sizes.Bool {
return false, false
}

return n.Value[0] != 0, true
}

// GetPaddedBool parses the value as boolean regardless of its size
func (n Node) GetPaddedBool() bool {
res, _ := n.GetBool()
return res
}

// GetString parses the value as UTF8 text
func (n Node) GetString() string {
return string(n.Value)
}

// GetDate parses the value as date if it has enough bytes
func (n Node) GetDate() (res time.Time, ok bool) {
if len(n.Value) == 0 {
return res, false
}

epoch := n.GetPaddedUint64()
return time.Unix(int64(epoch), 0).UTC(), true
}

// GetUint8 parses the value as uint8
func (n Node) GetUint8() (res uint8, ok bool) {
if len(n.Value) < sizes.Uint8 {
Expand Down Expand Up @@ -91,33 +121,3 @@ func (n Node) GetPaddedUint64() uint64 {
padding := utils.GetPadding(sizes.Uint64, len(n.Value))
return parser.Uint64(append(padding, n.Value...))
}

// GetString parses the value as UTF8 text
func (n Node) GetString() string {
return string(n.Value)
}

// GetBool parses the value as boolean if it has enough bytes
func (n Node) GetBool() (res, ok bool) {
if len(n.Value) < sizes.Bool {
return false, false
}

return n.Value[0] != 0, true
}

// GetPaddedBool parses the value as boolean regardless of its size
func (n Node) GetPaddedBool() bool {
res, _ := n.GetBool()
return res
}

// GetDate parses the value as date if it has enough bytes
func (n Node) GetDate() (res time.Time, ok bool) {
if len(n.Value) == 0 {
return res, false
}

epoch := n.GetPaddedUint64()
return time.Unix(int64(epoch), 0), true
}
Loading

0 comments on commit 8e27aa7

Please sign in to comment.