// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Iot.Device.Arduino
{
///
/// This class is used to encode larger chunks of data for transmission using the Firmata protocol.
/// It converts each block of 7 bytes into a block of 8 bytes, keeping the top bit 0.
///
public static class Encoder7Bit
{
///
/// Calculates the number of bytes generated during decode (the result is smaller than the input)
///
public static int Num8BitOutBytes(int inputBytes)
{
// Equals * 7 / 8
return (int)Math.Floor(((inputBytes) * 7) / 8.0);
}
///
/// Calculates the number of bytes required for the 7-byte encoding
///
public static int Num7BitOutBytes(int inputBytes)
{
return (int)Math.Ceiling(((inputBytes) * 8.0) / 7);
}
///
/// Encode a sequence of bytes
///
/// The data to encode
/// The encoded data
public static byte[] Encode(ReadOnlySpan data)
{
return Encode(data, 0, data.Length);
}
///
/// Encodes a sequence of bytes
///
/// The data to encode
/// The start index in the data
/// The length of the data
/// The encoded data
public static byte[] Encode(ReadOnlySpan data, int startIndex, int length)
{
int shift = 0;
byte[] retBytes = new byte[Num7BitOutBytes(length)];
int index = 0;
int previous = 0;
for (int i = startIndex; i < startIndex + length; i++)
{
if (shift == 0)
{
retBytes[index] = (byte)(data[i] & 0x7f);
index++;
shift++;
previous = data[i] >> 7;
}
else
{
retBytes[index] = (byte)(((data[i] << shift) & 0x7f) | previous);
index++;
if (shift == 6)
{
retBytes[index] = (byte)(data[i] >> 1);
index++;
shift = 0;
}
else
{
shift++;
previous = data[i] >> (8 - shift);
}
}
}
if (shift > 0)
{
// Write remainder
retBytes[index] = (byte)previous;
}
return retBytes;
}
///
/// Decodes the given data sequence
///
/// The data to decode
/// The decoded data
public static byte[] Decode(ReadOnlySpan inData)
{
byte[] outBytes = new byte[Num8BitOutBytes(inData.Length)];
for (int i = 0; i < outBytes.Length; i++)
{
int j = i << 3;
int pos = j / 7;
int shift = j % 7;
outBytes[i] = (byte)((inData[pos] >> shift) | ((inData[pos + 1] << (7 - shift)) & 0xFF));
}
return outBytes;
}
}
}