Je dois convertir une int
en un byte[]
. Une façon de le faire est d'utiliser BitConverter.GetBytes()
. Mais je ne sais pas si cela correspond à la spécification suivante:
Un entier signé XDR est une donnée 32 bits qui code un entier dans la plage [-2147483648,2147483647]. Le nombre entier est représenté dans notation du complément à deux. Les octets les plus significatifs et les moins significatifs sont 0 et 3, respectivement. Les entiers sont déclarés comme suit:
Source: RFC1014 3.2
Comment pourrais-je faire une transformation d'int à octet qui satisferait à la spécification ci-dessus?
Le RFC essaie simplement de dire qu'un entier signé est un entier normal de 4 octets avec des octets ordonnés en big-endian.
Maintenant, vous travaillez probablement sur une machine little-endian et BitConverter.GetBytes()
vous donnera le byte[]
inversé. Pour que vous puissiez essayer:
int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
Array.Reverse(intBytes);
byte[] result = intBytes;
Pour que le code soit plus portable, vous pouvez le faire comme suit:
int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
if (BitConverter.IsLittleEndian)
Array.Reverse(intBytes);
byte[] result = intBytes;
Voici une autre façon de procéder: comme nous le savons tous, 1x octet = 8x bits et un entier "normal" (int32) contient 32 bits (4 octets). Nous pouvons utiliser l'opérateur >> pour décaler les bits vers la droite (l'opérateur >> ne change pas de valeur.)
int intValue = 566;
byte[] bytes = new byte[4];
bytes[0] = (byte)(intValue >> 24);
bytes[1] = (byte)(intValue >> 16);
bytes[2] = (byte)(intValue >> 8);
bytes[3] = (byte)intValue;
Console.WriteLine("{0} breaks down to : {1} {2} {3} {4}",
intValue, bytes[0], bytes[1], bytes[2], bytes[3]);
BitConverter.GetBytes(int)
fait presque ce que vous voulez, sauf que l'endianité est fausse.
Vous pouvez utiliser la méthode IPAddress.HostToNetwork pour échanger les octets dans la valeur entière avant d'utiliser BitConverter.GetBytes
ou utiliser la classe EndianBitConverter de Jon Skeet . Les deux méthodes font ce qu'il faut (tm) en ce qui concerne la portabilité.
int value;
byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value));
Quand je regarde cette description, j'ai l’impression que cet entier xdr n’est qu’un entier big-endian "standard", mais il est exprimé de la manière la plus obscure. La notation du complément à deux est mieux connue sous le nom de U2, et c'est ce que nous utilisons sur les processeurs actuels. L'ordre des octets indique qu'il s'agit d'une notation big-endian .
Donc, pour répondre à votre question, vous devriez inverser les éléments de votre tableau (0 <-> 3, 1 <-> 2), car ils sont codés en little-endian. Pour vous en assurer, vous devez d'abord vérifier BitConverter.IsLittleEndian
pour voir sur quel ordinateur vous travaillez.
Si vous souhaitez des informations plus générales sur les différentes méthodes de représentation des nombres, dont le complément de deux, consultez:
Complément des deux et Représentation des nombres signés sur Wikipedia
Pourquoi tout ce code dans les exemples ci-dessus ...
Une structure avec une mise en page explicite agit dans les deux sens et n’a aucun impact sur les performances.
Mise à jour: Puisqu'il y a une question sur la façon de gérer l'endianisme, j'ai ajouté une interface qui illustre comment l'abstraction. Une autre structure d'implémentation peut traiter le cas contraire
public interface IIntToByte
{
Int32 Int { get; set;}
byte B0 { get; }
byte B1 { get; }
byte B2 { get; }
byte B3 { get; }
}
[StructLayout(LayoutKind.Explicit)]
public struct IntToByteLE : UserQuery.IIntToByte
{
[FieldOffset(0)]
public Int32 IntVal;
[FieldOffset(0)]
public byte b0;
[FieldOffset(1)]
public byte b1;
[FieldOffset(2)]
public byte b2;
[FieldOffset(3)]
public byte b3;
public Int32 Int {
get{ return IntVal; }
set{ IntVal = value;}
}
public byte B0 => b0;
public byte B1 => b0;
public byte B2 => b0;
public byte B3 => b0;
}