Skip to content

Commit

Permalink
Merge pull request #7 from quentinlampin/decompress
Browse files Browse the repository at this point in the history
decompressor implementation
  • Loading branch information
quentinlampin authored Nov 22, 2022
2 parents efe8b67 + df46d25 commit 0f2fa7f
Show file tree
Hide file tree
Showing 29 changed files with 1,222 additions and 570 deletions.
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ hatch build -t wheel
pip install dist/microschc-<version>-py3-none-any.whl
```



## microSCHC, developpement plan

microSCHC aims at implementing the SCHC Compression/Decompression (C/D) and Fragmentation/Reassembly (F/R) routines described in RFC 8724 [1].
Expand Down Expand Up @@ -56,10 +54,10 @@ Current features:
- [ ] AppIID
- [ ] compute-* (e.g. UDP-checksum)
2. Decompression counteparts
- [ ] not-sent
- [ ] value-sent
- [ ] mapping-sent
- [ ] LSB
- [x] not-sent
- [x] value-sent
- [x] mapping-sent
- [x] LSB
- [ ] devIID
- [ ] AppIID
- [ ] compute-* (e.g. UDP-checksum)
Expand Down
28 changes: 23 additions & 5 deletions microschc-implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,24 @@ structure of header fields represented here-after.

The actual option requires parsing, and interpreting the `Option Delta`, `Option Length`, etc. Similarly to the types support discussion, this requires keeping track of the encoding and decoding of those interpretations into their bytes counterparts. Furthermore, how the compression residue should be computed based on the interpretation is a mystery to me (Quentin), unless simple compression actions are performed, e.g. `not-sent`. For this reason, microSCHC parser only exposes fields and their raw content (except for the integers odd case).

## 2. The Ruler
## 2. Ruler

The Ruler is in charge of the rules and their application to packets, i.e.:
- rule storing: manages a collection of rules.
- rule matching: determine if a rule applies to a packet descriptor.
- rule matching for packet descriptors: determine if a rule applies to a packet descriptor.
- rule matching for SCHC packet: determine the rule descriptor corresponding to a SCHC packet.
- packet compression: compress packets according to matching rules.
- packet decompression: decompress packets according to rules IDs.

## 2.1 Rules field descriptors order matters
## 2.1 Rule ID

microSCHC expects rule IDs to be provided as right-aligned (left zero-padding) bytes. microSCHC further expects rule ID length to be strictly greater than 0, as opposed to what's defined in [4], section 5:

```text
A length of 0 is allowed to represent an implicit rule.
```

## 2.2 Rules field descriptors order matters

The Ruler makes the assumption that rules' field descriptors are provided in the same order as in the target packet structure.
For example, if the packet fields are, in order : [`field-1`, `field-2`, `field-3`, ...], it is supposed that rules field descriptors
Expand All @@ -75,14 +84,23 @@ it is mandated that field descriptors applying to a given packet, i.e. once the
The rationale is that unordered field descriptors eventually yield fields residues in a different order than that of the source packet. This means that the order is potentially
lost at the reconstruction, leading to advert effects, including reconstructed packets different from the source packets.

## 2.2 Default Compression/Decompression rule, implementation details
## 2.3 Default Compression/Decompression rule, implementation details

microSCHC expects the default Compression/Decompression rule is provided last in the list of rules. The default rule list of fields descriptors is assumed empty and matches any packet not matched by any prior rule.

## 2.3 Variable field length
## 2.4 Variable field length

In microSCHC, a field of variable length is denoted with a 0 value of the FL attribute of the corresponding Field Descriptor.

- [1] "Scapy, packet crafting for Python2 and Python3"
- [2] "OpenSCHC: Open implementation, hackathon support, ... of the IETF SCHC protocol (compression for LPWANs), https://github.com/openschc"
- [3] "RFC 7252 The Constrained Application Protocol (CoAP), Z. Shelby et al."
- [4] "Draft: Data Model for Static Context Header Compression (SCHC)", A. Minaburo et al.

## 3. Compressor

The compressor is in charge of the compression of a packet, provided as a packet descriptor by the parser, using the rule identified by the **Ruler**.
The compression procedure is:
- compress packet fields using compression actions defined in the corresponding field descriptors of the rule.
- concatenate fields residues prepended with the rule ID, removing (left)-padding in the process.

2 changes: 1 addition & 1 deletion microschc/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.5.0'
__version__ = '0.7.0'
50 changes: 20 additions & 30 deletions microschc/actions/compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,20 @@
"""
from microschc.binary.buffer import Buffer
from microschc.rfc8724 import FieldDescriptor, MatchMapping

from math import ceil
from microschc.rfc8724 import FieldDescriptor, FieldResidue, MatchMapping


def not_sent(_: FieldDescriptor) -> FieldResidue:
def not_sent(_: FieldDescriptor) -> Buffer:
"""
`not-sent` compression action (CA): do not send the field value as it's supposed known by the receiver
"""
field_residue: FieldResidue = FieldResidue(residue=b'', length=0)
field_residue: Buffer = Buffer(content=b'', bit_length=0)
return field_residue


def value_sent(field_descriptor: FieldDescriptor) -> FieldResidue:
def value_sent(field_descriptor: FieldDescriptor) -> Buffer:
"""
`value-sent` compression action (CA): send the original value
! important note in file header about integer residues: !
Expand All @@ -46,30 +45,22 @@ def value_sent(field_descriptor: FieldDescriptor) -> FieldResidue:
at the SCHC compression residue step (see Section 7.2 of [1]), when all fields residues are computed.
"""
field_value: bytes = field_descriptor.value
field_length: int = field_descriptor.length




residue: bytes = field_value

field_residue: FieldResidue = FieldResidue(residue=residue, length=field_length)
field_residue: Buffer = field_descriptor.value
return field_residue

def mapping_sent(field_descriptor: FieldDescriptor, mapping: MatchMapping) -> FieldResidue:
def mapping_sent(field_descriptor: FieldDescriptor, mapping: MatchMapping) -> Buffer:
"""
`mapping-sent`: send the index in the mapping, at decompression the target value stored in the reverse mapping at the index is used.
`mapping-sent`: send the index in the mapping, at decompression the target value stored
in the reverse mapping at the index is used.
"""
field_value: bytes = field_descriptor.value
index: int = mapping.forward[field_value]
residue_length: int = mapping.index_length
residue_bytes: int = ceil(residue_length/8)
residue: bytes = index.to_bytes(residue_bytes, 'big')
field_residue: FieldResidue = FieldResidue(residue=residue, length=residue_length)
field_value: Buffer = field_descriptor.value
index: Buffer = mapping.forward[field_value]

field_residue: Buffer = index
return field_residue

def least_significant_bits(field_descriptor: FieldDescriptor, match_pattern_length: int) -> FieldResidue:
def least_significant_bits(field_descriptor: FieldDescriptor, bit_length: int) -> Buffer:
"""
`LSB`: send bits not included in the pattern matched by the `MSB(x)` Matching Operator.
Expand Down Expand Up @@ -108,24 +99,23 @@ def least_significant_bits(field_descriptor: FieldDescriptor, match_pattern_leng
and the byte-padded residue is : 0 0 0 0 1 1 1 0
"""
field_value: bytes = field_descriptor.value
field_length: int = field_descriptor.length
field_value: Buffer = field_descriptor.value

# assume pattern is matched, we rerieve the residue_length last bits.
# assume pattern is matched, we retrieve the residue_length last bits.
residue: bytes = b''
residue_length: int = field_length - match_pattern_length
residue_length: int = bit_length
residue_full_bytes: int = residue_length // 8
if residue_full_bytes > 0:
residue = field_value[-(residue_length // 8):]
residue = field_value.content[-(residue_length // 8):]

residue_leading_bits: int = residue_length % 8
if residue_leading_bits > 0:
residue_partial_byte: int = field_value[-(residue_length // 8 + 1)]
residue_partial_byte: int = field_value.content[-(residue_length // 8 + 1)]
bitmask = (0xff >> (8-residue_leading_bits))
leading_bits_residue: int = residue_partial_byte & bitmask
residue = leading_bits_residue.to_bytes(1, 'big') + residue

field_residue: FieldResidue = FieldResidue(residue=residue, length=residue_length)
field_residue: Buffer = Buffer(content=residue, bit_length=residue_length)
return field_residue


Expand Down
Empty file added microschc/binary/__init__.py
Empty file.
Loading

0 comments on commit 0f2fa7f

Please sign in to comment.