Last active
March 5, 2018 09:58
Byte packer in C# (article: http://www.aspnet.cz/articles/5465-byte-packer-jak-na-binarni-datove-struktury)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
public class Program { | |
public static void Main() { | |
// Test data | |
byte[] prefix = { 0xDE, 0xAD, 0xBE, 0xEF }; // 4 bytes | |
byte[] block1 = { 0xAA, 0xAA, 0xAA, 0xAA }; // 4 bytes | |
byte[] block2 = { 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, }; // 8 bytes | |
byte[] block3 = { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, | |
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC }; // 16 bytes | |
// Pack | |
var data = BytePacker.Pack(prefix, block1, block2, block3); | |
Console.WriteLine($"Packed data ({data.Length} bytes):"); | |
Console.WriteLine(string.Join(' ', data.Select(x => x.ToString("x2")))); | |
Console.WriteLine(); | |
// Unpack | |
var blocks = BytePacker.Unpack(data, prefix.Length).ToArray(); | |
Console.WriteLine(new string('-', Console.WindowWidth - 1)); | |
Console.WriteLine(" B# | Length | Data"); | |
Console.WriteLine(new string('-', Console.WindowWidth - 1)); | |
for (int i = 0; i < blocks.Length; i++) { | |
Console.Write($" {i:X2} | {blocks[i].Length,5} B | "); | |
Console.WriteLine(string.Join(' ', blocks[i].Select(x => x.ToString("x2")))); | |
} | |
Console.WriteLine(new string('-', Console.WindowWidth - 1)); | |
Console.WriteLine($"{blocks.Sum(x => x.Length),11} B total"); | |
Console.WriteLine(); | |
} | |
} | |
public static class BytePacker { | |
public static byte[] Pack(byte[] prefix, params byte[][] blocks) { | |
// Validate arguments | |
if (blocks.Length < 2) throw new ArgumentOutOfRangeException(nameof(blocks), "Minimum number of blocks is 2."); | |
if (blocks.Length > 256) throw new ArgumentOutOfRangeException(nameof(blocks), "Maximum number of blocks is 256."); | |
// Write structure | |
using (var ms = new System.IO.MemoryStream()) { | |
using (var bw = new System.IO.BinaryWriter(ms)) { | |
// Write prefix if any | |
if (prefix != null && prefix.Length > 0) bw.Write(prefix); | |
// Write number of blocks | |
bw.Write((byte)(blocks.Length - 1)); | |
// Write block lengths | |
for (byte i = 0; i < blocks.Length - 1; i++) { | |
if (blocks[i].Length > 255) throw new ArgumentOutOfRangeException(nameof(blocks), $"Block #{i} is too long. Maximum length is 255 bytes."); | |
bw.Write((byte)blocks[i].Length); | |
} | |
// Write all blocks | |
for (byte i = 0; i < blocks.Length; i++) { | |
bw.Write(blocks[i]); | |
} | |
} | |
return ms.ToArray(); | |
} | |
} | |
public static IEnumerable<byte[]> Unpack(byte[] data, int prefixLength = 0) { | |
if (prefixLength < 0) throw new ArgumentOutOfRangeException(nameof(prefixLength)); | |
using (var ms = new System.IO.MemoryStream(data)) | |
using (var br = new System.IO.BinaryReader(ms)) { | |
// Read prefix | |
if (prefixLength > 0) yield return br.ReadBytes(prefixLength); | |
// Get block lengths | |
var numberOfBlocksExceptLast = ms.ReadByte(); | |
var lengths = br.ReadBytes(numberOfBlocksExceptLast); | |
// Read blocks except last | |
for (int i = 0; i < numberOfBlocksExceptLast; i++) { | |
yield return br.ReadBytes(lengths[i]); | |
} | |
// Read last block | |
var bytesRead = prefixLength + 1 + numberOfBlocksExceptLast + lengths.Sum(x => x); | |
var lastBlockSize = data.Length - bytesRead; | |
yield return br.ReadBytes(lastBlockSize); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment