Click here to Skip to main content
15,879,095 members
Articles / Programming Languages / C#

Encoding / Decoding 7 bit User Data for SMS PDU (PDU Bit Packer)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
10 Oct 2012CPOL3 min read 186.6K   10.7K   15  
A library for packing / unpacking 7bit user data for SMS according to the GSM 03.38 standards.
/************************************************************************
 * Copyright © 2012 Lama Barri
 *
 * Pdu Bit Packer Library is free software. You can redistribute it and/or modify
 * it under the terms of the Code Project Open License (COPL)
 *
 * Author: Lama Barri
 *
 ************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PduBitPackerDemo
{
    public class PduBitPacker
    {
        #region Fields

        private static byte[] _encodeMask;
        private static byte[] _decodeMask;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of the BytePacker class.
        /// </summary>
        static PduBitPacker()
        {
            _encodeMask = new byte[] { 1, 3, 7, 15, 31, 63, 127 };
            _decodeMask = new byte[] { 128, 192, 224, 240, 248, 252, 254 };
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Packs an unpacked 7 bit array to an 8 bit packed array according to the GSM
        /// protocol.
        /// </summary>
        /// <param name="unpackedBytes">The byte array that should be packed.</param>
        /// <returns>The packed bytes array.</returns>
        public static byte[] PackBytes(byte[] unpackedBytes)
        {
            return PackBytes(unpackedBytes, false, ' ');
        }

        /// <summary>
        /// Packs an unpacked 7 bit array to an 8 bit packed array according to the GSM
        /// protocol.
        /// </summary>
        /// <param name="unpackedBytes">The byte array that should be packed.</param>
        /// <param name="replaceInvalidChars">Indicates if invalid characters should be replaced by a '?' character.</param>
        /// <returns>The packed bytes array.</returns>
        public static byte[] PackBytes(byte[] unpackedBytes, bool replaceInvalidChars)
        {
            return PackBytes(unpackedBytes, replaceInvalidChars, '?');
        }

        /// <summary>
        /// Packs an unpacked 7 bit array to an 8 bit packed array according to the GSM
        /// protocol.
        /// </summary>
        /// <param name="unpackedBytes">The byte array that should be packed.</param>
        /// <param name="replaceInvalidChars">Indicates if invalid characters should be replaced by the default character.</param>
        /// <param name="defaultChar">The character that replaces invalid characters.</param>
        /// <returns>The packed bytes array.</returns>
        public static byte[] PackBytes(byte[] unpackedBytes, bool replaceInvalidChars, char defaultChar)
        {
            byte defaultByte = (byte)defaultChar;
            byte[] shiftedBytes = new byte[unpackedBytes.Length - (unpackedBytes.Length / 8)];

            int shiftOffset = 0;
            int shiftIndex = 0;

            // Shift the unpacked bytes to the right according to the offset (position of the byte)
            foreach (byte b in unpackedBytes)
            {
                byte tmpByte = b;

                // Handle invalid characters (bytes out of range)
                if (tmpByte > 127)
                {
                    if (!replaceInvalidChars)
                    {
                        // throw exception and exit the method
                        throw new Exception("Invalid character detected: " + tmpByte.ToString("X2"));
                    }
                    else
                    {
                        tmpByte = defaultByte;
                    }
                }

                // Perform the byte shifting
                if (shiftOffset == 7)
                {
                    shiftOffset = 0;
                }
                else
                {
                    shiftedBytes[shiftIndex] = (byte)(tmpByte >> shiftOffset);
                    shiftOffset++;
                    shiftIndex++;
                }
            }

            int moveOffset = 1;
            int moveIndex = 1;
            int packIndex = 0;
            byte[] packedBytes = new byte[shiftedBytes.Length];

            // Move the bits to the appropriate byte (pack the bits)
            foreach (byte b in unpackedBytes)
            {
                if (moveOffset == 8)
                {
                    moveOffset = 1;
                }
                else
                {
                    if (moveIndex != unpackedBytes.Length)
                    {
                        // Extract the bits to be moved
                        int extractedBitsByte = (unpackedBytes[moveIndex] & _encodeMask[moveOffset - 1]);
                        // Shift the extracted bits to the proper offset
                        extractedBitsByte = (extractedBitsByte << (8 - moveOffset));
                        // Move the bits to the appropriate byte (pack the bits)
                        int movedBitsByte = (extractedBitsByte | shiftedBytes[packIndex]);

                        packedBytes[packIndex] = (byte)movedBitsByte;

                        moveOffset++;
                        packIndex++;
                    }
                    else
                    {
                        packedBytes[packIndex] = shiftedBytes[packIndex];
                    }
                }

                moveIndex++;
            }

            return packedBytes;
        }


        /// <summary>
        ///  Unpacks a packed 8 bit array to a 7 bit unpacked array according to the GSM
        ///  Protocol.
        /// </summary>
        /// <param name="packedBytes">The byte array that should be unpacked.</param>
        /// <returns>The unpacked bytes array.</returns>
        public static byte[] UnpackBytes(byte[] packedBytes)
        {
            byte[] shiftedBytes = new byte[(packedBytes.Length * 8) / 7];

            int shiftOffset = 0;
            int shiftIndex = 0;

            // Shift the packed bytes to the left according to the offset (position of the byte)
            foreach (byte b in packedBytes)
            {
                if (shiftOffset == 7)
                {
                    shiftedBytes[shiftIndex] = 0;
                    shiftOffset = 0;
                    shiftIndex++;
                }

                shiftedBytes[shiftIndex] = (byte)((b << shiftOffset) & 127);

                shiftOffset++;
                shiftIndex++;
            }

            int moveOffset = 0;
            int moveIndex = 0;
            int unpackIndex = 1;
            byte[] unpackedBytes = new byte[shiftedBytes.Length];

            // 
            if (shiftedBytes.Length > 0)
            {
                unpackedBytes[unpackIndex - 1] = shiftedBytes[unpackIndex - 1];
            }

            // Move the bits to the appropriate byte (unpack the bits)
            foreach (byte b in packedBytes)
            {
                if (unpackIndex != shiftedBytes.Length)
                {
                    if (moveOffset == 7)
                    {
                        moveOffset = 0;
                        unpackIndex++;
                        unpackedBytes[unpackIndex - 1] = shiftedBytes[unpackIndex - 1];
                    }

                    if (unpackIndex != shiftedBytes.Length)
                    {
                        // Extract the bits to be moved
                        int extractedBitsByte = (packedBytes[moveIndex] & _decodeMask[moveOffset]);
                        // Shift the extracted bits to the proper offset
                        extractedBitsByte = (extractedBitsByte >> (7 - moveOffset));
                        // Move the bits to the appropriate byte (unpack the bits)
                        int movedBitsByte = (extractedBitsByte | shiftedBytes[unpackIndex]);

                        unpackedBytes[unpackIndex] = (byte)movedBitsByte;

                        moveOffset++;
                        unpackIndex++;
                        moveIndex++;
                    }
                }
            }

            // Remove the padding if exists
            if (unpackedBytes[unpackedBytes.Length - 1] == 0)
            {
                byte[] finalResultBytes = new byte[unpackedBytes.Length - 1];
                Array.Copy(unpackedBytes, 0, finalResultBytes, 0, finalResultBytes.Length);

                return finalResultBytes;
            }

            return unpackedBytes;
        }


        /// <summary>
        /// Converts hex string into the equivalent byte array.
        /// </summary>
        /// <param name="hexString">The hex string to be converted.</param>
        /// <returns>The equivalent byte array.</returns>
        public static byte[] ConvertHexToBytes(string hexString)
        {
            if (hexString.Length % 2 != 0)
                return null;

            int len = hexString.Length / 2;
            byte[] array = new byte[len];

            for (int i = 0; i < array.Length; i++)
            {
                string tmp = hexString.Substring(i * 2, 2);
                array[i] = byte.Parse(tmp, System.Globalization.NumberStyles.HexNumber);
            }

            return array;
        }

        /// <summary>
        /// Converts a byte array into the equivalent hex string.
        /// </summary>
        /// <param name="byteArray">The byte array to be converted.</param>
        /// <returns>The equivalent hex string.</returns>
        public static string ConvertBytesToHex(byte[] byteArray)
        {
            if (byteArray == null)
                return "";

            StringBuilder sb = new StringBuilder();
            foreach (byte b in byteArray)
            {
                sb.Append(b.ToString("X2"));
            }

            return sb.ToString();
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Syrian Arab Republic Syrian Arab Republic
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions