Skip to content

Commit

Permalink
Add initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
megabyde committed Dec 4, 2023
1 parent 13441a1 commit ac55a48
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 7 deletions.
104 changes: 100 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,103 @@
# Bootstrap for Python katas
# Hyper-optimized telemetry kata in Python

[![CI](https://github.com/Coding-Cuddles/bootstrap-python-kata/actions/workflows/main.yml/badge.svg)](https://github.com/Coding-Cuddles/bootstrap-python-kata/actions/workflows/main.yml)
[![Replit](https://img.shields.io/badge/Try%20with%20Replit-black?logo=replit)](https://replit.com/new/github/Coding-Cuddles/bootstrap-python-kata)
[![CI](https://github.com/Coding-Cuddles/hyper-optimized-telemetry-python-kata/actions/workflows/main.yml/badge.svg)](https://github.com/Coding-Cuddles/hyper-optimized-telemetry-python-kata/actions/workflows/main.yml)
[![Replit](https://img.shields.io/badge/Try%20with%20Replit-black?logo=replit)](https://replit.com/new/github/Coding-Cuddles/hyper-optimized-telemetry-python-kata)

## Overview

This is a bootstrap repository for clean code katas in Python 3 using pytest.
This kata complements [Clean Code: Advanced TDD, Ep. 20](https://cleancoders.com/episode/clean-code-episode-20).
and [Clean Code: Advanced TDD, Ep. 21](https://cleancoders.com/episode/clean-code-episode-21).

This repository contains two exercises designed to improve your skills in
test-driven development.

## Instructions

We will work on a telemetry system for a remote control car project. Bandwidth
in the telemetry system is at a premium and you have been asked to implement a
message protocol for communicating telemetry data.

Data is transmitted in a buffer (byte array). When integers are sent, the size
of the buffer is reduced by employing the protocol described below.

Each value should be represented in the smallest possible C integral type
(types of `char` and `unsigned char` are not included as the saving would be
trivial):

| From | To | Type |
|:---------------------------|:------------------------- |:-----------------|
| 4_294_967_296 | 9_223_372_036_854_775_807 | `long` |
| 2_147_483_648 | 4_294_967_295 | `unsigned int` |
| 65_536 | 2_147_483_647 | `int` |
| 0 | 65_535 | `unsigned short` |
| -32_768 | -1 | `short` |
| -2_147_483_648 | -32_769 | `int` |
| -9_223_372_036_854_775_808 | -2_147_483_649 | `long` |

The value should be converted to the appropriate number of bytes for its
assigned type. The complete buffer comprises a byte indicating the number of
additional bytes in the buffer (_prefix byte_) followed by the bytes holding
the integer (_payload bytes_).

Some of the types use an identical number of bytes (e.g. the `unsigned int` and
`int` types). Normally, they would have the same _prefix byte_, but that would
make decoding problematic. To counter this, the protocol introduces a little
trick: for signed types, their _prefix byte_ value is `256` minus the number of
additional bytes in the buffer.

Only the prefix byte and the number of following bytes indicated by the prefix
will be sent in the communication. Internally, a 9-byte buffer is used (with
trailing zeroes, as necessary) both by sending and receiving routines.

### Exercise 1

The task is to encode an integral value ready to send. Please implement the
static method `TelemetryBuffer.to_buffer()` to encode a buffer taking the
parameter passed to the method.

```python
# Type: unsigned short, bytes: 2, signed: no, prefix byte: 2
TelemetryBuffer.to_buffer(5)
# => [0x2, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]

# Type: int, bytes: 4, signed: yes, prefix byte: 256 - 4
TelemetryBuffer.to_buffer(2_147_483_647)
# => [0xfc, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x0, 0x0, 0x0]
```

### Exercise 2

The task is to decode a received buffer. Please implement the static method
`TelemetryBuffer.from_buffer()` to decode the buffer received and return the
value in the form of an integer.

```python
TelemetryBuffer.from_buffer([0xfc, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x0, 0x0, 0x0])
# => 2_147_483_647
```

If the prefix byte is of unexpected value then `0` should be returned.

## Integral numbers in C

> **Note**
>
> For type sizes, we assume a typical 64-bit system.
The C language provides a number of types that represent integers, each with
its own range of values. The ranges are determined by the storage width of the
type as allocated by the system:

| Type | Width | Minimum | Maximum |
|:---------------|:-------|:---------------------------|:--------------------------- |
| char | 8 bit | -128 | +127 |
| short | 16 bit | -32_768 | +32_767 |
| int | 32 bit | -2_147_483_648 | +2_147_483_647 |
| long | 64 bit | -9_223_372_036_854_775_808 | +9_223_372_036_854_775_807 |
| unsigned char | 8 bit | 0 | +255 |
| unsigned short | 16 bit | 0 | +65_535 |
| unsigned int | 32 bit | 0 | +4_294_967_295 |
| unsigned long | 64 bit | 0 | +18_446_744_073_709_551_615 |

## Usage

Expand All @@ -28,3 +120,7 @@ make run
```console
make test
```

## Credits and references

* <https://exercism.org/tracks/csharp/exercises/hyper-optimized-telemetry>
11 changes: 11 additions & 0 deletions bit_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class BitConverter:

@staticmethod
def get_bytes(value):
"""Convert an integer to a list of integers representing bytes."""
return list(value.to_bytes((value.bit_length() + 7) // 8, "little"))

@staticmethod
def to_int(byte_list):
"""Convert a list of integers representing bytes to an integer."""
return int.from_bytes(bytes(byte_list), "little")
8 changes: 8 additions & 0 deletions telemetry_buffer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from bit_converter import BitConverter


class TelemetryBuffer:

@classmethod
def to_buffer(cls, reading: int) -> list[int]:
return [0x2] + BitConverter.get_bytes(reading) + [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
3 changes: 0 additions & 3 deletions test_something.py

This file was deleted.

8 changes: 8 additions & 0 deletions test_telemetry_buffer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from telemetry_buffer import TelemetryBuffer


class TestToBuffer:

def test_unsigned_short(self):
expected = [0x2, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
assert TelemetryBuffer.to_buffer(5) == expected

0 comments on commit ac55a48

Please sign in to comment.